diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-04-20 10:00:54 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-04-20 10:00:54 +0000 |
commit | 3cccd102ba543e02725d247893729e5c73b38295 (patch) | |
tree | f36a04ec38517f5deaaacb5acc7d949688d1e187 /spec/frontend/releases/components/app_index_apollo_client_spec.js | |
parent | 205943281328046ef7b4528031b90fbda70c75ac (diff) | |
download | gitlab-ce-3cccd102ba543e02725d247893729e5c73b38295.tar.gz |
Add latest changes from gitlab-org/gitlab@14-10-stable-eev14.10.0-rc42
Diffstat (limited to 'spec/frontend/releases/components/app_index_apollo_client_spec.js')
-rw-r--r-- | spec/frontend/releases/components/app_index_apollo_client_spec.js | 398 |
1 files changed, 0 insertions, 398 deletions
diff --git a/spec/frontend/releases/components/app_index_apollo_client_spec.js b/spec/frontend/releases/components/app_index_apollo_client_spec.js deleted file mode 100644 index 9881ef9bc9f..00000000000 --- a/spec/frontend/releases/components/app_index_apollo_client_spec.js +++ /dev/null @@ -1,398 +0,0 @@ -import { cloneDeep } from 'lodash'; -import Vue, { nextTick } from 'vue'; -import VueApollo from 'vue-apollo'; -import originalAllReleasesQueryResponse from 'test_fixtures/graphql/releases/graphql/queries/all_releases.query.graphql.json'; -import createMockApollo from 'helpers/mock_apollo_helper'; -import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; -import waitForPromises from 'helpers/wait_for_promises'; -import allReleasesQuery from 'shared_queries/releases/all_releases.query.graphql'; -import createFlash from '~/flash'; -import { historyPushState } from '~/lib/utils/common_utils'; -import ReleasesIndexApolloClientApp from '~/releases/components/app_index_apollo_client.vue'; -import ReleaseBlock from '~/releases/components/release_block.vue'; -import ReleaseSkeletonLoader from '~/releases/components/release_skeleton_loader.vue'; -import ReleasesEmptyState from '~/releases/components/releases_empty_state.vue'; -import ReleasesPaginationApolloClient from '~/releases/components/releases_pagination_apollo_client.vue'; -import ReleasesSortApolloClient from '~/releases/components/releases_sort_apollo_client.vue'; -import { PAGE_SIZE, CREATED_ASC, DEFAULT_SORT } from '~/releases/constants'; - -Vue.use(VueApollo); - -jest.mock('~/flash'); - -let mockQueryParams; -jest.mock('~/lib/utils/common_utils', () => ({ - ...jest.requireActual('~/lib/utils/common_utils'), - historyPushState: jest.fn(), -})); - -jest.mock('~/lib/utils/url_utility', () => ({ - ...jest.requireActual('~/lib/utils/url_utility'), - getParameterByName: jest - .fn() - .mockImplementation((parameterName) => mockQueryParams[parameterName]), -})); - -describe('app_index_apollo_client.vue', () => { - const projectPath = 'project/path'; - const newReleasePath = 'path/to/new/release/page'; - const before = 'beforeCursor'; - const after = 'afterCursor'; - - let wrapper; - let allReleases; - let singleRelease; - let noReleases; - let queryMock; - - const createComponent = ({ - singleResponse = Promise.resolve(singleRelease), - fullResponse = Promise.resolve(allReleases), - } = {}) => { - const apolloProvider = createMockApollo([ - [ - allReleasesQuery, - queryMock.mockImplementation((vars) => { - return vars.first === 1 ? singleResponse : fullResponse; - }), - ], - ]); - - wrapper = shallowMountExtended(ReleasesIndexApolloClientApp, { - apolloProvider, - provide: { - newReleasePath, - projectPath, - }, - }); - }; - - beforeEach(() => { - mockQueryParams = {}; - - allReleases = cloneDeep(originalAllReleasesQueryResponse); - - singleRelease = cloneDeep(originalAllReleasesQueryResponse); - singleRelease.data.project.releases.nodes.splice( - 1, - singleRelease.data.project.releases.nodes.length, - ); - - noReleases = cloneDeep(originalAllReleasesQueryResponse); - noReleases.data.project.releases.nodes = []; - - queryMock = jest.fn(); - }); - - afterEach(() => { - wrapper.destroy(); - }); - - // Finders - const findLoadingIndicator = () => wrapper.findComponent(ReleaseSkeletonLoader); - const findEmptyState = () => wrapper.findComponent(ReleasesEmptyState); - const findNewReleaseButton = () => - wrapper.findByText(ReleasesIndexApolloClientApp.i18n.newRelease); - const findAllReleaseBlocks = () => wrapper.findAllComponents(ReleaseBlock); - const findPagination = () => wrapper.findComponent(ReleasesPaginationApolloClient); - const findSort = () => wrapper.findComponent(ReleasesSortApolloClient); - - // Tests - describe('component states', () => { - // These need to be defined as functions, since `singleRelease` and - // `allReleases` are generated in a `beforeEach`, and therefore - // aren't available at test definition time. - const getInProgressResponse = () => new Promise(() => {}); - const getErrorResponse = () => Promise.reject(new Error('Oops!')); - const getSingleRequestLoadedResponse = () => Promise.resolve(singleRelease); - const getFullRequestLoadedResponse = () => Promise.resolve(allReleases); - const getLoadedEmptyResponse = () => Promise.resolve(noReleases); - - const toDescription = (bool) => (bool ? 'does' : 'does not'); - - describe.each` - description | singleResponseFn | fullResponseFn | loadingIndicator | emptyState | flashMessage | releaseCount | pagination - ${'both requests loading'} | ${getInProgressResponse} | ${getInProgressResponse} | ${true} | ${false} | ${false} | ${0} | ${false} - ${'both requests failed'} | ${getErrorResponse} | ${getErrorResponse} | ${false} | ${false} | ${true} | ${0} | ${false} - ${'both requests loaded'} | ${getSingleRequestLoadedResponse} | ${getFullRequestLoadedResponse} | ${false} | ${false} | ${false} | ${2} | ${true} - ${'both requests loaded with no results'} | ${getLoadedEmptyResponse} | ${getLoadedEmptyResponse} | ${false} | ${true} | ${false} | ${0} | ${false} - ${'single request loading, full request loaded'} | ${getInProgressResponse} | ${getFullRequestLoadedResponse} | ${false} | ${false} | ${false} | ${2} | ${true} - ${'single request loading, full request failed'} | ${getInProgressResponse} | ${getErrorResponse} | ${true} | ${false} | ${true} | ${0} | ${false} - ${'single request loaded, full request loading'} | ${getSingleRequestLoadedResponse} | ${getInProgressResponse} | ${true} | ${false} | ${false} | ${1} | ${false} - ${'single request loaded, full request failed'} | ${getSingleRequestLoadedResponse} | ${getErrorResponse} | ${false} | ${false} | ${true} | ${1} | ${false} - ${'single request failed, full request loading'} | ${getErrorResponse} | ${getInProgressResponse} | ${true} | ${false} | ${false} | ${0} | ${false} - ${'single request failed, full request loaded'} | ${getErrorResponse} | ${getFullRequestLoadedResponse} | ${false} | ${false} | ${false} | ${2} | ${true} - ${'single request loaded with no results, full request loading'} | ${getLoadedEmptyResponse} | ${getInProgressResponse} | ${true} | ${false} | ${false} | ${0} | ${false} - ${'single request loading, full request loadied with no results'} | ${getInProgressResponse} | ${getLoadedEmptyResponse} | ${false} | ${true} | ${false} | ${0} | ${false} - `( - '$description', - ({ - singleResponseFn, - fullResponseFn, - loadingIndicator, - emptyState, - flashMessage, - releaseCount, - pagination, - }) => { - beforeEach(() => { - createComponent({ - singleResponse: singleResponseFn(), - fullResponse: fullResponseFn(), - }); - }); - - it(`${toDescription(loadingIndicator)} render a loading indicator`, async () => { - await waitForPromises(); - expect(findLoadingIndicator().exists()).toBe(loadingIndicator); - }); - - it(`${toDescription(emptyState)} render an empty state`, () => { - expect(findEmptyState().exists()).toBe(emptyState); - }); - - it(`${toDescription(flashMessage)} show a flash message`, () => { - if (flashMessage) { - expect(createFlash).toHaveBeenCalledWith({ - message: ReleasesIndexApolloClientApp.i18n.errorMessage, - captureError: true, - error: expect.any(Error), - }); - } else { - expect(createFlash).not.toHaveBeenCalled(); - } - }); - - it(`renders ${releaseCount} release(s)`, () => { - expect(findAllReleaseBlocks()).toHaveLength(releaseCount); - }); - - it(`${toDescription(pagination)} render the pagination controls`, () => { - expect(findPagination().exists()).toBe(pagination); - }); - - it('does render the "New release" button', () => { - expect(findNewReleaseButton().exists()).toBe(true); - }); - - it('does render the sort controls', () => { - expect(findSort().exists()).toBe(true); - }); - }, - ); - }); - - describe('URL parameters', () => { - describe('when the URL contains no query parameters', () => { - beforeEach(() => { - createComponent(); - }); - - it('makes a request with the correct GraphQL query parameters', () => { - expect(queryMock).toHaveBeenCalledTimes(2); - - expect(queryMock).toHaveBeenCalledWith({ - first: 1, - fullPath: projectPath, - sort: DEFAULT_SORT, - }); - - expect(queryMock).toHaveBeenCalledWith({ - first: PAGE_SIZE, - fullPath: projectPath, - sort: DEFAULT_SORT, - }); - }); - }); - - describe('when the URL contains a "before" query parameter', () => { - beforeEach(() => { - mockQueryParams = { before }; - createComponent(); - }); - - it('makes a request with the correct GraphQL query parameters', () => { - expect(queryMock).toHaveBeenCalledTimes(1); - - expect(queryMock).toHaveBeenCalledWith({ - before, - last: PAGE_SIZE, - fullPath: projectPath, - sort: DEFAULT_SORT, - }); - }); - }); - - describe('when the URL contains an "after" query parameter', () => { - beforeEach(() => { - mockQueryParams = { after }; - createComponent(); - }); - - it('makes a request with the correct GraphQL query parameters', () => { - expect(queryMock).toHaveBeenCalledTimes(2); - - expect(queryMock).toHaveBeenCalledWith({ - after, - first: 1, - fullPath: projectPath, - sort: DEFAULT_SORT, - }); - - expect(queryMock).toHaveBeenCalledWith({ - after, - first: PAGE_SIZE, - fullPath: projectPath, - sort: DEFAULT_SORT, - }); - }); - }); - - describe('when the URL contains both "before" and "after" query parameters', () => { - beforeEach(() => { - mockQueryParams = { before, after }; - createComponent(); - }); - - it('ignores the "before" parameter and behaves as if only the "after" parameter was provided', () => { - expect(queryMock).toHaveBeenCalledTimes(2); - - expect(queryMock).toHaveBeenCalledWith({ - after, - first: 1, - fullPath: projectPath, - sort: DEFAULT_SORT, - }); - - expect(queryMock).toHaveBeenCalledWith({ - after, - first: PAGE_SIZE, - fullPath: projectPath, - sort: DEFAULT_SORT, - }); - }); - }); - }); - - describe('New release button', () => { - beforeEach(() => { - createComponent(); - }); - - it('renders the new release button with the correct href', () => { - expect(findNewReleaseButton().attributes().href).toBe(newReleasePath); - }); - }); - - describe('pagination', () => { - beforeEach(() => { - mockQueryParams = { before }; - createComponent(); - }); - - it('requeries the GraphQL endpoint when a pagination button is clicked', async () => { - expect(queryMock.mock.calls).toEqual([[expect.objectContaining({ before })]]); - - mockQueryParams = { after }; - findPagination().vm.$emit('next', after); - - await nextTick(); - - expect(queryMock.mock.calls).toEqual([ - [expect.objectContaining({ before })], - [expect.objectContaining({ after })], - [expect.objectContaining({ after })], - ]); - }); - }); - - describe('sorting', () => { - beforeEach(() => { - createComponent(); - }); - - it(`sorts by ${DEFAULT_SORT} by default`, () => { - expect(queryMock.mock.calls).toEqual([ - [expect.objectContaining({ sort: DEFAULT_SORT })], - [expect.objectContaining({ sort: DEFAULT_SORT })], - ]); - }); - - it('requeries the GraphQL endpoint and updates the URL when the sort is changed', async () => { - findSort().vm.$emit('input', CREATED_ASC); - - await nextTick(); - - expect(queryMock.mock.calls).toEqual([ - [expect.objectContaining({ sort: DEFAULT_SORT })], - [expect.objectContaining({ sort: DEFAULT_SORT })], - [expect.objectContaining({ sort: CREATED_ASC })], - [expect.objectContaining({ sort: CREATED_ASC })], - ]); - - // URL manipulation is tested in more detail in the `describe` block below - expect(historyPushState).toHaveBeenCalled(); - }); - - it('does not requery the GraphQL endpoint or update the URL if the sort is updated to the same value', async () => { - findSort().vm.$emit('input', DEFAULT_SORT); - - await nextTick(); - - expect(queryMock.mock.calls).toEqual([ - [expect.objectContaining({ sort: DEFAULT_SORT })], - [expect.objectContaining({ sort: DEFAULT_SORT })], - ]); - - expect(historyPushState).not.toHaveBeenCalled(); - }); - }); - - describe('sorting + pagination interaction', () => { - const nonPaginationQueryParam = 'nonPaginationQueryParam'; - - beforeEach(() => { - historyPushState.mockImplementation((newUrl) => { - mockQueryParams = Object.fromEntries(new URL(newUrl).searchParams); - }); - }); - - describe.each` - queryParamsBefore | paramName | paramInitialValue - ${{ before, nonPaginationQueryParam }} | ${'before'} | ${before} - ${{ after, nonPaginationQueryParam }} | ${'after'} | ${after} - `( - 'when the URL contains a "$paramName" pagination cursor', - ({ queryParamsBefore, paramName, paramInitialValue }) => { - beforeEach(async () => { - mockQueryParams = queryParamsBefore; - createComponent(); - - findSort().vm.$emit('input', CREATED_ASC); - - await nextTick(); - }); - - it(`resets the page's "${paramName}" pagination cursor when the sort is changed`, () => { - const firstRequestVariables = queryMock.mock.calls[0][0]; - // Might be request #2 or #3, depending on the pagination direction - const mostRecentRequestVariables = - queryMock.mock.calls[queryMock.mock.calls.length - 1][0]; - - expect(firstRequestVariables[paramName]).toBe(paramInitialValue); - expect(mostRecentRequestVariables[paramName]).toBeUndefined(); - }); - - it(`updates the URL to not include the "${paramName}" URL query parameter`, () => { - expect(historyPushState).toHaveBeenCalledTimes(1); - - const updatedUrlQueryParams = Object.fromEntries( - new URL(historyPushState.mock.calls[0][0]).searchParams, - ); - - expect(updatedUrlQueryParams[paramName]).toBeUndefined(); - }); - }, - ); - }); -}); |