diff options
Diffstat (limited to 'spec/frontend/terms/components/app_spec.js')
-rw-r--r-- | spec/frontend/terms/components/app_spec.js | 171 |
1 files changed, 171 insertions, 0 deletions
diff --git a/spec/frontend/terms/components/app_spec.js b/spec/frontend/terms/components/app_spec.js new file mode 100644 index 00000000000..ee78b35843a --- /dev/null +++ b/spec/frontend/terms/components/app_spec.js @@ -0,0 +1,171 @@ +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); + }); + }); +}); |