diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-09-12 15:11:33 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-09-12 15:11:33 +0300 |
commit | 0127158127cb4f21b06ea39cc243d8ac17fc3e41 (patch) | |
tree | fa430e57fd398272df6fda9bbf9a8dd19af063f9 /spec/frontend | |
parent | fab43fda656e091104f79668db65f7c5e2a2e68c (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/frontend')
10 files changed, 328 insertions, 15 deletions
diff --git a/spec/frontend/ci/job_details/components/sidebar/external_links_block_spec.js b/spec/frontend/ci/job_details/components/sidebar/external_links_block_spec.js new file mode 100644 index 00000000000..1f2c448f1c6 --- /dev/null +++ b/spec/frontend/ci/job_details/components/sidebar/external_links_block_spec.js @@ -0,0 +1,49 @@ +import { GlLink } from '@gitlab/ui'; +import { mountExtended } from 'helpers/vue_test_utils_helper'; +import ExternalLinksBlock from '~/ci/job_details/components/sidebar/external_links_block.vue'; + +describe('External links block', () => { + let wrapper; + + const createWrapper = (propsData) => { + wrapper = mountExtended(ExternalLinksBlock, { + propsData: { + ...propsData, + }, + }); + }; + + const findAllLinks = () => wrapper.findAllComponents(GlLink); + const findLink = () => findAllLinks().at(0); + + it('renders a list of links', () => { + createWrapper({ + externalLinks: [ + { + label: 'URL 1', + url: 'https://url1.example.com/', + }, + { + label: 'URL 2', + url: 'https://url2.example.com/', + }, + ], + }); + + expect(findAllLinks()).toHaveLength(2); + }); + + it('renders a link', () => { + createWrapper({ + externalLinks: [ + { + label: 'Example URL', + url: 'https://example.com/', + }, + ], + }); + + expect(findLink().text()).toBe('Example URL'); + expect(findLink().attributes('href')).toBe('https://example.com/'); + }); +}); diff --git a/spec/frontend/ci/job_details/components/sidebar/sidebar_spec.js b/spec/frontend/ci/job_details/components/sidebar/sidebar_spec.js index 31767b1d0df..88e1f41b270 100644 --- a/spec/frontend/ci/job_details/components/sidebar/sidebar_spec.js +++ b/spec/frontend/ci/job_details/components/sidebar/sidebar_spec.js @@ -5,6 +5,7 @@ import { extendedWrapper } from 'helpers/vue_test_utils_helper'; import axios from '~/lib/utils/axios_utils'; import { HTTP_STATUS_OK } from '~/lib/utils/http_status'; import ArtifactsBlock from '~/ci/job_details/components/sidebar/artifacts_block.vue'; +import ExternalLinksBlock from '~/ci/job_details/components/sidebar/external_links_block.vue'; import JobRetryForwardDeploymentModal from '~/ci/job_details/components/sidebar/job_retry_forward_deployment_modal.vue'; import JobsContainer from '~/ci/job_details/components/sidebar/jobs_container.vue'; import Sidebar from '~/ci/job_details/components/sidebar/sidebar.vue'; @@ -20,6 +21,7 @@ describe('Sidebar details block', () => { const forwardDeploymentFailure = 'forward_deployment_failure'; const findModal = () => wrapper.findComponent(JobRetryForwardDeploymentModal); const findArtifactsBlock = () => wrapper.findComponent(ArtifactsBlock); + const findExternalLinksBlock = () => wrapper.findComponent(ExternalLinksBlock); const findJobStagesDropdown = () => wrapper.findComponent(StagesDropdown); const findJobsContainer = () => wrapper.findComponent(JobsContainer); @@ -181,4 +183,40 @@ describe('Sidebar details block', () => { expect(findArtifactsBlock().exists()).toBe(true); }); }); + + describe('external links', () => { + beforeEach(() => { + createWrapper(); + }); + + it('external links block is not shown if there are no external links', () => { + expect(findExternalLinksBlock().exists()).toBe(false); + }); + + it('external links block is shown if there are external links', async () => { + store.state.job.annotations = [ + { + name: 'external_links', + data: [ + { + external_link: { + label: 'URL 1', + url: 'https://url1.example.com/', + }, + }, + { + external_link: { + label: 'URL 2', + url: 'https://url2.example.com/', + }, + }, + ], + }, + ]; + + await nextTick(); + + expect(findExternalLinksBlock().exists()).toBe(true); + }); + }); }); diff --git a/spec/frontend/ci/job_details/utils_spec.js b/spec/frontend/ci/job_details/utils_spec.js index 37c3e60f3cf..7b5a97f3939 100644 --- a/spec/frontend/ci/job_details/utils_spec.js +++ b/spec/frontend/ci/job_details/utils_spec.js @@ -1,4 +1,4 @@ -import { compactJobLog } from '~/ci/job_details/utils'; +import { compactJobLog, filterAnnotations } from '~/ci/job_details/utils'; import { mockJobLog } from 'jest/ci/jobs_mock_data'; describe('Job utils', () => { @@ -219,4 +219,47 @@ describe('Job utils', () => { expect(compactJobLog(mockJobLog)).toStrictEqual(expectedResults); }); }); + + describe('filterAnnotations', () => { + it('filters annotations by type', () => { + const data = [ + { + name: 'b', + data: [ + { + dummy: {}, + }, + { + external_link: { + label: 'URL 2', + url: 'https://url2.example.com/', + }, + }, + ], + }, + { + name: 'a', + data: [ + { + external_link: { + label: 'URL 1', + url: 'https://url1.example.com/', + }, + }, + ], + }, + ]; + + expect(filterAnnotations(data, 'external_link')).toEqual([ + { + label: 'URL 1', + url: 'https://url1.example.com/', + }, + { + label: 'URL 2', + url: 'https://url2.example.com/', + }, + ]); + }); + }); }); diff --git a/spec/frontend/ci/jobs_mock_data.js b/spec/frontend/ci/jobs_mock_data.js index 253e669e889..c428de3b9d8 100644 --- a/spec/frontend/ci/jobs_mock_data.js +++ b/spec/frontend/ci/jobs_mock_data.js @@ -989,6 +989,7 @@ export default { }, erase_path: '/root/ci-mock/-/jobs/4757/erase', artifacts: [null], + annotations: [], runner: { id: 1, short_sha: 'ABCDEFGH', diff --git a/spec/frontend/protected_branches/protected_branch_edit_spec.js b/spec/frontend/protected_branches/protected_branch_edit_spec.js index e1966908452..6422856ba22 100644 --- a/spec/frontend/protected_branches/protected_branch_edit_spec.js +++ b/spec/frontend/protected_branches/protected_branch_edit_spec.js @@ -20,7 +20,7 @@ describe('ProtectedBranchEdit', () => { let mock; beforeEach(() => { - jest.spyOn(ProtectedBranchEdit.prototype, 'buildDropdowns').mockImplementation(); + jest.spyOn(ProtectedBranchEdit.prototype, 'initDropdowns').mockImplementation(); mock = new MockAdapter(axios); }); diff --git a/spec/frontend/search/mock_data.js b/spec/frontend/search/mock_data.js index a063f20aca6..7bddc4b1c48 100644 --- a/spec/frontend/search/mock_data.js +++ b/spec/frontend/search/mock_data.js @@ -194,7 +194,7 @@ export const MOCK_DATA_FOR_NAVIGATION_ACTION_MUTATION = { label: 'Projects', scope: 'projects', link: '/search?scope=projects&search=et', - count_link: '/search/count?scope=projects&search=et', + count_link: null, }, }; diff --git a/spec/frontend/search/sidebar/components/app_spec.js b/spec/frontend/search/sidebar/components/app_spec.js index 61a87af476e..3944ba86942 100644 --- a/spec/frontend/search/sidebar/components/app_spec.js +++ b/spec/frontend/search/sidebar/components/app_spec.js @@ -4,13 +4,18 @@ import Vue from 'vue'; import Vuex from 'vuex'; import { SEARCH_TYPE_ZOEKT, SEARCH_TYPE_ADVANCED } from '~/search/sidebar/constants'; import { MOCK_QUERY } from 'jest/search/mock_data'; +import { toggleSuperSidebarCollapsed } from '~/super_sidebar/super_sidebar_collapsed_state_manager'; import GlobalSearchSidebar from '~/search/sidebar/components/app.vue'; import IssuesFilters from '~/search/sidebar/components/issues_filters.vue'; import MergeRequestsFilters from '~/search/sidebar/components/merge_requests_filters.vue'; import BlobsFilters from '~/search/sidebar/components/blobs_filters.vue'; import ProjectsFilters from '~/search/sidebar/components/projects_filters.vue'; import ScopeLegacyNavigation from '~/search/sidebar/components/scope_legacy_navigation.vue'; +import SmallScreenDrawerNavigation from '~/search/sidebar/components/small_screen_drawer_navigation.vue'; import ScopeSidebarNavigation from '~/search/sidebar/components/scope_sidebar_navigation.vue'; +import DomElementListener from '~/vue_shared/components/dom_element_listener.vue'; + +jest.mock('~/super_sidebar/super_sidebar_collapsed_state_manager'); Vue.use(Vuex); @@ -41,13 +46,16 @@ describe('GlobalSearchSidebar', () => { const findBlobsFilters = () => wrapper.findComponent(BlobsFilters); const findProjectsFilters = () => wrapper.findComponent(ProjectsFilters); const findScopeLegacyNavigation = () => wrapper.findComponent(ScopeLegacyNavigation); + const findSmallScreenDrawerNavigation = () => wrapper.findComponent(SmallScreenDrawerNavigation); const findScopeSidebarNavigation = () => wrapper.findComponent(ScopeSidebarNavigation); + const findDomElementListener = () => wrapper.findComponent(DomElementListener); describe('renders properly', () => { describe('always', () => { beforeEach(() => { createComponent(); }); + it(`shows section`, () => { expect(findSidebarSection().exists()).toBe(true); }); @@ -104,6 +112,7 @@ describe('GlobalSearchSidebar', () => { it(`${!legacyNavShown ? 'hides' : 'shows'} the legacy navigation`, () => { expect(findScopeLegacyNavigation().exists()).toBe(legacyNavShown); + expect(findSmallScreenDrawerNavigation().exists()).toBe(legacyNavShown); }); it(`${!sidebarNavShown ? 'hides' : 'shows'} the sidebar navigation`, () => { @@ -111,4 +120,21 @@ describe('GlobalSearchSidebar', () => { }); }); }); + + describe('when useSidebarNavigation=true', () => { + beforeEach(() => { + createComponent({ useSidebarNavigation: true }); + }); + + it('toggles super sidebar when button is clicked', () => { + const elListener = findDomElementListener(); + + expect(toggleSuperSidebarCollapsed).not.toHaveBeenCalled(); + + elListener.vm.$emit('click'); + + expect(toggleSuperSidebarCollapsed).toHaveBeenCalledTimes(1); + expect(elListener.props('selector')).toBe('#js-open-mobile-filters'); + }); + }); }); diff --git a/spec/frontend/search/sidebar/components/issues_filters_spec.js b/spec/frontend/search/sidebar/components/issues_filters_spec.js index 84c4258cbdb..e6188436c81 100644 --- a/spec/frontend/search/sidebar/components/issues_filters_spec.js +++ b/spec/frontend/search/sidebar/components/issues_filters_spec.js @@ -7,6 +7,8 @@ import IssuesFilters from '~/search/sidebar/components/issues_filters.vue'; import ConfidentialityFilter from '~/search/sidebar/components/confidentiality_filter/index.vue'; import StatusFilter from '~/search/sidebar/components/status_filter/index.vue'; import LabelFilter from '~/search/sidebar/components/label_filter/index.vue'; +import ArchivedFilter from '~/search/sidebar/components/archived_filter/index.vue'; +import { SEARCH_TYPE_ADVANCED, SEARCH_TYPE_BASIC } from '~/search/sidebar/constants'; Vue.use(Vuex); @@ -17,10 +19,16 @@ describe('GlobalSearch IssuesFilters', () => { currentScope: () => 'issues', }; - const createComponent = (initialState, ff = true) => { + const createComponent = ({ + initialState = {}, + searchIssueLabelAggregation = true, + searchIssuesHideArchivedProjects = true, + } = {}) => { const store = new Vuex.Store({ state: { urlQuery: MOCK_QUERY, + useSidebarNavigation: false, + searchType: SEARCH_TYPE_ADVANCED, ...initialState, }, getters: defaultGetters, @@ -30,7 +38,8 @@ describe('GlobalSearch IssuesFilters', () => { store, provide: { glFeatures: { - searchIssueLabelAggregation: ff, + searchIssueLabelAggregation, + searchIssuesHideArchivedProjects, }, }, }); @@ -39,12 +48,23 @@ describe('GlobalSearch IssuesFilters', () => { const findStatusFilter = () => wrapper.findComponent(StatusFilter); const findConfidentialityFilter = () => wrapper.findComponent(ConfidentialityFilter); const findLabelFilter = () => wrapper.findComponent(LabelFilter); + const findArchivedFilter = () => wrapper.findComponent(ArchivedFilter); const findDividers = () => wrapper.findAll('hr'); - describe('Renders correctly with FF enabled', () => { + describe.each` + description | searchIssueLabelAggregation | searchIssuesHideArchivedProjects + ${'Renders correctly with Label Filter disabled'} | ${false} | ${true} + ${'Renders correctly with Archived Filter disabled'} | ${true} | ${false} + ${'Renders correctly with Archived Filter and Label Filter disabled'} | ${false} | ${false} + ${'Renders correctly with Archived Filter and Label Filter enabled'} | ${true} | ${true} + `('$description', ({ searchIssueLabelAggregation, searchIssuesHideArchivedProjects }) => { beforeEach(() => { - createComponent({ urlQuery: MOCK_QUERY }); + createComponent({ + searchIssueLabelAggregation, + searchIssuesHideArchivedProjects, + }); }); + it('renders StatusFilter', () => { expect(findStatusFilter().exists()).toBe(true); }); @@ -53,18 +73,30 @@ describe('GlobalSearch IssuesFilters', () => { expect(findConfidentialityFilter().exists()).toBe(true); }); - it('renders LabelFilter', () => { - expect(findLabelFilter().exists()).toBe(true); + it(`renders correctly LabelFilter when searchIssueLabelAggregation is ${searchIssueLabelAggregation}`, () => { + expect(findLabelFilter().exists()).toBe(searchIssueLabelAggregation); }); - it('renders dividers correctly', () => { - expect(findDividers()).toHaveLength(2); + it(`renders correctly ArchivedFilter when searchIssuesHideArchivedProjects is ${searchIssuesHideArchivedProjects}`, () => { + expect(findArchivedFilter().exists()).toBe(searchIssuesHideArchivedProjects); + }); + + it('renders divider correctly', () => { + // one divider can't be disabled + let dividersCount = 1; + if (searchIssueLabelAggregation) { + dividersCount += 1; + } + if (searchIssuesHideArchivedProjects) { + dividersCount += 1; + } + expect(findDividers()).toHaveLength(dividersCount); }); }); - describe('Renders correctly with FF disabled', () => { + describe('Renders correctly with basic search', () => { beforeEach(() => { - createComponent({ urlQuery: MOCK_QUERY }, false); + createComponent({ initialState: { searchType: SEARCH_TYPE_BASIC } }); }); it('renders StatusFilter', () => { expect(findStatusFilter().exists()).toBe(true); @@ -78,15 +110,51 @@ describe('GlobalSearch IssuesFilters', () => { expect(findLabelFilter().exists()).toBe(false); }); - it('renders divider correctly', () => { + it("doesn't render ArchivedFilter", () => { + expect(findArchivedFilter().exists()).toBe(false); + }); + + it('renders 1 divider', () => { expect(findDividers()).toHaveLength(1); }); }); + describe('Renders correctly in new nav', () => { + beforeEach(() => { + createComponent({ + initialState: { + searchType: SEARCH_TYPE_ADVANCED, + useSidebarNavigation: true, + }, + searchIssueLabelAggregation: true, + searchIssuesHideArchivedProjects: true, + }); + }); + it('renders StatusFilter', () => { + expect(findStatusFilter().exists()).toBe(true); + }); + + it('renders ConfidentialityFilter', () => { + expect(findConfidentialityFilter().exists()).toBe(true); + }); + + it('renders LabelFilter', () => { + expect(findLabelFilter().exists()).toBe(true); + }); + + it('renders ArchivedFilter', () => { + expect(findArchivedFilter().exists()).toBe(true); + }); + + it("doesn't render dividers", () => { + expect(findDividers()).toHaveLength(0); + }); + }); + describe('Renders correctly with wrong scope', () => { beforeEach(() => { defaultGetters.currentScope = () => 'blobs'; - createComponent({ urlQuery: MOCK_QUERY }); + createComponent(); }); it("doesn't render StatusFilter", () => { expect(findStatusFilter().exists()).toBe(false); @@ -100,6 +168,10 @@ describe('GlobalSearch IssuesFilters', () => { expect(findLabelFilter().exists()).toBe(false); }); + it("doesn't render ArchivedFilter", () => { + expect(findArchivedFilter().exists()).toBe(false); + }); + it("doesn't render dividers", () => { expect(findDividers()).toHaveLength(0); }); diff --git a/spec/frontend/search/sidebar/components/small_screen_drawer_navigation_spec.js b/spec/frontend/search/sidebar/components/small_screen_drawer_navigation_spec.js new file mode 100644 index 00000000000..5ab4afba7f0 --- /dev/null +++ b/spec/frontend/search/sidebar/components/small_screen_drawer_navigation_spec.js @@ -0,0 +1,68 @@ +import { nextTick } from 'vue'; +import { GlDrawer } from '@gitlab/ui'; +import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; +import DomElementListener from '~/vue_shared/components/dom_element_listener.vue'; +import { DRAWER_Z_INDEX } from '~/lib/utils/constants'; +import SmallScreenDrawerNavigation from '~/search/sidebar/components/small_screen_drawer_navigation.vue'; + +describe('ScopeLegacyNavigation', () => { + let wrapper; + let closeSpy; + let toggleSpy; + + const createComponent = () => { + wrapper = shallowMountExtended(SmallScreenDrawerNavigation, { + slots: { + default: '<div data-testid="default-slot-content">test</div>', + }, + }); + }; + + const findGlDrawer = () => wrapper.findComponent(GlDrawer); + const findTitle = () => wrapper.findComponent('h2'); + const findSlot = () => wrapper.findByTestId('default-slot-content'); + const findDomElementListener = () => wrapper.findComponent(DomElementListener); + + describe('small screen navigation', () => { + beforeEach(() => { + createComponent(); + }); + + it('renders drawer', () => { + expect(findGlDrawer().exists()).toBe(true); + expect(findGlDrawer().attributes('zindex')).toBe(DRAWER_Z_INDEX.toString()); + expect(findGlDrawer().attributes('headerheight')).toBe('0'); + }); + + it('renders title', () => { + expect(findTitle().exists()).toBe(true); + }); + + it('renders slots', () => { + expect(findSlot().exists()).toBe(true); + }); + }); + + describe('actions', () => { + beforeEach(() => { + closeSpy = jest.spyOn(SmallScreenDrawerNavigation.methods, 'closeSmallScreenFilters'); + toggleSpy = jest.spyOn(SmallScreenDrawerNavigation.methods, 'toggleSmallScreenFilters'); + createComponent(); + }); + + it('calls onClose', () => { + findGlDrawer().vm.$emit('close'); + expect(closeSpy).toHaveBeenCalled(); + }); + + it('calls toggleSmallScreenFilters', async () => { + expect(findGlDrawer().props('open')).toBe(false); + + findDomElementListener().vm.$emit('click'); + await nextTick(); + + expect(toggleSpy).toHaveBeenCalled(); + expect(findGlDrawer().props('open')).toBe(true); + }); + }); +}); diff --git a/spec/frontend/search/store/actions_spec.js b/spec/frontend/search/store/actions_spec.js index cc9c555b6c7..889260fc478 100644 --- a/spec/frontend/search/store/actions_spec.js +++ b/spec/frontend/search/store/actions_spec.js @@ -1,4 +1,5 @@ import MockAdapter from 'axios-mock-adapter'; +import { mapValues } from 'lodash'; import testAction from 'helpers/vuex_action_helper'; import Api from '~/api'; import { createAlert } from '~/alert'; @@ -312,6 +313,21 @@ describe('Global Search Store Actions', () => { }); }); + describe('fetchSidebarCount with no count_link', () => { + beforeEach(() => { + state.navigation = mapValues(MOCK_NAVIGATION_DATA, (navItem) => ({ + ...navItem, + count_link: null, + })); + }); + + it('should not request anything', async () => { + await testAction({ action: actions.fetchSidebarCount, state, expectedMutations: [] }); + + expect(mock.history.get.length).toBe(0); + }); + }); + describe.each` action | axiosMock | type | expectedMutations | errorLogs ${actions.fetchAllAggregation} | ${{ method: 'onGet', code: HTTP_STATUS_OK }} | ${'success'} | ${MOCK_RECEIVE_AGGREGATIONS_SUCCESS_MUTATION} | ${0} |