Commit be56f2fa authored by Lukas Eipert's avatar Lukas Eipert Committed by Phil Hughes

Vuex test helper improvements

parent 23a0b0a9
...@@ -31,7 +31,7 @@ export const fetchMergeRequests = ({ dispatch, state: { state } }, { type, searc ...@@ -31,7 +31,7 @@ export const fetchMergeRequests = ({ dispatch, state: { state } }, { type, searc
dispatch('requestMergeRequests', type); dispatch('requestMergeRequests', type);
dispatch('resetMergeRequests', type); dispatch('resetMergeRequests', type);
Api.mergeRequests({ scope, state, search }) return Api.mergeRequests({ scope, state, search })
.then(({ data }) => dispatch('receiveMergeRequestsSuccess', { type, data })) .then(({ data }) => dispatch('receiveMergeRequestsSuccess', { type, data }))
.catch(() => dispatch('receiveMergeRequestsError', { type, search })); .catch(() => dispatch('receiveMergeRequestsError', { type, search }));
}; };
......
...@@ -102,7 +102,7 @@ export const receiveJobsSuccess = ({ commit }, { id, data }) => ...@@ -102,7 +102,7 @@ export const receiveJobsSuccess = ({ commit }, { id, data }) =>
export const fetchJobs = ({ dispatch }, stage) => { export const fetchJobs = ({ dispatch }, stage) => {
dispatch('requestJobs', stage.id); dispatch('requestJobs', stage.id);
axios return axios
.get(stage.dropdownPath) .get(stage.dropdownPath)
.then(({ data }) => dispatch('receiveJobsSuccess', { id: stage.id, data })) .then(({ data }) => dispatch('receiveJobsSuccess', { id: stage.id, data }))
.catch(() => dispatch('receiveJobsError', stage)); .catch(() => dispatch('receiveJobsError', stage));
......
...@@ -205,7 +205,7 @@ describe('Frequent Items Dropdown Store Actions', () => { ...@@ -205,7 +205,7 @@ describe('Frequent Items Dropdown Store Actions', () => {
actions.setSearchQuery, actions.setSearchQuery,
{ query: 'test' }, { query: 'test' },
mockedState, mockedState,
[{ type: types.SET_SEARCH_QUERY }], [{ type: types.SET_SEARCH_QUERY, payload: { query: 'test' } }],
[{ type: 'fetchSearchedItems', payload: { query: 'test' } }], [{ type: 'fetchSearchedItems', payload: { query: 'test' } }],
done, done,
); );
...@@ -216,7 +216,7 @@ describe('Frequent Items Dropdown Store Actions', () => { ...@@ -216,7 +216,7 @@ describe('Frequent Items Dropdown Store Actions', () => {
actions.setSearchQuery, actions.setSearchQuery,
null, null,
mockedState, mockedState,
[{ type: types.SET_SEARCH_QUERY }], [{ type: types.SET_SEARCH_QUERY, payload: null }],
[{ type: 'fetchFrequentItems' }], [{ type: 'fetchFrequentItems' }],
done, done,
); );
......
const noop = () => {};
/** /**
* helper for testing action with expected mutations inspired in * Helper for testing action with expected mutations inspired in
* https://vuex.vuejs.org/en/testing.html * https://vuex.vuejs.org/en/testing.html
* *
* @param {Function} action to be tested
* @param {Object} payload will be provided to the action
* @param {Object} state will be provided to the action
* @param {Array} [expectedMutations=[]] mutations expected to be committed
* @param {Array} [expectedActions=[]] actions expected to be dispatched
* @param {Function} [done=noop] to be executed after the tests
* @return {Promise}
*
* @example * @example
* testAction( * testAction(
* actions.actionName, // action * actions.actionName, // action
* { }, // mocked response * { }, // mocked payload
* state, // state * state, //state
* // expected mutations
* [ * [
* { type: types.MUTATION} * { type: types.MUTATION}
* { type: types.MUTATION_1, payload: {}} * { type: types.MUTATION_1, payload: jasmine.any(Number)}
* ], // mutations * ],
* // expected actions
* [ * [
* { type: 'actionName', payload: {}}, * { type: 'actionName', payload: {param: 'foobar'}},
* { type: 'actionName1', payload: {}} * { type: 'actionName1'}
* ] //actions * ]
* done, * done,
* ); * );
*
* @example
* testAction(
* actions.actionName, // action
* { }, // mocked payload
* state, //state
* [ { type: types.MUTATION} ], // expected mutations
* [], // expected actions
* ).then(done)
* .catch(done.fail);
*/ */
export default (action, payload, state, expectedMutations, expectedActions, done) => { export default (
let mutationsCount = 0; action,
let actionsCount = 0; payload,
state,
expectedMutations = [],
expectedActions = [],
done = noop,
) => {
const mutations = [];
const actions = [];
// mock commit // mock commit
const commit = (type, mutationPayload) => { const commit = (type, mutationPayload) => {
const mutation = expectedMutations[mutationsCount]; const mutation = { type };
expect(mutation.type).toEqual(type);
if (mutation.payload) { if (typeof mutationPayload !== 'undefined') {
expect(mutation.payload).toEqual(mutationPayload); mutation.payload = mutationPayload;
} }
mutationsCount += 1; mutations.push(mutation);
if (mutationsCount >= expectedMutations.length) {
done();
}
}; };
// mock dispatch // mock dispatch
const dispatch = (type, actionPayload) => { const dispatch = (type, actionPayload) => {
const actionExpected = expectedActions[actionsCount]; const dispatchedAction = { type };
expect(actionExpected.type).toEqual(type);
if (actionExpected.payload) { if (typeof actionPayload !== 'undefined') {
expect(actionExpected.payload).toEqual(actionPayload); dispatchedAction.payload = actionPayload;
} }
actionsCount += 1; actions.push(dispatchedAction);
if (actionsCount >= expectedActions.length) {
done();
}
}; };
// call the action with mocked store and arguments const validateResults = () => {
action({ commit, state, dispatch, rootState: state }, payload); expect({
mutations,
// check if no mutations should have been dispatched actions,
if (expectedMutations.length === 0) { }).toEqual({
expect(mutationsCount).toEqual(0); mutations: expectedMutations,
actions: expectedActions,
});
done(); done();
} };
// check if no mutations should have been dispatched return new Promise((resolve, reject) => {
if (expectedActions.length === 0) { try {
expect(actionsCount).toEqual(0); const result = action({ commit, state, dispatch, rootState: state }, payload);
done(); resolve(result);
} } catch (e) {
reject(e);
}
})
.catch(error => {
validateResults();
throw error;
})
.then(data => {
validateResults();
return data;
});
}; };
import MockAdapter from 'axios-mock-adapter';
import { TEST_HOST } from 'spec/test_constants';
import axios from '~/lib/utils/axios_utils';
import testAction from './vuex_action_helper';
describe('VueX test helper (testAction)', () => {
let originalExpect;
let assertion;
let mock;
const noop = () => {};
beforeAll(() => {
mock = new MockAdapter(axios);
/*
In order to test the helper properly, we need to overwrite the jasmine `expect` helper.
We test that the testAction helper properly passes the dispatched actions/committed mutations
to the jasmine helper.
*/
originalExpect = expect;
assertion = null;
global.expect = actual => ({
toEqual: () => {
originalExpect(actual).toEqual(assertion);
},
});
});
afterAll(() => {
mock.restore();
global.expect = originalExpect;
});
it('should properly pass on state and payload', () => {
const exampleState = { FOO: 12, BAR: 3 };
const examplePayload = { BAZ: 73, BIZ: 55 };
const action = ({ state }, payload) => {
originalExpect(state).toEqual(exampleState);
originalExpect(payload).toEqual(examplePayload);
};
assertion = { mutations: [], actions: [] };
testAction(action, examplePayload, exampleState);
});
describe('should work with synchronous actions', () => {
it('committing mutation', () => {
const action = ({ commit }) => {
commit('MUTATION');
};
assertion = { mutations: [{ type: 'MUTATION' }], actions: [] };
testAction(action, null, {}, assertion.mutations, assertion.actions, noop);
});
it('dispatching action', () => {
const action = ({ dispatch }) => {
dispatch('ACTION');
};
assertion = { actions: [{ type: 'ACTION' }], mutations: [] };
testAction(action, null, {}, assertion.mutations, assertion.actions, noop);
});
it('work with jasmine done once finished', done => {
assertion = { mutations: [], actions: [] };
testAction(noop, null, {}, assertion.mutations, assertion.actions, done);
});
it('provide promise interface', done => {
assertion = { mutations: [], actions: [] };
testAction(noop, null, {}, assertion.mutations, assertion.actions)
.then(done)
.catch(done.fail);
});
});
describe('should work with promise based actions (fetch action)', () => {
let lastError;
const data = { FOO: 'BAR' };
const promiseAction = ({ commit, dispatch }) => {
dispatch('ACTION');
return axios
.get(TEST_HOST)
.catch(error => {
commit('ERROR');
lastError = error;
throw error;
})
.then(() => {
commit('SUCCESS');
return data;
});
};
beforeEach(() => {
lastError = null;
});
it('work with jasmine done once finished', done => {
mock.onGet(TEST_HOST).replyOnce(200, 42);
assertion = { mutations: [{ type: 'SUCCESS' }], actions: [{ type: 'ACTION' }] };
testAction(promiseAction, null, {}, assertion.mutations, assertion.actions, done);
});
it('return original data of successful promise while checking actions/mutations', done => {
mock.onGet(TEST_HOST).replyOnce(200, 42);
assertion = { mutations: [{ type: 'SUCCESS' }], actions: [{ type: 'ACTION' }] };
testAction(promiseAction, null, {}, assertion.mutations, assertion.actions)
.then(res => {
originalExpect(res).toEqual(data);
done();
})
.catch(done.fail);
});
it('return original error of rejected promise while checking actions/mutations', done => {
mock.onGet(TEST_HOST).replyOnce(500, '');
assertion = { mutations: [{ type: 'ERROR' }], actions: [{ type: 'ACTION' }] };
testAction(promiseAction, null, {}, assertion.mutations, assertion.actions)
.then(done.fail)
.catch(error => {
originalExpect(error).toBe(lastError);
done();
});
});
});
});
...@@ -601,10 +601,7 @@ describe('IDE store file actions', () => { ...@@ -601,10 +601,7 @@ describe('IDE store file actions', () => {
actions.unstageChange, actions.unstageChange,
'path', 'path',
store.state, store.state,
[ [{ type: types.UNSTAGE_CHANGE, payload: 'path' }],
{ type: types.UNSTAGE_CHANGE, payload: 'path' },
{ type: types.SET_LAST_COMMIT_MSG, payload: '' },
],
[], [],
done, done,
); );
......
...@@ -73,6 +73,7 @@ describe('IDE store project actions', () => { ...@@ -73,6 +73,7 @@ describe('IDE store project actions', () => {
branchId: store.state.currentBranchId, branchId: store.state.currentBranchId,
}, },
store.state, store.state,
// mutations
[ [
{ {
type: 'SET_BRANCH_COMMIT', type: 'SET_BRANCH_COMMIT',
...@@ -82,17 +83,9 @@ describe('IDE store project actions', () => { ...@@ -82,17 +83,9 @@ describe('IDE store project actions', () => {
commit: { id: '123' }, commit: { id: '123' },
}, },
}, },
], // mutations ],
[ // action
{ [],
type: 'getLastCommitPipeline',
payload: {
projectId: 'abc/def',
projectIdNumber: store.state.projects['abc/def'].id,
branchId: 'master',
},
},
], // action
done, done,
); );
}); });
......
...@@ -192,11 +192,8 @@ describe('Multi-file store tree actions', () => { ...@@ -192,11 +192,8 @@ describe('Multi-file store tree actions', () => {
showTreeEntry, showTreeEntry,
'grandparent/parent/child.txt', 'grandparent/parent/child.txt',
store.state, store.state,
[ [{ type: types.SET_TREE_OPEN, payload: 'grandparent/parent' }],
{ type: types.SET_TREE_OPEN, payload: 'grandparent/parent' }, [{ type: 'showTreeEntry', payload: 'grandparent/parent' }],
{ type: types.SET_TREE_OPEN, payload: 'grandparent' },
],
[{ type: 'showTreeEntry' }],
done, done,
); );
}); });
......
...@@ -122,21 +122,6 @@ describe('IDE merge requests actions', () => { ...@@ -122,21 +122,6 @@ describe('IDE merge requests actions', () => {
}); });
}); });
it('dispatches request', done => {
testAction(
fetchMergeRequests,
{ type: 'created' },
mockedState,
[],
[
{ type: 'requestMergeRequests' },
{ type: 'resetMergeRequests' },
{ type: 'receiveMergeRequestsSuccess' },
],
done,
);
});
it('dispatches success with received data', done => { it('dispatches success with received data', done => {
testAction( testAction(
fetchMergeRequests, fetchMergeRequests,
...@@ -144,8 +129,8 @@ describe('IDE merge requests actions', () => { ...@@ -144,8 +129,8 @@ describe('IDE merge requests actions', () => {
mockedState, mockedState,
[], [],
[ [
{ type: 'requestMergeRequests' }, { type: 'requestMergeRequests', payload: 'created' },
{ type: 'resetMergeRequests' }, { type: 'resetMergeRequests', payload: 'created' },
{ {
type: 'receiveMergeRequestsSuccess', type: 'receiveMergeRequestsSuccess',
payload: { type: 'created', data: mergeRequests }, payload: { type: 'created', data: mergeRequests },
...@@ -168,9 +153,9 @@ describe('IDE merge requests actions', () => { ...@@ -168,9 +153,9 @@ describe('IDE merge requests actions', () => {
mockedState, mockedState,
[], [],
[ [
{ type: 'requestMergeRequests' }, { type: 'requestMergeRequests', payload: 'created' },
{ type: 'resetMergeRequests' }, { type: 'resetMergeRequests', payload: 'created' },
{ type: 'receiveMergeRequestsError' }, { type: 'receiveMergeRequestsError', payload: { type: 'created', search: '' } },
], ],
done, done,
); );
......
...@@ -315,7 +315,7 @@ describe('IDE pipelines actions', () => { ...@@ -315,7 +315,7 @@ describe('IDE pipelines actions', () => {
'job', 'job',
mockedState, mockedState,
[{ type: types.SET_DETAIL_JOB, payload: 'job' }], [{ type: types.SET_DETAIL_JOB, payload: 'job' }],
[{ type: 'setRightPane' }], [{ type: 'setRightPane', payload: 'jobs-detail' }],
done, done,
); );
}); });
...@@ -325,7 +325,7 @@ describe('IDE pipelines actions', () => { ...@@ -325,7 +325,7 @@ describe('IDE pipelines actions', () => {
setDetailJob, setDetailJob,
null, null,
mockedState, mockedState,
[{ type: types.SET_DETAIL_JOB }], [{ type: types.SET_DETAIL_JOB, payload: null }],
[{ type: 'setRightPane', payload: rightSidebarViews.pipelines }], [{ type: 'setRightPane', payload: rightSidebarViews.pipelines }],
done, done,
); );
...@@ -336,7 +336,7 @@ describe('IDE pipelines actions', () => { ...@@ -336,7 +336,7 @@ describe('IDE pipelines actions', () => {
setDetailJob, setDetailJob,
'job', 'job',
mockedState, mockedState,
[{ type: types.SET_DETAIL_JOB }], [{ type: types.SET_DETAIL_JOB, payload: 'job' }],
[{ type: 'setRightPane', payload: rightSidebarViews.jobsDetail }], [{ type: 'setRightPane', payload: rightSidebarViews.jobsDetail }],
done, done,
); );
......
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