Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/ci/catalog/components')
-rw-r--r--spec/frontend/ci/catalog/components/details/ci_resource_components_spec.js16
-rw-r--r--spec/frontend/ci/catalog/components/details/ci_resource_details_spec.js2
-rw-r--r--spec/frontend/ci/catalog/components/details/ci_resource_header_spec.js8
-rw-r--r--spec/frontend/ci/catalog/components/details/ci_resource_readme_spec.js3
-rw-r--r--spec/frontend/ci/catalog/components/list/catalog_header_spec.js18
-rw-r--r--spec/frontend/ci/catalog/components/list/catalog_search_spec.js103
-rw-r--r--spec/frontend/ci/catalog/components/list/ci_resources_list_item_spec.js118
-rw-r--r--spec/frontend/ci/catalog/components/list/empty_state_spec.js64
-rw-r--r--spec/frontend/ci/catalog/components/pages/ci_resource_details_page_spec.js8
-rw-r--r--spec/frontend/ci/catalog/components/pages/ci_resources_page_spec.js118
10 files changed, 370 insertions, 88 deletions
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 382f8e46203..330163e9f39 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
@@ -2,7 +2,6 @@ import Vue from 'vue';
import VueApollo from 'vue-apollo';
import { GlEmptyState, GlLoadingIcon } from '@gitlab/ui';
import { mountExtended } from 'helpers/vue_test_utils_helper';
-import { resolvers } from '~/ci/catalog/graphql/settings';
import CiResourceComponents from '~/ci/catalog/components/details/ci_resource_components.vue';
import getCiCatalogcomponentComponents from '~/ci/catalog/graphql/queries/get_ci_catalog_resource_components.query.graphql';
import createMockApollo from 'helpers/mock_apollo_helper';
@@ -17,15 +16,15 @@ describe('CiResourceComponents', () => {
let wrapper;
let mockComponentsResponse;
- const components = mockComponents.data.ciCatalogResource.components.nodes;
+ const components = mockComponents.data.ciCatalogResource.latestVersion.components.nodes;
- const resourceId = 'gid://gitlab/Ci::Catalog::Resource/1';
+ const resourcePath = 'twitter/project-1';
- const defaultProps = { resourceId };
+ const defaultProps = { resourcePath };
const createComponent = async () => {
const handlers = [[getCiCatalogcomponentComponents, mockComponentsResponse]];
- const mockApollo = createMockApollo(handlers, resolvers);
+ const mockApollo = createMockApollo(handlers);
wrapper = mountExtended(CiResourceComponents, {
propsData: {
@@ -113,10 +112,9 @@ describe('CiResourceComponents', () => {
expect(findComponents()).toHaveLength(components.length);
});
- it('renders the component name, description and snippet', () => {
+ it('renders the component name and snippet', () => {
components.forEach((component) => {
expect(wrapper.text()).toContain(component.name);
- expect(wrapper.text()).toContain(component.description);
expect(wrapper.text()).toContain(component.path);
});
});
@@ -134,9 +132,9 @@ describe('CiResourceComponents', () => {
it('renders the component parameter attributes', () => {
const [firstComponent] = components;
- firstComponent.inputs.nodes.forEach((input) => {
+ firstComponent.inputs.forEach((input) => {
expect(findComponents().at(0).text()).toContain(input.name);
- expect(findComponents().at(0).text()).toContain(input.defaultValue);
+ expect(findComponents().at(0).text()).toContain(input.default);
expect(findComponents().at(0).text()).toContain('Yes');
});
});
diff --git a/spec/frontend/ci/catalog/components/details/ci_resource_details_spec.js b/spec/frontend/ci/catalog/components/details/ci_resource_details_spec.js
index 1f7dcf9d4e5..e4b6c1cd046 100644
--- a/spec/frontend/ci/catalog/components/details/ci_resource_details_spec.js
+++ b/spec/frontend/ci/catalog/components/details/ci_resource_details_spec.js
@@ -8,7 +8,7 @@ describe('CiResourceDetails', () => {
let wrapper;
const defaultProps = {
- resourceId: 'gid://gitlab/Ci::Catalog::Resource/1',
+ resourcePath: 'twitter/project-1',
};
const defaultProvide = {
glFeatures: { ciCatalogComponentsTab: true },
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 c061332ba13..6af9daabea0 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
@@ -3,7 +3,7 @@ import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import CiResourceHeader from '~/ci/catalog/components/details/ci_resource_header.vue';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import CiResourceAbout from '~/ci/catalog/components/details/ci_resource_about.vue';
-import CiIcon from '~/vue_shared/components/ci_icon.vue';
+import CiIcon from '~/vue_shared/components/ci_icon/ci_icon.vue';
import { catalogSharedDataMock, catalogAdditionalDetailsMock } from '../../mock';
describe('CiResourceHeader', () => {
@@ -45,9 +45,9 @@ describe('CiResourceHeader', () => {
expect(wrapper.html()).toContain(resource.description);
});
- it('renders the namespace and project path', () => {
- expect(wrapper.html()).toContain(resource.rootNamespace.fullPath);
- expect(wrapper.html()).toContain(resource.rootNamespace.name);
+ it('renders the project path and name', () => {
+ expect(wrapper.html()).toContain(resource.webPath);
+ expect(wrapper.html()).toContain(resource.name);
});
it('renders the avatar', () => {
diff --git a/spec/frontend/ci/catalog/components/details/ci_resource_readme_spec.js b/spec/frontend/ci/catalog/components/details/ci_resource_readme_spec.js
index 0dadac236a8..ad76b47db57 100644
--- a/spec/frontend/ci/catalog/components/details/ci_resource_readme_spec.js
+++ b/spec/frontend/ci/catalog/components/details/ci_resource_readme_spec.js
@@ -23,12 +23,13 @@ describe('CiResourceReadme', () => {
data: {
ciCatalogResource: {
id: resourceId,
+ webPath: 'twitter/project-1',
readmeHtml,
},
},
};
- const defaultProps = { resourceId };
+ const defaultProps = { resourcePath: readmeMockData.data.ciCatalogResource.webPath };
const createComponent = ({ props = {} } = {}) => {
const handlers = [[getCiCatalogResourceReadme, mockReadmeResponse]];
diff --git a/spec/frontend/ci/catalog/components/list/catalog_header_spec.js b/spec/frontend/ci/catalog/components/list/catalog_header_spec.js
index 2a5c24d0515..e9d2e68c1a3 100644
--- a/spec/frontend/ci/catalog/components/list/catalog_header_spec.js
+++ b/spec/frontend/ci/catalog/components/list/catalog_header_spec.js
@@ -1,6 +1,7 @@
import { GlBanner, GlButton } from '@gitlab/ui';
import { useLocalStorageSpy } from 'helpers/local_storage_helper';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import BetaBadge from '~/vue_shared/components/badges/beta_badge.vue';
import CatalogHeader from '~/ci/catalog/components/list/catalog_header.vue';
import { CATALOG_FEEDBACK_DISMISSED_KEY } from '~/ci/catalog/constants';
@@ -16,9 +17,10 @@ describe('CatalogHeader', () => {
};
const findBanner = () => wrapper.findComponent(GlBanner);
+ const findBetaBadge = () => wrapper.findComponent(BetaBadge);
const findFeedbackButton = () => findBanner().findComponent(GlButton);
const findTitle = () => wrapper.find('h1');
- const findDescription = () => wrapper.findByTestId('description');
+ const findDescription = () => wrapper.findByTestId('page-description');
const createComponent = ({ props = {}, provide = {}, stubs = {} } = {}) => {
wrapper = shallowMountExtended(CatalogHeader, {
@@ -33,6 +35,16 @@ describe('CatalogHeader', () => {
});
};
+ describe('Default view', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('renders a Beta Badge', () => {
+ expect(findBetaBadge().exists()).toBe(true);
+ });
+ });
+
describe('title and description', () => {
describe('when there are no values provided', () => {
beforeEach(() => {
@@ -42,10 +54,11 @@ describe('CatalogHeader', () => {
it('renders the default values', () => {
expect(findTitle().text()).toBe('CI/CD Catalog');
expect(findDescription().text()).toBe(
- 'Discover CI configuration resources for a seamless CI/CD experience.',
+ 'Discover CI/CD components that can improve your pipeline with additional functionality.',
);
});
});
+
describe('when custom values are provided', () => {
beforeEach(() => {
createComponent({ provide: customProvide });
@@ -57,6 +70,7 @@ describe('CatalogHeader', () => {
});
});
});
+
describe('Feedback banner', () => {
describe('when user has never dismissed', () => {
beforeEach(() => {
diff --git a/spec/frontend/ci/catalog/components/list/catalog_search_spec.js b/spec/frontend/ci/catalog/components/list/catalog_search_spec.js
new file mode 100644
index 00000000000..c6f8498f2fd
--- /dev/null
+++ b/spec/frontend/ci/catalog/components/list/catalog_search_spec.js
@@ -0,0 +1,103 @@
+import { GlSearchBoxByClick, GlSorting, GlSortingItem } 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';
+
+describe('CatalogSearch', () => {
+ let wrapper;
+
+ const findSearchBar = () => wrapper.findComponent(GlSearchBoxByClick);
+ const findSorting = () => wrapper.findComponent(GlSorting);
+ const findAllSortingItems = () => wrapper.findAllComponents(GlSortingItem);
+
+ const createComponent = () => {
+ wrapper = shallowMountExtended(CatalogSearch, {});
+ };
+
+ beforeEach(() => {
+ createComponent();
+ });
+
+ describe('default UI', () => {
+ it('renders the search bar', () => {
+ expect(findSearchBar().exists()).toBe(true);
+ });
+
+ it('renders the sorting options', () => {
+ expect(findSorting().exists()).toBe(true);
+ expect(findAllSortingItems()).toHaveLength(1);
+ });
+
+ it('renders the `Created at` option as the default', () => {
+ expect(findAllSortingItems().at(0).text()).toBe('Created at');
+ });
+ });
+
+ describe('search', () => {
+ it('passes down the search value to the search component', async () => {
+ const newSearchTerm = 'cat';
+
+ expect(findSearchBar().props().value).toBe('');
+
+ await findSearchBar().vm.$emit('input', newSearchTerm);
+
+ expect(findSearchBar().props().value).toBe(newSearchTerm);
+ });
+
+ it('does not submit only when typing', async () => {
+ expect(wrapper.emitted('update-search-term')).toBeUndefined();
+
+ await findSearchBar().vm.$emit('input', 'new');
+
+ expect(wrapper.emitted('update-search-term')).toBeUndefined();
+ });
+
+ describe('when submitting the search', () => {
+ const newSearchTerm = 'dog';
+
+ beforeEach(async () => {
+ await findSearchBar().vm.$emit('input', newSearchTerm);
+ await findSearchBar().vm.$emit('submit');
+ });
+
+ it('emits the event up with the new payload', () => {
+ expect(wrapper.emitted('update-search-term')).toEqual([[newSearchTerm]]);
+ });
+ });
+
+ describe('when clearing the search', () => {
+ beforeEach(async () => {
+ await findSearchBar().vm.$emit('input', 'new');
+ await findSearchBar().vm.$emit('clear');
+ });
+
+ it('emits an update event with an empty string payload', () => {
+ expect(wrapper.emitted('update-search-term')).toEqual([['']]);
+ });
+ });
+ });
+
+ describe('sort', () => {
+ describe('when changing sort order', () => {
+ it('changes the `isAscending` prop to the sorting component', async () => {
+ expect(findSorting().props().isAscending).toBe(false);
+
+ await findSorting().vm.$emit('sortDirectionChange');
+
+ expect(findSorting().props().isAscending).toBe(true);
+ });
+
+ it('emits an `update-sorting` event with the new direction', async () => {
+ expect(wrapper.emitted('update-sorting')).toBeUndefined();
+
+ await findSorting().vm.$emit('sortDirectionChange');
+ await findSorting().vm.$emit('sortDirectionChange');
+
+ expect(wrapper.emitted('update-sorting')).toEqual([
+ [`${SORT_OPTION_CREATED}_${SORT_ASC}`],
+ [`${SORT_OPTION_CREATED}_${SORT_DESC}`],
+ ]);
+ });
+ });
+ });
+});
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 3862195d8c7..d74b133f386 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
@@ -1,21 +1,22 @@
import Vue from 'vue';
import VueRouter from 'vue-router';
-import { GlAvatar, GlBadge, GlButton, GlSprintf } from '@gitlab/ui';
+import { GlAvatar, GlBadge, GlSprintf } from '@gitlab/ui';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import { cleanLeadingSeparator } from '~/lib/utils/url_utility';
import { createRouter } from '~/ci/catalog/router/index';
import CiResourcesListItem from '~/ci/catalog/components/list/ci_resources_list_item.vue';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
-import { CI_RESOURCE_DETAILS_PAGE_NAME } from '~/ci/catalog/router/constants';
import { catalogSinglePageResponse } from '../../mock';
Vue.use(VueRouter);
-let router;
-let routerPush;
+const defaultEvent = { preventDefault: jest.fn, ctrlKey: false, metaKey: false };
describe('CiResourcesListItem', () => {
let wrapper;
+ let routerPush;
+ const router = createRouter();
const resource = catalogSinglePageResponse.data.ciCatalogResources.nodes[0];
const release = {
author: { name: 'author', webUrl: '/user/1' },
@@ -35,22 +36,19 @@ describe('CiResourcesListItem', () => {
},
stubs: {
GlSprintf,
- RouterLink: true,
- RouterView: true,
},
});
};
const findAvatar = () => wrapper.findComponent(GlAvatar);
const findBadge = () => wrapper.findComponent(GlBadge);
- const findResourceName = () => wrapper.findComponent(GlButton);
+ const findResourceName = () => wrapper.findByTestId('ci-resource-link');
const findResourceDescription = () => wrapper.findByText(defaultProps.resource.description);
const findUserLink = () => wrapper.findByTestId('user-link');
const findTimeAgoMessage = () => wrapper.findComponent(GlSprintf);
const findFavorites = () => wrapper.findByTestId('stats-favorites');
beforeEach(() => {
- router = createRouter();
routerPush = jest.spyOn(router, 'push').mockImplementation(() => {});
});
@@ -70,8 +68,9 @@ describe('CiResourcesListItem', () => {
});
});
- it('renders the resource name button', () => {
+ it('renders the resource name and link', () => {
expect(findResourceName().exists()).toBe(true);
+ expect(findResourceName().attributes().href).toBe(defaultProps.resource.webPath);
});
it('renders the resource version badge', () => {
@@ -81,58 +80,69 @@ describe('CiResourcesListItem', () => {
it('renders the resource description', () => {
expect(findResourceDescription().exists()).toBe(true);
});
+ });
- describe('release time', () => {
- describe('when there is no release data', () => {
- beforeEach(() => {
- createComponent({ props: { resource: { ...resource, latestVersion: null } } });
- });
+ describe('release time', () => {
+ describe('when there is no release data', () => {
+ beforeEach(() => {
+ createComponent({ props: { resource: { ...resource, latestVersion: null } } });
+ });
- it('does not render the release', () => {
- expect(findTimeAgoMessage().exists()).toBe(false);
- });
+ it('does not render the release', () => {
+ expect(findTimeAgoMessage().exists()).toBe(false);
+ });
- it('renders the generic `unreleased` badge', () => {
- expect(findBadge().exists()).toBe(true);
- expect(findBadge().text()).toBe('Unreleased');
- });
+ it('renders the generic `unreleased` badge', () => {
+ expect(findBadge().exists()).toBe(true);
+ expect(findBadge().text()).toBe('Unreleased');
});
+ });
- describe('when there is release data', () => {
- beforeEach(() => {
- createComponent({ props: { resource: { ...resource, latestVersion: { ...release } } } });
- });
+ describe('when there is release data', () => {
+ beforeEach(() => {
+ createComponent({ props: { resource: { ...resource, latestVersion: { ...release } } } });
+ });
- it('renders the user link', () => {
- expect(findUserLink().exists()).toBe(true);
- expect(findUserLink().attributes('href')).toBe(release.author.webUrl);
- });
+ it('renders the user link', () => {
+ expect(findUserLink().exists()).toBe(true);
+ expect(findUserLink().attributes('href')).toBe(release.author.webUrl);
+ });
- it('renders the time since the resource was released', () => {
- expect(findTimeAgoMessage().exists()).toBe(true);
- });
+ it('renders the time since the resource was released', () => {
+ expect(findTimeAgoMessage().exists()).toBe(true);
+ });
- it('renders the version badge', () => {
- expect(findBadge().exists()).toBe(true);
- expect(findBadge().text()).toBe(release.tagName);
- });
+ it('renders the version badge', () => {
+ expect(findBadge().exists()).toBe(true);
+ expect(findBadge().text()).toBe(release.tagName);
});
});
});
describe('when clicking on an item title', () => {
- beforeEach(async () => {
- createComponent();
+ describe('without holding down a modifier key', () => {
+ it('navigates to the details page in the same tab', async () => {
+ createComponent();
+ await findResourceName().vm.$emit('click', defaultEvent);
- await findResourceName().vm.$emit('click');
+ expect(routerPush).toHaveBeenCalledWith({
+ path: cleanLeadingSeparator(resource.webPath),
+ });
+ });
});
- it('navigates to the details page', () => {
- expect(routerPush).toHaveBeenCalledWith({
- name: CI_RESOURCE_DETAILS_PAGE_NAME,
- params: {
- id: getIdFromGraphQLId(resource.id),
- },
+ describe.each`
+ keyName
+ ${'ctrlKey'}
+ ${'metaKey'}
+ `('when $keyName is being held down', ({ keyName }) => {
+ beforeEach(async () => {
+ createComponent();
+ await findResourceName().vm.$emit('click', { ...defaultEvent, [keyName]: true });
+ });
+
+ it('does not call VueRouter push', () => {
+ expect(routerPush).not.toHaveBeenCalled();
});
});
});
@@ -141,43 +151,35 @@ describe('CiResourcesListItem', () => {
beforeEach(async () => {
createComponent();
- await findAvatar().vm.$emit('click');
+ await findAvatar().vm.$emit('click', defaultEvent);
});
it('navigates to the details page', () => {
- expect(routerPush).toHaveBeenCalledWith({
- name: CI_RESOURCE_DETAILS_PAGE_NAME,
- params: {
- id: getIdFromGraphQLId(resource.id),
- },
- });
+ expect(routerPush).toHaveBeenCalledWith({ path: cleanLeadingSeparator(resource.webPath) });
});
});
describe('statistics', () => {
describe('when there are no statistics', () => {
- beforeEach(() => {
+ it('render favorites as 0', () => {
createComponent({
props: {
resource: {
+ ...resource,
starCount: 0,
},
},
});
- });
- it('render favorites as 0', () => {
expect(findFavorites().exists()).toBe(true);
expect(findFavorites().text()).toBe('0');
});
});
describe('where there are statistics', () => {
- beforeEach(() => {
+ it('render favorites', () => {
createComponent();
- });
- it('render favorites', () => {
expect(findFavorites().exists()).toBe(true);
expect(findFavorites().text()).toBe(String(defaultProps.resource.starCount));
});
diff --git a/spec/frontend/ci/catalog/components/list/empty_state_spec.js b/spec/frontend/ci/catalog/components/list/empty_state_spec.js
index f589ad96a9d..5db0c61371d 100644
--- a/spec/frontend/ci/catalog/components/list/empty_state_spec.js
+++ b/spec/frontend/ci/catalog/components/list/empty_state_spec.js
@@ -1,27 +1,83 @@
-import { GlEmptyState } from '@gitlab/ui';
+import { GlEmptyState, GlLink, GlSprintf } from '@gitlab/ui';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import EmptyState from '~/ci/catalog/components/list/empty_state.vue';
+import { COMPONENTS_DOCS_URL } from '~/ci/catalog/constants';
describe('EmptyState', () => {
let wrapper;
const findEmptyState = () => wrapper.findComponent(GlEmptyState);
+ const findComponentsDocLink = () => wrapper.findComponent(GlLink);
const createComponent = ({ props = {} } = {}) => {
wrapper = shallowMountExtended(EmptyState, {
propsData: {
...props,
},
+ stubs: {
+ GlEmptyState,
+ GlSprintf,
+ },
});
};
- describe('when mounted', () => {
+ describe('default', () => {
beforeEach(() => {
createComponent();
});
- it('renders the empty state', () => {
- expect(findEmptyState().exists()).toBe(true);
+ it('renders the default empty state', () => {
+ const emptyState = findEmptyState();
+
+ expect(emptyState.exists()).toBe(true);
+ expect(emptyState.props().title).toBe('Get started with the CI/CD Catalog');
+ expect(emptyState.props().description).toBe(
+ 'Create a pipeline component repository and make reusing pipeline configurations faster and easier.',
+ );
+ });
+ });
+
+ describe('when there is a search query', () => {
+ beforeEach(() => {
+ createComponent({
+ props: { searchTerm: 'a' },
+ });
+ });
+
+ it('renders the search description', () => {
+ expect(findEmptyState().text()).toContain(
+ 'Edit your search and try again. Or learn to create a component repository.',
+ );
+ });
+
+ it('renders the link to the components documentation', () => {
+ const docsLink = findComponentsDocLink();
+ expect(docsLink.exists()).toBe(true);
+ expect(docsLink.attributes().href).toBe(COMPONENTS_DOCS_URL);
+ });
+
+ describe('and it is less than 3 characters', () => {
+ beforeEach(() => {
+ createComponent({
+ props: { searchTerm: 'a' },
+ });
+ });
+
+ it('render the too few chars empty state title', () => {
+ expect(findEmptyState().props().title).toBe('Search must be at least 3 characters');
+ });
+ });
+
+ describe('and it has more than 3 characters', () => {
+ beforeEach(() => {
+ createComponent({
+ props: { searchTerm: 'my component' },
+ });
+ });
+
+ it('renders the search empty state title', () => {
+ expect(findEmptyState().props().title).toBe('No result found');
+ });
});
});
});
diff --git a/spec/frontend/ci/catalog/components/pages/ci_resource_details_page_spec.js b/spec/frontend/ci/catalog/components/pages/ci_resource_details_page_spec.js
index 40f243ed891..015c6504fa5 100644
--- a/spec/frontend/ci/catalog/components/pages/ci_resource_details_page_spec.js
+++ b/spec/frontend/ci/catalog/components/pages/ci_resource_details_page_spec.js
@@ -5,7 +5,8 @@ import { GlEmptyState } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
-import { CI_CATALOG_RESOURCE_TYPE, cacheConfig } from '~/ci/catalog/graphql/settings';
+import { cacheConfig } from '~/ci/catalog/graphql/settings';
+import { cleanLeadingSeparator } from '~/lib/utils/url_utility';
import getCiCatalogResourceSharedData from '~/ci/catalog/graphql/queries/get_ci_catalog_resource_shared_data.query.graphql';
import getCiCatalogResourceDetails from '~/ci/catalog/graphql/queries/get_ci_catalog_resource_details.query.graphql';
@@ -17,7 +18,6 @@ import CiResourceHeaderSkeletonLoader from '~/ci/catalog/components/details/ci_r
import { createRouter } from '~/ci/catalog/router/index';
import { CI_RESOURCE_DETAILS_PAGE_NAME } from '~/ci/catalog/router/constants';
-import { convertToGraphQLId } from '~/graphql_shared/utils';
import { catalogSharedDataMock, catalogAdditionalDetailsMock } from '../../mock';
Vue.use(VueApollo);
@@ -75,7 +75,7 @@ describe('CiResourceDetailsPage', () => {
router = createRouter();
await router.push({
name: CI_RESOURCE_DETAILS_PAGE_NAME,
- params: { id: defaultSharedData.id },
+ params: { id: defaultSharedData.webPath },
});
});
@@ -178,7 +178,7 @@ describe('CiResourceDetailsPage', () => {
it('passes expected props', () => {
expect(findDetailsComponent().props()).toEqual({
- resourceId: convertToGraphQLId(CI_CATALOG_RESOURCE_TYPE, defaultAdditionalData.id),
+ resourcePath: cleanLeadingSeparator(defaultSharedData.webPath),
});
});
});
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 e18b418b155..e6fbd63f307 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,10 +7,12 @@ 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 CatalogListSkeletonLoader from '~/ci/catalog/components/list/catalog_list_skeleton_loader.vue';
import EmptyState from '~/ci/catalog/components/list/empty_state.vue';
-import { cacheConfig } from '~/ci/catalog/graphql/settings';
+import { cacheConfig, resolvers } from '~/ci/catalog/graphql/settings';
+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';
@@ -24,9 +26,11 @@ describe('CiResourcesPage', () => {
let wrapper;
let catalogResourcesResponse;
+ const defaultQueryVariables = { first: 20 };
+
const createComponent = () => {
const handlers = [[getCatalogResources, catalogResourcesResponse]];
- const mockApollo = createMockApollo(handlers, {}, cacheConfig);
+ const mockApollo = createMockApollo(handlers, resolvers, { cacheConfig, typeDefs });
wrapper = shallowMountExtended(ciResourcesPage, {
apolloProvider: mockApollo,
@@ -36,6 +40,7 @@ describe('CiResourcesPage', () => {
};
const findCatalogHeader = () => wrapper.findComponent(CatalogHeader);
+ const findCatalogSearch = () => wrapper.findComponent(CatalogSearch);
const findCiResourcesList = () => wrapper.findComponent(CiResourcesList);
const findLoadingState = () => wrapper.findComponent(CatalogListSkeletonLoader);
const findEmptyState = () => wrapper.findComponent(EmptyState);
@@ -71,8 +76,14 @@ describe('CiResourcesPage', () => {
});
it('renders the empty state', () => {
- expect(findLoadingState().exists()).toBe(false);
expect(findEmptyState().exists()).toBe(true);
+ });
+
+ it('renders the search', () => {
+ expect(findCatalogSearch().exists()).toBe(true);
+ });
+
+ it('does not render the list', () => {
expect(findCiResourcesList().exists()).toBe(false);
});
});
@@ -99,6 +110,10 @@ describe('CiResourcesPage', () => {
totalCount: count,
});
});
+
+ it('renders the search and sort', () => {
+ expect(findCatalogSearch().exists()).toBe(true);
+ });
});
});
@@ -121,11 +136,12 @@ describe('CiResourcesPage', () => {
if (eventName === 'onNextPage') {
expect(catalogResourcesResponse.mock.calls[1][0]).toEqual({
+ ...defaultQueryVariables,
after: pageInfo.endCursor,
- first: 20,
});
} else {
expect(catalogResourcesResponse.mock.calls[1][0]).toEqual({
+ ...defaultQueryVariables,
before: pageInfo.startCursor,
last: 20,
first: null,
@@ -134,8 +150,75 @@ describe('CiResourcesPage', () => {
});
});
+ describe('search and sort', () => {
+ describe('on initial load', () => {
+ beforeEach(async () => {
+ catalogResourcesResponse.mockResolvedValue(catalogResponseBody);
+ await createComponent();
+ });
+
+ it('calls the query without search or sort', () => {
+ expect(catalogResourcesResponse).toHaveBeenCalledTimes(1);
+ expect(catalogResourcesResponse.mock.calls[0][0]).toEqual({
+ ...defaultQueryVariables,
+ });
+ });
+ });
+
+ describe('when sorting changes', () => {
+ const newSort = 'MOST_AWESOME_ASC';
+
+ beforeEach(async () => {
+ catalogResourcesResponse.mockResolvedValue(catalogResponseBody);
+ await createComponent();
+ await findCatalogSearch().vm.$emit('update-sorting', newSort);
+ });
+
+ it('passes it to the graphql query', () => {
+ expect(catalogResourcesResponse).toHaveBeenCalledTimes(2);
+ expect(catalogResourcesResponse.mock.calls[1][0]).toEqual({
+ ...defaultQueryVariables,
+ sortValue: newSort,
+ });
+ });
+ });
+
+ describe('when search component emits a new search term', () => {
+ const newSearch = 'sloths';
+
+ describe('and there are no results', () => {
+ 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', () => {
+ expect(findEmptyState().exists()).toBe(true);
+ expect(findEmptyState().props().searchTerm).toBe(newSearch);
+ });
+ });
+
+ describe('and there are results', () => {
+ beforeEach(async () => {
+ catalogResourcesResponse.mockResolvedValue(catalogResponseBody);
+ await createComponent();
+ await findCatalogSearch().vm.$emit('update-search-term', newSearch);
+ });
+
+ it('passes it to the graphql query', () => {
+ expect(catalogResourcesResponse).toHaveBeenCalledTimes(2);
+ expect(catalogResourcesResponse.mock.calls[1][0]).toEqual({
+ ...defaultQueryVariables,
+ searchTerm: newSearch,
+ });
+ });
+ });
+ });
+ });
+
describe('pages count', () => {
- describe('when the fetchMore call suceeds', () => {
+ describe('when the fetchMore call succeeds', () => {
beforeEach(async () => {
catalogResourcesResponse.mockResolvedValue(catalogResponseBody);
@@ -157,6 +240,31 @@ describe('CiResourcesPage', () => {
});
});
+ describe.each`
+ event | payload
+ ${'update-search-term'} | ${'cat'}
+ ${'update-sorting'} | ${'CREATED_ASC'}
+ `('when $event event is emitted', ({ event, payload }) => {
+ beforeEach(async () => {
+ catalogResourcesResponse.mockResolvedValue(catalogResponseBody);
+ await createComponent();
+ });
+
+ it('resets the page count', async () => {
+ expect(findCiResourcesList().props().currentPage).toBe(1);
+
+ findCiResourcesList().vm.$emit('onNextPage');
+ await waitForPromises();
+
+ expect(findCiResourcesList().props().currentPage).toBe(2);
+
+ await findCatalogSearch().vm.$emit(event, payload);
+ await waitForPromises();
+
+ expect(findCiResourcesList().props().currentPage).toBe(1);
+ });
+ });
+
describe('when the fetchMore call fails', () => {
const errorMessage = 'there was an error';