diff options
Diffstat (limited to 'spec/frontend/pages/projects')
7 files changed, 202 insertions, 16 deletions
diff --git a/spec/frontend/pages/projects/forks/new/components/app_spec.js b/spec/frontend/pages/projects/forks/new/components/app_spec.js index e1820606704..a7b4b9c42bd 100644 --- a/spec/frontend/pages/projects/forks/new/components/app_spec.js +++ b/spec/frontend/pages/projects/forks/new/components/app_spec.js @@ -13,6 +13,7 @@ describe('App component', () => { projectPath: 'project-name', projectDescription: 'some project description', projectVisibility: 'private', + restrictedVisibilityLevels: [], }; const createComponent = (props = {}) => { 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 6d853120232..c80ccfa8256 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 @@ -1,4 +1,5 @@ -import { GlFormInputGroup, GlFormInput, GlForm } from '@gitlab/ui'; +import { GlFormInputGroup, GlFormInput, GlForm, GlFormRadioGroup, GlFormRadio } from '@gitlab/ui'; +import { getByRole, getAllByRole } from '@testing-library/dom'; import { mount, shallowMount } from '@vue/test-utils'; import axios from 'axios'; import AxiosMockAdapter from 'axios-mock-adapter'; @@ -15,6 +16,13 @@ describe('ForkForm component', () => { let wrapper; let axiosMock; + const PROJECT_VISIBILITY_TYPE = { + private: + 'Private Project access must be granted explicitly to each user. If this project is part of a group, access will be granted to members of the group.', + internal: 'Internal The project can be accessed by any logged in user.', + public: 'Public The project can be accessed without any authentication.', + }; + const GON_GITLAB_URL = 'https://gitlab.com'; const GON_API_VERSION = 'v7'; @@ -37,6 +45,7 @@ describe('ForkForm component', () => { projectPath: 'project-name', projectDescription: 'some project description', projectVisibility: 'private', + restrictedVisibilityLevels: [], }; const mockGetRequest = (data = {}, statusCode = httpStatus.OK) => { @@ -61,6 +70,8 @@ describe('ForkForm component', () => { stubs: { GlFormInputGroup, GlFormInput, + GlFormRadioGroup, + GlFormRadio, }, }); }; @@ -81,6 +92,7 @@ describe('ForkForm component', () => { axiosMock.restore(); }); + const findFormSelectOptions = () => wrapper.find('select[name="namespace"]').findAll('option'); const findPrivateRadio = () => wrapper.find('[data-testid="radio-private"]'); const findInternalRadio = () => wrapper.find('[data-testid="radio-internal"]'); const findPublicRadio = () => wrapper.find('[data-testid="radio-public"]'); @@ -203,24 +215,145 @@ describe('ForkForm component', () => { }); describe('visibility level', () => { + it('displays the correct description', () => { + mockGetRequest(); + createComponent(); + + const formRadios = wrapper.findAll(GlFormRadio); + + Object.keys(PROJECT_VISIBILITY_TYPE).forEach((visibilityType, index) => { + expect(formRadios.at(index).text()).toBe(PROJECT_VISIBILITY_TYPE[visibilityType]); + }); + }); + + it('displays all 3 visibility levels', () => { + mockGetRequest(); + createComponent(); + + expect(wrapper.findAll(GlFormRadio)).toHaveLength(3); + }); + + describe('when the namespace is changed', () => { + const namespaces = [ + { + visibility: 'private', + }, + { + visibility: 'internal', + }, + { + visibility: 'public', + }, + ]; + + beforeEach(() => { + mockGetRequest(); + }); + + it('resets the visibility to default "private"', async () => { + createFullComponent({ projectVisibility: 'public' }, { namespaces }); + + expect(wrapper.vm.form.fields.visibility.value).toBe('public'); + await findFormSelectOptions().at(1).setSelected(); + + await wrapper.vm.$nextTick(); + + expect(getByRole(wrapper.element, 'radio', { name: /private/i }).checked).toBe(true); + }); + + it('sets the visibility to be null when restrictedVisibilityLevels is set', async () => { + createFullComponent({ restrictedVisibilityLevels: [10] }, { namespaces }); + + await findFormSelectOptions().at(1).setSelected(); + + await wrapper.vm.$nextTick(); + + const container = getByRole(wrapper.element, 'radiogroup', { name: /visibility/i }); + const visibilityRadios = getAllByRole(container, 'radio'); + expect(visibilityRadios.filter((e) => e.checked)).toHaveLength(0); + }); + }); + + it.each` + project | restrictedVisibilityLevels + ${'private'} | ${[]} + ${'internal'} | ${[]} + ${'public'} | ${[]} + ${'private'} | ${[0]} + ${'private'} | ${[10]} + ${'private'} | ${[20]} + ${'private'} | ${[0, 10]} + ${'private'} | ${[0, 20]} + ${'private'} | ${[10, 20]} + ${'private'} | ${[0, 10, 20]} + ${'internal'} | ${[0]} + ${'internal'} | ${[10]} + ${'internal'} | ${[20]} + ${'internal'} | ${[0, 10]} + ${'internal'} | ${[0, 20]} + ${'internal'} | ${[10, 20]} + ${'internal'} | ${[0, 10, 20]} + ${'public'} | ${[0]} + ${'public'} | ${[10]} + ${'public'} | ${[0, 10]} + ${'public'} | ${[0, 20]} + ${'public'} | ${[10, 20]} + ${'public'} | ${[0, 10, 20]} + `('checks the correct radio button', async ({ project, restrictedVisibilityLevels }) => { + mockGetRequest(); + createFullComponent({ + projectVisibility: project, + restrictedVisibilityLevels, + }); + + if (restrictedVisibilityLevels.length === 0) { + expect(wrapper.find('[name="visibility"]:checked').attributes('value')).toBe(project); + } else { + expect(wrapper.find('[name="visibility"]:checked').exists()).toBe(false); + } + }); + it.each` - project | namespace | privateIsDisabled | internalIsDisabled | publicIsDisabled - ${'private'} | ${'private'} | ${undefined} | ${'true'} | ${'true'} - ${'private'} | ${'internal'} | ${undefined} | ${'true'} | ${'true'} - ${'private'} | ${'public'} | ${undefined} | ${'true'} | ${'true'} - ${'internal'} | ${'private'} | ${undefined} | ${'true'} | ${'true'} - ${'internal'} | ${'internal'} | ${undefined} | ${undefined} | ${'true'} - ${'internal'} | ${'public'} | ${undefined} | ${undefined} | ${'true'} - ${'public'} | ${'private'} | ${undefined} | ${'true'} | ${'true'} - ${'public'} | ${'internal'} | ${undefined} | ${undefined} | ${'true'} - ${'public'} | ${'public'} | ${undefined} | ${undefined} | ${undefined} + project | namespace | privateIsDisabled | internalIsDisabled | publicIsDisabled | restrictedVisibilityLevels + ${'private'} | ${'private'} | ${undefined} | ${'true'} | ${'true'} | ${[]} + ${'private'} | ${'internal'} | ${undefined} | ${'true'} | ${'true'} | ${[]} + ${'private'} | ${'public'} | ${undefined} | ${'true'} | ${'true'} | ${[]} + ${'internal'} | ${'private'} | ${undefined} | ${'true'} | ${'true'} | ${[]} + ${'internal'} | ${'internal'} | ${undefined} | ${undefined} | ${'true'} | ${[]} + ${'internal'} | ${'public'} | ${undefined} | ${undefined} | ${'true'} | ${[]} + ${'public'} | ${'private'} | ${undefined} | ${'true'} | ${'true'} | ${[]} + ${'public'} | ${'internal'} | ${undefined} | ${undefined} | ${'true'} | ${[]} + ${'public'} | ${'public'} | ${undefined} | ${undefined} | ${undefined} | ${[]} + ${'private'} | ${'private'} | ${undefined} | ${'true'} | ${'true'} | ${[0]} + ${'internal'} | ${'internal'} | ${'true'} | ${undefined} | ${'true'} | ${[0]} + ${'public'} | ${'public'} | ${'true'} | ${undefined} | ${undefined} | ${[0]} + ${'private'} | ${'private'} | ${undefined} | ${'true'} | ${'true'} | ${[10]} + ${'internal'} | ${'internal'} | ${undefined} | ${'true'} | ${'true'} | ${[10]} + ${'public'} | ${'public'} | ${undefined} | ${'true'} | ${undefined} | ${[10]} + ${'private'} | ${'private'} | ${undefined} | ${'true'} | ${'true'} | ${[20]} + ${'internal'} | ${'internal'} | ${undefined} | ${undefined} | ${'true'} | ${[20]} + ${'public'} | ${'public'} | ${undefined} | ${undefined} | ${'true'} | ${[20]} + ${'private'} | ${'private'} | ${undefined} | ${'true'} | ${'true'} | ${[10, 20]} + ${'internal'} | ${'internal'} | ${undefined} | ${'true'} | ${'true'} | ${[10, 20]} + ${'public'} | ${'public'} | ${undefined} | ${'true'} | ${'true'} | ${[10, 20]} + ${'private'} | ${'private'} | ${undefined} | ${'true'} | ${'true'} | ${[0, 10, 20]} + ${'internal'} | ${'internal'} | ${undefined} | ${'true'} | ${'true'} | ${[0, 10, 20]} + ${'public'} | ${'public'} | ${undefined} | ${'true'} | ${'true'} | ${[0, 10, 20]} `( 'sets appropriate radio button disabled state', - async ({ project, namespace, privateIsDisabled, internalIsDisabled, publicIsDisabled }) => { + async ({ + project, + namespace, + privateIsDisabled, + internalIsDisabled, + publicIsDisabled, + restrictedVisibilityLevels, + }) => { mockGetRequest(); createComponent( { projectVisibility: project, + restrictedVisibilityLevels, }, { form: { fields: { namespace: { value: { visibility: namespace } } } }, @@ -235,7 +368,7 @@ describe('ForkForm component', () => { }); describe('onSubmit', () => { - beforeEach(() => { + const setupComponent = (fields = {}) => { jest.spyOn(urlUtility, 'redirectTo').mockImplementation(); mockGetRequest(); @@ -245,9 +378,14 @@ describe('ForkForm component', () => { namespaces: MOCK_NAMESPACES_RESPONSE, form: { state: true, + ...fields, }, }, ); + }; + + beforeEach(() => { + setupComponent(); }); const selectedMockNamespaceIndex = 1; @@ -279,6 +417,22 @@ describe('ForkForm component', () => { expect(urlUtility.redirectTo).not.toHaveBeenCalled(); }); + + it('does not make POST request if no visbility is checked', async () => { + jest.spyOn(axios, 'post'); + + setupComponent({ + fields: { + visibility: { + value: null, + }, + }, + }); + + await submitForm(); + + expect(axios.post).not.toHaveBeenCalled(); + }); }); describe('with valid form', () => { @@ -330,7 +484,7 @@ describe('ForkForm component', () => { expect(urlUtility.redirectTo).not.toHaveBeenCalled(); expect(createFlash).toHaveBeenCalledWith({ - message: dummyError, + message: 'An error occurred while forking the project. Please try again.', }); }); }); diff --git a/spec/frontend/pages/projects/forks/new/components/fork_groups_list_spec.js b/spec/frontend/pages/projects/forks/new/components/fork_groups_list_spec.js index e7ac837a4c8..9f8dbf3d542 100644 --- a/spec/frontend/pages/projects/forks/new/components/fork_groups_list_spec.js +++ b/spec/frontend/pages/projects/forks/new/components/fork_groups_list_spec.js @@ -3,7 +3,7 @@ import { shallowMount } from '@vue/test-utils'; import AxiosMockAdapter from 'axios-mock-adapter'; import { nextTick } from 'vue'; import waitForPromises from 'helpers/wait_for_promises'; -import { deprecatedCreateFlash as createFlash } from '~/flash'; +import createFlash from '~/flash'; import axios from '~/lib/utils/axios_utils'; import ForkGroupsList from '~/pages/projects/forks/new/components/fork_groups_list.vue'; import ForkGroupsListItem from '~/pages/projects/forks/new/components/fork_groups_list_item.vue'; diff --git a/spec/frontend/pages/projects/graphs/__snapshots__/code_coverage_spec.js.snap b/spec/frontend/pages/projects/graphs/__snapshots__/code_coverage_spec.js.snap index c4c48ea7517..4ba9120d196 100644 --- a/spec/frontend/pages/projects/graphs/__snapshots__/code_coverage_spec.js.snap +++ b/spec/frontend/pages/projects/graphs/__snapshots__/code_coverage_spec.js.snap @@ -66,7 +66,7 @@ exports[`Code Coverage when fetching data is successful matches the snapshot 1`] <gl-area-chart-stub annotations="" data="[object Object]" - formattooltiptext="function () { [native code] }" + formattooltiptext="[Function]" height="200" includelegendavgmax="true" legendaveragetext="Avg" diff --git a/spec/frontend/pages/projects/learn_gitlab/components/__snapshots__/learn_gitlab_a_spec.js.snap b/spec/frontend/pages/projects/learn_gitlab/components/__snapshots__/learn_gitlab_a_spec.js.snap index 350669433f0..59b42de2485 100644 --- a/spec/frontend/pages/projects/learn_gitlab/components/__snapshots__/learn_gitlab_a_spec.js.snap +++ b/spec/frontend/pages/projects/learn_gitlab/components/__snapshots__/learn_gitlab_a_spec.js.snap @@ -92,6 +92,7 @@ exports[`Learn GitLab Design A renders correctly 1`] = ` aria-hidden="true" class="gl-icon s16" data-testid="completed-icon" + role="img" > <use href="#check-circle-filled" @@ -114,6 +115,7 @@ exports[`Learn GitLab Design A renders correctly 1`] = ` aria-hidden="true" class="gl-icon s16" data-testid="completed-icon" + role="img" > <use href="#check-circle-filled" diff --git a/spec/frontend/pages/projects/learn_gitlab/components/__snapshots__/learn_gitlab_b_spec.js.snap b/spec/frontend/pages/projects/learn_gitlab/components/__snapshots__/learn_gitlab_b_spec.js.snap index c9d8ab4566c..091edc7505c 100644 --- a/spec/frontend/pages/projects/learn_gitlab/components/__snapshots__/learn_gitlab_b_spec.js.snap +++ b/spec/frontend/pages/projects/learn_gitlab/components/__snapshots__/learn_gitlab_b_spec.js.snap @@ -81,6 +81,7 @@ exports[`Learn GitLab Design B renders correctly 1`] = ` aria-hidden="true" class="gl-text-green-500 gl-icon s16" data-testid="completed-icon" + role="img" > <use href="#check-circle-filled" @@ -142,6 +143,7 @@ exports[`Learn GitLab Design B renders correctly 1`] = ` aria-hidden="true" class="gl-text-green-500 gl-icon s16" data-testid="completed-icon" + role="img" > <use href="#check-circle-filled" diff --git a/spec/frontend/pages/projects/pipeline_schedules/shared/components/interval_pattern_input_spec.js b/spec/frontend/pages/projects/pipeline_schedules/shared/components/interval_pattern_input_spec.js index 27cd0fe34bf..de0d70a07d7 100644 --- a/spec/frontend/pages/projects/pipeline_schedules/shared/components/interval_pattern_input_spec.js +++ b/spec/frontend/pages/projects/pipeline_schedules/shared/components/interval_pattern_input_spec.js @@ -1,3 +1,4 @@ +import { GlIcon } from '@gitlab/ui'; import { mount } from '@vue/test-utils'; import { trimText } from 'helpers/text_helper'; import IntervalPatternInput from '~/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue'; @@ -27,6 +28,7 @@ describe('Interval Pattern Input Component', () => { const findAllLabels = () => wrapper.findAll('label'); const findSelectedRadio = () => wrapper.findAll('input[type="radio"]').wrappers.find((x) => x.element.checked); + const findIcon = () => wrapper.findComponent(GlIcon); const findSelectedRadioKey = () => findSelectedRadio()?.attributes('data-testid'); const selectEveryDayRadio = () => findEveryDayRadio().trigger('click'); const selectEveryWeekRadio = () => findEveryWeekRadio().trigger('click'); @@ -40,6 +42,11 @@ describe('Interval Pattern Input Component', () => { wrapper = mount(IntervalPatternInput, { propsData: { ...props }, + provide: { + glFeatures: { + ciDailyLimitForPipelineSchedules: true, + }, + }, data() { return { randomHour: data?.hour || mockHour, @@ -202,4 +209,24 @@ describe('Interval Pattern Input Component', () => { expect(findSelectedRadioKey()).toBe(customKey); }); }); + + describe('Custom cron syntax quota info', () => { + it('the info message includes 5 minutes', () => { + createWrapper({ dailyLimit: '288' }); + + expect(findIcon().attributes('title')).toContain('5 minutes'); + }); + + it('the info message includes 60 minutes', () => { + createWrapper({ dailyLimit: '24' }); + + expect(findIcon().attributes('title')).toContain('60 minutes'); + }); + + it('the info message icon is not shown when there is no daily limit', () => { + createWrapper(); + + expect(findIcon().exists()).toBe(false); + }); + }); }); |