diff options
Diffstat (limited to 'spec/frontend/projects')
12 files changed, 474 insertions, 196 deletions
diff --git a/spec/frontend/projects/commits/components/author_select_spec.js b/spec/frontend/projects/commits/components/author_select_spec.js index dab91d8b37c..d6fac6f5f79 100644 --- a/spec/frontend/projects/commits/components/author_select_spec.js +++ b/spec/frontend/projects/commits/components/author_select_spec.js @@ -1,14 +1,14 @@ import { shallowMount, createLocalVue } from '@vue/test-utils'; import Vuex from 'vuex'; -import * as urlUtility from '~/lib/utils/url_utility'; -import AuthorSelect from '~/projects/commits/components/author_select.vue'; -import { createStore } from '~/projects/commits/store'; import { GlNewDropdown, GlNewDropdownHeader, GlSearchBoxByType, GlNewDropdownItem, } from '@gitlab/ui'; +import * as urlUtility from '~/lib/utils/url_utility'; +import AuthorSelect from '~/projects/commits/components/author_select.vue'; +import { createStore } from '~/projects/commits/store'; const localVue = createLocalVue(); localVue.use(Vuex); diff --git a/spec/frontend/projects/commits/store/actions_spec.js b/spec/frontend/projects/commits/store/actions_spec.js index 886224252ad..a842aaa2a76 100644 --- a/spec/frontend/projects/commits/store/actions_spec.js +++ b/spec/frontend/projects/commits/store/actions_spec.js @@ -1,10 +1,10 @@ import axios from 'axios'; import MockAdapter from 'axios-mock-adapter'; -import * as types from '~/projects/commits/store/mutation_types'; import testAction from 'helpers/vuex_action_helper'; +import * as types from '~/projects/commits/store/mutation_types'; import actions from '~/projects/commits/store/actions'; import createState from '~/projects/commits/store/state'; -import createFlash from '~/flash'; +import { deprecatedCreateFlash as createFlash } from '~/flash'; jest.mock('~/flash'); diff --git a/spec/frontend/projects/components/__snapshots__/project_delete_button_spec.js.snap b/spec/frontend/projects/components/__snapshots__/project_delete_button_spec.js.snap new file mode 100644 index 00000000000..44220bdef64 --- /dev/null +++ b/spec/frontend/projects/components/__snapshots__/project_delete_button_spec.js.snap @@ -0,0 +1,83 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Project remove modal initialized matches the snapshot 1`] = ` +<form + action="some/path" + method="post" +> + <input + name="_method" + type="hidden" + value="delete" + /> + + <input + name="authenticity_token" + type="hidden" + /> + + <gl-button-stub + category="primary" + icon="" + role="button" + size="medium" + tabindex="0" + variant="danger" + > + Delete project + </gl-button-stub> + + <gl-modal-stub + actioncancel="[object Object]" + actionprimary="[object Object]" + footer-class="gl-bg-gray-10 gl-p-5" + modalclass="" + modalid="fakeUniqueId" + ok-variant="danger" + size="sm" + title-class="gl-text-red-500" + titletag="h4" + > + + <div> + <gl-alert-stub + class="gl-mb-5" + dismisslabel="Dismiss" + primarybuttonlink="" + primarybuttontext="" + secondarybuttonlink="" + secondarybuttontext="" + title="You are about to permanently delete this project" + variant="danger" + > + <gl-sprintf-stub + message="Once a project is permanently deleted it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its respositories and %{strongStart}all related resources%{strongEnd} including issues, merge requests etc." + /> + </gl-alert-stub> + + <p> + This action cannot be undone. You will lose the project's respository and all conent: issues, merge requests, etc. + </p> + + <p + class="gl-mb-1" + > + Please type the following to confirm: + </p> + + <p> + <code> + foo + </code> + </p> + + <gl-form-input-stub + id="confirm_name_input" + name="confirm_name_input" + type="text" + /> + + </div> + </gl-modal-stub> +</form> +`; diff --git a/spec/frontend/projects/components/__snapshots__/remove_modal_spec.js.snap b/spec/frontend/projects/components/__snapshots__/remove_modal_spec.js.snap deleted file mode 100644 index 4d5b6c56a34..00000000000 --- a/spec/frontend/projects/components/__snapshots__/remove_modal_spec.js.snap +++ /dev/null @@ -1,126 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Project remove modal initialized matches the snapshot 1`] = ` -<form - action="some/path" - method="post" -> - <input - name="_method" - type="hidden" - value="delete" - /> - - <input - name="authenticity_token" - type="hidden" - /> - - <b-button-stub - class="[object Object]" - event="click" - role="button" - routertag="a" - size="md" - tabindex="0" - tag="button" - type="button" - variant="danger" - > - <!----> - - <!----> - - <span - class="gl-button-text" - > - Remove project - </span> - </b-button-stub> - - <b-modal-stub - canceltitle="Cancel" - cancelvariant="secondary" - footerclass="bg-gray-light gl-p-5" - headerclosecontent="×" - headercloselabel="Close" - id="remove-project-modal" - ignoreenforcefocusselector="" - lazy="true" - modalclass="gl-modal," - oktitle="OK" - okvariant="danger" - size="sm" - title="" - titletag="h4" - > - - <div> - <p - class="gl-text-red-500 gl-font-weight-bold" - > - This can lead to data loss. - </p> - - <p - class="gl-mb-0" - > - This action can lead to data loss. To prevent accidental actions we ask you to confirm your intention. - </p> - - <p> - <gl-sprintf-stub - message="Please type %{phrase_code} to proceed or close this modal to cancel." - /> - </p> - - <gl-form-input-stub - id="confirm_name_input" - name="confirm_name_input" - type="text" - /> - </div> - - <template /> - - <template> - Confirmation required - </template> - - <template /> - - <template /> - - <template /> - - <template> - <div - class="gl-w-full gl-display-flex gl-just-content-start gl-m-0" - > - <b-button-stub - class="[object Object]" - disabled="true" - event="click" - routertag="a" - size="md" - tag="button" - type="button" - variant="danger" - > - <!----> - - <!----> - - <span - class="gl-button-text" - > - - Confirm - - </span> - </b-button-stub> - </div> - </template> - </b-modal-stub> -</form> -`; diff --git a/spec/frontend/projects/components/project_delete_button_spec.js b/spec/frontend/projects/components/project_delete_button_spec.js new file mode 100644 index 00000000000..444e465ebaa --- /dev/null +++ b/spec/frontend/projects/components/project_delete_button_spec.js @@ -0,0 +1,47 @@ +import { shallowMount } from '@vue/test-utils'; +import ProjectDeleteButton from '~/projects/components/project_delete_button.vue'; +import SharedDeleteButton from '~/projects/components/shared/delete_button.vue'; + +jest.mock('lodash/uniqueId', () => () => 'fakeUniqueId'); + +describe('Project remove modal', () => { + let wrapper; + + const findSharedDeleteButton = () => wrapper.find(SharedDeleteButton); + + const defaultProps = { + confirmPhrase: 'foo', + formPath: 'some/path', + }; + + const createComponent = (props = {}) => { + wrapper = shallowMount(ProjectDeleteButton, { + propsData: { + ...defaultProps, + ...props, + }, + stubs: { + SharedDeleteButton, + }, + }); + }; + + afterEach(() => { + wrapper.destroy(); + wrapper = null; + }); + + describe('initialized', () => { + beforeEach(() => { + createComponent(); + }); + + it('matches the snapshot', () => { + expect(wrapper.element).toMatchSnapshot(); + }); + + it('passes confirmPhrase and formPath props to the shared delete button', () => { + expect(findSharedDeleteButton().props()).toEqual(defaultProps); + }); + }); +}); diff --git a/spec/frontend/projects/components/remove_modal_spec.js b/spec/frontend/projects/components/remove_modal_spec.js deleted file mode 100644 index 339aee65b99..00000000000 --- a/spec/frontend/projects/components/remove_modal_spec.js +++ /dev/null @@ -1,62 +0,0 @@ -import { shallowMount } from '@vue/test-utils'; -import { GlButton, GlModal } from '@gitlab/ui'; -import ProjectRemoveModal from '~/projects/components/remove_modal.vue'; - -describe('Project remove modal', () => { - let wrapper; - - const findFormElement = () => wrapper.find('form').element; - const findConfirmButton = () => wrapper.find(GlModal).find(GlButton); - - const defaultProps = { - formPath: 'some/path', - confirmPhrase: 'foo', - warningMessage: 'This can lead to data loss.', - }; - - const createComponent = (data = {}) => { - wrapper = shallowMount(ProjectRemoveModal, { - propsData: defaultProps, - data: () => data, - stubs: { - GlButton, - GlModal, - }, - }); - }; - - afterEach(() => { - wrapper.destroy(); - wrapper = null; - }); - - describe('initialized', () => { - beforeEach(() => { - createComponent(); - }); - - it('matches the snapshot', () => { - expect(wrapper.element).toMatchSnapshot(); - }); - }); - - describe('user input matches the confirmPhrase', () => { - beforeEach(() => { - createComponent({ userInput: defaultProps.confirmPhrase }); - }); - - it('the confirm button is not dislabled', () => { - expect(findConfirmButton().attributes('disabled')).toBe(undefined); - }); - - describe('and when the confirmation button is clicked', () => { - beforeEach(() => { - findConfirmButton().vm.$emit('click'); - }); - - it('submits the form element', () => { - expect(findFormElement().submit).toHaveBeenCalled(); - }); - }); - }); -}); diff --git a/spec/frontend/projects/components/shared/__snapshots__/delete_button_spec.js.snap b/spec/frontend/projects/components/shared/__snapshots__/delete_button_spec.js.snap new file mode 100644 index 00000000000..a43acc8c002 --- /dev/null +++ b/spec/frontend/projects/components/shared/__snapshots__/delete_button_spec.js.snap @@ -0,0 +1,113 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Project remove modal intialized matches the snapshot 1`] = ` +<form + action="some/path" + method="post" +> + <input + name="_method" + type="hidden" + value="delete" + /> + + <input + name="authenticity_token" + type="hidden" + value="test-csrf-token" + /> + + <gl-button-stub + category="primary" + icon="" + role="button" + size="medium" + tabindex="0" + variant="danger" + > + Delete project + </gl-button-stub> + + <b-modal-stub + canceltitle="Cancel" + cancelvariant="secondary" + footerclass="gl-bg-gray-10 gl-p-5" + headerclosecontent="×" + headercloselabel="Close" + id="delete-project-modal-2" + ignoreenforcefocusselector="" + lazy="true" + modalclass="gl-modal," + oktitle="OK" + okvariant="danger" + size="sm" + title="" + titleclass="gl-text-red-500" + titletag="h4" + > + + <div> + + <p + class="gl-mb-1" + > + Please type the following to confirm: + </p> + + <p> + <code> + foo + </code> + </p> + + <gl-form-input-stub + id="confirm_name_input" + name="confirm_name_input" + type="text" + /> + + </div> + + <template /> + + <template> + Delete project. Are you ABSOLUTELY SURE? + </template> + + <template /> + + <template /> + + <template /> + + <template> + <gl-button-stub + category="primary" + class="js-modal-action-cancel" + icon="" + size="medium" + variant="default" + > + + Cancel, keep project + + </gl-button-stub> + + <!----> + + <gl-button-stub + category="primary" + class="js-modal-action-primary" + disabled="true" + icon="" + size="medium" + variant="danger" + > + + Yes, delete project + + </gl-button-stub> + </template> + </b-modal-stub> +</form> +`; diff --git a/spec/frontend/projects/components/shared/delete_button_spec.js b/spec/frontend/projects/components/shared/delete_button_spec.js new file mode 100644 index 00000000000..a6394a50011 --- /dev/null +++ b/spec/frontend/projects/components/shared/delete_button_spec.js @@ -0,0 +1,83 @@ +import { shallowMount } from '@vue/test-utils'; +import { GlModal } from '@gitlab/ui'; +import SharedDeleteButton from '~/projects/components/shared/delete_button.vue'; + +jest.mock('~/lib/utils/csrf', () => ({ token: 'test-csrf-token' })); + +describe('Project remove modal', () => { + let wrapper; + + const findFormElement = () => wrapper.find('form'); + const findConfirmButton = () => wrapper.find('.js-modal-action-primary'); + const findAuthenticityTokenInput = () => findFormElement().find('input[name=authenticity_token]'); + const findModal = () => wrapper.find(GlModal); + + const defaultProps = { + confirmPhrase: 'foo', + formPath: 'some/path', + }; + + const createComponent = (data = {}) => { + wrapper = shallowMount(SharedDeleteButton, { + propsData: defaultProps, + data: () => data, + stubs: { + GlModal, + }, + }); + }; + + afterEach(() => { + wrapper.destroy(); + wrapper = null; + }); + + describe('intialized', () => { + beforeEach(() => { + createComponent(); + }); + + it('matches the snapshot', () => { + expect(wrapper.element).toMatchSnapshot(); + }); + + it('sets a csrf token on the authenticity form input', () => { + expect(findAuthenticityTokenInput().element.value).toEqual('test-csrf-token'); + }); + + it('sets the form action to the provided path', () => { + expect(findFormElement().attributes('action')).toEqual(defaultProps.formPath); + }); + }); + + describe('when the user input does not match the confirmPhrase', () => { + beforeEach(() => { + createComponent({ userInput: 'bar' }); + }); + + it('the confirm button is disabled', () => { + expect(findConfirmButton().attributes('disabled')).toBe('true'); + }); + }); + + describe('when the user input matches the confirmPhrase', () => { + beforeEach(() => { + createComponent({ userInput: defaultProps.confirmPhrase }); + }); + + it('the confirm button is not disabled', () => { + expect(findConfirmButton().attributes('disabled')).toBe(undefined); + }); + }); + + describe('when the modal is confirmed', () => { + beforeEach(() => { + createComponent(); + findModal().vm.$emit('ok'); + }); + + it('submits the form element', () => { + expect(findFormElement().element.submit).toHaveBeenCalled(); + }); + }); +}); diff --git a/spec/frontend/projects/experiment_new_project_creation/components/legacy_container_spec.js b/spec/frontend/projects/experiment_new_project_creation/components/legacy_container_spec.js index cd8b39f0426..42a7aa6bc88 100644 --- a/spec/frontend/projects/experiment_new_project_creation/components/legacy_container_spec.js +++ b/spec/frontend/projects/experiment_new_project_creation/components/legacy_container_spec.js @@ -1,6 +1,6 @@ import { shallowMount } from '@vue/test-utils'; -import LegacyContainer from '~/projects/experiment_new_project_creation/components/legacy_container.vue'; import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures'; +import LegacyContainer from '~/projects/experiment_new_project_creation/components/legacy_container.vue'; describe('Legacy container component', () => { let wrapper; diff --git a/spec/frontend/projects/experiment_new_project_creation/components/welcome_spec.js b/spec/frontend/projects/experiment_new_project_creation/components/welcome_spec.js index acd142fa5ba..cf23ba281f9 100644 --- a/spec/frontend/projects/experiment_new_project_creation/components/welcome_spec.js +++ b/spec/frontend/projects/experiment_new_project_creation/components/welcome_spec.js @@ -1,6 +1,6 @@ import { shallowMount } from '@vue/test-utils'; -import WelcomePage from '~/projects/experiment_new_project_creation/components/welcome.vue'; import { mockTracking } from 'helpers/tracking_helper'; +import WelcomePage from '~/projects/experiment_new_project_creation/components/welcome.vue'; describe('Welcome page', () => { let wrapper; diff --git a/spec/frontend/projects/project_new_spec.js b/spec/frontend/projects/project_new_spec.js index 7aafbd33fc8..c32979dcd74 100644 --- a/spec/frontend/projects/project_new_spec.js +++ b/spec/frontend/projects/project_new_spec.js @@ -1,6 +1,6 @@ import $ from 'jquery'; -import projectNew from '~/projects/project_new'; import { TEST_HOST } from 'jest/helpers/test_constants'; +import projectNew from '~/projects/project_new'; describe('New Project', () => { let $projectImportUrl; diff --git a/spec/frontend/projects/settings/access_dropdown_spec.js b/spec/frontend/projects/settings/access_dropdown_spec.js new file mode 100644 index 00000000000..6d323b0408b --- /dev/null +++ b/spec/frontend/projects/settings/access_dropdown_spec.js @@ -0,0 +1,140 @@ +import $ from 'jquery'; +import '~/gl_dropdown'; +import AccessDropdown from '~/projects/settings/access_dropdown'; +import { LEVEL_TYPES } from '~/projects/settings/constants'; + +describe('AccessDropdown', () => { + const defaultLabel = 'dummy default label'; + let dropdown; + + beforeEach(() => { + setFixtures(` + <div id="dummy-dropdown"> + <span class="dropdown-toggle-text"></span> + </div> + `); + const $dropdown = $('#dummy-dropdown'); + $dropdown.data('defaultLabel', defaultLabel); + const options = { + $dropdown, + accessLevelsData: { + roles: [ + { + id: 42, + text: 'Dummy Role', + }, + ], + }, + }; + dropdown = new AccessDropdown(options); + }); + + describe('toggleLabel', () => { + let $dropdownToggleText; + const dummyItems = [ + { type: LEVEL_TYPES.ROLE, access_level: 42 }, + { type: LEVEL_TYPES.USER }, + { type: LEVEL_TYPES.USER }, + { type: LEVEL_TYPES.GROUP }, + { type: LEVEL_TYPES.GROUP }, + { type: LEVEL_TYPES.GROUP }, + ]; + + beforeEach(() => { + $dropdownToggleText = $('.dropdown-toggle-text'); + }); + + it('displays number of items', () => { + dropdown.setSelectedItems(dummyItems); + $dropdownToggleText.addClass('is-default'); + + const label = dropdown.toggleLabel(); + + expect(label).toBe('1 role, 2 users, 3 groups'); + expect($dropdownToggleText).not.toHaveClass('is-default'); + }); + + describe('without selected items', () => { + beforeEach(() => { + dropdown.setSelectedItems([]); + }); + + it('falls back to default label', () => { + const label = dropdown.toggleLabel(); + + expect(label).toBe(defaultLabel); + expect($dropdownToggleText).toHaveClass('is-default'); + }); + }); + + describe('with only role', () => { + beforeEach(() => { + dropdown.setSelectedItems(dummyItems.filter(item => item.type === LEVEL_TYPES.ROLE)); + $dropdownToggleText.addClass('is-default'); + }); + + it('displays the role name', () => { + const label = dropdown.toggleLabel(); + + expect(label).toBe('Dummy Role'); + expect($dropdownToggleText).not.toHaveClass('is-default'); + }); + }); + + describe('with only users', () => { + beforeEach(() => { + dropdown.setSelectedItems(dummyItems.filter(item => item.type === LEVEL_TYPES.USER)); + $dropdownToggleText.addClass('is-default'); + }); + + it('displays number of users', () => { + const label = dropdown.toggleLabel(); + + expect(label).toBe('2 users'); + expect($dropdownToggleText).not.toHaveClass('is-default'); + }); + }); + + describe('with only groups', () => { + beforeEach(() => { + dropdown.setSelectedItems(dummyItems.filter(item => item.type === LEVEL_TYPES.GROUP)); + $dropdownToggleText.addClass('is-default'); + }); + + it('displays number of groups', () => { + const label = dropdown.toggleLabel(); + + expect(label).toBe('3 groups'); + expect($dropdownToggleText).not.toHaveClass('is-default'); + }); + }); + + describe('with users and groups', () => { + beforeEach(() => { + const selectedTypes = [LEVEL_TYPES.GROUP, LEVEL_TYPES.USER]; + dropdown.setSelectedItems(dummyItems.filter(item => selectedTypes.includes(item.type))); + $dropdownToggleText.addClass('is-default'); + }); + + it('displays number of groups', () => { + const label = dropdown.toggleLabel(); + + expect(label).toBe('2 users, 3 groups'); + expect($dropdownToggleText).not.toHaveClass('is-default'); + }); + }); + }); + + describe('userRowHtml', () => { + it('escapes users name', () => { + const user = { + avatar_url: '', + name: '<img src=x onerror=alert(document.domain)>', + username: 'test', + }; + const template = dropdown.userRowHtml(user); + + expect(template).not.toContain(user.name); + }); + }); +}); |