diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-06-20 11:10:13 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-06-20 11:10:13 +0000 |
commit | 0ea3fcec397b69815975647f5e2aa5fe944a8486 (patch) | |
tree | 7979381b89d26011bcf9bdc989a40fcc2f1ed4ff /spec/frontend/projects | |
parent | 72123183a20411a36d607d70b12d57c484394c8e (diff) | |
download | gitlab-ce-0ea3fcec397b69815975647f5e2aa5fe944a8486.tar.gz |
Add latest changes from gitlab-org/gitlab@15-1-stable-eev15.1.0-rc42
Diffstat (limited to 'spec/frontend/projects')
8 files changed, 287 insertions, 58 deletions
diff --git a/spec/frontend/projects/clusters_deprecation_slert/components/clusters_deprecation_alert_spec.js b/spec/frontend/projects/clusters_deprecation_slert/components/clusters_deprecation_alert_spec.js new file mode 100644 index 00000000000..d230b96ad82 --- /dev/null +++ b/spec/frontend/projects/clusters_deprecation_slert/components/clusters_deprecation_alert_spec.js @@ -0,0 +1,45 @@ +import { GlAlert, GlSprintf } from '@gitlab/ui'; +import { shallowMount } from '@vue/test-utils'; +import ClustersDeprecationAlert from '~/projects/clusters_deprecation_alert/components/clusters_deprecation_alert.vue'; + +const message = 'Alert message'; + +describe('ClustersDeprecationAlert', () => { + let wrapper; + + const provideData = { + message, + }; + + const findAlert = () => wrapper.findComponent(GlAlert); + + const createComponent = () => { + wrapper = shallowMount(ClustersDeprecationAlert, { + provide: provideData, + stubs: { + GlSprintf, + }, + }); + }; + + beforeEach(() => { + createComponent(); + }); + + afterEach(() => { + wrapper.destroy(); + }); + + describe('template', () => { + it('should render a non-dismissible warning alert', () => { + expect(findAlert().props()).toMatchObject({ + dismissible: false, + variant: 'warning', + }); + }); + + it('should display the correct message', () => { + expect(findAlert().text()).toBe(message); + }); + }); +}); diff --git a/spec/frontend/projects/compare/components/revision_card_spec.js b/spec/frontend/projects/compare/components/revision_card_spec.js index 57906045337..a741393fcf3 100644 --- a/spec/frontend/projects/compare/components/revision_card_spec.js +++ b/spec/frontend/projects/compare/components/revision_card_spec.js @@ -1,4 +1,3 @@ -import { GlCard } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; import RepoDropdown from '~/projects/compare/components/repo_dropdown.vue'; import RevisionCard from '~/projects/compare/components/revision_card.vue'; @@ -14,9 +13,6 @@ describe('RepoDropdown component', () => { ...defaultProps, ...props, }, - stubs: { - GlCard, - }, }); }; @@ -29,8 +25,10 @@ describe('RepoDropdown component', () => { createComponent(); }); + const RevisionCardWrapper = () => wrapper.find('.revision-card'); + it('displays revision text', () => { - expect(wrapper.find(GlCard).text()).toContain(defaultProps.revisionText); + expect(RevisionCardWrapper().text()).toContain(defaultProps.revisionText); }); it('renders RepoDropdown component', () => { diff --git a/spec/frontend/projects/new/components/new_project_push_tip_popover_spec.js b/spec/frontend/projects/new/components/new_project_push_tip_popover_spec.js index 42259a5c392..f50dd393174 100644 --- a/spec/frontend/projects/new/components/new_project_push_tip_popover_spec.js +++ b/spec/frontend/projects/new/components/new_project_push_tip_popover_spec.js @@ -57,7 +57,7 @@ describe('New project push tip popover', () => { }); expect(findFormInput().attributes()).toMatchObject({ 'aria-label': 'Push project from command line', - readonly: 'readonly', + readonly: '', }); }); diff --git a/spec/frontend/projects/pipelines/charts/components/app_spec.js b/spec/frontend/projects/pipelines/charts/components/app_spec.js index 9c94925c817..98c7856a61a 100644 --- a/spec/frontend/projects/pipelines/charts/components/app_spec.js +++ b/spec/frontend/projects/pipelines/charts/components/app_spec.js @@ -13,6 +13,7 @@ jest.mock('~/lib/utils/url_utility'); const DeploymentFrequencyChartsStub = { name: 'DeploymentFrequencyCharts', render: () => {} }; const LeadTimeChartsStub = { name: 'LeadTimeCharts', render: () => {} }; +const TimeToRestoreServiceChartsStub = { name: 'TimeToRestoreServiceCharts', render: () => {} }; const ProjectQualitySummaryStub = { name: 'ProjectQualitySummary', render: () => {} }; describe('ProjectsPipelinesChartsApp', () => { @@ -31,6 +32,7 @@ describe('ProjectsPipelinesChartsApp', () => { stubs: { DeploymentFrequencyCharts: DeploymentFrequencyChartsStub, LeadTimeCharts: LeadTimeChartsStub, + TimeToRestoreServiceCharts: TimeToRestoreServiceChartsStub, ProjectQualitySummary: ProjectQualitySummaryStub, }, }, @@ -47,6 +49,7 @@ describe('ProjectsPipelinesChartsApp', () => { const findAllGlTabs = () => wrapper.findAll(GlTab); const findGlTabAtIndex = (index) => findAllGlTabs().at(index); const findLeadTimeCharts = () => wrapper.find(LeadTimeChartsStub); + const findTimeToRestoreServiceCharts = () => wrapper.find(TimeToRestoreServiceChartsStub); const findDeploymentFrequencyCharts = () => wrapper.find(DeploymentFrequencyChartsStub); const findPipelineCharts = () => wrapper.find(PipelineCharts); const findProjectQualitySummary = () => wrapper.find(ProjectQualitySummaryStub); @@ -62,6 +65,7 @@ describe('ProjectsPipelinesChartsApp', () => { expect(findGlTabAtIndex(0).attributes('title')).toBe('Pipelines'); expect(findGlTabAtIndex(1).attributes('title')).toBe('Deployment frequency'); expect(findGlTabAtIndex(2).attributes('title')).toBe('Lead time'); + expect(findGlTabAtIndex(3).attributes('title')).toBe('Time to restore service'); }); it('renders the pipeline charts', () => { @@ -76,6 +80,10 @@ describe('ProjectsPipelinesChartsApp', () => { expect(findLeadTimeCharts().exists()).toBe(true); }); + it('renders the time to restore service charts', () => { + expect(findTimeToRestoreServiceCharts().exists()).toBe(true); + }); + it('renders the project quality summary', () => { expect(findProjectQualitySummary().exists()).toBe(true); }); @@ -123,10 +131,11 @@ describe('ProjectsPipelinesChartsApp', () => { describe('event tracking', () => { it.each` - testId | event - ${'pipelines-tab'} | ${'p_analytics_ci_cd_pipelines'} - ${'deployment-frequency-tab'} | ${'p_analytics_ci_cd_deployment_frequency'} - ${'lead-time-tab'} | ${'p_analytics_ci_cd_lead_time'} + testId | event + ${'pipelines-tab'} | ${'p_analytics_ci_cd_pipelines'} + ${'deployment-frequency-tab'} | ${'p_analytics_ci_cd_deployment_frequency'} + ${'lead-time-tab'} | ${'p_analytics_ci_cd_lead_time'} + ${'time-to-restore-service-tab'} | ${'p_analytics_ci_cd_time_to_restore_service'} `('tracks the $event event when clicked', ({ testId, event }) => { jest.spyOn(API, 'trackRedisHllUserEvent'); @@ -141,12 +150,13 @@ describe('ProjectsPipelinesChartsApp', () => { describe('when provided with a query param', () => { it.each` - chart | tab - ${'lead-time'} | ${'2'} - ${'deployment-frequency'} | ${'1'} - ${'pipelines'} | ${'0'} - ${'fake'} | ${'0'} - ${''} | ${'0'} + chart | tab + ${'time-to-restore-service'} | ${'3'} + ${'lead-time'} | ${'2'} + ${'deployment-frequency'} | ${'1'} + ${'pipelines'} | ${'0'} + ${'fake'} | ${'0'} + ${''} | ${'0'} `('shows the correct tab for URL parameter "$chart"', ({ chart, tab }) => { setWindowLocation(`${TEST_HOST}/gitlab-org/gitlab-test/-/pipelines/charts?chart=${chart}`); getParameterValues.mockImplementation((name) => { diff --git a/spec/frontend/projects/project_new_spec.js b/spec/frontend/projects/project_new_spec.js index fe325343da8..3034037fb1d 100644 --- a/spec/frontend/projects/project_new_spec.js +++ b/spec/frontend/projects/project_new_spec.js @@ -1,4 +1,3 @@ -import $ from 'jquery'; import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures'; import { TEST_HOST } from 'helpers/test_constants'; import projectNew from '~/projects/project_new'; @@ -8,6 +7,9 @@ describe('New Project', () => { let $projectPath; let $projectName; + const mockKeyup = (el) => el.dispatchEvent(new KeyboardEvent('keyup')); + const mockChange = (el) => el.dispatchEvent(new Event('change')); + beforeEach(() => { setHTMLFixture(` <div class='toggle-import-form'> @@ -29,122 +31,127 @@ describe('New Project', () => { </div> `); - $projectImportUrl = $('#project_import_url'); - $projectPath = $('#project_path'); - $projectName = $('#project_name'); + $projectImportUrl = document.querySelector('#project_import_url'); + $projectPath = document.querySelector('#project_path'); + $projectName = document.querySelector('#project_name'); }); afterEach(() => { resetHTMLFixture(); }); + const setValueAndTriggerEvent = (el, value, event) => { + event(el); + el.value = value; + }; + describe('deriveProjectPathFromUrl', () => { const dummyImportUrl = `${TEST_HOST}/dummy/import/url.git`; beforeEach(() => { projectNew.bindEvents(); - $projectPath.val('').keyup().val(dummyImportUrl); + setValueAndTriggerEvent($projectPath, dummyImportUrl, mockKeyup); }); it('does not change project path for disabled $projectImportUrl', () => { - $projectImportUrl.prop('disabled', true); + $projectImportUrl.setAttribute('disabled', true); projectNew.deriveProjectPathFromUrl($projectImportUrl); - expect($projectPath.val()).toEqual(dummyImportUrl); + expect($projectPath.value).toEqual(dummyImportUrl); }); describe('for enabled $projectImportUrl', () => { beforeEach(() => { - $projectImportUrl.prop('disabled', false); + $projectImportUrl.setAttribute('disabled', false); }); it('does not change project path if it is set by user', () => { - $projectPath.keyup(); + mockKeyup($projectPath); projectNew.deriveProjectPathFromUrl($projectImportUrl); - expect($projectPath.val()).toEqual(dummyImportUrl); + expect($projectPath.value).toEqual(dummyImportUrl); }); it('does not change project path for empty $projectImportUrl', () => { - $projectImportUrl.val(''); + $projectImportUrl.value = ''; projectNew.deriveProjectPathFromUrl($projectImportUrl); - expect($projectPath.val()).toEqual(dummyImportUrl); + expect($projectPath.value).toEqual(dummyImportUrl); }); it('does not change project path for whitespace $projectImportUrl', () => { - $projectImportUrl.val(' '); + $projectImportUrl.value = ' '; projectNew.deriveProjectPathFromUrl($projectImportUrl); - expect($projectPath.val()).toEqual(dummyImportUrl); + expect($projectPath.value).toEqual(dummyImportUrl); }); it('does not change project path for $projectImportUrl without slashes', () => { - $projectImportUrl.val('has-no-slash'); + $projectImportUrl.value = 'has-no-slash'; projectNew.deriveProjectPathFromUrl($projectImportUrl); - expect($projectPath.val()).toEqual(dummyImportUrl); + expect($projectPath.value).toEqual(dummyImportUrl); }); it('changes project path to last $projectImportUrl component', () => { - $projectImportUrl.val('/this/is/last'); + $projectImportUrl.value = '/this/is/last'; projectNew.deriveProjectPathFromUrl($projectImportUrl); - expect($projectPath.val()).toEqual('last'); + expect($projectPath.value).toEqual('last'); }); it('ignores trailing slashes in $projectImportUrl', () => { - $projectImportUrl.val('/has/trailing/slash/'); + $projectImportUrl.value = '/has/trailing/slash/'; projectNew.deriveProjectPathFromUrl($projectImportUrl); - expect($projectPath.val()).toEqual('slash'); + expect($projectPath.value).toEqual('slash'); }); it('ignores fragment identifier in $projectImportUrl', () => { - $projectImportUrl.val('/this/has/a#fragment-identifier/'); + $projectImportUrl.value = '/this/has/a#fragment-identifier/'; projectNew.deriveProjectPathFromUrl($projectImportUrl); - expect($projectPath.val()).toEqual('a'); + expect($projectPath.value).toEqual('a'); }); it('ignores query string in $projectImportUrl', () => { - $projectImportUrl.val('/url/with?query=string'); + $projectImportUrl.value = '/url/with?query=string'; projectNew.deriveProjectPathFromUrl($projectImportUrl); - expect($projectPath.val()).toEqual('with'); + expect($projectPath.value).toEqual('with'); }); it('ignores trailing .git in $projectImportUrl', () => { - $projectImportUrl.val('/repository.git'); + $projectImportUrl.value = '/repository.git'; projectNew.deriveProjectPathFromUrl($projectImportUrl); - expect($projectPath.val()).toEqual('repository'); + expect($projectPath.value).toEqual('repository'); }); it('changes project path for HTTPS URL in $projectImportUrl', () => { - $projectImportUrl.val('https://gitlab.company.com/group/project.git'); + $projectImportUrl.value = 'https://gitlab.company.com/group/project.git'; projectNew.deriveProjectPathFromUrl($projectImportUrl); - expect($projectPath.val()).toEqual('project'); + expect($projectPath.value).toEqual('project'); }); it('changes project path for SSH URL in $projectImportUrl', () => { - $projectImportUrl.val('git@gitlab.com:gitlab-org/gitlab-ce.git'); + $projectImportUrl.value = 'git@gitlab.com:gitlab-org/gitlab-ce.git'; projectNew.deriveProjectPathFromUrl($projectImportUrl); - expect($projectPath.val()).toEqual('gitlab-ce'); + expect($projectPath.value).toEqual('gitlab-ce'); }); }); }); @@ -152,27 +159,27 @@ describe('New Project', () => { describe('deriveSlugFromProjectName', () => { beforeEach(() => { projectNew.bindEvents(); - $projectName.val('').keyup(); + setValueAndTriggerEvent($projectName, '', mockKeyup); }); it('converts project name to lower case and dash-limited slug', () => { const dummyProjectName = 'My Awesome Project'; - $projectName.val(dummyProjectName); + $projectName.value = dummyProjectName; projectNew.onProjectNameChange($projectName, $projectPath); - expect($projectPath.val()).toEqual('my-awesome-project'); + expect($projectPath.value).toEqual('my-awesome-project'); }); it('does not add additional dashes in the slug if the project name already contains dashes', () => { const dummyProjectName = 'My-Dash-Delimited Awesome Project'; - $projectName.val(dummyProjectName); + $projectName.value = dummyProjectName; projectNew.onProjectNameChange($projectName, $projectPath); - expect($projectPath.val()).toEqual('my-dash-delimited-awesome-project'); + expect($projectPath.value).toEqual('my-dash-delimited-awesome-project'); }); }); @@ -182,27 +189,28 @@ describe('New Project', () => { beforeEach(() => { projectNew.bindEvents(); - $projectPath.val('').change(); + setValueAndTriggerEvent($projectPath, '', mockChange); }); it('converts slug to humanized project name', () => { - $projectPath.val(dummyProjectPath); + $projectPath.value = dummyProjectPath; + mockChange($projectPath); projectNew.onProjectPathChange($projectName, $projectPath); - expect($projectName.val()).toEqual('My Awesome Project'); + expect($projectName.value).toEqual('My Awesome Project'); }); it('does not convert slug to humanized project name if a project name already exists', () => { - $projectName.val(dummyProjectName); - $projectPath.val(dummyProjectPath); + $projectName.value = dummyProjectName; + $projectPath.value = dummyProjectPath; projectNew.onProjectPathChange( $projectName, $projectPath, - $projectName.val().trim().length > 0, + $projectName.value.trim().length > 0, ); - expect($projectName.val()).toEqual(dummyProjectName); + expect($projectName.value).toEqual(dummyProjectName); }); }); }); diff --git a/spec/frontend/projects/settings/branch_rules/branch_dropdown_spec.js b/spec/frontend/projects/settings/branch_rules/branch_dropdown_spec.js new file mode 100644 index 00000000000..5997c2a083c --- /dev/null +++ b/spec/frontend/projects/settings/branch_rules/branch_dropdown_spec.js @@ -0,0 +1,101 @@ +import Vue, { nextTick } from 'vue'; +import VueApollo from 'vue-apollo'; +import { GlDropdown, GlSearchBoxByType, GlDropdownItem } from '@gitlab/ui'; +import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; +import BranchDropdown, { + i18n, +} from '~/projects/settings/branch_rules/components/branch_dropdown.vue'; +import createMockApollo from 'helpers/mock_apollo_helper'; +import branchesQuery from '~/projects/settings/branch_rules/queries/branches.query.graphql'; +import waitForPromises from 'helpers/wait_for_promises'; +import { createAlert } from '~/flash'; + +Vue.use(VueApollo); +jest.mock('~/flash'); + +describe('Branch dropdown', () => { + let wrapper; + + const projectPath = 'test/project'; + const value = 'main'; + const mockBranchNames = ['test 1', 'test 2']; + + const createComponent = async ({ branchNames = mockBranchNames, resolver } = {}) => { + const mockResolver = + resolver || + jest.fn().mockResolvedValue({ + data: { project: { id: '1', repository: { branchNames } } }, + }); + const apolloProvider = createMockApollo([[branchesQuery, mockResolver]]); + + wrapper = shallowMountExtended(BranchDropdown, { + apolloProvider, + propsData: { projectPath, value }, + }); + + await waitForPromises(); + }; + + const findGlDropdown = () => wrapper.find(GlDropdown); + const findAllBranches = () => wrapper.findAll(GlDropdownItem); + const findNoDataMsg = () => wrapper.findByTestId('no-data'); + const findGlSearchBoxByType = () => wrapper.find(GlSearchBoxByType); + const findWildcardButton = () => wrapper.findByTestId('create-wildcard-button'); + const setSearchTerm = (searchTerm) => findGlSearchBoxByType().vm.$emit('input', searchTerm); + + beforeEach(() => createComponent()); + + it('renders a GlDropdown component with the correct props', () => { + expect(findGlDropdown().props()).toMatchObject({ text: value }); + }); + + it('renders GlDropdownItem components for each branch', () => { + expect(findAllBranches().length).toBe(mockBranchNames.length); + + mockBranchNames.forEach((branchName, index) => + expect(findAllBranches().at(index).text()).toBe(branchName), + ); + }); + + it('emits `select` with the branch name when a branch is clicked', () => { + findAllBranches().at(0).vm.$emit('click'); + expect(wrapper.emitted('input')).toEqual([[mockBranchNames[0]]]); + }); + + describe('branch searching', () => { + it('displays a message if no branches can be found', async () => { + await createComponent({ branchNames: [] }); + + expect(findNoDataMsg().text()).toBe(i18n.noMatch); + }); + + it('displays a loading state while search request is in flight', async () => { + setSearchTerm('test'); + await nextTick(); + + expect(findGlSearchBoxByType().props()).toMatchObject({ isLoading: true }); + }); + + it('renders a wildcard button', async () => { + const searchTerm = 'test-*'; + setSearchTerm(searchTerm); + await nextTick(); + + expect(findWildcardButton().exists()).toBe(true); + findWildcardButton().vm.$emit('click'); + expect(wrapper.emitted('createWildcard')).toEqual([[searchTerm]]); + }); + }); + + it('displays an error message if fetch failed', async () => { + const error = new Error('an error occurred'); + const resolver = jest.fn().mockRejectedValueOnce(error); + await createComponent({ resolver }); + + expect(createAlert).toHaveBeenCalledWith({ + message: i18n.fetchBranchesError, + captureError: true, + error, + }); + }); +}); diff --git a/spec/frontend/projects/settings/branch_rules/rule_edit_spec.js b/spec/frontend/projects/settings/branch_rules/rule_edit_spec.js new file mode 100644 index 00000000000..66ae6ddc02d --- /dev/null +++ b/spec/frontend/projects/settings/branch_rules/rule_edit_spec.js @@ -0,0 +1,49 @@ +import { nextTick } from 'vue'; +import { getParameterByName } from '~/lib/utils/url_utility'; +import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; +import RuleEdit from '~/projects/settings/branch_rules/components/rule_edit.vue'; +import BranchDropdown from '~/projects/settings/branch_rules/components/branch_dropdown.vue'; + +jest.mock('~/lib/utils/url_utility', () => ({ + getParameterByName: jest.fn().mockImplementation(() => 'main'), +})); + +describe('Edit branch rule', () => { + let wrapper; + const projectPath = 'test/testing'; + + const createComponent = () => { + wrapper = shallowMountExtended(RuleEdit, { propsData: { projectPath } }); + }; + + const findBranchDropdown = () => wrapper.find(BranchDropdown); + + beforeEach(() => createComponent()); + + it('gets the branch param from url', () => { + expect(getParameterByName).toHaveBeenCalledWith('branch'); + }); + + describe('BranchDropdown', () => { + it('renders a BranchDropdown component with the correct props', () => { + expect(findBranchDropdown().props()).toMatchObject({ + projectPath, + value: 'main', + }); + }); + + it('sets the correct value when `input` is emitted', async () => { + const branch = 'test'; + findBranchDropdown().vm.$emit('input', branch); + await nextTick(); + expect(findBranchDropdown().props('value')).toBe(branch); + }); + + it('sets the correct value when `createWildcard` is emitted', async () => { + const wildcard = 'test-*'; + findBranchDropdown().vm.$emit('createWildcard', wildcard); + await nextTick(); + expect(findBranchDropdown().props('value')).toBe(wildcard); + }); + }); +}); diff --git a/spec/frontend/projects/settings/repository/branch_rules/app_spec.js b/spec/frontend/projects/settings/repository/branch_rules/app_spec.js new file mode 100644 index 00000000000..e12c3aeedd6 --- /dev/null +++ b/spec/frontend/projects/settings/repository/branch_rules/app_spec.js @@ -0,0 +1,18 @@ +import { mountExtended } from 'helpers/vue_test_utils_helper'; +import BranchRules from '~/projects/settings/repository/branch_rules/app.vue'; + +describe('Branch rules app', () => { + let wrapper; + + const createComponent = () => { + wrapper = mountExtended(BranchRules); + }; + + const findTitle = () => wrapper.find('strong'); + + beforeEach(() => createComponent()); + + it('renders a title', () => { + expect(findTitle().text()).toBe('Branch'); + }); +}); |