summaryrefslogtreecommitdiff
path: root/spec/frontend/releases/components/app_index_apollo_client_spec.js
diff options
context:
space:
mode:
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.js398
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();
- });
- },
- );
- });
-});