Welcome to mirror list, hosted at ThFree Co, Russian Federation.

captcha_modal_spec.js « captcha « frontend « spec - gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: b8448f9ff0a2231f75c992bc57c2265733c064b2 (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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
import { GlModal } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import { nextTick } from 'vue';
import { stubComponent } from 'helpers/stub_component';
import CaptchaModal from '~/captcha/captcha_modal.vue';
import { initRecaptchaScript } from '~/captcha/init_recaptcha_script';

jest.mock('~/captcha/init_recaptcha_script');

describe('Captcha Modal', () => {
  let wrapper;
  let modal;
  let grecaptcha;

  const captchaSiteKey = 'abc123';

  function createComponent({ props = {} } = {}) {
    wrapper = shallowMount(CaptchaModal, {
      propsData: {
        captchaSiteKey,
        ...props,
      },
      stubs: {
        GlModal: stubComponent(GlModal),
      },
    });
  }

  beforeEach(() => {
    grecaptcha = {
      render: jest.fn(),
    };

    initRecaptchaScript.mockResolvedValue(grecaptcha);
  });

  afterEach(() => {
    wrapper.destroy();
    wrapper = null;
  });

  const findGlModal = () => {
    const glModal = wrapper.find(GlModal);

    jest.spyOn(glModal.vm, 'show').mockImplementation(() => glModal.vm.$emit('shown'));
    jest
      .spyOn(glModal.vm, 'hide')
      .mockImplementation(() => glModal.vm.$emit('hide', { trigger: '' }));

    return glModal;
  };

  const showModal = () => {
    wrapper.setProps({ needsCaptchaResponse: true });
  };

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

  describe('rendering', () => {
    it('renders', () => {
      expect(modal.exists()).toBe(true);
    });

    it('assigns the modal a unique ID', () => {
      const firstInstanceModalId = modal.props('modalId');
      createComponent();
      const secondInstanceModalId = findGlModal().props('modalId');
      expect(firstInstanceModalId).not.toEqual(secondInstanceModalId);
    });
  });

  describe('functionality', () => {
    describe('when modal is shown', () => {
      describe('when initRecaptchaScript promise resolves successfully', () => {
        beforeEach(async () => {
          showModal();

          await nextTick();
        });

        it('shows modal', async () => {
          expect(findGlModal().vm.show).toHaveBeenCalled();
        });

        it('renders window.grecaptcha', () => {
          expect(grecaptcha.render).toHaveBeenCalledWith(wrapper.vm.$refs.captcha, {
            sitekey: captchaSiteKey,
            callback: expect.any(Function),
          });
        });

        describe('then the user solves the captcha', () => {
          const captchaResponse = 'a captcha response';

          beforeEach(() => {
            // simulate the grecaptcha library invoking the callback
            const { callback } = grecaptcha.render.mock.calls[0][1];
            callback(captchaResponse);
          });

          it('emits receivedCaptchaResponse exactly once with the captcha response', () => {
            expect(wrapper.emitted('receivedCaptchaResponse')).toEqual([[captchaResponse]]);
          });

          it('hides modal with null trigger', async () => {
            // Assert that hide is called with zero args, so that we don't trigger the logic
            // for hiding the modal via cancel, esc, headerclose, etc, without a captcha response
            expect(modal.vm.hide).toHaveBeenCalledWith();
          });
        });

        describe('then the user hides the modal without solving the captcha', () => {
          // Even though we don't explicitly check for these trigger values, these are the
          // currently supported ones which can be emitted.
          // See https://bootstrap-vue.org/docs/components/modal#prevent-closing
          describe.each`
            trigger          | expected
            ${'cancel'}      | ${[[null]]}
            ${'esc'}         | ${[[null]]}
            ${'backdrop'}    | ${[[null]]}
            ${'headerclose'} | ${[[null]]}
          `('using the $trigger trigger', ({ trigger, expected }) => {
            beforeEach(() => {
              const bvModalEvent = {
                trigger,
              };
              modal.vm.$emit('hide', bvModalEvent);
            });

            it(`emits receivedCaptchaResponse with ${JSON.stringify(expected)}`, () => {
              expect(wrapper.emitted('receivedCaptchaResponse')).toEqual(expected);
            });
          });
        });
      });

      describe('when initRecaptchaScript promise rejects', () => {
        const fakeError = {};

        beforeEach(() => {
          initRecaptchaScript.mockImplementation(() => Promise.reject(fakeError));

          jest.spyOn(console, 'error').mockImplementation();

          showModal();
        });

        it('emits receivedCaptchaResponse exactly once with null', () => {
          expect(wrapper.emitted('receivedCaptchaResponse')).toEqual([[null]]);
        });

        it('hides modal with null trigger', async () => {
          // Assert that hide is called with zero args, so that we don't trigger the logic
          // for hiding the modal via cancel, esc, headerclose, etc, without a captcha response
          expect(modal.vm.hide).toHaveBeenCalledWith();
        });

        it('calls console.error with a message and the exception', () => {
          // eslint-disable-next-line no-console
          expect(console.error).toHaveBeenCalledWith(
            expect.stringMatching(/exception.*captcha/),
            fakeError,
          );
        });
      });
    });
  });
});