1import { promptAsync } from '../../../utils/prompts';
2import { ApiV2Error } from '../../rest/client';
3import { showLoginPromptAsync } from '../actions';
4import { retryUsernamePasswordAuthWithOTPAsync, UserSecondFactorDeviceMethod } from '../otp';
5import { loginAsync } from '../user';
6
7jest.mock('../../../utils/prompts');
8jest.mock('../../rest/client', () => {
9  const { ApiV2Error } = jest.requireActual('../../rest/client');
10  return {
11    ApiV2Error,
12  };
13});
14jest.mock('../otp');
15jest.mock('../user');
16
17const asMock = (fn: any): jest.Mock => fn;
18
19beforeEach(() => {
20  asMock(promptAsync).mockClear();
21  asMock(promptAsync).mockImplementation(() => {
22    throw new Error('Should not be called');
23  });
24
25  asMock(loginAsync).mockClear();
26});
27
28describe(showLoginPromptAsync, () => {
29  it('prompts for OTP when 2FA is enabled', async () => {
30    asMock(promptAsync)
31      .mockImplementationOnce(() => ({ username: 'hello', password: 'world' }))
32      .mockImplementationOnce(() => ({ otp: '123456' }))
33      .mockImplementation(() => {
34        throw new Error("shouldn't happen");
35      });
36    asMock(loginAsync)
37      .mockImplementationOnce(async () => {
38        throw new ApiV2Error({
39          message: 'An OTP is required',
40          code: 'ONE_TIME_PASSWORD_REQUIRED',
41          metadata: {
42            secondFactorDevices: [
43              {
44                id: 'p0',
45                is_primary: true,
46                method: UserSecondFactorDeviceMethod.SMS,
47                sms_phone_number: 'testphone',
48              },
49            ],
50            smsAutomaticallySent: true,
51          },
52        });
53      })
54      .mockImplementation(() => {});
55
56    await showLoginPromptAsync();
57
58    expect(retryUsernamePasswordAuthWithOTPAsync).toHaveBeenCalledWith('hello', 'world', {
59      secondFactorDevices: [
60        {
61          id: 'p0',
62          is_primary: true,
63          method: UserSecondFactorDeviceMethod.SMS,
64          sms_phone_number: 'testphone',
65        },
66      ],
67      smsAutomaticallySent: true,
68    });
69  });
70
71  it('does not prompt if all required credentials are provided', async () => {
72    asMock(promptAsync).mockImplementation(() => {
73      throw new Error("shouldn't happen");
74    });
75    asMock(loginAsync).mockImplementation(() => {});
76
77    await showLoginPromptAsync({ username: 'hello', password: 'world' });
78  });
79});
80