Commit 25dc9f11 authored by Vitaly Slobodin's avatar Vitaly Slobodin Committed by Paul Slaughter

Migrate ee/approvals to Jest

Closes https://gitlab.com/gitlab-org/gitlab/-/issues/194266
parent 9552bbee
import { createLocalVue, mount } from '@vue/test-utils';
import { createLocalVue, shallowMount } from '@vue/test-utils';
import $ from 'jquery';
import Api from 'ee/api';
import ApproversSelect from 'ee/approvals/components/approvers_select.vue';
import { TYPE_USER, TYPE_GROUP } from 'ee/approvals/constants';
import { TEST_HOST } from 'spec/test_constants';
import { TEST_HOST } from 'helpers/test_constants';
const DEBOUNCE_TIME = 250;
const TEST_PROJECT_ID = '17';
const TEST_GROUP_AVATAR = `${TEST_HOST}/group-avatar.png`;
const TEST_USER_AVATAR = `${TEST_HOST}/user-avatar.png`;
......@@ -26,7 +25,8 @@ const TEST_USERS = [
const localVue = createLocalVue();
const waitForEvent = ($input, event) => new Promise(resolve => $input.one(event, resolve));
const parseAvatar = element => (element.classList.contains('identicon') ? null : element.src);
const parseAvatar = element =>
element.classList.contains('identicon') ? null : element.getAttribute('src');
const select2Container = () => document.querySelector('.select2-container');
const select2DropdownOptions = () => document.querySelectorAll('#select2-drop .user-result');
const select2DropdownItems = () =>
......@@ -57,7 +57,7 @@ describe('Approvals ApproversSelect', () => {
...options.propsData,
};
wrapper = mount(localVue.extend(ApproversSelect), {
wrapper = shallowMount(ApproversSelect, {
...options,
propsData,
localVue,
......@@ -68,18 +68,15 @@ describe('Approvals ApproversSelect', () => {
};
const search = (term = '') => {
$input.select2('search', term);
jasmine.clock().mockDate();
jasmine.clock().tick(DEBOUNCE_TIME);
jest.runOnlyPendingTimers();
};
beforeEach(() => {
jasmine.clock().install();
spyOn(Api, 'groups').and.returnValue(Promise.resolve(TEST_GROUPS));
spyOn(Api, 'projectUsers').and.returnValue(Promise.resolve(TEST_USERS));
jest.spyOn(Api, 'groups').mockResolvedValue(TEST_GROUPS);
jest.spyOn(Api, 'projectUsers').mockReturnValue(Promise.resolve(TEST_USERS));
});
afterEach(() => {
jasmine.clock().uninstall();
wrapper.destroy();
});
......@@ -117,6 +114,7 @@ describe('Approvals ApproversSelect', () => {
factory();
waitForEvent($input, 'select2-loaded')
.then(jest.runOnlyPendingTimers)
.then(done)
.catch(done.fail);
......@@ -147,6 +145,7 @@ describe('Approvals ApproversSelect', () => {
});
waitForEvent($input, 'select2-loaded')
.then(jest.runOnlyPendingTimers)
.then(done)
.catch(done.fail);
......@@ -185,10 +184,12 @@ describe('Approvals ApproversSelect', () => {
$(options[TEST_GROUPS.length]).trigger('mouseup');
$(options[0]).trigger('mouseup');
})
.then(jest.runOnlyPendingTimers)
.then(done)
.catch(done.fail);
waitForEvent($input, 'change')
.then(jest.runOnlyPendingTimers)
.then(() => {
expect(wrapper.emittedByOrder()).toEqual(expected);
})
......
import { mount, createLocalVue } from '@vue/test-utils';
import Vuex from 'vuex';
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import { createStoreOptions } from 'ee/approvals/stores';
import MREditModule from 'ee/approvals/stores/modules/mr_edit';
import MREditApp from 'ee/approvals/components/mr_edit/app.vue';
......@@ -12,21 +14,26 @@ localVue.use(Vuex);
describe('EE Approvals MREditApp', () => {
let wrapper;
let store;
let axiosMock;
const factory = () => {
wrapper = mount(localVue.extend(MREditApp), {
wrapper = mount(MREditApp, {
localVue,
store: new Vuex.Store(store),
});
};
beforeEach(() => {
axiosMock = new MockAdapter(axios);
axiosMock.onGet('*');
store = createStoreOptions(MREditModule());
store.modules.approvals.state.hasLoaded = true;
});
afterEach(() => {
wrapper.destroy();
axiosMock.restore();
});
describe('with empty rules', () => {
......@@ -36,11 +43,11 @@ describe('EE Approvals MREditApp', () => {
});
it('does not render MR rules', () => {
expect(wrapper.find(MRRules).exists()).toBe(true);
expect(wrapper.find(MRRules).findAll('.js-name')).toHaveLength(0);
});
it('renders hidden inputs', () => {
expect(wrapper.find(MRRulesHiddenInputs).exists()).toBe(true);
expect(wrapper.find('.js-approval-rules').contains(MRRulesHiddenInputs)).toBe(true);
});
});
......@@ -51,11 +58,11 @@ describe('EE Approvals MREditApp', () => {
});
it('renders MR rules', () => {
expect(wrapper.find(MRRules).exists()).toBe(true);
expect(wrapper.find(MRRules).findAll('.js-name')).toHaveLength(1);
});
it('renders hidden inputs', () => {
expect(wrapper.find(MRRulesHiddenInputs).exists()).toBe(true);
expect(wrapper.find('.js-approval-rules').contains(MRRulesHiddenInputs)).toBe(true);
});
});
});
import MockAdapter from 'axios-mock-adapter';
import testAction from 'spec/helpers/vuex_action_helper';
import createFlash from '~/flash';
import testAction from 'helpers/vuex_action_helper';
import * as types from 'ee/approvals/stores/modules/base/mutation_types';
import actionsModule, * as actions from 'ee/approvals/stores/modules/project_settings/actions';
import * as actions from 'ee/approvals/stores/modules/project_settings/actions';
import { mapApprovalRuleRequest, mapApprovalSettingsResponse } from 'ee/approvals/mappers';
import axios from '~/lib/utils/axios_utils';
jest.mock('~/flash');
const TEST_PROJECT_ID = 9;
const TEST_RULE_ID = 7;
const TEST_RULE_REQUEST = {
......@@ -26,7 +29,6 @@ const TEST_RULES_PATH = 'projects/9/approval_settings/rules';
describe('EE approvals project settings module actions', () => {
let state;
let flashSpy;
let mock;
beforeEach(() => {
......@@ -37,7 +39,6 @@ describe('EE approvals project settings module actions', () => {
rulesPath: TEST_RULES_PATH,
},
};
flashSpy = spyOnDependency(actionsModule, 'createFlash');
mock = new MockAdapter(axios);
});
......@@ -46,23 +47,22 @@ describe('EE approvals project settings module actions', () => {
});
describe('requestRules', () => {
it('sets loading', done => {
testAction(
it('sets loading', () => {
return testAction(
actions.requestRules,
null,
{},
[{ type: types.SET_LOADING, payload: true }],
[],
done,
);
});
});
describe('receiveRulesSuccess', () => {
it('sets rules', done => {
it('sets rules', () => {
const settings = { rules: [{ id: 1 }] };
testAction(
return testAction(
actions.receiveRulesSuccess,
settings,
{},
......@@ -71,28 +71,27 @@ describe('EE approvals project settings module actions', () => {
{ type: types.SET_LOADING, payload: false },
],
[],
done,
);
});
});
describe('receiveRulesError', () => {
it('creates a flash', () => {
expect(flashSpy).not.toHaveBeenCalled();
expect(createFlash).not.toHaveBeenCalled();
actions.receiveRulesError();
expect(flashSpy).toHaveBeenCalledTimes(1);
expect(flashSpy).toHaveBeenCalledWith(jasmine.stringMatching('error occurred'));
expect(createFlash).toHaveBeenCalledTimes(1);
expect(createFlash).toHaveBeenCalledWith(expect.stringMatching('error occurred'));
});
});
describe('fetchRules', () => {
it('dispatches request/receive', done => {
it('dispatches request/receive', () => {
const data = { rules: [TEST_RULE_RESPONSE] };
mock.onGet(TEST_SETTINGS_PATH).replyOnce(200, data);
testAction(
return testAction(
actions.fetchRules,
null,
state,
......@@ -103,54 +102,50 @@ describe('EE approvals project settings module actions', () => {
],
() => {
expect(mock.history.get.map(x => x.url)).toEqual([TEST_SETTINGS_PATH]);
done();
},
);
});
it('dispatches request/receive on error', done => {
it('dispatches request/receive on error', () => {
mock.onGet(TEST_SETTINGS_PATH).replyOnce(500);
testAction(
return testAction(
actions.fetchRules,
null,
state,
[],
[{ type: 'requestRules' }, { type: 'receiveRulesError' }],
done,
);
});
});
describe('postRuleSuccess', () => {
it('closes modal and fetches', done => {
testAction(
it('closes modal and fetches', () => {
return testAction(
actions.postRuleSuccess,
null,
{},
[],
[{ type: 'createModal/close' }, { type: 'fetchRules' }],
done,
);
});
});
describe('postRuleError', () => {
it('creates a flash', () => {
expect(flashSpy).not.toHaveBeenCalled();
expect(createFlash).not.toHaveBeenCalled();
actions.postRuleError();
expect(flashSpy.calls.allArgs()).toEqual([[jasmine.stringMatching('error occurred')]]);
expect(createFlash.mock.calls[0]).toEqual([expect.stringMatching('error occurred')]);
});
});
describe('postRule', () => {
it('dispatches success on success', done => {
it('dispatches success on success', () => {
mock.onPost(TEST_RULES_PATH).replyOnce(200);
testAction(
return testAction(
actions.postRule,
TEST_RULE_REQUEST,
state,
......@@ -158,29 +153,33 @@ describe('EE approvals project settings module actions', () => {
[{ type: 'postRuleSuccess' }],
() => {
expect(mock.history.post).toEqual([
jasmine.objectContaining({
expect.objectContaining({
url: TEST_RULES_PATH,
data: JSON.stringify(mapApprovalRuleRequest(TEST_RULE_REQUEST)),
}),
]);
done();
},
);
});
it('dispatches error on error', done => {
it('dispatches error on error', () => {
mock.onPost(TEST_RULES_PATH).replyOnce(500);
testAction(actions.postRule, TEST_RULE_REQUEST, state, [], [{ type: 'postRuleError' }], done);
return testAction(
actions.postRule,
TEST_RULE_REQUEST,
state,
[],
[{ type: 'postRuleError' }],
);
});
});
describe('putRule', () => {
it('dispatches success on success', done => {
it('dispatches success on success', () => {
mock.onPut(`${TEST_RULES_PATH}/${TEST_RULE_ID}`).replyOnce(200);
testAction(
return testAction(
actions.putRule,
{ id: TEST_RULE_ID, ...TEST_RULE_REQUEST },
state,
......@@ -188,59 +187,55 @@ describe('EE approvals project settings module actions', () => {
[{ type: 'postRuleSuccess' }],
() => {
expect(mock.history.put).toEqual([
jasmine.objectContaining({
expect.objectContaining({
url: `${TEST_RULES_PATH}/${TEST_RULE_ID}`,
data: JSON.stringify(mapApprovalRuleRequest(TEST_RULE_REQUEST)),
}),
]);
done();
},
);
});
it('dispatches error on error', done => {
it('dispatches error on error', () => {
mock.onPut(`${TEST_RULES_PATH}/${TEST_RULE_ID}`).replyOnce(500);
testAction(
return testAction(
actions.putRule,
{ id: TEST_RULE_ID, ...TEST_RULE_REQUEST },
state,
[],
[{ type: 'postRuleError' }],
done,
);
});
});
describe('deleteRuleSuccess', () => {
it('closes modal and fetches', done => {
testAction(
it('closes modal and fetches', () => {
return testAction(
actions.deleteRuleSuccess,
null,
{},
[],
[{ type: 'deleteModal/close' }, { type: 'fetchRules' }],
done,
);
});
});
describe('deleteRuleError', () => {
it('creates a flash', () => {
expect(flashSpy).not.toHaveBeenCalled();
expect(createFlash).not.toHaveBeenCalled();
actions.deleteRuleError();
expect(flashSpy.calls.allArgs()).toEqual([[jasmine.stringMatching('error occurred')]]);
expect(createFlash.mock.calls[0]).toEqual([expect.stringMatching('error occurred')]);
});
});
describe('deleteRule', () => {
it('dispatches success on success', done => {
it('dispatches success on success', () => {
mock.onDelete(`${TEST_RULES_PATH}/${TEST_RULE_ID}`).replyOnce(200);
testAction(
return testAction(
actions.deleteRule,
TEST_RULE_ID,
state,
......@@ -248,20 +243,18 @@ describe('EE approvals project settings module actions', () => {
[{ type: 'deleteRuleSuccess' }],
() => {
expect(mock.history.delete).toEqual([
jasmine.objectContaining({
expect.objectContaining({
url: `${TEST_RULES_PATH}/${TEST_RULE_ID}`,
}),
]);
done();
},
);
});
it('dispatches error on error', done => {
it('dispatches error on error', () => {
mock.onDelete(`${TEST_RULES_PATH}/${TEST_RULE_ID}`).replyOnce(500);
testAction(actions.deleteRule, TEST_RULE_ID, state, [], [{ type: 'deleteRuleError' }], done);
return testAction(actions.deleteRule, TEST_RULE_ID, state, [], [{ type: 'deleteRuleError' }]);
});
});
});
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