Commit c7cc41e8 authored by Muhammad Fawwaz's avatar Muhammad Fawwaz Committed by Paul Slaughter

Migrate spec/javascripts/badges/ to Jest

https://gitlab.com/gitlab-org/gitlab/-/merge_requests/25118
parent ddccf62d
import Vue from 'vue';
import MockAdapter from 'axios-mock-adapter';
import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
import { mountComponentWithStore } from 'helpers/vue_mount_component_helper';
import axios from '~/lib/utils/axios_utils';
import store from '~/badges/store';
import createEmptyBadge from '~/badges/empty_badge';
import BadgeForm from '~/badges/components/badge_form.vue';
import { DUMMY_IMAGE_URL, TEST_HOST } from '../../test_constants';
import { DUMMY_IMAGE_URL, TEST_HOST } from 'helpers/test_constants';
// avoid preview background process
BadgeForm.methods.debouncedPreview = () => {};
......@@ -41,7 +41,7 @@ describe('BadgeForm component', () => {
describe('onCancel', () => {
it('calls stopEditing', () => {
spyOn(vm, 'stopEditing');
jest.spyOn(vm, 'stopEditing').mockImplementation(() => {});
vm.onCancel();
......@@ -68,14 +68,14 @@ describe('BadgeForm component', () => {
const expectInvalidInput = inputElementSelector => {
const inputElement = vm.$el.querySelector(inputElementSelector);
expect(inputElement).toBeMatchedBy(':invalid');
expect(inputElement.checkValidity()).toBe(false);
const feedbackElement = vm.$el.querySelector(`${inputElementSelector} + .invalid-feedback`);
expect(feedbackElement).toBeVisible();
};
beforeEach(() => {
spyOn(vm, submitAction).and.returnValue(Promise.resolve());
beforeEach(done => {
jest.spyOn(vm, submitAction).mockReturnValue(Promise.resolve());
store.replaceState({
...store.state,
badgeInAddForm: createEmptyBadge(),
......@@ -83,9 +83,14 @@ describe('BadgeForm component', () => {
isSaving: false,
});
Vue.nextTick()
.then(() => {
setValue(nameSelector, 'TestBadge');
setValue(linkUrlSelector, `${TEST_HOST}/link/url`);
setValue(imageUrlSelector, `${window.location.origin}${DUMMY_IMAGE_URL}`);
})
.then(done)
.catch(done.fail);
});
it('returns immediately if imageUrl is empty', () => {
......@@ -131,8 +136,8 @@ describe('BadgeForm component', () => {
it(`calls ${submitAction}`, () => {
submitForm();
expect(findImageUrlElement()).toBeMatchedBy(':valid');
expect(findLinkUrlElement()).toBeMatchedBy(':valid');
expect(findImageUrlElement().checkValidity()).toBe(true);
expect(findLinkUrlElement().checkValidity()).toBe(true);
expect(vm[submitAction]).toHaveBeenCalled();
});
};
......
import $ from 'jquery';
import Vue from 'vue';
import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
import { mountComponentWithStore } from 'helpers/vue_mount_component_helper';
import { GROUP_BADGE, PROJECT_BADGE } from '~/badges/constants';
import store from '~/badges/store';
import BadgeListRow from '~/badges/components/badge_list_row.vue';
......@@ -40,15 +39,15 @@ describe('BadgeListRow component', () => {
});
it('renders the badge name', () => {
expect(vm.$el).toContainText(badge.name);
expect(vm.$el.innerText).toMatch(badge.name);
});
it('renders the badge link', () => {
expect(vm.$el).toContainText(badge.linkUrl);
expect(vm.$el.innerText).toMatch(badge.linkUrl);
});
it('renders the badge kind', () => {
expect(vm.$el).toContainText('Project Badge');
expect(vm.$el.innerText).toMatch('Project Badge');
});
it('shows edit and delete buttons', () => {
......@@ -66,7 +65,7 @@ describe('BadgeListRow component', () => {
});
it('calls editBadge when clicking then edit button', () => {
spyOn(vm, 'editBadge');
jest.spyOn(vm, 'editBadge').mockImplementation(() => {});
const editButton = vm.$el.querySelector('.table-button-footer button:first-of-type');
editButton.click();
......@@ -75,13 +74,17 @@ describe('BadgeListRow component', () => {
});
it('calls updateBadgeInModal and shows modal when clicking then delete button', done => {
spyOn(vm, 'updateBadgeInModal');
$('#delete-badge-modal').on('shown.bs.modal', () => done());
jest.spyOn(vm, 'updateBadgeInModal').mockImplementation(() => {});
const deleteButton = vm.$el.querySelector('.table-button-footer button:last-of-type');
deleteButton.click();
Vue.nextTick()
.then(() => {
expect(vm.updateBadgeInModal).toHaveBeenCalled();
})
.then(done)
.catch(done.fail);
});
describe('for a group badge', () => {
......@@ -94,7 +97,7 @@ describe('BadgeListRow component', () => {
});
it('renders the badge kind', () => {
expect(vm.$el).toContainText('Group Badge');
expect(vm.$el.innerText).toMatch('Group Badge');
});
it('hides edit and delete buttons', () => {
......
import Vue from 'vue';
import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
import { mountComponentWithStore } from 'helpers/vue_mount_component_helper';
import { GROUP_BADGE, PROJECT_BADGE } from '~/badges/constants';
import store from '~/badges/store';
import BadgeList from '~/badges/components/badge_list.vue';
......@@ -22,6 +22,10 @@ describe('BadgeList component', () => {
kind: PROJECT_BADGE,
isLoading: false,
});
// Can be removed once GlLoadingIcon no longer throws a warning
jest.spyOn(global.console, 'warn').mockImplementation(() => jest.fn());
vm = mountComponentWithStore(Component, {
el: '#dummy-element',
store,
......@@ -49,7 +53,7 @@ describe('BadgeList component', () => {
Vue.nextTick()
.then(() => {
expect(vm.$el).toContainText('This project has no badges');
expect(vm.$el.innerText).toMatch('This project has no badges');
})
.then(done)
.catch(done.fail);
......@@ -82,7 +86,7 @@ describe('BadgeList component', () => {
Vue.nextTick()
.then(() => {
expect(vm.$el).toContainText('This group has no badges');
expect(vm.$el.innerText).toMatch('This group has no badges');
})
.then(done)
.catch(done.fail);
......
import $ from 'jquery';
import Vue from 'vue';
import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
import { mountComponentWithStore } from 'helpers/vue_mount_component_helper';
import store from '~/badges/store';
import BadgeSettings from '~/badges/components/badge_settings.vue';
import { createDummyBadge } from '../dummy_badge';
......@@ -19,6 +18,10 @@ describe('BadgeSettings component', () => {
data-target="#delete-badge-modal"
>Show modal</button>
`);
// Can be removed once GlLoadingIcon no longer throws a warning
jest.spyOn(global.console, 'warn').mockImplementation(() => jest.fn());
vm = mountComponentWithStore(Component, {
el: '#dummy-element',
store,
......@@ -35,20 +38,16 @@ describe('BadgeSettings component', () => {
const modal = vm.$el.querySelector('#delete-badge-modal');
const button = document.getElementById('dummy-modal-button');
$(modal).on('shown.bs.modal', () => {
expect(modal).toContainText('Delete badge?');
const badgeElement = modal.querySelector('img.project-badge');
expect(badgeElement).not.toBe(null);
expect(badgeElement.getAttribute('src')).toBe(badge.renderedImageUrl);
done();
});
button.click();
Vue.nextTick()
.then(() => {
button.click();
expect(modal.innerText).toMatch('Delete badge?');
const badgeElement = modal.querySelector('img.project-badge');
expect(badgeElement).not.toBe(null);
expect(badgeElement.getAttribute('src')).toBe(badge.renderedImageUrl);
})
.then(done)
.catch(done.fail);
});
......@@ -67,7 +66,7 @@ describe('BadgeSettings component', () => {
expect(badgeListElement).not.toBe(null);
expect(badgeListElement).toBeVisible();
expect(badgeListElement).toContainText('Your badges');
expect(badgeListElement.innerText).toMatch('Your badges');
});
describe('when editing', () => {
......@@ -103,7 +102,7 @@ describe('BadgeSettings component', () => {
describe('methods', () => {
describe('onSubmitModal', () => {
it('triggers ', () => {
spyOn(vm, 'deleteBadge').and.callFake(() => Promise.resolve());
jest.spyOn(vm, 'deleteBadge').mockImplementation(() => Promise.resolve());
const modal = vm.$el.querySelector('#delete-badge-modal');
const deleteButton = modal.querySelector('.btn-danger');
......
import Vue from 'vue';
import mountComponent from 'spec/helpers/vue_mount_component_helper';
import mountComponent from 'helpers/vue_mount_component_helper';
import { DUMMY_IMAGE_URL, TEST_HOST } from 'spec/test_constants';
import Badge from '~/badges/components/badge.vue';
......@@ -23,9 +23,11 @@ describe('Badge component', () => {
const createComponent = (props, el = null) => {
vm = mountComponent(Component, props, el);
const { badgeImage } = findElements();
return new Promise(resolve => badgeImage.addEventListener('load', resolve)).then(() =>
Vue.nextTick(),
);
return new Promise(resolve => {
badgeImage.addEventListener('load', resolve);
// Manually dispatch load event as it is not triggered
badgeImage.dispatchEvent(new Event('load'));
}).then(() => Vue.nextTick());
};
afterEach(() => {
......@@ -111,7 +113,7 @@ describe('Badge component', () => {
expect(badgeImage).toBeVisible();
expect(loadingIcon).toBeHidden();
expect(reloadButton).toBeHidden();
expect(vm.$el.innerText).toBe('');
expect(vm.$el.querySelector('.btn-group')).toBeHidden();
});
it('shows a loading icon when loading', done => {
......@@ -124,7 +126,7 @@ describe('Badge component', () => {
expect(badgeImage).toBeHidden();
expect(loadingIcon).toBeVisible();
expect(reloadButton).toBeHidden();
expect(vm.$el.innerText).toBe('');
expect(vm.$el.querySelector('.btn-group')).toBeHidden();
})
.then(done)
.catch(done.fail);
......
import MockAdapter from 'axios-mock-adapter';
import { TEST_HOST } from 'spec/test_constants';
import testAction from 'spec/helpers/vuex_action_helper';
import testAction from 'helpers/vuex_action_helper';
import axios from '~/lib/utils/axios_utils';
import actions, { transformBackendBadge } from '~/badges/store/actions';
import mutationTypes from '~/badges/store/mutation_types';
......@@ -76,7 +76,7 @@ describe('Badges store actions', () => {
beforeEach(() => {
endpointMock = axiosMock.onPost(dummyEndpointUrl);
dispatch = jasmine.createSpy('dispatch');
dispatch = jest.fn();
badgeInAddForm = createDummyBadge();
state = {
...state,
......@@ -96,8 +96,8 @@ describe('Badges store actions', () => {
}),
);
expect(dispatch.calls.allArgs()).toEqual([['requestNewBadge']]);
dispatch.calls.reset();
expect(dispatch.mock.calls).toEqual([['requestNewBadge']]);
dispatch.mockClear();
return [200, dummyResponse];
});
......@@ -105,7 +105,7 @@ describe('Badges store actions', () => {
actions
.addBadge({ state, dispatch })
.then(() => {
expect(dispatch.calls.allArgs()).toEqual([['receiveNewBadge', dummyBadge]]);
expect(dispatch.mock.calls).toEqual([['receiveNewBadge', dummyBadge]]);
})
.then(done)
.catch(done.fail);
......@@ -121,8 +121,8 @@ describe('Badges store actions', () => {
}),
);
expect(dispatch.calls.allArgs()).toEqual([['requestNewBadge']]);
dispatch.calls.reset();
expect(dispatch.mock.calls).toEqual([['requestNewBadge']]);
dispatch.mockClear();
return [500, ''];
});
......@@ -130,7 +130,7 @@ describe('Badges store actions', () => {
.addBadge({ state, dispatch })
.then(() => done.fail('Expected Ajax call to fail!'))
.catch(() => {
expect(dispatch.calls.allArgs()).toEqual([['receiveNewBadgeError']]);
expect(dispatch.mock.calls).toEqual([['receiveNewBadgeError']]);
})
.then(done)
.catch(done.fail);
......@@ -182,20 +182,20 @@ describe('Badges store actions', () => {
beforeEach(() => {
endpointMock = axiosMock.onDelete(`${dummyEndpointUrl}/${badgeId}`);
dispatch = jasmine.createSpy('dispatch');
dispatch = jest.fn();
});
it('dispatches requestDeleteBadge and receiveDeleteBadge for successful response', done => {
endpointMock.replyOnce(() => {
expect(dispatch.calls.allArgs()).toEqual([['requestDeleteBadge', badgeId]]);
dispatch.calls.reset();
expect(dispatch.mock.calls).toEqual([['requestDeleteBadge', badgeId]]);
dispatch.mockClear();
return [200, ''];
});
actions
.deleteBadge({ state, dispatch }, { id: badgeId })
.then(() => {
expect(dispatch.calls.allArgs()).toEqual([['receiveDeleteBadge', badgeId]]);
expect(dispatch.mock.calls).toEqual([['receiveDeleteBadge', badgeId]]);
})
.then(done)
.catch(done.fail);
......@@ -203,8 +203,8 @@ describe('Badges store actions', () => {
it('dispatches requestDeleteBadge and receiveDeleteBadgeError for error response', done => {
endpointMock.replyOnce(() => {
expect(dispatch.calls.allArgs()).toEqual([['requestDeleteBadge', badgeId]]);
dispatch.calls.reset();
expect(dispatch.mock.calls).toEqual([['requestDeleteBadge', badgeId]]);
dispatch.mockClear();
return [500, ''];
});
......@@ -212,7 +212,7 @@ describe('Badges store actions', () => {
.deleteBadge({ state, dispatch }, { id: badgeId })
.then(() => done.fail('Expected Ajax call to fail!'))
.catch(() => {
expect(dispatch.calls.allArgs()).toEqual([['receiveDeleteBadgeError', badgeId]]);
expect(dispatch.mock.calls).toEqual([['receiveDeleteBadgeError', badgeId]]);
})
.then(done)
.catch(done.fail);
......@@ -280,7 +280,7 @@ describe('Badges store actions', () => {
beforeEach(() => {
endpointMock = axiosMock.onGet(dummyEndpointUrl);
dispatch = jasmine.createSpy('dispatch');
dispatch = jest.fn();
});
it('dispatches requestLoadBadges and receiveLoadBadges for successful response', done => {
......@@ -291,8 +291,8 @@ describe('Badges store actions', () => {
createDummyBadgeResponse(),
];
endpointMock.replyOnce(() => {
expect(dispatch.calls.allArgs()).toEqual([['requestLoadBadges', dummyData]]);
dispatch.calls.reset();
expect(dispatch.mock.calls).toEqual([['requestLoadBadges', dummyData]]);
dispatch.mockClear();
return [200, dummyReponse];
});
......@@ -301,7 +301,7 @@ describe('Badges store actions', () => {
.then(() => {
const badges = dummyReponse.map(transformBackendBadge);
expect(dispatch.calls.allArgs()).toEqual([['receiveLoadBadges', badges]]);
expect(dispatch.mock.calls).toEqual([['receiveLoadBadges', badges]]);
})
.then(done)
.catch(done.fail);
......@@ -310,8 +310,8 @@ describe('Badges store actions', () => {
it('dispatches requestLoadBadges and receiveLoadBadgesError for error response', done => {
const dummyData = 'this is just some data';
endpointMock.replyOnce(() => {
expect(dispatch.calls.allArgs()).toEqual([['requestLoadBadges', dummyData]]);
dispatch.calls.reset();
expect(dispatch.mock.calls).toEqual([['requestLoadBadges', dummyData]]);
dispatch.mockClear();
return [500, ''];
});
......@@ -319,7 +319,7 @@ describe('Badges store actions', () => {
.loadBadges({ state, dispatch }, dummyData)
.then(() => done.fail('Expected Ajax call to fail!'))
.catch(() => {
expect(dispatch.calls.allArgs()).toEqual([['receiveLoadBadgesError']]);
expect(dispatch.mock.calls).toEqual([['receiveLoadBadgesError']]);
})
.then(done)
.catch(done.fail);
......@@ -382,11 +382,11 @@ describe('Badges store actions', () => {
`image_url=${encodeURIComponent(badgeInForm.imageUrl)}`,
].join('&');
endpointMock = axiosMock.onGet(`${dummyEndpointUrl}/render?${urlParameters}`);
dispatch = jasmine.createSpy('dispatch');
dispatch = jest.fn();
});
it('returns immediately if imageUrl is empty', done => {
spyOn(axios, 'get');
jest.spyOn(axios, 'get').mockImplementation(() => {});
badgeInForm.imageUrl = '';
actions
......@@ -399,7 +399,7 @@ describe('Badges store actions', () => {
});
it('returns immediately if linkUrl is empty', done => {
spyOn(axios, 'get');
jest.spyOn(axios, 'get').mockImplementation(() => {});
badgeInForm.linkUrl = '';
actions
......@@ -412,19 +412,23 @@ describe('Badges store actions', () => {
});
it('escapes user input', done => {
spyOn(axios, 'get').and.callFake(() => Promise.resolve({ data: createDummyBadgeResponse() }));
jest
.spyOn(axios, 'get')
.mockImplementation(() => Promise.resolve({ data: createDummyBadgeResponse() }));
badgeInForm.imageUrl = '&make-sandwich=true';
badgeInForm.linkUrl = '<script>I am dangerous!</script>';
actions
.renderBadge({ state, dispatch })
.then(() => {
expect(axios.get.calls.count()).toBe(1);
const url = axios.get.calls.argsFor(0)[0];
expect(axios.get.mock.calls.length).toBe(1);
const url = axios.get.mock.calls[0][0];
expect(url).toMatch(`^${dummyEndpointUrl}/render?`);
expect(url).toMatch('\\?link_url=%3Cscript%3EI%20am%20dangerous!%3C%2Fscript%3E&');
expect(url).toMatch('&image_url=%26make-sandwich%3Dtrue$');
expect(url).toMatch(new RegExp(`^${dummyEndpointUrl}/render?`));
expect(url).toMatch(
new RegExp('\\?link_url=%3Cscript%3EI%20am%20dangerous!%3C%2Fscript%3E&'),
);
expect(url).toMatch(new RegExp('&image_url=%26make-sandwich%3Dtrue$'));
})
.then(done)
.catch(done.fail);
......@@ -433,8 +437,8 @@ describe('Badges store actions', () => {
it('dispatches requestRenderedBadge and receiveRenderedBadge for successful response', done => {
const dummyReponse = createDummyBadgeResponse();
endpointMock.replyOnce(() => {
expect(dispatch.calls.allArgs()).toEqual([['requestRenderedBadge']]);
dispatch.calls.reset();
expect(dispatch.mock.calls).toEqual([['requestRenderedBadge']]);
dispatch.mockClear();
return [200, dummyReponse];
});
......@@ -443,7 +447,7 @@ describe('Badges store actions', () => {
.then(() => {
const renderedBadge = transformBackendBadge(dummyReponse);
expect(dispatch.calls.allArgs()).toEqual([['receiveRenderedBadge', renderedBadge]]);
expect(dispatch.mock.calls).toEqual([['receiveRenderedBadge', renderedBadge]]);
})
.then(done)
.catch(done.fail);
......@@ -451,8 +455,8 @@ describe('Badges store actions', () => {
it('dispatches requestRenderedBadge and receiveRenderedBadgeError for error response', done => {
endpointMock.replyOnce(() => {
expect(dispatch.calls.allArgs()).toEqual([['requestRenderedBadge']]);
dispatch.calls.reset();
expect(dispatch.mock.calls).toEqual([['requestRenderedBadge']]);
dispatch.mockClear();
return [500, ''];
});
......@@ -460,7 +464,7 @@ describe('Badges store actions', () => {
.renderBadge({ state, dispatch })
.then(() => done.fail('Expected Ajax call to fail!'))
.catch(() => {
expect(dispatch.calls.allArgs()).toEqual([['receiveRenderedBadgeError']]);
expect(dispatch.mock.calls).toEqual([['receiveRenderedBadgeError']]);
})
.then(done)
.catch(done.fail);
......@@ -519,7 +523,7 @@ describe('Badges store actions', () => {
badgeInEditForm,
};
endpointMock = axiosMock.onPut(`${dummyEndpointUrl}/${badgeInEditForm.id}`);
dispatch = jasmine.createSpy('dispatch');
dispatch = jest.fn();
});
it('dispatches requestUpdatedBadge and receiveUpdatedBadge for successful response', done => {
......@@ -534,8 +538,8 @@ describe('Badges store actions', () => {
}),
);
expect(dispatch.calls.allArgs()).toEqual([['requestUpdatedBadge']]);
dispatch.calls.reset();
expect(dispatch.mock.calls).toEqual([['requestUpdatedBadge']]);
dispatch.mockClear();
return [200, dummyResponse];
});
......@@ -543,7 +547,7 @@ describe('Badges store actions', () => {
actions
.saveBadge({ state, dispatch })
.then(() => {
expect(dispatch.calls.allArgs()).toEqual([['receiveUpdatedBadge', updatedBadge]]);
expect(dispatch.mock.calls).toEqual([['receiveUpdatedBadge', updatedBadge]]);
})
.then(done)
.catch(done.fail);
......@@ -559,8 +563,8 @@ describe('Badges store actions', () => {
}),
);
expect(dispatch.calls.allArgs()).toEqual([['requestUpdatedBadge']]);
dispatch.calls.reset();
expect(dispatch.mock.calls).toEqual([['requestUpdatedBadge']]);
dispatch.mockClear();
return [500, ''];
});
......@@ -568,7 +572,7 @@ describe('Badges store actions', () => {
.saveBadge({ state, dispatch })
.then(() => done.fail('Expected Ajax call to fail!'))
.catch(() => {
expect(dispatch.calls.allArgs()).toEqual([['receiveUpdatedBadgeError']]);
expect(dispatch.mock.calls).toEqual([['receiveUpdatedBadgeError']]);
})
.then(done)
.catch(done.fail);
......
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