Commit 82d51739 authored by Nathan Friend's avatar Nathan Friend Committed by Nicolò Maria Mezzopera

Add GraphQL pagination to Releases page

This commit updates the Releases page to include pagination when in
"GraphQL mode".

Because this is the last step in the GraphQL feature, this MR also
enables the `graphql_releases_page` feature flag by default.
parent 83ccc18d
...@@ -6,14 +6,10 @@ import { ...@@ -6,14 +6,10 @@ import {
GlLink, GlLink,
GlButton, GlButton,
} from '@gitlab/ui'; } from '@gitlab/ui';
import { import { getParameterByName } from '~/lib/utils/common_utils';
getParameterByName,
historyPushState,
buildUrlWithCurrentLocation,
} from '~/lib/utils/common_utils';
import { __ } from '~/locale'; import { __ } from '~/locale';
import TablePagination from '~/vue_shared/components/pagination/table_pagination.vue';
import ReleaseBlock from './release_block.vue'; import ReleaseBlock from './release_block.vue';
import ReleasesPagination from './releases_pagination.vue';
export default { export default {
name: 'ReleasesApp', name: 'ReleasesApp',
...@@ -21,7 +17,7 @@ export default { ...@@ -21,7 +17,7 @@ export default {
GlSkeletonLoading, GlSkeletonLoading,
GlEmptyState, GlEmptyState,
ReleaseBlock, ReleaseBlock,
TablePagination, ReleasesPagination,
GlLink, GlLink,
GlButton, GlButton,
}, },
...@@ -33,7 +29,6 @@ export default { ...@@ -33,7 +29,6 @@ export default {
'isLoading', 'isLoading',
'releases', 'releases',
'hasError', 'hasError',
'pageInfo',
]), ]),
shouldRenderEmptyState() { shouldRenderEmptyState() {
return !this.releases.length && !this.hasError && !this.isLoading; return !this.releases.length && !this.hasError && !this.isLoading;
...@@ -48,15 +43,23 @@ export default { ...@@ -48,15 +43,23 @@ export default {
}, },
}, },
created() { created() {
this.fetchReleases({ this.fetchReleases();
page: getParameterByName('page'),
}); window.addEventListener('popstate', this.fetchReleases);
}, },
methods: { methods: {
...mapActions('list', ['fetchReleases']), ...mapActions('list', {
onChangePage(page) { fetchReleasesStoreAction: 'fetchReleases',
historyPushState(buildUrlWithCurrentLocation(`?page=${page}`)); }),
this.fetchReleases({ page }); fetchReleases() {
this.fetchReleasesStoreAction({
// these two parameters are only used in "GraphQL mode"
before: getParameterByName('before'),
after: getParameterByName('after'),
// this parameter is only used when in "REST mode"
page: getParameterByName('page'),
});
}, },
}, },
}; };
...@@ -105,7 +108,7 @@ export default { ...@@ -105,7 +108,7 @@ export default {
/> />
</div> </div>
<table-pagination v-if="!isLoading" :change="onChangePage" :page-info="pageInfo" /> <releases-pagination v-if="!isLoading" />
</div> </div>
</template> </template>
<style> <style>
......
...@@ -13,14 +13,14 @@ export default { ...@@ -13,14 +13,14 @@ export default {
}, },
}, },
methods: { methods: {
...mapActions('list', ['fetchReleasesGraphQl']), ...mapActions('list', ['fetchReleases']),
onPrev(before) { onPrev(before) {
historyPushState(buildUrlWithCurrentLocation(`?before=${before}`)); historyPushState(buildUrlWithCurrentLocation(`?before=${before}`));
this.fetchReleasesGraphQl({ before }); this.fetchReleases({ before });
}, },
onNext(after) { onNext(after) {
historyPushState(buildUrlWithCurrentLocation(`?after=${after}`)); historyPushState(buildUrlWithCurrentLocation(`?after=${after}`));
this.fetchReleasesGraphQl({ after }); this.fetchReleases({ after });
}, },
}, },
}; };
......
...@@ -7,18 +7,18 @@ export default { ...@@ -7,18 +7,18 @@ export default {
name: 'ReleasesPaginationRest', name: 'ReleasesPaginationRest',
components: { TablePagination }, components: { TablePagination },
computed: { computed: {
...mapState('list', ['pageInfo']), ...mapState('list', ['restPageInfo']),
}, },
methods: { methods: {
...mapActions('list', ['fetchReleasesRest']), ...mapActions('list', ['fetchReleases']),
onChangePage(page) { onChangePage(page) {
historyPushState(buildUrlWithCurrentLocation(`?page=${page}`)); historyPushState(buildUrlWithCurrentLocation(`?page=${page}`));
this.fetchReleasesRest({ page }); this.fetchReleases({ page });
}, },
}, },
}; };
</script> </script>
<template> <template>
<table-pagination :change="onChangePage" :page-info="pageInfo" /> <table-pagination :change="onChangePage" :page-info="restPageInfo" />
</template> </template>
...@@ -10,3 +10,5 @@ export const ASSET_LINK_TYPE = Object.freeze({ ...@@ -10,3 +10,5 @@ export const ASSET_LINK_TYPE = Object.freeze({
}); });
export const DEFAULT_ASSET_LINK_TYPE = ASSET_LINK_TYPE.OTHER; export const DEFAULT_ASSET_LINK_TYPE = ASSET_LINK_TYPE.OTHER;
export const PAGE_SIZE = 20;
query allReleases($fullPath: ID!) { query allReleases($fullPath: ID!, $first: Int, $last: Int, $before: String, $after: String) {
project(fullPath: $fullPath) { project(fullPath: $fullPath) {
releases(first: 20) { releases(first: $first, last: $last, before: $before, after: $after) {
count
nodes { nodes {
name name
tagName tagName
...@@ -64,6 +63,12 @@ query allReleases($fullPath: ID!) { ...@@ -64,6 +63,12 @@ query allReleases($fullPath: ID!) {
} }
} }
} }
pageInfo {
startCursor
hasPreviousPage
hasNextPage
endCursor
}
} }
} }
} }
/**
* @returns {Boolean} `true` if all the feature flags
* required to enable the GraphQL endpoint are enabled
*/
export const useGraphQLEndpoint = rootState => {
return Boolean(
rootState.featureFlags.graphqlReleaseData &&
rootState.featureFlags.graphqlReleasesPage &&
rootState.featureFlags.graphqlMilestoneStats,
);
};
import Vuex from 'vuex'; import Vuex from 'vuex';
import * as getters from './getters';
export default ({ modules, featureFlags }) => export default ({ modules, featureFlags }) =>
new Vuex.Store({ new Vuex.Store({
modules, modules,
state: { featureFlags }, state: { featureFlags },
getters,
}); });
...@@ -9,54 +9,89 @@ import { ...@@ -9,54 +9,89 @@ import {
} from '~/lib/utils/common_utils'; } from '~/lib/utils/common_utils';
import allReleasesQuery from '~/releases/queries/all_releases.query.graphql'; import allReleasesQuery from '~/releases/queries/all_releases.query.graphql';
import { gqClient, convertGraphQLResponse } from '../../../util'; import { gqClient, convertGraphQLResponse } from '../../../util';
import { PAGE_SIZE } from '../../../constants';
/** /**
* Commits a mutation to update the state while the main endpoint is being requested. * Gets a paginated list of releases from the server
*
* @param {Object} vuexParams
* @param {Object} actionParams
* @param {Number} [actionParams.page] The page number of results to fetch
* (this parameter is only used when fetching results from the REST API)
* @param {String} [actionParams.before] A GraphQL cursor. If provided,
* the items returned will proceed the provided cursor (this parameter is only
* used when fetching results from the GraphQL API).
* @param {String} [actionParams.after] A GraphQL cursor. If provided,
* the items returned will follow the provided cursor (this parameter is only
* used when fetching results from the GraphQL API).
*/ */
export const requestReleases = ({ commit }) => commit(types.REQUEST_RELEASES); export const fetchReleases = ({ dispatch, rootGetters }, { page = 1, before, after }) => {
if (rootGetters.useGraphQLEndpoint) {
dispatch('fetchReleasesGraphQl', { before, after });
} else {
dispatch('fetchReleasesRest', { page });
}
};
/** /**
* Fetches the main endpoint. * Gets a paginated list of releases from the GraphQL endpoint
* Will dispatch requestNamespace action before starting the request.
* Will dispatch receiveNamespaceSuccess if the request is successful
* Will dispatch receiveNamesapceError if the request returns an error
*
* @param {String} projectId
*/ */
export const fetchReleases = ({ dispatch, rootState, state }, { page = '1' }) => { export const fetchReleasesGraphQl = (
dispatch('requestReleases'); { dispatch, commit, state },
{ before = null, after = null },
) => {
commit(types.REQUEST_RELEASES);
let paginationParams;
if (!before && !after) {
paginationParams = { first: PAGE_SIZE };
} else if (before && !after) {
paginationParams = { last: PAGE_SIZE, before };
} else if (!before && after) {
paginationParams = { first: PAGE_SIZE, after };
} else {
throw new Error(
'Both a `before` and an `after` parameter were provided to fetchReleasesGraphQl. These parameters cannot be used together.',
);
}
if (
rootState.featureFlags.graphqlReleaseData &&
rootState.featureFlags.graphqlReleasesPage &&
rootState.featureFlags.graphqlMilestoneStats
) {
gqClient gqClient
.query({ .query({
query: allReleasesQuery, query: allReleasesQuery,
variables: { variables: {
fullPath: state.projectPath, fullPath: state.projectPath,
...paginationParams,
}, },
}) })
.then(response => { .then(response => {
dispatch('receiveReleasesSuccess', convertGraphQLResponse(response)); const { data, paginationInfo: graphQlPageInfo } = convertGraphQLResponse(response);
commit(types.RECEIVE_RELEASES_SUCCESS, {
data,
graphQlPageInfo,
});
}) })
.catch(() => dispatch('receiveReleasesError')); .catch(() => dispatch('receiveReleasesError'));
} else {
api
.releases(state.projectId, { page })
.then(response => dispatch('receiveReleasesSuccess', response))
.catch(() => dispatch('receiveReleasesError'));
}
}; };
export const receiveReleasesSuccess = ({ commit }, { data, headers }) => { /**
const pageInfo = parseIntPagination(normalizeHeaders(headers)); * Gets a paginated list of releases from the REST endpoint
*/
export const fetchReleasesRest = ({ dispatch, commit, state }, { page }) => {
commit(types.REQUEST_RELEASES);
api
.releases(state.projectId, { page })
.then(({ data, headers }) => {
const restPageInfo = parseIntPagination(normalizeHeaders(headers));
const camelCasedReleases = convertObjectPropsToCamelCase(data, { deep: true }); const camelCasedReleases = convertObjectPropsToCamelCase(data, { deep: true });
commit(types.RECEIVE_RELEASES_SUCCESS, { commit(types.RECEIVE_RELEASES_SUCCESS, {
data: camelCasedReleases, data: camelCasedReleases,
pageInfo, restPageInfo,
}); });
})
.catch(() => dispatch('receiveReleasesError'));
}; };
export const receiveReleasesError = ({ commit }) => { export const receiveReleasesError = ({ commit }) => {
......
...@@ -17,11 +17,12 @@ export default { ...@@ -17,11 +17,12 @@ export default {
* @param {Object} state * @param {Object} state
* @param {Object} resp * @param {Object} resp
*/ */
[types.RECEIVE_RELEASES_SUCCESS](state, { data, pageInfo }) { [types.RECEIVE_RELEASES_SUCCESS](state, { data, restPageInfo, graphQlPageInfo }) {
state.hasError = false; state.hasError = false;
state.isLoading = false; state.isLoading = false;
state.releases = data; state.releases = data;
state.pageInfo = pageInfo; state.restPageInfo = restPageInfo;
state.graphQlPageInfo = graphQlPageInfo;
}, },
/** /**
...@@ -35,5 +36,7 @@ export default { ...@@ -35,5 +36,7 @@ export default {
state.isLoading = false; state.isLoading = false;
state.releases = []; state.releases = [];
state.hasError = true; state.hasError = true;
state.restPageInfo = {};
state.graphQlPageInfo = {};
}, },
}; };
...@@ -14,5 +14,6 @@ export default ({ ...@@ -14,5 +14,6 @@ export default ({
isLoading: false, isLoading: false,
hasError: false, hasError: false,
releases: [], releases: [],
pageInfo: {}, restPageInfo: {},
graphQlPageInfo: {},
}); });
...@@ -126,5 +126,9 @@ export const convertGraphQLResponse = response => { ...@@ -126,5 +126,9 @@ export const convertGraphQLResponse = response => {
...convertMilestones(r), ...convertMilestones(r),
})); }));
return { data: releases }; const paginationInfo = {
...response.data.project.releases.pageInfo,
};
return { data: releases, paginationInfo };
}; };
...@@ -13,7 +13,7 @@ class Projects::ReleasesController < Projects::ApplicationController ...@@ -13,7 +13,7 @@ class Projects::ReleasesController < Projects::ApplicationController
push_frontend_feature_flag(:release_asset_link_type, project, default_enabled: true) push_frontend_feature_flag(:release_asset_link_type, project, default_enabled: true)
push_frontend_feature_flag(:graphql_release_data, project, default_enabled: true) push_frontend_feature_flag(:graphql_release_data, project, default_enabled: true)
push_frontend_feature_flag(:graphql_milestone_stats, project, default_enabled: true) push_frontend_feature_flag(:graphql_milestone_stats, project, default_enabled: true)
push_frontend_feature_flag(:graphql_releases_page, project, default_enabled: false) push_frontend_feature_flag(:graphql_releases_page, project, default_enabled: true)
end end
before_action :authorize_update_release!, only: %i[edit update] before_action :authorize_update_release!, only: %i[edit update]
before_action :authorize_create_release!, only: :new before_action :authorize_create_release!, only: :new
......
...@@ -109,5 +109,11 @@ Object { ...@@ -109,5 +109,11 @@ Object {
"upcomingRelease": false, "upcomingRelease": false,
}, },
], ],
"paginationInfo": Object {
"endCursor": "eyJpZCI6IjMiLCJyZWxlYXNlZF9hdCI6IjIwMjAtMDctMDkgMjA6MTE6MzMuODA0OTYxMDAwIFVUQyJ9",
"hasNextPage": true,
"hasPreviousPage": false,
"startCursor": "eyJpZCI6IjQ0IiwicmVsZWFzZWRfYXQiOiIyMDMwLTAzLTE1IDA4OjAwOjAwLjAwMDAwMDAwMCBVVEMifQ",
},
} }
`; `;
...@@ -13,7 +13,14 @@ import { ...@@ -13,7 +13,14 @@ import {
releases, releases,
} from '../mock_data'; } from '../mock_data';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import TablePagination from '~/vue_shared/components/pagination/table_pagination.vue'; import ReleasesPagination from '~/releases/components/releases_pagination.vue';
jest.mock('~/lib/utils/common_utils', () => ({
...jest.requireActual('~/lib/utils/common_utils'),
getParameterByName: jest.fn().mockImplementation(paramName => {
return `${paramName}_param_value`;
}),
}));
const localVue = createLocalVue(); const localVue = createLocalVue();
localVue.use(Vuex); localVue.use(Vuex);
...@@ -22,7 +29,7 @@ describe('Releases App ', () => { ...@@ -22,7 +29,7 @@ describe('Releases App ', () => {
let wrapper; let wrapper;
let fetchReleaseSpy; let fetchReleaseSpy;
const releasesPagination = rge(21).map(index => ({ const paginatedReleases = rge(21).map(index => ({
...convertObjectPropsToCamelCase(release, { deep: true }), ...convertObjectPropsToCamelCase(release, { deep: true }),
tagName: `${index}.00`, tagName: `${index}.00`,
})); }));
...@@ -70,9 +77,13 @@ describe('Releases App ', () => { ...@@ -70,9 +77,13 @@ describe('Releases App ', () => {
createComponent(); createComponent();
}); });
it('calls fetchRelease with the page parameter', () => { it('calls fetchRelease with the page, before, and after parameters', () => {
expect(fetchReleaseSpy).toHaveBeenCalledTimes(1); expect(fetchReleaseSpy).toHaveBeenCalledTimes(1);
expect(fetchReleaseSpy).toHaveBeenCalledWith(expect.anything(), { page: null }); expect(fetchReleaseSpy).toHaveBeenCalledWith(expect.anything(), {
page: 'page_param_value',
before: 'before_param_value',
after: 'after_param_value',
});
}); });
}); });
...@@ -91,7 +102,7 @@ describe('Releases App ', () => { ...@@ -91,7 +102,7 @@ describe('Releases App ', () => {
expect(wrapper.contains('.js-loading')).toBe(true); expect(wrapper.contains('.js-loading')).toBe(true);
expect(wrapper.contains('.js-empty-state')).toBe(false); expect(wrapper.contains('.js-empty-state')).toBe(false);
expect(wrapper.contains('.js-success-state')).toBe(false); expect(wrapper.contains('.js-success-state')).toBe(false);
expect(wrapper.contains(TablePagination)).toBe(false); expect(wrapper.contains(ReleasesPagination)).toBe(false);
}); });
}); });
...@@ -108,7 +119,7 @@ describe('Releases App ', () => { ...@@ -108,7 +119,7 @@ describe('Releases App ', () => {
expect(wrapper.contains('.js-loading')).toBe(false); expect(wrapper.contains('.js-loading')).toBe(false);
expect(wrapper.contains('.js-empty-state')).toBe(false); expect(wrapper.contains('.js-empty-state')).toBe(false);
expect(wrapper.contains('.js-success-state')).toBe(true); expect(wrapper.contains('.js-success-state')).toBe(true);
expect(wrapper.contains(TablePagination)).toBe(true); expect(wrapper.contains(ReleasesPagination)).toBe(true);
}); });
}); });
...@@ -116,7 +127,7 @@ describe('Releases App ', () => { ...@@ -116,7 +127,7 @@ describe('Releases App ', () => {
beforeEach(() => { beforeEach(() => {
jest jest
.spyOn(api, 'releases') .spyOn(api, 'releases')
.mockResolvedValue({ data: releasesPagination, headers: pageInfoHeadersWithPagination }); .mockResolvedValue({ data: paginatedReleases, headers: pageInfoHeadersWithPagination });
createComponent(); createComponent();
}); });
...@@ -125,7 +136,7 @@ describe('Releases App ', () => { ...@@ -125,7 +136,7 @@ describe('Releases App ', () => {
expect(wrapper.contains('.js-loading')).toBe(false); expect(wrapper.contains('.js-loading')).toBe(false);
expect(wrapper.contains('.js-empty-state')).toBe(false); expect(wrapper.contains('.js-empty-state')).toBe(false);
expect(wrapper.contains('.js-success-state')).toBe(true); expect(wrapper.contains('.js-success-state')).toBe(true);
expect(wrapper.contains(TablePagination)).toBe(true); expect(wrapper.contains(ReleasesPagination)).toBe(true);
}); });
}); });
...@@ -154,7 +165,7 @@ describe('Releases App ', () => { ...@@ -154,7 +165,7 @@ describe('Releases App ', () => {
const newReleasePath = 'path/to/new/release'; const newReleasePath = 'path/to/new/release';
beforeEach(() => { beforeEach(() => {
createComponent({ ...defaultInitialState, newReleasePath }); createComponent({ newReleasePath });
}); });
it('renders the "New release" button', () => { it('renders the "New release" button', () => {
...@@ -174,4 +185,27 @@ describe('Releases App ', () => { ...@@ -174,4 +185,27 @@ describe('Releases App ', () => {
}); });
}); });
}); });
describe('when the back button is pressed', () => {
beforeEach(() => {
jest
.spyOn(api, 'releases')
.mockResolvedValue({ data: releases, headers: pageInfoHeadersWithoutPagination });
createComponent();
fetchReleaseSpy.mockClear();
window.dispatchEvent(new PopStateEvent('popstate'));
});
it('calls fetchRelease with the page parameter', () => {
expect(fetchReleaseSpy).toHaveBeenCalledTimes(1);
expect(fetchReleaseSpy).toHaveBeenCalledWith(expect.anything(), {
page: 'page_param_value',
before: 'before_param_value',
after: 'after_param_value',
});
});
});
}); });
...@@ -29,7 +29,7 @@ describe('~/releases/components/releases_pagination_graphql.vue', () => { ...@@ -29,7 +29,7 @@ describe('~/releases/components/releases_pagination_graphql.vue', () => {
listModule.state.graphQlPageInfo = pageInfo; listModule.state.graphQlPageInfo = pageInfo;
listModule.actions.fetchReleasesGraphQl = jest.fn(); listModule.actions.fetchReleases = jest.fn();
wrapper = mount(ReleasesPaginationGraphql, { wrapper = mount(ReleasesPaginationGraphql, {
store: createStore({ store: createStore({
...@@ -141,8 +141,8 @@ describe('~/releases/components/releases_pagination_graphql.vue', () => { ...@@ -141,8 +141,8 @@ describe('~/releases/components/releases_pagination_graphql.vue', () => {
findNextButton().trigger('click'); findNextButton().trigger('click');
}); });
it('calls fetchReleasesGraphQl with the correct after cursor', () => { it('calls fetchReleases with the correct after cursor', () => {
expect(listModule.actions.fetchReleasesGraphQl.mock.calls).toEqual([ expect(listModule.actions.fetchReleases.mock.calls).toEqual([
[expect.anything(), { after: cursors.endCursor }], [expect.anything(), { after: cursors.endCursor }],
]); ]);
}); });
...@@ -159,8 +159,8 @@ describe('~/releases/components/releases_pagination_graphql.vue', () => { ...@@ -159,8 +159,8 @@ describe('~/releases/components/releases_pagination_graphql.vue', () => {
findPrevButton().trigger('click'); findPrevButton().trigger('click');
}); });
it('calls fetchReleasesGraphQl with the correct before cursor', () => { it('calls fetchReleases with the correct before cursor', () => {
expect(listModule.actions.fetchReleasesGraphQl.mock.calls).toEqual([ expect(listModule.actions.fetchReleases.mock.calls).toEqual([
[expect.anything(), { before: cursors.startCursor }], [expect.anything(), { before: cursors.startCursor }],
]); ]);
}); });
......
...@@ -20,9 +20,9 @@ describe('~/releases/components/releases_pagination_rest.vue', () => { ...@@ -20,9 +20,9 @@ describe('~/releases/components/releases_pagination_rest.vue', () => {
const createComponent = pageInfo => { const createComponent = pageInfo => {
listModule = createListModule({ projectId }); listModule = createListModule({ projectId });
listModule.state.pageInfo = pageInfo; listModule.state.restPageInfo = pageInfo;
listModule.actions.fetchReleasesRest = jest.fn(); listModule.actions.fetchReleases = jest.fn();
wrapper = mount(ReleasesPaginationRest, { wrapper = mount(ReleasesPaginationRest, {
store: createStore({ store: createStore({
...@@ -57,8 +57,8 @@ describe('~/releases/components/releases_pagination_rest.vue', () => { ...@@ -57,8 +57,8 @@ describe('~/releases/components/releases_pagination_rest.vue', () => {
findGlPagination().vm.$emit('input', newPage); findGlPagination().vm.$emit('input', newPage);
}); });
it('calls fetchReleasesRest with the correct page', () => { it('calls fetchReleases with the correct page', () => {
expect(listModule.actions.fetchReleasesRest.mock.calls).toEqual([ expect(listModule.actions.fetchReleases.mock.calls).toEqual([
[expect.anything(), { page: newPage }], [expect.anything(), { page: newPage }],
]); ]);
}); });
......
...@@ -346,6 +346,14 @@ export const graphqlReleasesResponse = { ...@@ -346,6 +346,14 @@ export const graphqlReleasesResponse = {
}, },
}, },
], ],
pageInfo: {
startCursor:
'eyJpZCI6IjQ0IiwicmVsZWFzZWRfYXQiOiIyMDMwLTAzLTE1IDA4OjAwOjAwLjAwMDAwMDAwMCBVVEMifQ',
hasPreviousPage: false,
hasNextPage: true,
endCursor:
'eyJpZCI6IjMiLCJyZWxlYXNlZF9hdCI6IjIwMjAtMDctMDkgMjA6MTE6MzMuODA0OTYxMDAwIFVUQyJ9',
},
}, },
}, },
}, },
......
import * as getters from '~/releases/stores/getters';
describe('~/releases/stores/getters.js', () => {
it.each`
graphqlReleaseData | graphqlReleasesPage | graphqlMilestoneStats | result
${false} | ${false} | ${false} | ${false}
${false} | ${false} | ${true} | ${false}
${false} | ${true} | ${false} | ${false}
${false} | ${true} | ${true} | ${false}
${true} | ${false} | ${false} | ${false}
${true} | ${false} | ${true} | ${false}
${true} | ${true} | ${false} | ${false}
${true} | ${true} | ${true} | ${true}
`(
'returns $result with feature flag values graphqlReleaseData=$graphqlReleaseData, graphqlReleasesPage=$graphqlReleasesPage, and graphqlMilestoneStats=$graphqlMilestoneStats',
({ result: expectedResult, ...featureFlags }) => {
const actualResult = getters.useGraphQLEndpoint({ featureFlags });
expect(actualResult).toBe(expectedResult);
},
);
});
...@@ -2,15 +2,22 @@ import createState from '~/releases/stores/modules/list/state'; ...@@ -2,15 +2,22 @@ import createState from '~/releases/stores/modules/list/state';
import mutations from '~/releases/stores/modules/list/mutations'; import mutations from '~/releases/stores/modules/list/mutations';
import * as types from '~/releases/stores/modules/list/mutation_types'; import * as types from '~/releases/stores/modules/list/mutation_types';
import { parseIntPagination } from '~/lib/utils/common_utils'; import { parseIntPagination } from '~/lib/utils/common_utils';
import { pageInfoHeadersWithoutPagination, releases } from '../../../mock_data'; import {
pageInfoHeadersWithoutPagination,
releases,
graphqlReleasesResponse,
} from '../../../mock_data';
import { convertGraphQLResponse } from '~/releases/util';
describe('Releases Store Mutations', () => { describe('Releases Store Mutations', () => {
let stateCopy; let stateCopy;
let pageInfo; let restPageInfo;
let graphQlPageInfo;
beforeEach(() => { beforeEach(() => {
stateCopy = createState({}); stateCopy = createState({});
pageInfo = parseIntPagination(pageInfoHeadersWithoutPagination); restPageInfo = parseIntPagination(pageInfoHeadersWithoutPagination);
graphQlPageInfo = convertGraphQLResponse(graphqlReleasesResponse).paginationInfo;
}); });
describe('REQUEST_RELEASES', () => { describe('REQUEST_RELEASES', () => {
...@@ -23,7 +30,11 @@ describe('Releases Store Mutations', () => { ...@@ -23,7 +30,11 @@ describe('Releases Store Mutations', () => {
describe('RECEIVE_RELEASES_SUCCESS', () => { describe('RECEIVE_RELEASES_SUCCESS', () => {
beforeEach(() => { beforeEach(() => {
mutations[types.RECEIVE_RELEASES_SUCCESS](stateCopy, { pageInfo, data: releases }); mutations[types.RECEIVE_RELEASES_SUCCESS](stateCopy, {
restPageInfo,
graphQlPageInfo,
data: releases,
});
}); });
it('sets is loading to false', () => { it('sets is loading to false', () => {
...@@ -38,18 +49,29 @@ describe('Releases Store Mutations', () => { ...@@ -38,18 +49,29 @@ describe('Releases Store Mutations', () => {
expect(stateCopy.releases).toEqual(releases); expect(stateCopy.releases).toEqual(releases);
}); });
it('sets pageInfo', () => { it('sets restPageInfo', () => {
expect(stateCopy.pageInfo).toEqual(pageInfo); expect(stateCopy.restPageInfo).toEqual(restPageInfo);
});
it('sets graphQlPageInfo', () => {
expect(stateCopy.graphQlPageInfo).toEqual(graphQlPageInfo);
}); });
}); });
describe('RECEIVE_RELEASES_ERROR', () => { describe('RECEIVE_RELEASES_ERROR', () => {
it('resets data', () => { it('resets data', () => {
mutations[types.RECEIVE_RELEASES_SUCCESS](stateCopy, {
restPageInfo,
graphQlPageInfo,
data: releases,
});
mutations[types.RECEIVE_RELEASES_ERROR](stateCopy); mutations[types.RECEIVE_RELEASES_ERROR](stateCopy);
expect(stateCopy.isLoading).toEqual(false); expect(stateCopy.isLoading).toEqual(false);
expect(stateCopy.releases).toEqual([]); expect(stateCopy.releases).toEqual([]);
expect(stateCopy.pageInfo).toEqual({}); expect(stateCopy.restPageInfo).toEqual({});
expect(stateCopy.graphQlPageInfo).toEqual({});
}); });
}); });
}); });
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment