diff options
Diffstat (limited to 'spec/frontend/ci/catalog/components/pages/ci_resource_details_page_spec.js')
-rw-r--r-- | spec/frontend/ci/catalog/components/pages/ci_resource_details_page_spec.js | 186 |
1 files changed, 186 insertions, 0 deletions
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 new file mode 100644 index 00000000000..40f243ed891 --- /dev/null +++ b/spec/frontend/ci/catalog/components/pages/ci_resource_details_page_spec.js @@ -0,0 +1,186 @@ +import Vue from 'vue'; +import VueApollo from 'vue-apollo'; +import VueRouter from 'vue-router'; +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 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'; + +import CiResourceDetails from '~/ci/catalog/components/details/ci_resource_details.vue'; +import CiResourceDetailsPage from '~/ci/catalog/components/pages/ci_resource_details_page.vue'; +import CiResourceHeader from '~/ci/catalog/components/details/ci_resource_header.vue'; +import CiResourceHeaderSkeletonLoader from '~/ci/catalog/components/details/ci_resource_header_skeleton_loader.vue'; + +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); +Vue.use(VueRouter); + +let router; + +const defaultSharedData = { ...catalogSharedDataMock.data.ciCatalogResource }; +const defaultAdditionalData = { ...catalogAdditionalDetailsMock.data.ciCatalogResource }; + +describe('CiResourceDetailsPage', () => { + let wrapper; + let sharedDataResponse; + let additionalDataResponse; + + const defaultProps = {}; + + const defaultProvide = { + ciCatalogPath: '/ci/catalog/resources', + }; + + const findDetailsComponent = () => wrapper.findComponent(CiResourceDetails); + const findHeaderComponent = () => wrapper.findComponent(CiResourceHeader); + const findEmptyState = () => wrapper.findComponent(GlEmptyState); + const findHeaderSkeletonLoader = () => wrapper.findComponent(CiResourceHeaderSkeletonLoader); + + const createComponent = ({ props = {} } = {}) => { + const handlers = [ + [getCiCatalogResourceSharedData, sharedDataResponse], + [getCiCatalogResourceDetails, additionalDataResponse], + ]; + + const mockApollo = createMockApollo(handlers, undefined, cacheConfig); + + wrapper = shallowMount(CiResourceDetailsPage, { + router, + apolloProvider: mockApollo, + provide: { + ...defaultProvide, + }, + propsData: { + ...defaultProps, + ...props, + }, + stubs: { + RouterView: true, + }, + }); + }; + + beforeEach(async () => { + sharedDataResponse = jest.fn(); + additionalDataResponse = jest.fn(); + + router = createRouter(); + await router.push({ + name: CI_RESOURCE_DETAILS_PAGE_NAME, + params: { id: defaultSharedData.id }, + }); + }); + + describe('when the app is loading', () => { + describe('and shared data is pre-fetched', () => { + beforeEach(() => { + // By mocking a return value and not a promise, we skip the loading + // to simulate having the pre-fetched query + sharedDataResponse.mockReturnValueOnce(catalogSharedDataMock); + additionalDataResponse.mockResolvedValue(catalogAdditionalDetailsMock); + createComponent(); + }); + + it('renders the header skeleton loader', () => { + expect(findHeaderSkeletonLoader().exists()).toBe(false); + }); + + it('passes down the loading state to the header component', () => { + sharedDataResponse.mockReturnValueOnce(catalogSharedDataMock); + + expect(findHeaderComponent().props()).toMatchObject({ + isLoadingDetails: true, + isLoadingSharedData: false, + }); + }); + }); + + describe('and shared data is not pre-fetched', () => { + beforeEach(() => { + sharedDataResponse.mockResolvedValue(catalogSharedDataMock); + additionalDataResponse.mockResolvedValue(catalogAdditionalDetailsMock); + createComponent(); + }); + + it('does not render the header skeleton', () => { + expect(findHeaderSkeletonLoader().exists()).toBe(false); + }); + + it('passes all loading state to the header component as true', () => { + expect(findHeaderComponent().props()).toMatchObject({ + isLoadingDetails: true, + isLoadingSharedData: true, + }); + }); + }); + }); + + describe('and there are no resources', () => { + beforeEach(async () => { + const mockError = new Error('error'); + sharedDataResponse.mockRejectedValue(mockError); + additionalDataResponse.mockRejectedValue(mockError); + + createComponent(); + await waitForPromises(); + }); + + it('renders the empty state', () => { + expect(findDetailsComponent().exists()).toBe(false); + expect(findEmptyState().exists()).toBe(true); + expect(findEmptyState().props('primaryButtonLink')).toBe(defaultProvide.ciCatalogPath); + }); + }); + + describe('when data has loaded', () => { + beforeEach(async () => { + sharedDataResponse.mockResolvedValue(catalogSharedDataMock); + additionalDataResponse.mockResolvedValue(catalogAdditionalDetailsMock); + createComponent(); + + await waitForPromises(); + }); + + it('does not render the header skeleton loader', () => { + expect(findHeaderSkeletonLoader().exists()).toBe(false); + }); + + describe('Catalog header', () => { + it('exists', () => { + expect(findHeaderComponent().exists()).toBe(true); + }); + + it('passes expected props', () => { + expect(findHeaderComponent().props()).toEqual({ + isLoadingDetails: false, + isLoadingSharedData: false, + openIssuesCount: defaultAdditionalData.openIssuesCount, + openMergeRequestsCount: defaultAdditionalData.openMergeRequestsCount, + pipelineStatus: + defaultAdditionalData.versions.nodes[0].commit.pipelines.nodes[0].detailedStatus, + resource: defaultSharedData, + }); + }); + }); + + describe('Catalog details', () => { + it('exists', () => { + expect(findDetailsComponent().exists()).toBe(true); + }); + + it('passes expected props', () => { + expect(findDetailsComponent().props()).toEqual({ + resourceId: convertToGraphQLId(CI_CATALOG_RESOURCE_TYPE, defaultAdditionalData.id), + }); + }); + }); + }); +}); |