summaryrefslogtreecommitdiff
path: root/spec/frontend/authentication/two_factor_auth/components/manage_two_factor_form_spec.js
blob: 694c16a85c442090d1a9798e400bb543786e6f66 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
import { GlForm, GlModal } from '@gitlab/ui';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import { stubComponent } from 'helpers/stub_component';
import ManageTwoFactorForm, {
  i18n,
} from '~/authentication/two_factor_auth/components/manage_two_factor_form.vue';

const defaultProvide = {
  profileTwoFactorAuthPath: '2fa_auth_path',
  profileTwoFactorAuthMethod: '2fa_auth_method',
  codesProfileTwoFactorAuthPath: '2fa_codes_path',
  codesProfileTwoFactorAuthMethod: '2fa_codes_method',
};

describe('ManageTwoFactorForm', () => {
  let wrapper;

  const createComponent = (options = {}) => {
    wrapper = mountExtended(ManageTwoFactorForm, {
      provide: {
        ...defaultProvide,
        webauthnEnabled: options?.webauthnEnabled ?? false,
        isCurrentPasswordRequired: options?.currentPasswordRequired ?? true,
      },
      stubs: {
        GlModal: stubComponent(GlModal, {
          template: `
            <div>
              <slot name="modal-title"></slot>
              <slot></slot>
              <slot name="modal-footer"></slot>
            </div>`,
        }),
      },
    });
  };

  const findForm = () => wrapper.findComponent(GlForm);
  const findMethodInput = () => wrapper.findByTestId('test-2fa-method-field');
  const findDisableButton = () => wrapper.findByTestId('test-2fa-disable-button');
  const findRegenerateCodesButton = () => wrapper.findByTestId('test-2fa-regenerate-codes-button');
  const findConfirmationModal = () => wrapper.findComponent(GlModal);

  const itShowsConfirmationModal = (confirmText) => {
    it('shows confirmation modal', async () => {
      await wrapper.findByLabelText('Current password').setValue('foo bar');
      await findDisableButton().trigger('click');

      expect(findConfirmationModal().props('visible')).toBe(true);
      expect(findConfirmationModal().html()).toContain(confirmText);
    });
  };

  const itShowsValidationMessageIfCurrentPasswordFieldIsEmpty = (findButtonFunction) => {
    it('shows validation message if `Current password` is empty', async () => {
      await findButtonFunction().trigger('click');

      expect(wrapper.findByText(i18n.currentPasswordInvalidFeedback).exists()).toBe(true);
    });
  };

  beforeEach(() => {
    createComponent();
  });

  describe('`Current password` field', () => {
    describe('when required', () => {
      it('renders the current password field', () => {
        expect(wrapper.findByLabelText(i18n.currentPassword).exists()).toBe(true);
      });
    });

    describe('when not required', () => {
      beforeEach(() => {
        createComponent({
          currentPasswordRequired: false,
        });
      });

      it('does not render the current password field', () => {
        expect(wrapper.findByLabelText(i18n.currentPassword).exists()).toBe(false);
      });
    });
  });

  describe('Disable button', () => {
    it('renders the component with correct attributes', () => {
      expect(findDisableButton().exists()).toBe(true);
    });

    describe('when clicked', () => {
      itShowsValidationMessageIfCurrentPasswordFieldIsEmpty(findDisableButton);

      itShowsConfirmationModal(i18n.confirm);

      describe('when webauthnEnabled', () => {
        beforeEach(() => {
          createComponent({
            webauthnEnabled: true,
          });
        });

        itShowsConfirmationModal(i18n.confirmWebAuthn);
      });

      it('modifies the form action and method when submitted through the button', async () => {
        const form = findForm();
        const methodInput = findMethodInput();
        const submitSpy = jest.spyOn(form.element, 'submit');

        await wrapper.findByLabelText('Current password').setValue('foo bar');
        await findDisableButton().trigger('click');

        expect(form.attributes('action')).toBe(defaultProvide.profileTwoFactorAuthPath);
        expect(methodInput.attributes('value')).toBe(defaultProvide.profileTwoFactorAuthMethod);

        findConfirmationModal().vm.$emit('primary');

        expect(submitSpy).toHaveBeenCalled();
      });
    });
  });

  describe('Regenerate recovery codes button', () => {
    it('renders the button', () => {
      expect(findRegenerateCodesButton().exists()).toBe(true);
    });

    describe('when clicked', () => {
      itShowsValidationMessageIfCurrentPasswordFieldIsEmpty(findRegenerateCodesButton);

      it('modifies the form action and method when submitted through the button', async () => {
        const form = findForm();
        const methodInput = findMethodInput();
        const submitSpy = jest.spyOn(form.element, 'submit');

        await wrapper.findByLabelText('Current password').setValue('foo bar');
        await findRegenerateCodesButton().trigger('click');

        expect(form.attributes('action')).toBe(defaultProvide.codesProfileTwoFactorAuthPath);
        expect(methodInput.attributes('value')).toBe(
          defaultProvide.codesProfileTwoFactorAuthMethod,
        );
        expect(submitSpy).toHaveBeenCalled();
      });
    });
  });
});