diff options
Diffstat (limited to 'spec/frontend/pages')
9 files changed, 104 insertions, 433 deletions
diff --git a/spec/frontend/pages/admin/application_settings/metrics_and_profiling/usage_statistics_spec.js b/spec/frontend/pages/admin/application_settings/metrics_and_profiling/usage_statistics_spec.js new file mode 100644 index 00000000000..858c7b76ac8 --- /dev/null +++ b/spec/frontend/pages/admin/application_settings/metrics_and_profiling/usage_statistics_spec.js @@ -0,0 +1,57 @@ +import initSetHelperText, { + HELPER_TEXT_SERVICE_PING_DISABLED, + HELPER_TEXT_SERVICE_PING_ENABLED, +} from '~/pages/admin/application_settings/metrics_and_profiling/usage_statistics'; + +describe('UsageStatistics', () => { + const FIXTURE = 'application_settings/usage.html'; + let usagePingCheckBox; + let usagePingFeaturesCheckBox; + let usagePingFeaturesLabel; + let usagePingFeaturesHelperText; + + beforeEach(() => { + loadFixtures(FIXTURE); + initSetHelperText(); + usagePingCheckBox = document.getElementById('application_setting_usage_ping_enabled'); + usagePingFeaturesCheckBox = document.getElementById( + 'application_setting_usage_ping_features_enabled', + ); + usagePingFeaturesLabel = document.getElementById('service_ping_features_label'); + usagePingFeaturesHelperText = document.getElementById('service_ping_features_helper_text'); + }); + + const expectEnabledUsagePingFeaturesCheckBox = () => { + expect(usagePingFeaturesCheckBox.classList.contains('gl-cursor-not-allowed')).toBe(false); + expect(usagePingFeaturesHelperText.textContent).toEqual(HELPER_TEXT_SERVICE_PING_ENABLED); + }; + + const expectDisabledUsagePingFeaturesCheckBox = () => { + expect(usagePingFeaturesLabel.classList.contains('gl-cursor-not-allowed')).toBe(true); + expect(usagePingFeaturesHelperText.textContent).toEqual(HELPER_TEXT_SERVICE_PING_DISABLED); + }; + + describe('Registration Features checkbox', () => { + it('is disabled when Usage Ping checkbox is unchecked', () => { + expect(usagePingCheckBox.checked).toBe(false); + expectDisabledUsagePingFeaturesCheckBox(); + }); + + it('is enabled when Usage Ping checkbox is checked', () => { + usagePingCheckBox.click(); + expect(usagePingCheckBox.checked).toBe(true); + expectEnabledUsagePingFeaturesCheckBox(); + }); + + it('is switched to disabled when Usage Ping checkbox is unchecked ', () => { + usagePingCheckBox.click(); + usagePingFeaturesCheckBox.click(); + expectEnabledUsagePingFeaturesCheckBox(); + + usagePingCheckBox.click(); + expect(usagePingCheckBox.checked).toBe(false); + expect(usagePingFeaturesCheckBox.checked).toBe(false); + expectDisabledUsagePingFeaturesCheckBox(); + }); + }); +}); diff --git a/spec/frontend/pages/admin/users/components/__snapshots__/delete_user_modal_spec.js.snap b/spec/frontend/pages/admin/users/components/__snapshots__/delete_user_modal_spec.js.snap deleted file mode 100644 index 4c644a0d05f..00000000000 --- a/spec/frontend/pages/admin/users/components/__snapshots__/delete_user_modal_spec.js.snap +++ /dev/null @@ -1,79 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`User Operation confirmation modal renders modal with form included 1`] = ` -<div> - <p> - <gl-sprintf-stub - message="content" - /> - </p> - - <oncall-schedules-list-stub - schedules="schedule1,schedule2" - /> - - <p> - <gl-sprintf-stub - message="To confirm, type %{username}" - /> - </p> - - <form - action="delete-url" - method="post" - > - <input - name="_method" - type="hidden" - value="delete" - /> - - <input - name="authenticity_token" - type="hidden" - value="csrf" - /> - - <gl-form-input-stub - autocomplete="off" - autofocus="" - name="username" - type="text" - value="" - /> - </form> - <gl-button-stub - buttontextclasses="" - category="primary" - icon="" - size="medium" - variant="default" - > - Cancel - </gl-button-stub> - - <gl-button-stub - buttontextclasses="" - category="secondary" - disabled="true" - icon="" - size="medium" - variant="danger" - > - - secondaryAction - - </gl-button-stub> - - <gl-button-stub - buttontextclasses="" - category="primary" - disabled="true" - icon="" - size="medium" - variant="danger" - > - action - </gl-button-stub> -</div> -`; diff --git a/spec/frontend/pages/admin/users/components/delete_user_modal_spec.js b/spec/frontend/pages/admin/users/components/delete_user_modal_spec.js deleted file mode 100644 index 93d9ee43179..00000000000 --- a/spec/frontend/pages/admin/users/components/delete_user_modal_spec.js +++ /dev/null @@ -1,167 +0,0 @@ -import { GlButton, GlFormInput } from '@gitlab/ui'; -import { shallowMount } from '@vue/test-utils'; -import DeleteUserModal from '~/pages/admin/users/components/delete_user_modal.vue'; -import OncallSchedulesList from '~/vue_shared/components/oncall_schedules_list.vue'; -import ModalStub from './stubs/modal_stub'; - -const TEST_DELETE_USER_URL = 'delete-url'; -const TEST_BLOCK_USER_URL = 'block-url'; -const TEST_CSRF = 'csrf'; - -describe('User Operation confirmation modal', () => { - let wrapper; - let formSubmitSpy; - - const findButton = (variant, category) => - wrapper - .findAll(GlButton) - .filter((w) => w.attributes('variant') === variant && w.attributes('category') === category) - .at(0); - const findForm = () => wrapper.find('form'); - const findUsernameInput = () => wrapper.findComponent(GlFormInput); - const findPrimaryButton = () => findButton('danger', 'primary'); - const findSecondaryButton = () => findButton('danger', 'secondary'); - const findAuthenticityToken = () => new FormData(findForm().element).get('authenticity_token'); - const getUsername = () => findUsernameInput().attributes('value'); - const getMethodParam = () => new FormData(findForm().element).get('_method'); - const getFormAction = () => findForm().attributes('action'); - const findOnCallSchedulesList = () => wrapper.findComponent(OncallSchedulesList); - - const setUsername = (username) => { - findUsernameInput().vm.$emit('input', username); - }; - - const username = 'username'; - const badUsername = 'bad_username'; - const oncallSchedules = '["schedule1", "schedule2"]'; - - const createComponent = (props = {}) => { - wrapper = shallowMount(DeleteUserModal, { - propsData: { - username, - title: 'title', - content: 'content', - action: 'action', - secondaryAction: 'secondaryAction', - deleteUserUrl: TEST_DELETE_USER_URL, - blockUserUrl: TEST_BLOCK_USER_URL, - csrfToken: TEST_CSRF, - oncallSchedules, - ...props, - }, - stubs: { - GlModal: ModalStub, - }, - }); - }; - - beforeEach(() => { - formSubmitSpy = jest.spyOn(HTMLFormElement.prototype, 'submit').mockImplementation(); - }); - - afterEach(() => { - wrapper.destroy(); - wrapper = null; - }); - - it('renders modal with form included', () => { - createComponent(); - expect(wrapper.element).toMatchSnapshot(); - }); - - describe('on created', () => { - beforeEach(() => { - createComponent(); - }); - - it('has disabled buttons', () => { - expect(findPrimaryButton().attributes('disabled')).toBeTruthy(); - expect(findSecondaryButton().attributes('disabled')).toBeTruthy(); - }); - }); - - describe('with incorrect username', () => { - beforeEach(() => { - createComponent(); - setUsername(badUsername); - - return wrapper.vm.$nextTick(); - }); - - it('shows incorrect username', () => { - expect(getUsername()).toEqual(badUsername); - }); - - it('has disabled buttons', () => { - expect(findPrimaryButton().attributes('disabled')).toBeTruthy(); - expect(findSecondaryButton().attributes('disabled')).toBeTruthy(); - }); - }); - - describe('with correct username', () => { - beforeEach(() => { - createComponent(); - setUsername(username); - - return wrapper.vm.$nextTick(); - }); - - it('shows correct username', () => { - expect(getUsername()).toEqual(username); - }); - - it('has enabled buttons', () => { - expect(findPrimaryButton().attributes('disabled')).toBeFalsy(); - expect(findSecondaryButton().attributes('disabled')).toBeFalsy(); - }); - - describe('when primary action is submitted', () => { - beforeEach(() => { - findPrimaryButton().vm.$emit('click'); - - return wrapper.vm.$nextTick(); - }); - - it('clears the input', () => { - expect(getUsername()).toEqual(''); - }); - - it('has correct form attributes and calls submit', () => { - expect(getFormAction()).toBe(TEST_DELETE_USER_URL); - expect(getMethodParam()).toBe('delete'); - expect(findAuthenticityToken()).toBe(TEST_CSRF); - expect(formSubmitSpy).toHaveBeenCalled(); - }); - }); - - describe('when secondary action is submitted', () => { - beforeEach(() => { - findSecondaryButton().vm.$emit('click'); - - return wrapper.vm.$nextTick(); - }); - - it('has correct form attributes and calls submit', () => { - expect(getFormAction()).toBe(TEST_BLOCK_USER_URL); - expect(getMethodParam()).toBe('put'); - expect(findAuthenticityToken()).toBe(TEST_CSRF); - expect(formSubmitSpy).toHaveBeenCalled(); - }); - }); - }); - - describe('Related oncall-schedules list', () => { - it('does NOT render the list when user has no related schedules', () => { - createComponent({ oncallSchedules: '[]' }); - expect(findOnCallSchedulesList().exists()).toBe(false); - }); - - it('renders the list when user has related schedules', () => { - createComponent(); - - const schedules = findOnCallSchedulesList(); - expect(schedules.exists()).toBe(true); - expect(schedules.props('schedules')).toEqual(JSON.parse(oncallSchedules)); - }); - }); -}); diff --git a/spec/frontend/pages/admin/users/components/stubs/modal_stub.js b/spec/frontend/pages/admin/users/components/stubs/modal_stub.js deleted file mode 100644 index 4dc55e909a0..00000000000 --- a/spec/frontend/pages/admin/users/components/stubs/modal_stub.js +++ /dev/null @@ -1,23 +0,0 @@ -const ModalStub = { - inheritAttrs: false, - name: 'glmodal-stub', - data() { - return { - showWasCalled: false, - }; - }, - methods: { - show() { - this.showWasCalled = true; - }, - hide() {}, - }, - render(h) { - const children = [this.$slots.default, this.$slots['modal-footer']] - .filter(Boolean) - .reduce((acc, nodes) => acc.concat(nodes), []); - return h('div', children); - }, -}; - -export default ModalStub; diff --git a/spec/frontend/pages/admin/users/components/user_modal_manager_spec.js b/spec/frontend/pages/admin/users/components/user_modal_manager_spec.js deleted file mode 100644 index 3669bc40d7e..00000000000 --- a/spec/frontend/pages/admin/users/components/user_modal_manager_spec.js +++ /dev/null @@ -1,126 +0,0 @@ -import { mount } from '@vue/test-utils'; -import UserModalManager from '~/pages/admin/users/components/user_modal_manager.vue'; -import ModalStub from './stubs/modal_stub'; - -describe('Users admin page Modal Manager', () => { - let wrapper; - - const modalConfiguration = { - action1: { - title: 'action1', - content: 'Action Modal 1', - }, - action2: { - title: 'action2', - content: 'Action Modal 2', - }, - }; - - const findModal = () => wrapper.find({ ref: 'modal' }); - - const createComponent = (props = {}) => { - wrapper = mount(UserModalManager, { - propsData: { - selector: '.js-delete-user-modal-button', - modalConfiguration, - csrfToken: 'dummyCSRF', - ...props, - }, - stubs: { - DeleteUserModal: ModalStub, - }, - }); - }; - - afterEach(() => { - wrapper.destroy(); - wrapper = null; - }); - - describe('render behavior', () => { - it('does not renders modal when initialized', () => { - createComponent(); - expect(findModal().exists()).toBeFalsy(); - }); - - it('throws if action has no proper configuration', () => { - createComponent({ - modalConfiguration: {}, - }); - expect(() => wrapper.vm.show({ glModalAction: 'action1' })).toThrow(); - }); - - it('renders modal with expected props when valid configuration is passed', () => { - createComponent(); - wrapper.vm.show({ - glModalAction: 'action1', - extraProp: 'extraPropValue', - }); - - return wrapper.vm.$nextTick().then(() => { - const modal = findModal(); - expect(modal.exists()).toBeTruthy(); - expect(modal.vm.$attrs.csrfToken).toEqual('dummyCSRF'); - expect(modal.vm.$attrs.extraProp).toEqual('extraPropValue'); - expect(modal.vm.showWasCalled).toBeTruthy(); - }); - }); - }); - - describe('click handling', () => { - let button; - let button2; - - const createButtons = () => { - button = document.createElement('button'); - button2 = document.createElement('button'); - button.setAttribute('class', 'js-delete-user-modal-button'); - button.setAttribute('data-username', 'foo'); - button.setAttribute('data-gl-modal-action', 'action1'); - button.setAttribute('data-block-user-url', '/block'); - button.setAttribute('data-delete-user-url', '/delete'); - document.body.appendChild(button); - document.body.appendChild(button2); - }; - const removeButtons = () => { - button.remove(); - button = null; - button2.remove(); - button2 = null; - }; - - beforeEach(() => { - createButtons(); - createComponent(); - }); - - afterEach(() => { - removeButtons(); - }); - - it('renders the modal when the button is clicked', async () => { - button.click(); - - await wrapper.vm.$nextTick(); - - expect(findModal().exists()).toBe(true); - }); - - it('does not render the modal when a misconfigured button is clicked', async () => { - button.removeAttribute('data-gl-modal-action'); - button.click(); - - await wrapper.vm.$nextTick(); - - expect(findModal().exists()).toBe(false); - }); - - it('does not render the modal when a button without the selector class is clicked', async () => { - button2.click(); - - await wrapper.vm.$nextTick(); - - expect(findModal().exists()).toBe(false); - }); - }); -}); diff --git a/spec/frontend/pages/projects/forks/new/components/fork_form_spec.js b/spec/frontend/pages/projects/forks/new/components/fork_form_spec.js index c80ccfa8256..dd617b1ffc2 100644 --- a/spec/frontend/pages/projects/forks/new/components/fork_form_spec.js +++ b/spec/frontend/pages/projects/forks/new/components/fork_form_spec.js @@ -29,10 +29,12 @@ describe('ForkForm component', () => { const MOCK_NAMESPACES_RESPONSE = [ { name: 'one', + full_name: 'one-group/one', id: 1, }, { name: 'two', + full_name: 'two-group/two', id: 2, }, ]; @@ -155,7 +157,7 @@ describe('ForkForm component', () => { describe('forks namespaces', () => { beforeEach(() => { mockGetRequest({ namespaces: MOCK_NAMESPACES_RESPONSE }); - createComponent(); + createFullComponent(); }); it('make GET request from endpoint', async () => { @@ -178,8 +180,23 @@ describe('ForkForm component', () => { const optionsArray = findForkUrlInput().findAll('option'); expect(optionsArray).toHaveLength(MOCK_NAMESPACES_RESPONSE.length + 1); - expect(optionsArray.at(1).text()).toBe(MOCK_NAMESPACES_RESPONSE[0].name); - expect(optionsArray.at(2).text()).toBe(MOCK_NAMESPACES_RESPONSE[1].name); + expect(optionsArray.at(1).text()).toBe(MOCK_NAMESPACES_RESPONSE[0].full_name); + expect(optionsArray.at(2).text()).toBe(MOCK_NAMESPACES_RESPONSE[1].full_name); + }); + + it('set namespaces in alphabetical order', async () => { + const namespace = { + name: 'three', + full_name: 'aaa/three', + id: 3, + }; + mockGetRequest({ + namespaces: [...MOCK_NAMESPACES_RESPONSE, namespace], + }); + createComponent(); + await axios.waitForAll(); + + expect(wrapper.vm.namespaces).toEqual([namespace, ...MOCK_NAMESPACES_RESPONSE]); }); }); diff --git a/spec/frontend/pages/projects/forks/new/components/fork_groups_list_item_spec.js b/spec/frontend/pages/projects/forks/new/components/fork_groups_list_item_spec.js index b5425fa6f2e..490dafed4ae 100644 --- a/spec/frontend/pages/projects/forks/new/components/fork_groups_list_item_spec.js +++ b/spec/frontend/pages/projects/forks/new/components/fork_groups_list_item_spec.js @@ -34,10 +34,10 @@ describe('Fork groups list item component', () => { }); }; - it('renders pending removal badge if applicable', () => { + it('renders pending deletion badge if applicable', () => { createWrapper({ group: { ...DEFAULT_GROUP_DATA, marked_for_deletion: true } }); - expect(wrapper.find(GlBadge).text()).toBe('pending removal'); + expect(wrapper.find(GlBadge).text()).toBe('pending deletion'); }); it('renders go to fork button if has forked project', () => { diff --git a/spec/frontend/pages/projects/new/components/app_spec.js b/spec/frontend/pages/projects/new/components/app_spec.js index b604e636243..ab8c6d529a8 100644 --- a/spec/frontend/pages/projects/new/components/app_spec.js +++ b/spec/frontend/pages/projects/new/components/app_spec.js @@ -1,13 +1,10 @@ import { shallowMount } from '@vue/test-utils'; -import { assignGitlabExperiment } from 'helpers/experimentation_helper'; import App from '~/pages/projects/new/components/app.vue'; import NewNamespacePage from '~/vue_shared/new_namespace/new_namespace_page.vue'; describe('Experimental new project creation app', () => { let wrapper; - const findNewNamespacePage = () => wrapper.findComponent(NewNamespacePage); - const createComponent = (propsData) => { wrapper = shallowMount(App, { propsData }); }; @@ -16,36 +13,6 @@ describe('Experimental new project creation app', () => { wrapper.destroy(); }); - describe('new_repo experiment', () => { - it('passes new_repo experiment', () => { - createComponent(); - - expect(findNewNamespacePage().props().experiment).toBe('new_repo'); - }); - - describe('when in the candidate variant', () => { - assignGitlabExperiment('new_repo', 'candidate'); - - it('has "repository" in the panel title', () => { - createComponent(); - - expect(findNewNamespacePage().props().panels[0].title).toBe( - 'Create blank project/repository', - ); - }); - }); - - describe('when in the control variant', () => { - assignGitlabExperiment('new_repo', 'control'); - - it('has "project" in the panel title', () => { - createComponent(); - - expect(findNewNamespacePage().props().panels[0].title).toBe('Create blank project'); - }); - }); - }); - it('passes custom new project guideline text to underlying component', () => { const DEMO_GUIDELINES = 'Demo guidelines'; const guidelineSelector = '#new-project-guideline'; diff --git a/spec/frontend/pages/projects/shared/permissions/components/settings_panel_spec.js b/spec/frontend/pages/projects/shared/permissions/components/settings_panel_spec.js index 878721666ff..4c253f0610b 100644 --- a/spec/frontend/pages/projects/shared/permissions/components/settings_panel_spec.js +++ b/spec/frontend/pages/projects/shared/permissions/components/settings_panel_spec.js @@ -94,6 +94,8 @@ describe('Settings Panel', () => { const findPackageSettings = () => wrapper.find({ ref: 'package-settings' }); const findPackagesEnabledInput = () => wrapper.find('[name="project[packages_enabled]"]'); const findPagesSettings = () => wrapper.find({ ref: 'pages-settings' }); + const findPagesAccessLevels = () => + wrapper.find('[name="project[project_feature_attributes][pages_access_level]"]'); const findEmailSettings = () => wrapper.find({ ref: 'email-settings' }); const findShowDefaultAwardEmojis = () => wrapper.find('input[name="project[project_setting_attributes][show_default_award_emojis]"]'); @@ -479,6 +481,29 @@ describe('Settings Panel', () => { describe('Pages', () => { it.each` + visibilityLevel | pagesAccessControlForced | output + ${visibilityOptions.PRIVATE} | ${true} | ${[[visibilityOptions.INTERNAL, 'Only Project Members'], [visibilityOptions.PUBLIC, 'Everyone With Access']]} + ${visibilityOptions.PRIVATE} | ${false} | ${[[visibilityOptions.INTERNAL, 'Only Project Members'], [visibilityOptions.PUBLIC, 'Everyone With Access'], [30, 'Everyone']]} + ${visibilityOptions.INTERNAL} | ${true} | ${[[visibilityOptions.INTERNAL, 'Only Project Members'], [visibilityOptions.PUBLIC, 'Everyone With Access']]} + ${visibilityOptions.INTERNAL} | ${false} | ${[[visibilityOptions.INTERNAL, 'Only Project Members'], [visibilityOptions.PUBLIC, 'Everyone With Access'], [30, 'Everyone']]} + ${visibilityOptions.PUBLIC} | ${true} | ${[[visibilityOptions.INTERNAL, 'Only Project Members'], [visibilityOptions.PUBLIC, 'Everyone With Access']]} + ${visibilityOptions.PUBLIC} | ${false} | ${[[visibilityOptions.INTERNAL, 'Only Project Members'], [visibilityOptions.PUBLIC, 'Everyone With Access'], [30, 'Everyone']]} + `( + 'renders correct options when pagesAccessControlForced is $pagesAccessControlForced and visibilityLevel is $visibilityLevel', + async ({ visibilityLevel, pagesAccessControlForced, output }) => { + wrapper = mountComponent({ + pagesAvailable: true, + pagesAccessControlEnabled: true, + pagesAccessControlForced, + }); + + await findProjectVisibilityLevelInput().trigger('change', visibilityLevel); + + expect(findPagesAccessLevels().props('options')).toStrictEqual(output); + }, + ); + + it.each` pagesAvailable | pagesAccessControlEnabled | visibility ${true} | ${true} | ${'show'} ${true} | ${false} | ${'hide'} |