1import { asMock } from '../../../__tests__/asMock';
2import { promptAsync } from '../../../utils/prompts';
3import { ApiV2Error } from '../../rest/client';
4import { showLoginPromptAsync } from '../actions';
5import { retryUsernamePasswordAuthWithOTPAsync, UserSecondFactorDeviceMethod } from '../otp';
6import { loginAsync } from '../user';
7
8jest.mock('../../../log');
9jest.mock('../../../utils/prompts');
10jest.mock('../../rest/client', () => {
11  const { ApiV2Error } = jest.requireActual('../../rest/client');
12  return {
13    ApiV2Error,
14  };
15});
16jest.mock('../otp');
17jest.mock('../user');
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(async () => ({ username: 'hello', password: 'world' }))
32      .mockImplementationOnce(async () => ({ 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(async () => {});
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(async () => {});
76
77    await showLoginPromptAsync({ username: 'hello', password: 'world' });
78  });
79});
80