debian-mirror-gitlab/spec/frontend/authentication/two_factor_auth/components/recovery_codes_spec.js
2021-03-11 19:13:27 +05:30

243 lines
7.3 KiB
JavaScript

import { GlAlert, GlButton } from '@gitlab/ui';
import { within } from '@testing-library/dom';
import { mount } from '@vue/test-utils';
import { nextTick } from 'vue';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import RecoveryCodes, {
i18n,
} from '~/authentication/two_factor_auth/components/recovery_codes.vue';
import {
RECOVERY_CODE_DOWNLOAD_FILENAME,
COPY_KEYBOARD_SHORTCUT,
} from '~/authentication/two_factor_auth/constants';
import Tracking from '~/tracking';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import { codes, codesFormattedString, codesDownloadHref, profileAccountPath } from '../mock_data';
describe('RecoveryCodes', () => {
let wrapper;
const createComponent = (options = {}) => {
wrapper = extendedWrapper(
mount(RecoveryCodes, {
propsData: {
codes,
profileAccountPath,
...(options?.propsData || {}),
},
...options,
}),
);
};
const queryByText = (text, options) => within(wrapper.element).queryByText(text, options);
const findAlert = () => wrapper.find(GlAlert);
const findRecoveryCodes = () => wrapper.findByTestId('recovery-codes');
const findCopyButton = () => wrapper.find(ClipboardButton);
const findButtonByText = (text) =>
wrapper.findAll(GlButton).wrappers.find((buttonWrapper) => buttonWrapper.text() === text);
const findDownloadButton = () => findButtonByText('Download codes');
const findPrintButton = () => findButtonByText('Print codes');
const findProceedButton = () => findButtonByText('Proceed');
const manuallyCopyRecoveryCodes = () =>
wrapper.vm.$options.mousetrap.trigger(COPY_KEYBOARD_SHORTCUT);
beforeEach(() => {
jest.spyOn(Tracking, 'event');
createComponent();
});
it('renders title', () => {
expect(queryByText(i18n.pageTitle)).toEqual(expect.any(HTMLElement));
});
it('renders alert', () => {
expect(findAlert().exists()).toBe(true);
expect(findAlert().text()).toBe(i18n.alertTitle);
});
it('renders codes', () => {
const recoveryCodes = findRecoveryCodes().text();
codes.forEach((code) => {
expect(recoveryCodes).toContain(code);
});
});
describe('"Proceed" button', () => {
it('renders button as disabled', () => {
const proceedButton = findProceedButton();
expect(proceedButton.exists()).toBe(true);
expect(proceedButton.props('disabled')).toBe(true);
expect(proceedButton.attributes()).toMatchObject({
title: i18n.proceedButton,
href: profileAccountPath,
});
});
it('fires Snowplow event', () => {
expect(findProceedButton().attributes()).toMatchObject({
'data-track-event': 'click_button',
'data-track-label': '2fa_recovery_codes_proceed_button',
});
});
});
describe('"Copy codes" button', () => {
it('renders button', () => {
const copyButton = findCopyButton();
expect(copyButton.exists()).toBe(true);
expect(copyButton.text()).toBe(i18n.copyButton);
expect(copyButton.props()).toMatchObject({
title: i18n.copyButton,
text: codesFormattedString,
});
});
describe('when button is clicked', () => {
beforeEach(async () => {
findCopyButton().trigger('click');
await nextTick();
});
it('enables "Proceed" button', () => {
expect(findProceedButton().props('disabled')).toBe(false);
});
it('fires Snowplow event', () => {
expect(Tracking.event).toHaveBeenCalledWith(undefined, 'click_button', {
label: '2fa_recovery_codes_copy_button',
});
});
});
});
describe('"Download codes" button', () => {
it('renders button', () => {
const downloadButton = findDownloadButton();
expect(downloadButton.exists()).toBe(true);
expect(downloadButton.attributes()).toMatchObject({
title: i18n.downloadButton,
download: RECOVERY_CODE_DOWNLOAD_FILENAME,
href: codesDownloadHref,
});
});
describe('when button is clicked', () => {
beforeEach(async () => {
const downloadButton = findDownloadButton();
// jsdom does not support navigating.
// Since we are clicking an anchor tag there is no way to mock this
// and we are forced to instead remove the `href` attribute.
// More info: https://github.com/jsdom/jsdom/issues/2112#issuecomment-663672587
downloadButton.element.removeAttribute('href');
downloadButton.trigger('click');
await nextTick();
});
it('enables "Proceed" button', () => {
expect(findProceedButton().props('disabled')).toBe(false);
});
it('fires Snowplow event', () => {
expect(Tracking.event).toHaveBeenCalledWith(undefined, 'click_button', {
label: '2fa_recovery_codes_download_button',
});
});
});
});
describe('"Print codes" button', () => {
it('renders button', () => {
const printButton = findPrintButton();
expect(printButton.exists()).toBe(true);
expect(printButton.attributes()).toMatchObject({
title: i18n.printButton,
});
});
describe('when button is clicked', () => {
beforeEach(async () => {
window.print = jest.fn();
findPrintButton().trigger('click');
await nextTick();
});
it('enables "Proceed" button and opens print dialog', () => {
expect(findProceedButton().props('disabled')).toBe(false);
expect(window.print).toHaveBeenCalled();
});
it('fires Snowplow event', () => {
expect(Tracking.event).toHaveBeenCalledWith(undefined, 'click_button', {
label: '2fa_recovery_codes_print_button',
});
});
});
});
describe('when codes are manually copied', () => {
describe('when selected text is the recovery codes', () => {
beforeEach(async () => {
jest.spyOn(window, 'getSelection').mockImplementation(() => ({
toString: jest.fn(() => codesFormattedString),
}));
manuallyCopyRecoveryCodes();
await nextTick();
});
it('enables "Proceed" button', () => {
expect(findProceedButton().props('disabled')).toBe(false);
});
it('fires Snowplow event', () => {
expect(Tracking.event).toHaveBeenCalledWith(undefined, 'copy_keyboard_shortcut', {
label: '2fa_recovery_codes_manual_copy',
});
});
});
describe('when selected text includes the recovery codes', () => {
beforeEach(() => {
jest.spyOn(window, 'getSelection').mockImplementation(() => ({
toString: jest.fn(() => `foo bar ${codesFormattedString}`),
}));
});
it('enables "Proceed" button', async () => {
manuallyCopyRecoveryCodes();
await nextTick();
expect(findProceedButton().props('disabled')).toBe(false);
});
});
describe('when selected text does not include the recovery codes', () => {
beforeEach(() => {
jest.spyOn(window, 'getSelection').mockImplementation(() => ({
toString: jest.fn(() => 'foo bar'),
}));
});
it('keeps "Proceed" button disabled', async () => {
manuallyCopyRecoveryCodes();
await nextTick();
expect(findProceedButton().props('disabled')).toBe(true);
});
});
});
});