172 lines
5.3 KiB
JavaScript
172 lines
5.3 KiB
JavaScript
|
import $ from 'jquery';
|
||
|
import { merge } from 'lodash';
|
||
|
import { GlIntersectionObserver } from '@gitlab/ui';
|
||
|
import { nextTick } from 'vue';
|
||
|
|
||
|
import { mountExtended } from 'helpers/vue_test_utils_helper';
|
||
|
import { FLASH_TYPES, FLASH_CLOSED_EVENT } from '~/flash';
|
||
|
import { isLoggedIn } from '~/lib/utils/common_utils';
|
||
|
import TermsApp from '~/terms/components/app.vue';
|
||
|
|
||
|
jest.mock('~/lib/utils/csrf', () => ({ token: 'mock-csrf-token' }));
|
||
|
jest.mock('~/lib/utils/common_utils');
|
||
|
|
||
|
describe('TermsApp', () => {
|
||
|
let wrapper;
|
||
|
let renderGFMSpy;
|
||
|
|
||
|
const defaultProvide = {
|
||
|
terms: 'foo bar',
|
||
|
paths: {
|
||
|
accept: '/-/users/terms/1/accept',
|
||
|
decline: '/-/users/terms/1/decline',
|
||
|
root: '/',
|
||
|
},
|
||
|
permissions: {
|
||
|
canAccept: true,
|
||
|
canDecline: true,
|
||
|
},
|
||
|
};
|
||
|
|
||
|
const createComponent = (provide = {}) => {
|
||
|
wrapper = mountExtended(TermsApp, {
|
||
|
provide: merge({}, defaultProvide, provide),
|
||
|
});
|
||
|
};
|
||
|
|
||
|
beforeEach(() => {
|
||
|
renderGFMSpy = jest.spyOn($.fn, 'renderGFM');
|
||
|
isLoggedIn.mockReturnValue(true);
|
||
|
});
|
||
|
|
||
|
afterEach(() => {
|
||
|
wrapper.destroy();
|
||
|
});
|
||
|
|
||
|
const findFormWithAction = (path) => wrapper.find(`form[action="${path}"]`);
|
||
|
const findButton = (path) => findFormWithAction(path).find('button[type="submit"]');
|
||
|
const findScrollableViewport = () => wrapper.findByTestId('scrollable-viewport');
|
||
|
|
||
|
const expectFormWithSubmitButton = (buttonText, path) => {
|
||
|
const form = findFormWithAction(path);
|
||
|
const submitButton = findButton(path);
|
||
|
|
||
|
expect(form.exists()).toBe(true);
|
||
|
expect(submitButton.exists()).toBe(true);
|
||
|
expect(submitButton.text()).toBe(buttonText);
|
||
|
expect(
|
||
|
form
|
||
|
.find('input[type="hidden"][name="authenticity_token"][value="mock-csrf-token"]')
|
||
|
.exists(),
|
||
|
).toBe(true);
|
||
|
};
|
||
|
|
||
|
it('renders terms of service as markdown', () => {
|
||
|
createComponent();
|
||
|
|
||
|
expect(wrapper.findByText(defaultProvide.terms).exists()).toBe(true);
|
||
|
expect(renderGFMSpy).toHaveBeenCalled();
|
||
|
});
|
||
|
|
||
|
describe('accept button', () => {
|
||
|
it('is disabled until user scrolls to the bottom of the terms', async () => {
|
||
|
createComponent();
|
||
|
|
||
|
expect(findButton(defaultProvide.paths.accept).attributes('disabled')).toBe('disabled');
|
||
|
|
||
|
wrapper.find(GlIntersectionObserver).vm.$emit('appear');
|
||
|
|
||
|
await nextTick();
|
||
|
|
||
|
expect(findButton(defaultProvide.paths.accept).attributes('disabled')).toBeUndefined();
|
||
|
});
|
||
|
|
||
|
describe('when user has permissions to accept', () => {
|
||
|
it('renders form and button to accept terms', () => {
|
||
|
createComponent();
|
||
|
|
||
|
expectFormWithSubmitButton(TermsApp.i18n.accept, defaultProvide.paths.accept);
|
||
|
});
|
||
|
});
|
||
|
|
||
|
describe('when user does not have permissions to accept', () => {
|
||
|
it('renders continue button', () => {
|
||
|
createComponent({ permissions: { canAccept: false } });
|
||
|
|
||
|
expect(wrapper.findByText(TermsApp.i18n.continue).exists()).toBe(true);
|
||
|
});
|
||
|
});
|
||
|
});
|
||
|
|
||
|
describe('decline button', () => {
|
||
|
describe('when user has permissions to decline', () => {
|
||
|
it('renders form and button to decline terms', () => {
|
||
|
createComponent();
|
||
|
|
||
|
expectFormWithSubmitButton(TermsApp.i18n.decline, defaultProvide.paths.decline);
|
||
|
});
|
||
|
});
|
||
|
|
||
|
describe('when user does not have permissions to decline', () => {
|
||
|
it('does not render decline button', () => {
|
||
|
createComponent({ permissions: { canDecline: false } });
|
||
|
|
||
|
expect(wrapper.findByText(TermsApp.i18n.decline).exists()).toBe(false);
|
||
|
});
|
||
|
});
|
||
|
});
|
||
|
|
||
|
it('sets height of scrollable viewport', () => {
|
||
|
jest.spyOn(document.documentElement, 'scrollHeight', 'get').mockImplementation(() => 800);
|
||
|
jest.spyOn(document.documentElement, 'clientHeight', 'get').mockImplementation(() => 600);
|
||
|
|
||
|
createComponent();
|
||
|
|
||
|
expect(findScrollableViewport().attributes('style')).toBe('max-height: calc(100vh - 200px);');
|
||
|
});
|
||
|
|
||
|
describe('when flash is closed', () => {
|
||
|
let flashEl;
|
||
|
|
||
|
beforeEach(() => {
|
||
|
flashEl = document.createElement('div');
|
||
|
flashEl.classList.add(`flash-${FLASH_TYPES.ALERT}`);
|
||
|
document.body.appendChild(flashEl);
|
||
|
});
|
||
|
|
||
|
afterEach(() => {
|
||
|
document.body.innerHTML = '';
|
||
|
});
|
||
|
|
||
|
it('recalculates height of scrollable viewport', () => {
|
||
|
jest.spyOn(document.documentElement, 'scrollHeight', 'get').mockImplementation(() => 800);
|
||
|
jest.spyOn(document.documentElement, 'clientHeight', 'get').mockImplementation(() => 600);
|
||
|
|
||
|
createComponent();
|
||
|
|
||
|
expect(findScrollableViewport().attributes('style')).toBe('max-height: calc(100vh - 200px);');
|
||
|
|
||
|
jest.spyOn(document.documentElement, 'scrollHeight', 'get').mockImplementation(() => 700);
|
||
|
jest.spyOn(document.documentElement, 'clientHeight', 'get').mockImplementation(() => 600);
|
||
|
|
||
|
flashEl.dispatchEvent(new Event(FLASH_CLOSED_EVENT));
|
||
|
|
||
|
expect(findScrollableViewport().attributes('style')).toBe('max-height: calc(100vh - 100px);');
|
||
|
});
|
||
|
});
|
||
|
|
||
|
describe('when user is signed out', () => {
|
||
|
beforeEach(() => {
|
||
|
isLoggedIn.mockReturnValue(false);
|
||
|
});
|
||
|
|
||
|
it('does not show any buttons', () => {
|
||
|
createComponent();
|
||
|
|
||
|
expect(wrapper.findByText(TermsApp.i18n.accept).exists()).toBe(false);
|
||
|
expect(wrapper.findByText(TermsApp.i18n.decline).exists()).toBe(false);
|
||
|
expect(wrapper.findByText(TermsApp.i18n.continue).exists()).toBe(false);
|
||
|
});
|
||
|
});
|
||
|
});
|