summaryrefslogtreecommitdiff
path: root/spec/frontend/milestones
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/milestones')
-rw-r--r--spec/frontend/milestones/stores/actions_spec.js140
-rw-r--r--spec/frontend/milestones/stores/getter_spec.js15
-rw-r--r--spec/frontend/milestones/stores/mutations_spec.js159
3 files changed, 314 insertions, 0 deletions
diff --git a/spec/frontend/milestones/stores/actions_spec.js b/spec/frontend/milestones/stores/actions_spec.js
new file mode 100644
index 00000000000..ad73d0e4238
--- /dev/null
+++ b/spec/frontend/milestones/stores/actions_spec.js
@@ -0,0 +1,140 @@
+import testAction from 'helpers/vuex_action_helper';
+import createState from '~/milestones/stores/state';
+import * as actions from '~/milestones/stores/actions';
+import * as types from '~/milestones/stores/mutation_types';
+
+let mockProjectMilestonesReturnValue;
+let mockProjectSearchReturnValue;
+
+jest.mock('~/api', () => ({
+ // `__esModule: true` is required when mocking modules with default exports:
+ // https://jestjs.io/docs/en/jest-object#jestmockmodulename-factory-options
+ __esModule: true,
+ default: {
+ projectMilestones: () => mockProjectMilestonesReturnValue,
+ projectSearch: () => mockProjectSearchReturnValue,
+ },
+}));
+
+describe('Milestone combobox Vuex store actions', () => {
+ let state;
+
+ beforeEach(() => {
+ state = createState();
+ });
+
+ describe('setProjectId', () => {
+ it(`commits ${types.SET_PROJECT_ID} with the new project ID`, () => {
+ const projectId = '4';
+ testAction(actions.setProjectId, projectId, state, [
+ { type: types.SET_PROJECT_ID, payload: projectId },
+ ]);
+ });
+ });
+
+ describe('setSelectedMilestones', () => {
+ it(`commits ${types.SET_SELECTED_MILESTONES} with the new selected milestones name`, () => {
+ const selectedMilestones = ['v1.2.3'];
+ testAction(actions.setSelectedMilestones, selectedMilestones, state, [
+ { type: types.SET_SELECTED_MILESTONES, payload: selectedMilestones },
+ ]);
+ });
+ });
+
+ describe('toggleMilestones', () => {
+ const selectedMilestone = 'v1.2.3';
+ it(`commits ${types.ADD_SELECTED_MILESTONE} with the new selected milestone name`, () => {
+ testAction(actions.toggleMilestones, selectedMilestone, state, [
+ { type: types.ADD_SELECTED_MILESTONE, payload: selectedMilestone },
+ ]);
+ });
+
+ it(`commits ${types.REMOVE_SELECTED_MILESTONE} with the new selected milestone name`, () => {
+ state.selectedMilestones = [selectedMilestone];
+ testAction(actions.toggleMilestones, selectedMilestone, state, [
+ { type: types.REMOVE_SELECTED_MILESTONE, payload: selectedMilestone },
+ ]);
+ });
+ });
+
+ describe('search', () => {
+ it(`commits ${types.SET_QUERY} with the new search query`, () => {
+ const query = 'v1.0';
+ testAction(
+ actions.search,
+ query,
+ state,
+ [{ type: types.SET_QUERY, payload: query }],
+ [{ type: 'searchMilestones' }],
+ );
+ });
+ });
+
+ describe('searchMilestones', () => {
+ describe('when the search is successful', () => {
+ const projectSearchApiResponse = { data: [{ title: 'v1.0' }] };
+
+ beforeEach(() => {
+ mockProjectSearchReturnValue = Promise.resolve(projectSearchApiResponse);
+ });
+
+ it(`commits ${types.REQUEST_START}, ${types.RECEIVE_PROJECT_MILESTONES_SUCCESS} with the response from the API, and ${types.REQUEST_FINISH}`, () => {
+ return testAction(actions.searchMilestones, undefined, state, [
+ { type: types.REQUEST_START },
+ { type: types.RECEIVE_PROJECT_MILESTONES_SUCCESS, payload: projectSearchApiResponse },
+ { type: types.REQUEST_FINISH },
+ ]);
+ });
+ });
+
+ describe('when the search fails', () => {
+ const error = new Error('Something went wrong!');
+
+ beforeEach(() => {
+ mockProjectSearchReturnValue = Promise.reject(error);
+ });
+
+ it(`commits ${types.REQUEST_START}, ${types.RECEIVE_PROJECT_MILESTONES_ERROR} with the error object, and ${types.REQUEST_FINISH}`, () => {
+ return testAction(actions.searchMilestones, undefined, state, [
+ { type: types.REQUEST_START },
+ { type: types.RECEIVE_PROJECT_MILESTONES_ERROR, payload: error },
+ { type: types.REQUEST_FINISH },
+ ]);
+ });
+ });
+ });
+
+ describe('fetchMilestones', () => {
+ describe('when the fetch is successful', () => {
+ const projectMilestonesApiResponse = { data: [{ title: 'v1.0' }] };
+
+ beforeEach(() => {
+ mockProjectMilestonesReturnValue = Promise.resolve(projectMilestonesApiResponse);
+ });
+
+ it(`commits ${types.REQUEST_START}, ${types.RECEIVE_PROJECT_MILESTONES_SUCCESS} with the response from the API, and ${types.REQUEST_FINISH}`, () => {
+ return testAction(actions.fetchMilestones, undefined, state, [
+ { type: types.REQUEST_START },
+ { type: types.RECEIVE_PROJECT_MILESTONES_SUCCESS, payload: projectMilestonesApiResponse },
+ { type: types.REQUEST_FINISH },
+ ]);
+ });
+ });
+
+ describe('when the fetch fails', () => {
+ const error = new Error('Something went wrong!');
+
+ beforeEach(() => {
+ mockProjectMilestonesReturnValue = Promise.reject(error);
+ });
+
+ it(`commits ${types.REQUEST_START}, ${types.RECEIVE_PROJECT_MILESTONES_ERROR} with the error object, and ${types.REQUEST_FINISH}`, () => {
+ return testAction(actions.fetchMilestones, undefined, state, [
+ { type: types.REQUEST_START },
+ { type: types.RECEIVE_PROJECT_MILESTONES_ERROR, payload: error },
+ { type: types.REQUEST_FINISH },
+ ]);
+ });
+ });
+ });
+});
diff --git a/spec/frontend/milestones/stores/getter_spec.js b/spec/frontend/milestones/stores/getter_spec.js
new file mode 100644
index 00000000000..df7c3d28e67
--- /dev/null
+++ b/spec/frontend/milestones/stores/getter_spec.js
@@ -0,0 +1,15 @@
+import * as getters from '~/milestones/stores/getters';
+
+describe('Milestone comboxbox Vuex store getters', () => {
+ describe('isLoading', () => {
+ it.each`
+ requestCount | isLoading
+ ${2} | ${true}
+ ${1} | ${true}
+ ${0} | ${false}
+ ${-1} | ${false}
+ `('returns true when at least one request is in progress', ({ requestCount, isLoading }) => {
+ expect(getters.isLoading({ requestCount })).toBe(isLoading);
+ });
+ });
+});
diff --git a/spec/frontend/milestones/stores/mutations_spec.js b/spec/frontend/milestones/stores/mutations_spec.js
new file mode 100644
index 00000000000..8f8ce3c87ad
--- /dev/null
+++ b/spec/frontend/milestones/stores/mutations_spec.js
@@ -0,0 +1,159 @@
+import createState from '~/milestones/stores/state';
+import mutations from '~/milestones/stores/mutations';
+import * as types from '~/milestones/stores/mutation_types';
+
+describe('Milestones combobox Vuex store mutations', () => {
+ let state;
+
+ beforeEach(() => {
+ state = createState();
+ });
+
+ describe('initial state', () => {
+ it('is created with the correct structure and initial values', () => {
+ expect(state).toEqual({
+ projectId: null,
+ groupId: null,
+ query: '',
+ matches: {
+ projectMilestones: {
+ list: [],
+ totalCount: 0,
+ error: null,
+ },
+ },
+ selectedMilestones: [],
+ requestCount: 0,
+ });
+ });
+ });
+
+ describe(`${types.SET_PROJECT_ID}`, () => {
+ it('updates the project ID', () => {
+ const newProjectId = '4';
+ mutations[types.SET_PROJECT_ID](state, newProjectId);
+
+ expect(state.projectId).toBe(newProjectId);
+ });
+ });
+
+ describe(`${types.SET_SELECTED_MILESTONES}`, () => {
+ it('sets the selected milestones', () => {
+ const selectedMilestones = ['v1.2.3'];
+ mutations[types.SET_SELECTED_MILESTONES](state, selectedMilestones);
+
+ expect(state.selectedMilestones).toEqual(['v1.2.3']);
+ });
+ });
+
+ describe(`${types.ADD_SELECTED_MILESTONESs}`, () => {
+ it('adds the selected milestones', () => {
+ const selectedMilestone = 'v1.2.3';
+ mutations[types.ADD_SELECTED_MILESTONE](state, selectedMilestone);
+
+ expect(state.selectedMilestones).toEqual(['v1.2.3']);
+ });
+ });
+
+ describe(`${types.REMOVE_SELECTED_MILESTONES}`, () => {
+ it('removes the selected milestones', () => {
+ const selectedMilestone = 'v1.2.3';
+
+ mutations[types.SET_SELECTED_MILESTONES](state, [selectedMilestone]);
+ expect(state.selectedMilestones).toEqual(['v1.2.3']);
+
+ mutations[types.REMOVE_SELECTED_MILESTONE](state, selectedMilestone);
+ expect(state.selectedMilestones).toEqual([]);
+ });
+ });
+
+ describe(`${types.SET_QUERY}`, () => {
+ it('updates the search query', () => {
+ const newQuery = 'hello';
+ mutations[types.SET_QUERY](state, newQuery);
+
+ expect(state.query).toBe(newQuery);
+ });
+ });
+
+ describe(`${types.REQUEST_START}`, () => {
+ it('increments requestCount by 1', () => {
+ mutations[types.REQUEST_START](state);
+ expect(state.requestCount).toBe(1);
+
+ mutations[types.REQUEST_START](state);
+ expect(state.requestCount).toBe(2);
+
+ mutations[types.REQUEST_START](state);
+ expect(state.requestCount).toBe(3);
+ });
+ });
+
+ describe(`${types.REQUEST_FINISH}`, () => {
+ it('decrements requestCount by 1', () => {
+ state.requestCount = 3;
+
+ mutations[types.REQUEST_FINISH](state);
+ expect(state.requestCount).toBe(2);
+
+ mutations[types.REQUEST_FINISH](state);
+ expect(state.requestCount).toBe(1);
+
+ mutations[types.REQUEST_FINISH](state);
+ expect(state.requestCount).toBe(0);
+ });
+ });
+
+ describe(`${types.RECEIVE_PROJECT_MILESTONES_SUCCESS}`, () => {
+ it('updates state.matches.projectMilestones based on the provided API response', () => {
+ const response = {
+ data: [
+ {
+ title: 'v0.1',
+ },
+ {
+ title: 'v0.2',
+ },
+ ],
+ headers: {
+ 'x-total': 2,
+ },
+ };
+
+ mutations[types.RECEIVE_PROJECT_MILESTONES_SUCCESS](state, response);
+
+ expect(state.matches.projectMilestones).toEqual({
+ list: [
+ {
+ title: 'v0.1',
+ },
+ {
+ title: 'v0.2',
+ },
+ ],
+ error: null,
+ totalCount: 2,
+ });
+ });
+
+ describe(`${types.RECEIVE_PROJECT_MILESTONES_ERROR}`, () => {
+ it('updates state.matches.projectMilestones to an empty state with the error object', () => {
+ const error = new Error('Something went wrong!');
+
+ state.matches.projectMilestones = {
+ list: [{ title: 'v0.1' }],
+ totalCount: 1,
+ error: null,
+ };
+
+ mutations[types.RECEIVE_PROJECT_MILESTONES_ERROR](state, error);
+
+ expect(state.matches.projectMilestones).toEqual({
+ list: [],
+ totalCount: 0,
+ error,
+ });
+ });
+ });
+ });
+});