diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2024-01-16 13:42:19 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2024-01-16 13:42:19 +0300 |
commit | 84d1bd786125c1c14a3ba5f63e38a4cc736a9027 (patch) | |
tree | f550fa965f507077e20dbb6d61a8269a99ef7107 /spec/frontend/ci | |
parent | 3a105e36e689f7b75482236712f1a47fd5a76814 (diff) |
Add latest changes from gitlab-org/gitlab@16-8-stable-eev16.8.0-rc42
Diffstat (limited to 'spec/frontend/ci')
23 files changed, 345 insertions, 146 deletions
diff --git a/spec/frontend/ci/catalog/components/details/ci_resource_about_spec.js b/spec/frontend/ci/catalog/components/details/ci_resource_about_spec.js index 658a135534b..1c791857df9 100644 --- a/spec/frontend/ci/catalog/components/details/ci_resource_about_spec.js +++ b/spec/frontend/ci/catalog/components/details/ci_resource_about_spec.js @@ -12,8 +12,8 @@ describe('CiResourceAbout', () => { openMergeRequestsCount: 9, latestVersion: { id: 1, - tagName: 'v1.0.0', - tagPath: 'path/to/release', + name: 'v1.0.0', + path: 'path/to/release', releasedAt: '2022-08-23T17:19:09Z', }, webPath: 'path/to/project', diff --git a/spec/frontend/ci/catalog/components/details/ci_resource_components_spec.js b/spec/frontend/ci/catalog/components/details/ci_resource_components_spec.js index 330163e9f39..f81344fa291 100644 --- a/spec/frontend/ci/catalog/components/details/ci_resource_components_spec.js +++ b/spec/frontend/ci/catalog/components/details/ci_resource_components_spec.js @@ -115,7 +115,7 @@ describe('CiResourceComponents', () => { it('renders the component name and snippet', () => { components.forEach((component) => { expect(wrapper.text()).toContain(component.name); - expect(wrapper.text()).toContain(component.path); + expect(wrapper.text()).toContain(component.includePath); }); }); @@ -124,7 +124,7 @@ describe('CiResourceComponents', () => { const button = findCopyToClipboardButton(i); expect(button.props().icon).toBe('copy-to-clipboard'); - expect(button.attributes('data-clipboard-text')).toContain(component.path); + expect(button.attributes('data-clipboard-text')).toContain(component.includePath); }); }); diff --git a/spec/frontend/ci/catalog/components/details/ci_resource_header_spec.js b/spec/frontend/ci/catalog/components/details/ci_resource_header_spec.js index 6af9daabea0..b35c8a40744 100644 --- a/spec/frontend/ci/catalog/components/details/ci_resource_header_spec.js +++ b/spec/frontend/ci/catalog/components/details/ci_resource_header_spec.js @@ -113,7 +113,7 @@ describe('CiResourceHeader', () => { createComponent({ props: { pipelineStatus: status, - latestVersion: { tagName: '1.0.0', tagPath: 'path/to/release' }, + latestVersion: { name: '1.0.0', path: 'path/to/release' }, }, }); }); diff --git a/spec/frontend/ci/catalog/components/list/catalog_search_spec.js b/spec/frontend/ci/catalog/components/list/catalog_search_spec.js index c6f8498f2fd..803deeb0d45 100644 --- a/spec/frontend/ci/catalog/components/list/catalog_search_spec.js +++ b/spec/frontend/ci/catalog/components/list/catalog_search_spec.js @@ -1,4 +1,4 @@ -import { GlSearchBoxByClick, GlSorting, GlSortingItem } from '@gitlab/ui'; +import { GlSearchBoxByClick, GlSorting } from '@gitlab/ui'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import CatalogSearch from '~/ci/catalog/components/list/catalog_search.vue'; import { SORT_ASC, SORT_DESC, SORT_OPTION_CREATED } from '~/ci/catalog/constants'; @@ -8,7 +8,7 @@ describe('CatalogSearch', () => { const findSearchBar = () => wrapper.findComponent(GlSearchBoxByClick); const findSorting = () => wrapper.findComponent(GlSorting); - const findAllSortingItems = () => wrapper.findAllComponents(GlSortingItem); + const findAllSortingItems = () => findSorting().props('sortOptions'); const createComponent = () => { wrapper = shallowMountExtended(CatalogSearch, {}); @@ -23,13 +23,14 @@ describe('CatalogSearch', () => { expect(findSearchBar().exists()).toBe(true); }); - it('renders the sorting options', () => { - expect(findSorting().exists()).toBe(true); - expect(findAllSortingItems()).toHaveLength(1); + it('sets sorting options', () => { + const sortOptionsProp = findAllSortingItems(); + expect(sortOptionsProp).toHaveLength(1); + expect(sortOptionsProp[0].text).toBe('Created at'); }); it('renders the `Created at` option as the default', () => { - expect(findAllSortingItems().at(0).text()).toBe('Created at'); + expect(findSorting().props('text')).toBe('Created at'); }); }); diff --git a/spec/frontend/ci/catalog/components/list/catalog_tabs_spec.js b/spec/frontend/ci/catalog/components/list/catalog_tabs_spec.js new file mode 100644 index 00000000000..ea216300017 --- /dev/null +++ b/spec/frontend/ci/catalog/components/list/catalog_tabs_spec.js @@ -0,0 +1,71 @@ +import { GlTab, GlTabs, GlLoadingIcon } from '@gitlab/ui'; +import { shallowMount } from '@vue/test-utils'; +import { extendedWrapper } from 'helpers/vue_test_utils_helper'; +import { trimText } from 'helpers/text_helper'; +import CatalogTabs from '~/ci/catalog/components/list/catalog_tabs.vue'; +import { SCOPE } from '~/ci/catalog/constants'; + +describe('Catalog Tabs', () => { + let wrapper; + + const defaultProps = { + isLoading: false, + resourceCounts: { + all: 11, + namespaces: 4, + }, + }; + + const findAllTab = () => wrapper.findByTestId('resources-all-tab'); + const findYourResourcesTab = () => wrapper.findByTestId('resources-your-tab'); + const findLoadingIcons = () => wrapper.findAllComponents(GlLoadingIcon); + + const triggerTabChange = (index) => wrapper.findAllComponents(GlTab).at(index).vm.$emit('click'); + + const createComponent = (props = defaultProps) => { + wrapper = extendedWrapper( + shallowMount(CatalogTabs, { + propsData: { + ...props, + }, + stubs: { GlTabs }, + }), + ); + }; + + describe('When count queries are loading', () => { + beforeEach(() => { + createComponent({ ...defaultProps, isLoading: true }); + }); + + it('renders loading icons', () => { + expect(findLoadingIcons()).toHaveLength(2); + }); + }); + + describe('When both tabs have resources', () => { + beforeEach(() => { + createComponent(); + }); + + it('renders All tab with count', () => { + expect(trimText(findAllTab().text())).toBe(`All ${defaultProps.resourceCounts.all}`); + }); + + it('renders your resources tab with count', () => { + expect(trimText(findYourResourcesTab().text())).toBe( + `Your resources ${defaultProps.resourceCounts.namespaces}`, + ); + }); + + it.each` + tabIndex | expectedScope + ${0} | ${SCOPE.all} + ${1} | ${SCOPE.namespaces} + `('emits setScope with $expectedScope on tab change', ({ tabIndex, expectedScope }) => { + triggerTabChange(tabIndex); + + expect(wrapper.emitted()).toEqual({ setScope: [[expectedScope]] }); + }); + }); +}); diff --git a/spec/frontend/ci/catalog/components/list/ci_resources_list_item_spec.js b/spec/frontend/ci/catalog/components/list/ci_resources_list_item_spec.js index d74b133f386..15add3f307f 100644 --- a/spec/frontend/ci/catalog/components/list/ci_resources_list_item_spec.js +++ b/spec/frontend/ci/catalog/components/list/ci_resources_list_item_spec.js @@ -21,7 +21,7 @@ describe('CiResourcesListItem', () => { const release = { author: { name: 'author', webUrl: '/user/1' }, releasedAt: Date.now(), - tagName: '1.0.0', + name: '1.0.0', }; const defaultProps = { resource, @@ -114,7 +114,7 @@ describe('CiResourcesListItem', () => { it('renders the version badge', () => { expect(findBadge().exists()).toBe(true); - expect(findBadge().text()).toBe(release.tagName); + expect(findBadge().text()).toBe(release.name); }); }); }); diff --git a/spec/frontend/ci/catalog/components/list/ci_resources_list_spec.js b/spec/frontend/ci/catalog/components/list/ci_resources_list_spec.js index aca20a83979..41c6ccdd112 100644 --- a/spec/frontend/ci/catalog/components/list/ci_resources_list_spec.js +++ b/spec/frontend/ci/catalog/components/list/ci_resources_list_spec.js @@ -3,20 +3,19 @@ import { GlKeysetPagination } from '@gitlab/ui'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import CiResourcesList from '~/ci/catalog/components/list/ci_resources_list.vue'; import CiResourcesListItem from '~/ci/catalog/components/list/ci_resources_list_item.vue'; -import { ciCatalogResourcesItemsCount } from '~/ci/catalog/graphql/settings'; import { catalogResponseBody, catalogSinglePageResponse } from '../../mock'; describe('CiResourcesList', () => { let wrapper; const createComponent = ({ props = {} } = {}) => { - const { nodes, pageInfo, count } = catalogResponseBody.data.ciCatalogResources; + const { nodes, pageInfo } = catalogResponseBody.data.ciCatalogResources; const defaultProps = { currentPage: 1, resources: nodes, pageInfo, - totalCount: count, + totalCount: 20, }; wrapper = shallowMountExtended(CiResourcesList, { @@ -36,11 +35,11 @@ describe('CiResourcesList', () => { const findNextBtn = () => wrapper.findByTestId('nextButton'); describe('contains only one page', () => { - const { nodes, pageInfo, count } = catalogSinglePageResponse.data.ciCatalogResources; + const { nodes, pageInfo } = catalogSinglePageResponse.data.ciCatalogResources; beforeEach(async () => { await createComponent({ - props: { currentPage: 1, resources: nodes, pageInfo, totalCount: count }, + props: { currentPage: 1, resources: nodes, pageInfo, totalCount: nodes.length }, }); }); @@ -62,58 +61,56 @@ describe('CiResourcesList', () => { }); describe.each` - hasPreviousPage | hasNextPage | pageText | expectedTotal | currentPage - ${false} | ${true} | ${'1 of 3'} | ${ciCatalogResourcesItemsCount} | ${1} - ${true} | ${true} | ${'2 of 3'} | ${ciCatalogResourcesItemsCount} | ${2} - ${true} | ${false} | ${'3 of 3'} | ${ciCatalogResourcesItemsCount} | ${3} - `( - 'when on page $pageText', - ({ currentPage, expectedTotal, pageText, hasPreviousPage, hasNextPage }) => { - const { nodes, pageInfo, count } = catalogResponseBody.data.ciCatalogResources; - - const previousPageState = hasPreviousPage ? 'enabled' : 'disabled'; - const nextPageState = hasNextPage ? 'enabled' : 'disabled'; - - beforeEach(async () => { - await createComponent({ - props: { - currentPage, - resources: nodes, - pageInfo: { ...pageInfo, hasPreviousPage, hasNextPage }, - totalCount: count, - }, - }); - }); + hasPreviousPage | hasNextPage | pageText | currentPage + ${false} | ${true} | ${'1 of 3'} | ${1} + ${true} | ${true} | ${'2 of 3'} | ${2} + ${true} | ${false} | ${'3 of 3'} | ${3} + `('when on page $pageText', ({ currentPage, pageText, hasPreviousPage, hasNextPage }) => { + const { nodes, pageInfo } = catalogResponseBody.data.ciCatalogResources; + const count = 50; // We want 3 pages of data to test. There are 20 items per page. + + const previousPageState = hasPreviousPage ? 'enabled' : 'disabled'; + const nextPageState = hasNextPage ? 'enabled' : 'disabled'; - it('shows the right number of items', () => { - expect(findResourcesListItems()).toHaveLength(expectedTotal); + beforeEach(async () => { + await createComponent({ + props: { + currentPage, + resources: nodes, + pageInfo: { ...pageInfo, hasPreviousPage, hasNextPage }, + totalCount: count, + }, }); + }); - it(`shows the keyset control for previous page as ${previousPageState}`, () => { - const disableAttr = findPrevBtn().attributes('disabled'); + it('shows the right number of items', () => { + expect(findResourcesListItems()).toHaveLength(20); + }); - if (previousPageState === 'disabled') { - expect(disableAttr).toBeDefined(); - } else { - expect(disableAttr).toBeUndefined(); - } - }); + it(`shows the keyset control for previous page as ${previousPageState}`, () => { + const disableAttr = findPrevBtn().attributes('disabled'); - it(`shows the keyset control for next page as ${nextPageState}`, () => { - const disableAttr = findNextBtn().attributes('disabled'); + if (previousPageState === 'disabled') { + expect(disableAttr).toBeDefined(); + } else { + expect(disableAttr).toBeUndefined(); + } + }); - if (nextPageState === 'disabled') { - expect(disableAttr).toBeDefined(); - } else { - expect(disableAttr).toBeUndefined(); - } - }); + it(`shows the keyset control for next page as ${nextPageState}`, () => { + const disableAttr = findNextBtn().attributes('disabled'); - it('shows the correct count of current page', () => { - expect(findPageCount().text()).toContain(pageText); - }); - }, - ); + if (nextPageState === 'disabled') { + expect(disableAttr).toBeDefined(); + } else { + expect(disableAttr).toBeUndefined(); + } + }); + + it('shows the correct count of current page', () => { + expect(findPageCount().text()).toContain(pageText); + }); + }); describe('when there is an error getting the page count', () => { beforeEach(() => { diff --git a/spec/frontend/ci/catalog/components/pages/ci_resources_page_spec.js b/spec/frontend/ci/catalog/components/pages/ci_resources_page_spec.js index e6fbd63f307..6fb5eed0d93 100644 --- a/spec/frontend/ci/catalog/components/pages/ci_resources_page_spec.js +++ b/spec/frontend/ci/catalog/components/pages/ci_resources_page_spec.js @@ -7,17 +7,25 @@ import createMockApollo from 'helpers/mock_apollo_helper'; import { createAlert } from '~/alert'; import CatalogHeader from '~/ci/catalog/components/list/catalog_header.vue'; -import CatalogSearch from '~/ci/catalog/components/list/catalog_search.vue'; import CiResourcesList from '~/ci/catalog/components/list/ci_resources_list.vue'; +import CiResourcesPage from '~/ci/catalog/components/pages/ci_resources_page.vue'; +import CatalogSearch from '~/ci/catalog/components/list/catalog_search.vue'; +import CatalogTabs from '~/ci/catalog/components/list/catalog_tabs.vue'; import CatalogListSkeletonLoader from '~/ci/catalog/components/list/catalog_list_skeleton_loader.vue'; import EmptyState from '~/ci/catalog/components/list/empty_state.vue'; + import { cacheConfig, resolvers } from '~/ci/catalog/graphql/settings'; +import { DEFAULT_SORT_VALUE, SCOPE } from '~/ci/catalog/constants'; import typeDefs from '~/ci/catalog/graphql/typedefs.graphql'; -import ciResourcesPage from '~/ci/catalog/components/pages/ci_resources_page.vue'; import getCatalogResources from '~/ci/catalog/graphql/queries/get_ci_catalog_resources.query.graphql'; +import getCatalogResourcesCount from '~/ci/catalog/graphql/queries/get_ci_catalog_resources_count.query.graphql'; -import { emptyCatalogResponseBody, catalogResponseBody } from '../../mock'; +import { + emptyCatalogResponseBody, + catalogResponseBody, + catalogResourcesCountResponseBody, +} from '../../mock'; Vue.use(VueApollo); jest.mock('~/alert'); @@ -25,14 +33,23 @@ jest.mock('~/alert'); describe('CiResourcesPage', () => { let wrapper; let catalogResourcesResponse; + let catalogResourcesCountResponse; - const defaultQueryVariables = { first: 20 }; + const defaultQueryVariables = { + first: 20, + scope: SCOPE.all, + searchTerm: null, + sortValue: DEFAULT_SORT_VALUE, + }; const createComponent = () => { - const handlers = [[getCatalogResources, catalogResourcesResponse]]; + const handlers = [ + [getCatalogResources, catalogResourcesResponse], + [getCatalogResourcesCount, catalogResourcesCountResponse], + ]; const mockApollo = createMockApollo(handlers, resolvers, { cacheConfig, typeDefs }); - wrapper = shallowMountExtended(ciResourcesPage, { + wrapper = shallowMountExtended(CiResourcesPage, { apolloProvider: mockApollo, }); @@ -41,12 +58,15 @@ describe('CiResourcesPage', () => { const findCatalogHeader = () => wrapper.findComponent(CatalogHeader); const findCatalogSearch = () => wrapper.findComponent(CatalogSearch); + const findCatalogTabs = () => wrapper.findComponent(CatalogTabs); const findCiResourcesList = () => wrapper.findComponent(CiResourcesList); const findLoadingState = () => wrapper.findComponent(CatalogListSkeletonLoader); const findEmptyState = () => wrapper.findComponent(EmptyState); beforeEach(() => { catalogResourcesResponse = jest.fn(); + catalogResourcesCountResponse = jest.fn(); + catalogResourcesCountResponse.mockResolvedValue(catalogResourcesCountResponseBody); }); describe('when initial queries are loading', () => { @@ -83,31 +103,56 @@ describe('CiResourcesPage', () => { expect(findCatalogSearch().exists()).toBe(true); }); + it('renders the tabs', () => { + expect(findCatalogTabs().exists()).toBe(true); + }); + it('does not render the list', () => { expect(findCiResourcesList().exists()).toBe(false); }); }); describe('and there are resources', () => { - const { nodes, pageInfo, count } = catalogResponseBody.data.ciCatalogResources; + const { nodes, pageInfo } = catalogResponseBody.data.ciCatalogResources; beforeEach(async () => { catalogResourcesResponse.mockResolvedValue(catalogResponseBody); await createComponent(); }); + it('renders the resources list', () => { expect(findLoadingState().exists()).toBe(false); expect(findEmptyState().exists()).toBe(false); expect(findCiResourcesList().exists()).toBe(true); }); + it('renders the catalog tabs', () => { + expect(findCatalogTabs().exists()).toBe(true); + }); + + it('updates the scope after switching tabs', async () => { + await findCatalogTabs().vm.$emit('setScope', SCOPE.namespaces); + + expect(catalogResourcesResponse).toHaveBeenCalledWith({ + ...defaultQueryVariables, + scope: SCOPE.namespaces, + }); + + await findCatalogTabs().vm.$emit('setScope', SCOPE.all); + + expect(catalogResourcesResponse).toHaveBeenCalledWith({ + ...defaultQueryVariables, + scope: SCOPE.all, + }); + }); + it('passes down props to the resources list', () => { expect(findCiResourcesList().props()).toMatchObject({ currentPage: 1, resources: nodes, pageInfo, - totalCount: count, + totalCount: 0, }); }); @@ -145,6 +190,7 @@ describe('CiResourcesPage', () => { before: pageInfo.startCursor, last: 20, first: null, + scope: SCOPE.all, }); } }); @@ -190,10 +236,12 @@ describe('CiResourcesPage', () => { beforeEach(async () => { catalogResourcesResponse.mockResolvedValue(emptyCatalogResponseBody); await createComponent(); - await findCatalogSearch().vm.$emit('update-search-term', newSearch); }); - it('renders the empty state and passes down the search query', () => { + it('renders the empty state and passes down the search query', async () => { + await findCatalogSearch().vm.$emit('update-search-term', newSearch); + await waitForPromises(); + expect(findEmptyState().exists()).toBe(true); expect(findEmptyState().props().searchTerm).toBe(newSearch); }); diff --git a/spec/frontend/ci/catalog/mock.js b/spec/frontend/ci/catalog/mock.js index e370ac5054f..c9256435990 100644 --- a/spec/frontend/ci/catalog/mock.js +++ b/spec/frontend/ci/catalog/mock.js @@ -10,12 +10,26 @@ export const emptyCatalogResponseBody = { hasPreviousPage: false, __typename: 'PageInfo', }, - count: 0, nodes: [], }, }, }; +export const catalogResourcesCountResponseBody = { + data: { + ciCatalogResources: { + all: { + count: 1, + __typename: 'CiCatalogResourceConnection', + }, + namespaces: { + count: 7, + __typename: 'CiCatalogResourceConnection', + }, + }, + }, +}; + export const catalogResponseBody = { data: { ciCatalogResources: { @@ -28,7 +42,6 @@ export const catalogResponseBody = { hasPreviousPage: false, __typename: 'PageInfo', }, - count: 41, nodes: [ { id: 'gid://gitlab/Ci::Catalog::Resource/129', @@ -248,7 +261,6 @@ export const catalogSinglePageResponse = { hasPreviousPage: false, __typename: 'PageInfo', }, - count: 3, nodes: [ { id: 'gid://gitlab/Ci::Catalog::Resource/132', @@ -298,8 +310,8 @@ export const catalogSharedDataMock = { latestVersion: { __typename: 'Release', id: '3', - tagName: '1.0.0', - tagPath: 'path/to/release', + name: '1.0.0', + path: 'path/to/release', releasedAt: Date.now(), author: { id: 1, webUrl: 'profile/1', name: 'username' }, }, @@ -344,7 +356,7 @@ export const catalogAdditionalDetailsMock = { ], }, }, - tagName: 'v1.0.2', + name: 'v1.0.2', releasedAt: '2022-08-23T17:19:09Z', }, ], @@ -366,8 +378,8 @@ const generateResourcesNodes = (count = 20, startId = 0) => { latestVersion: { __typename: 'Release', id: '3', - tagName: '1.0.0', - tagPath: 'path/to/release', + name: '1.0.0', + path: 'path/to/release', releasedAt: Date.now(), author: { id: 1, webUrl: 'profile/1', name: 'username' }, }, @@ -387,14 +399,14 @@ const componentsMockData = { id: 'gid://gitlab/Ci::Component/1', name: 'Ruby gal', description: 'This is a pretty amazing component that does EVERYTHING ruby.', - path: 'gitlab.com/gitlab-org/ruby-gal@~latest', + includePath: 'gitlab.com/gitlab-org/ruby-gal@~latest', inputs: [{ name: 'version', default: '1.0.0', required: true }], }, { id: 'gid://gitlab/Ci::Component/2', name: 'Javascript madness', description: 'Adds some spice to your life.', - path: 'gitlab.com/gitlab-org/javascript-madness@~latest', + includePath: 'gitlab.com/gitlab-org/javascript-madness@~latest', inputs: [ { name: 'isFun', default: 'true', required: true }, { name: 'RandomNumber', default: '10', required: false }, @@ -404,7 +416,7 @@ const componentsMockData = { id: 'gid://gitlab/Ci::Component/3', name: 'Go go go', description: 'When you write Go, you gotta go go go.', - path: 'gitlab.com/gitlab-org/go-go-go@~latest', + includePath: 'gitlab.com/gitlab-org/go-go-go@~latest', inputs: [{ name: 'version', default: '1.0.0', required: true }], }, ], diff --git a/spec/frontend/ci/ci_variable_list/components/ci_environments_dropdown_spec.js b/spec/frontend/ci/ci_environments_dropdown/ci_environments_dropdown_spec.js index 353b5fd3c47..d26827de57b 100644 --- a/spec/frontend/ci/ci_variable_list/components/ci_environments_dropdown_spec.js +++ b/spec/frontend/ci/ci_environments_dropdown/ci_environments_dropdown_spec.js @@ -1,14 +1,15 @@ import { GlListboxItem, GlCollapsibleListbox, GlDropdownDivider, GlIcon } from '@gitlab/ui'; import { mountExtended } from 'helpers/vue_test_utils_helper'; -import { allEnvironments, ENVIRONMENT_QUERY_LIMIT } from '~/ci/ci_variable_list/constants'; -import CiEnvironmentsDropdown from '~/ci/ci_variable_list/components/ci_environments_dropdown.vue'; +import CiEnvironmentsDropdown from '~/ci/common/private/ci_environments_dropdown'; describe('Ci environments dropdown', () => { let wrapper; const envs = ['dev', 'prod', 'staging']; const defaultProps = { + isEnvironmentRequired: true, areEnvironmentsLoading: false, + canCreateWildcard: true, environments: envs, selectedEnvironmentScope: '', }; @@ -33,19 +34,43 @@ describe('Ci environments dropdown', () => { findListbox().vm.$emit('search', searchTerm); }; - describe('No environments found', () => { - beforeEach(() => { - createComponent({ searchTerm: 'stable' }); + describe('create wildcard button', () => { + describe('when canCreateWildcard is true', () => { + beforeEach(() => { + createComponent({ props: { canCreateWildcard: true }, searchTerm: 'stable' }); + }); + + it('renders create button during search', () => { + expect(findCreateWildcardButton().exists()).toBe(true); + }); }); - it('renders dropdown divider', () => { - expect(findDropdownDivider().exists()).toBe(true); + describe('when canCreateWildcard is false', () => { + beforeEach(() => { + createComponent({ props: { canCreateWildcard: false }, searchTerm: 'stable' }); + }); + + it('does not render create button during search', () => { + expect(findCreateWildcardButton().exists()).toBe(false); + }); }); + }); - it('renders create button with search term if environments do not contain search term', () => { - const button = findCreateWildcardButton(); - expect(button.exists()).toBe(true); - expect(button.text()).toBe('Create wildcard: stable'); + describe('No environments found', () => { + describe('default behavior', () => { + beforeEach(() => { + createComponent({ searchTerm: 'stable' }); + }); + + it('renders dropdown divider', () => { + expect(findDropdownDivider().exists()).toBe(true); + }); + + it('renders create button with search term if environments do not contain search term', () => { + const button = findCreateWildcardButton(); + expect(button.exists()).toBe(true); + expect(button.text()).toBe('Create wildcard: stable'); + }); }); }); @@ -54,7 +79,7 @@ describe('Ci environments dropdown', () => { createComponent({ props: { environments: envs } }); }); - it(`prepends * in listbox`, () => { + it('prepends * in listbox', () => { expect(findListboxItemByIndex(0).text()).toBe('*'); }); @@ -67,6 +92,16 @@ describe('Ci environments dropdown', () => { it('does not display active checkmark', () => { expect(findActiveIconByIndex(0).classes('gl-visibility-hidden')).toBe(true); }); + + describe('when isEnvironmentRequired is false', () => { + beforeEach(() => { + createComponent({ props: { isEnvironmentRequired: false, environments: envs } }); + }); + + it('adds Not applicable as an option', () => { + expect(findListboxItemByIndex(1).text()).toBe('Not applicable'); + }); + }); }); describe('when `*` is the value of selectedEnvironmentScope props', () => { @@ -77,7 +112,7 @@ describe('Ci environments dropdown', () => { }); it('shows the `All environments` text and not the wildcard', () => { - expect(findListboxText()).toContain(allEnvironments.text); + expect(findListboxText()).toContain('All (default)'); expect(findListboxText()).not.toContain(wildcardScope); }); }); @@ -124,9 +159,9 @@ describe('Ci environments dropdown', () => { expect(wrapper.emitted('search-environment-scope')[1]).toEqual([currentEnv]); }); - it('displays note about max environments shown', () => { + it('displays note about max environments', () => { expect(findMaxEnvNote().exists()).toBe(true); - expect(findMaxEnvNote().text()).toContain(String(ENVIRONMENT_QUERY_LIMIT)); + expect(findMaxEnvNote().text()).toContain('30'); }); }); diff --git a/spec/frontend/ci/ci_variable_list/utils_spec.js b/spec/frontend/ci/ci_environments_dropdown/utils_spec.js index fbcf0e7c5a5..6da0d7cdbca 100644 --- a/spec/frontend/ci/ci_variable_list/utils_spec.js +++ b/spec/frontend/ci/ci_environments_dropdown/utils_spec.js @@ -1,13 +1,19 @@ -import { convertEnvironmentScope, mapEnvironmentNames } from '~/ci/ci_variable_list/utils'; -import { allEnvironments } from '~/ci/ci_variable_list/constants'; +import { + convertEnvironmentScope, + mapEnvironmentNames, +} from '~/ci/common/private/ci_environments_dropdown'; describe('utils', () => { describe('convertEnvironmentScope', () => { it('converts the * to the `All environments` text', () => { - expect(convertEnvironmentScope('*')).toBe(allEnvironments.text); + expect(convertEnvironmentScope('*')).toBe('All (default)'); }); - it('returns the environment as is if not the *', () => { + it('converts the `Not applicable` to the `Not applicable`', () => { + expect(convertEnvironmentScope('Not applicable')).toBe('Not applicable'); + }); + + it('returns other environments as-is', () => { expect(convertEnvironmentScope('prod')).toBe('prod'); }); }); diff --git a/spec/frontend/ci/ci_variable_list/components/ci_group_variables_spec.js b/spec/frontend/ci/ci_variable_list/components/ci_group_variables_spec.js index 567a49d663c..0b5440d1bee 100644 --- a/spec/frontend/ci/ci_variable_list/components/ci_group_variables_spec.js +++ b/spec/frontend/ci/ci_variable_list/components/ci_group_variables_spec.js @@ -9,7 +9,7 @@ import { DELETE_MUTATION_ACTION, UPDATE_MUTATION_ACTION, } from '~/ci/ci_variable_list/constants'; -import getGroupEnvironments from '~/ci/ci_variable_list/graphql/queries/group_environments.query.graphql'; +import { getGroupEnvironments } from '~/ci/common/private/ci_environments_dropdown'; import getGroupVariables from '~/ci/ci_variable_list/graphql/queries/group_variables.query.graphql'; import addGroupVariable from '~/ci/ci_variable_list/graphql/mutations/group_add_variable.mutation.graphql'; import deleteGroupVariable from '~/ci/ci_variable_list/graphql/mutations/group_delete_variable.mutation.graphql'; diff --git a/spec/frontend/ci/ci_variable_list/components/ci_project_variables_spec.js b/spec/frontend/ci/ci_variable_list/components/ci_project_variables_spec.js index 69b0d4261b2..66a085f2661 100644 --- a/spec/frontend/ci/ci_variable_list/components/ci_project_variables_spec.js +++ b/spec/frontend/ci/ci_variable_list/components/ci_project_variables_spec.js @@ -9,7 +9,7 @@ import { DELETE_MUTATION_ACTION, UPDATE_MUTATION_ACTION, } from '~/ci/ci_variable_list/constants'; -import getProjectEnvironments from '~/ci/ci_variable_list/graphql/queries/project_environments.query.graphql'; +import { getProjectEnvironments } from '~/ci/common/private/ci_environments_dropdown'; import getProjectVariables from '~/ci/ci_variable_list/graphql/queries/project_variables.query.graphql'; import addProjectVariable from '~/ci/ci_variable_list/graphql/mutations/project_add_variable.mutation.graphql'; import deleteProjectVariable from '~/ci/ci_variable_list/graphql/mutations/project_delete_variable.mutation.graphql'; diff --git a/spec/frontend/ci/ci_variable_list/components/ci_variable_drawer_spec.js b/spec/frontend/ci/ci_variable_list/components/ci_variable_drawer_spec.js index 721e2b831fc..645aaf798d4 100644 --- a/spec/frontend/ci/ci_variable_list/components/ci_variable_drawer_spec.js +++ b/spec/frontend/ci/ci_variable_list/components/ci_variable_drawer_spec.js @@ -11,7 +11,7 @@ import { } from '@gitlab/ui'; import { mountExtended, shallowMountExtended } from 'helpers/vue_test_utils_helper'; import { helpPagePath } from '~/helpers/help_page_helper'; -import CiEnvironmentsDropdown from '~/ci/ci_variable_list/components/ci_environments_dropdown.vue'; +import CiEnvironmentsDropdown from '~/ci/common/private/ci_environments_dropdown'; import CiVariableDrawer from '~/ci/ci_variable_list/components/ci_variable_drawer.vue'; import { awsTokenList } from '~/ci/ci_variable_list/components/ci_variable_autocomplete_tokens'; import { @@ -113,6 +113,10 @@ describe('CI Variable Drawer', () => { helpPagePath('ci/variables/index', { anchor: 'define-a-cicd-variable-in-the-ui' }), ); }); + + it('value field is resizable', () => { + expect(findValueField().props('noResize')).toBe(false); + }); }); describe('validations', () => { @@ -513,7 +517,7 @@ describe('CI Variable Drawer', () => { it('title and confirm button renders the correct text', () => { expect(findTitle().text()).toBe('Edit variable'); - expect(findConfirmBtn().text()).toBe('Edit variable'); + expect(findConfirmBtn().text()).toBe('Save changes'); }); it('dispatches the edit-variable event', async () => { diff --git a/spec/frontend/ci/ci_variable_list/components/ci_variable_settings_spec.js b/spec/frontend/ci/ci_variable_list/components/ci_variable_settings_spec.js index 01d3cdf504d..078958fe44a 100644 --- a/spec/frontend/ci/ci_variable_list/components/ci_variable_settings_spec.js +++ b/spec/frontend/ci/ci_variable_list/components/ci_variable_settings_spec.js @@ -2,14 +2,12 @@ import { shallowMount } from '@vue/test-utils'; import CiVariableSettings from '~/ci/ci_variable_list/components/ci_variable_settings.vue'; import CiVariableTable from '~/ci/ci_variable_list/components/ci_variable_table.vue'; import CiVariableDrawer from '~/ci/ci_variable_list/components/ci_variable_drawer.vue'; - import { ADD_VARIABLE_ACTION, EDIT_VARIABLE_ACTION, projectString, } from '~/ci/ci_variable_list/constants'; -import { mapEnvironmentNames } from '~/ci/ci_variable_list/utils'; - +import { mapEnvironmentNames } from '~/ci/common/private/ci_environments_dropdown'; import { mockEnvs, mockVariablesWithScopes, newVariable } from '../mocks'; describe('Ci variable table', () => { diff --git a/spec/frontend/ci/ci_variable_list/components/ci_variable_shared_spec.js b/spec/frontend/ci/ci_variable_list/components/ci_variable_shared_spec.js index c90ff4cc682..f9c1cbe0d30 100644 --- a/spec/frontend/ci/ci_variable_list/components/ci_variable_shared_spec.js +++ b/spec/frontend/ci/ci_variable_list/components/ci_variable_shared_spec.js @@ -11,13 +11,11 @@ import { resolvers } from '~/ci/ci_variable_list/graphql/settings'; import ciVariableShared from '~/ci/ci_variable_list/components/ci_variable_shared.vue'; import ciVariableSettings from '~/ci/ci_variable_list/components/ci_variable_settings.vue'; import ciVariableTable from '~/ci/ci_variable_list/components/ci_variable_table.vue'; -import getProjectEnvironments from '~/ci/ci_variable_list/graphql/queries/project_environments.query.graphql'; +import { getProjectEnvironments } from '~/ci/common/private/ci_environments_dropdown'; import getAdminVariables from '~/ci/ci_variable_list/graphql/queries/variables.query.graphql'; import getGroupVariables from '~/ci/ci_variable_list/graphql/queries/group_variables.query.graphql'; import getProjectVariables from '~/ci/ci_variable_list/graphql/queries/project_variables.query.graphql'; - import { - ENVIRONMENT_QUERY_LIMIT, environmentFetchErrorText, genericMutationErrorText, variableFetchErrorText, @@ -230,7 +228,7 @@ describe('Ci Variable Shared Component', () => { it('initial query is called with the correct variables', () => { expect(mockEnvironments).toHaveBeenCalledWith({ - first: ENVIRONMENT_QUERY_LIMIT, + first: 30, fullPath: '/namespace/project/', search: '', }); diff --git a/spec/frontend/ci/ci_variable_list/mocks.js b/spec/frontend/ci/ci_variable_list/mocks.js index 9c9c99ad5ea..35bca408f17 100644 --- a/spec/frontend/ci/ci_variable_list/mocks.js +++ b/spec/frontend/ci/ci_variable_list/mocks.js @@ -20,7 +20,7 @@ import updateProjectVariable from '~/ci/ci_variable_list/graphql/mutations/proje import getAdminVariables from '~/ci/ci_variable_list/graphql/queries/variables.query.graphql'; import getGroupVariables from '~/ci/ci_variable_list/graphql/queries/group_variables.query.graphql'; -import getProjectEnvironments from '~/ci/ci_variable_list/graphql/queries/project_environments.query.graphql'; +import { getProjectEnvironments } from '~/ci/common/private/ci_environments_dropdown'; import getProjectVariables from '~/ci/ci_variable_list/graphql/queries/project_variables.query.graphql'; export const devName = 'dev'; diff --git a/spec/frontend/ci/pipeline_details/test_reports/mock_data.js b/spec/frontend/ci/pipeline_details/test_reports/mock_data.js index 7c9f9287c86..643863c9d24 100644 --- a/spec/frontend/ci/pipeline_details/test_reports/mock_data.js +++ b/spec/frontend/ci/pipeline_details/test_reports/mock_data.js @@ -1,4 +1,4 @@ -import { TestStatus } from '~/ci/pipeline_details/constants'; +import { testStatus } from '~/ci/pipeline_details/constants'; export default [ { @@ -7,7 +7,7 @@ export default [ execution_time: 0, name: 'Test#skipped text', stack_trace: null, - status: TestStatus.SKIPPED, + status: testStatus.SKIPPED, system_output: null, }, { @@ -16,7 +16,7 @@ export default [ execution_time: 0, name: 'Test#error text', stack_trace: null, - status: TestStatus.ERROR, + status: testStatus.ERROR, system_output: null, }, { @@ -25,7 +25,7 @@ export default [ execution_time: 0, name: 'Test#unknown text', stack_trace: null, - status: TestStatus.UNKNOWN, + status: testStatus.UNKNOWN, system_output: null, }, ]; diff --git a/spec/frontend/ci/pipeline_details/test_reports/test_reports_spec.js b/spec/frontend/ci/pipeline_details/test_reports/test_reports_spec.js index d318aa36bcf..836c35977b4 100644 --- a/spec/frontend/ci/pipeline_details/test_reports/test_reports_spec.js +++ b/spec/frontend/ci/pipeline_details/test_reports/test_reports_spec.js @@ -5,7 +5,12 @@ import Vue from 'vue'; import Vuex from 'vuex'; import testReports from 'test_fixtures/pipelines/test_report.json'; import { extendedWrapper } from 'helpers/vue_test_utils_helper'; -import { getParameterValues } from '~/lib/utils/url_utility'; +import { + getParameterValues, + updateHistory, + removeParams, + setUrlParams, +} from '~/lib/utils/url_utility'; import EmptyState from '~/ci/pipeline_details/test_reports/empty_state.vue'; import TestReports from '~/ci/pipeline_details/test_reports/test_reports.vue'; import TestSummary from '~/ci/pipeline_details/test_reports/test_summary.vue'; @@ -17,6 +22,9 @@ Vue.use(Vuex); jest.mock('~/lib/utils/url_utility', () => ({ ...jest.requireActual('~/lib/utils/url_utility'), getParameterValues: jest.fn().mockReturnValue([]), + updateHistory: jest.fn().mockName('updateHistory'), + removeParams: jest.fn().mockName('removeParams'), + setUrlParams: jest.fn().mockName('setUrlParams'), })); describe('Test reports app', () => { @@ -36,7 +44,7 @@ describe('Test reports app', () => { removeSelectedSuiteIndex: jest.fn(), }; - const createComponent = ({ state = {} } = {}) => { + const createComponent = ({ state = {}, getterStubs = {} } = {}) => { store = new Vuex.Store({ modules: { testReports: { @@ -48,7 +56,10 @@ describe('Test reports app', () => { ...state, }, actions: actionSpies, - getters, + getters: { + ...getters, + ...getterStubs, + }, }, }, }); @@ -124,24 +135,41 @@ describe('Test reports app', () => { describe('when a suite is clicked', () => { beforeEach(() => { - createComponent({ state: { hasFullReport: true } }); + document.title = 'Test reports'; + createComponent({ + state: { hasFullReport: true }, + getters: { getSelectedSuite: jest.fn().mockReturnValue({ name: 'test' }) }, + }); testSummaryTable().vm.$emit('row-click', 0); }); - it('should call setSelectedSuiteIndex and fetchTestSuite', () => { - expect(actionSpies.setSelectedSuiteIndex).toHaveBeenCalled(); - expect(actionSpies.fetchTestSuite).toHaveBeenCalled(); + it('should call setSelectedSuiteIndex, fetchTestSuite and updateHistory', () => { + expect(actionSpies.setSelectedSuiteIndex).toHaveBeenCalledWith(expect.anything(Object), 0); + expect(actionSpies.fetchTestSuite).toHaveBeenCalledWith(expect.anything(Object), 0); + expect(setUrlParams).toHaveBeenCalledWith({ job_name: undefined }); + expect(updateHistory).toHaveBeenCalledWith({ + replace: true, + title: 'Test reports', + url: undefined, + }); }); }); describe('when clicking back to summary', () => { beforeEach(() => { + document.title = 'Test reports'; createComponent({ state: { selectedSuiteIndex: 0 } }); testSummary().vm.$emit('on-back-click'); }); - it('should call removeSelectedSuiteIndex', () => { + it('should call removeSelectedSuiteIndex and updateHistory', () => { expect(actionSpies.removeSelectedSuiteIndex).toHaveBeenCalled(); + expect(removeParams).toHaveBeenCalledWith(['job_name']); + expect(updateHistory).toHaveBeenCalledWith({ + replace: true, + title: 'Test reports', + url: undefined, + }); }); }); }); diff --git a/spec/frontend/ci/pipeline_details/test_reports/test_suite_table_spec.js b/spec/frontend/ci/pipeline_details/test_reports/test_suite_table_spec.js index 5bdea6bbcbf..181b8df31f4 100644 --- a/spec/frontend/ci/pipeline_details/test_reports/test_suite_table_spec.js +++ b/spec/frontend/ci/pipeline_details/test_reports/test_suite_table_spec.js @@ -5,7 +5,7 @@ import Vuex from 'vuex'; import testReports from 'test_fixtures/pipelines/test_report.json'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import SuiteTable, { i18n } from '~/ci/pipeline_details/test_reports/test_suite_table.vue'; -import { TestStatus } from '~/ci/pipeline_details/constants'; +import { testStatus } from '~/ci/pipeline_details/constants'; import * as getters from '~/ci/pipeline_details/stores/test_reports/getters'; import { formatFilePath } from '~/ci/pipeline_details/stores/test_reports/utils'; import { ARTIFACTS_EXPIRED_ERROR_MESSAGE } from '~/ci/pipeline_details/stores/test_reports/constants'; @@ -92,10 +92,10 @@ describe('Test reports suite table', () => { }); it.each([ - TestStatus.ERROR, - TestStatus.FAILED, - TestStatus.SKIPPED, - TestStatus.SUCCESS, + testStatus.ERROR, + testStatus.FAILED, + testStatus.SKIPPED, + testStatus.SUCCESS, 'unknown', ])('renders the correct icon for test case with %s status', (status) => { const test = testCases.findIndex((x) => x.status === status); diff --git a/spec/frontend/ci/pipeline_editor/components/popovers/walkthrough_popover_spec.js b/spec/frontend/ci/pipeline_editor/components/popovers/walkthrough_popover_spec.js index 37339b1c422..d379da390a4 100644 --- a/spec/frontend/ci/pipeline_editor/components/popovers/walkthrough_popover_spec.js +++ b/spec/frontend/ci/pipeline_editor/components/popovers/walkthrough_popover_spec.js @@ -1,24 +1,23 @@ -import { mount, shallowMount } from '@vue/test-utils'; -import Vue from 'vue'; +import { shallowMount } from '@vue/test-utils'; +import { GlButton } from '@gitlab/ui'; import WalkthroughPopover from '~/ci/pipeline_editor/components/popovers/walkthrough_popover.vue'; -import { extendedWrapper } from 'helpers/vue_test_utils_helper'; - -Vue.config.ignoredElements = ['gl-emoji']; describe('WalkthroughPopover component', () => { let wrapper; - const createComponent = (mountFn = shallowMount) => { - return extendedWrapper(mountFn(WalkthroughPopover)); + const createComponent = () => { + wrapper = shallowMount(WalkthroughPopover, { + components: { + GlEmoji: { template: '<img/>' }, + }, + }); }; describe('CTA button clicked', () => { - beforeEach(async () => { - wrapper = createComponent(mount); - await wrapper.findByTestId('ctaBtn').trigger('click'); - }); - it('emits "walkthrough-popover-cta-clicked" event', () => { + createComponent(shallowMount); + wrapper.findComponent(GlButton).vm.$emit('click'); + expect(wrapper.emitted()['walkthrough-popover-cta-clicked']).toHaveLength(1); }); }); diff --git a/spec/frontend/ci/pipelines_page/components/empty_state/pipelines_ci_templates_spec.js b/spec/frontend/ci/pipelines_page/components/empty_state/pipelines_ci_templates_spec.js index f824dab9ae1..96de1d18aa2 100644 --- a/spec/frontend/ci/pipelines_page/components/empty_state/pipelines_ci_templates_spec.js +++ b/spec/frontend/ci/pipelines_page/components/empty_state/pipelines_ci_templates_spec.js @@ -18,6 +18,9 @@ describe('Pipelines CI Templates', () => { showJenkinsCiPrompt: false, ...propsData, }, + components: { + GlEmoji: { template: '<img/>' }, + }, stubs, }); }; diff --git a/spec/frontend/ci/runner/components/runner_job_status_badge_spec.js b/spec/frontend/ci/runner/components/runner_job_status_badge_spec.js index c4476d01386..adb07d4086d 100644 --- a/spec/frontend/ci/runner/components/runner_job_status_badge_spec.js +++ b/spec/frontend/ci/runner/components/runner_job_status_badge_spec.js @@ -35,8 +35,7 @@ describe('RunnerTypeBadge', () => { expect(findBadge().classes().sort()).toEqual( [ ...classes, - 'gl-border', - 'gl-display-inline-block', + 'gl-inset-border-1-gray-400', 'gl-max-w-full', 'gl-text-truncate', 'gl-bg-transparent!', |