Commit 7b72248b authored by Tim Zallmann's avatar Tim Zallmann

Merge branch 'leipert-jsdom-sixteen' into 'master'

Utilize jest-environment-jsdom-sixteen for tests

See merge request gitlab-org/gitlab!34362
parents ad899ebd 4d1389e6
...@@ -4,6 +4,7 @@ exports[`PathNavigation matches snapshot 1`] = ` ...@@ -4,6 +4,7 @@ exports[`PathNavigation matches snapshot 1`] = `
<div <div
class="gl-path-nav" class="gl-path-nav"
data-testid="gl-path-nav" data-testid="gl-path-nav"
style="--path-bg-color: #fafafa;"
> >
<span <span
class="gl-path-fade gl-path-fade-left" class="gl-path-fade gl-path-fade-left"
......
...@@ -3,13 +3,13 @@ ...@@ -3,13 +3,13 @@
exports[`AuditFilterToken when initialized with a value matches the snapshot 1`] = ` exports[`AuditFilterToken when initialized with a value matches the snapshot 1`] = `
<div <div
config="[object Object]" config="[object Object]"
fetchitem="function mockConstructor() { fetchitem="function () {
return fn.apply(this, arguments); return fn.apply(this, arguments);
}" }"
fetchsuggestions="function mockConstructor() { fetchsuggestions="function () {
return fn.apply(this, arguments); return fn.apply(this, arguments);
}" }"
getitemname="function mockConstructor() { getitemname="function () {
return fn.apply(this, arguments); return fn.apply(this, arguments);
}" }"
id="filtered-search-token" id="filtered-search-token"
...@@ -47,13 +47,13 @@ exports[`AuditFilterToken when initialized with a value matches the snapshot 1`] ...@@ -47,13 +47,13 @@ exports[`AuditFilterToken when initialized with a value matches the snapshot 1`]
exports[`AuditFilterToken when initialized without a value matches the snapshot 1`] = ` exports[`AuditFilterToken when initialized without a value matches the snapshot 1`] = `
<div <div
config="[object Object]" config="[object Object]"
fetchitem="function mockConstructor() { fetchitem="function () {
return fn.apply(this, arguments); return fn.apply(this, arguments);
}" }"
fetchsuggestions="function mockConstructor() { fetchsuggestions="function () {
return fn.apply(this, arguments); return fn.apply(this, arguments);
}" }"
getitemname="function mockConstructor() { getitemname="function () {
return fn.apply(this, arguments); return fn.apply(this, arguments);
}" }"
id="filtered-search-token" id="filtered-search-token"
......
...@@ -63,7 +63,6 @@ describe('PackagesApp', () => { ...@@ -63,7 +63,6 @@ describe('PackagesApp', () => {
stubs: { stubs: {
...stubChildren(PackagesApp), ...stubChildren(PackagesApp),
GlDeprecatedButton: false, GlDeprecatedButton: false,
GlLink: false,
GlModal: false, GlModal: false,
GlTab: false, GlTab: false,
GlTabs: false, GlTabs: false,
...@@ -278,7 +277,8 @@ describe('PackagesApp', () => { ...@@ -278,7 +277,8 @@ describe('PackagesApp', () => {
it(`file download link call event with ${TrackingActions.PULL_PACKAGE}`, () => { it(`file download link call event with ${TrackingActions.PULL_PACKAGE}`, () => {
createComponent({ packageEntity: conanPackage }); createComponent({ packageEntity: conanPackage });
firstFileDownloadLink().trigger('click');
firstFileDownloadLink().vm.$emit('click');
expect(eventSpy).toHaveBeenCalledWith( expect(eventSpy).toHaveBeenCalledWith(
category, category,
TrackingActions.PULL_PACKAGE, TrackingActions.PULL_PACKAGE,
......
...@@ -56,6 +56,7 @@ describe('packages_coming_soon', () => { ...@@ -56,6 +56,7 @@ describe('packages_coming_soon', () => {
}, },
stubs: { stubs: {
ApolloQuery, ApolloQuery,
GlLink: true,
}, },
mocks: { mocks: {
$apolloData, $apolloData,
...@@ -115,7 +116,7 @@ describe('packages_coming_soon', () => { ...@@ -115,7 +116,7 @@ describe('packages_coming_soon', () => {
it('tracks when an issue title link is clicked', () => { it('tracks when an issue title link is clicked', () => {
eventSpy.mockClear(); eventSpy.mockClear();
findIssueTitleLink().trigger('click'); findIssueTitleLink().vm.$emit('click');
expect(eventSpy).toHaveBeenCalledWith(undefined, TrackingActions.COMING_SOON_LIST, { expect(eventSpy).toHaveBeenCalledWith(undefined, TrackingActions.COMING_SOON_LIST, {
label: firstIssue.title, label: firstIssue.title,
...@@ -126,7 +127,7 @@ describe('packages_coming_soon', () => { ...@@ -126,7 +127,7 @@ describe('packages_coming_soon', () => {
it('tracks when an issue id link is clicked', () => { it('tracks when an issue id link is clicked', () => {
eventSpy.mockClear(); eventSpy.mockClear();
findIssueIdLink().trigger('click'); findIssueIdLink().vm.$emit('click');
expect(eventSpy).toHaveBeenCalledWith(undefined, TrackingActions.COMING_SOON_LIST, { expect(eventSpy).toHaveBeenCalledWith(undefined, TrackingActions.COMING_SOON_LIST, {
label: firstIssue.title, label: firstIssue.title,
......
...@@ -70,19 +70,21 @@ describe('RelatedIssuableInput', () => { ...@@ -70,19 +70,21 @@ describe('RelatedIssuableInput', () => {
}); });
describe('focus', () => { describe('focus', () => {
it('when clicking anywhere on the input wrapper it should focus the input', () => { it('when clicking anywhere on the input wrapper it should focus the input', async () => {
const wrapper = shallowMount(RelatedIssuableInput, { const wrapper = shallowMount(RelatedIssuableInput, {
propsData: { propsData: {
...propsData, ...propsData,
references: ['foo', 'bar'], references: ['foo', 'bar'],
}, },
// We need to attach to document, so that `document.activeElement` is properly set in jsdom
attachToDocument: true,
}); });
wrapper.find('li').trigger('click'); wrapper.find('li').trigger('click');
return wrapper.vm.$nextTick().then(() => { await wrapper.vm.$nextTick();
expect(document.activeElement).toBe(wrapper.find({ ref: 'input' }).element);
}); expect(document.activeElement).toBe(wrapper.find({ ref: 'input' }).element);
}); });
}); });
......
import testAction from 'helpers/vuex_action_helper'; import testAction from 'helpers/vuex_action_helper';
import { useMockLocationHelper } from 'helpers/mock_window_location_helper';
import MockAdapter from 'axios-mock-adapter'; import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import createFlash from '~/flash'; import createFlash from '~/flash';
...@@ -628,13 +629,13 @@ describe('Subscriptions Actions', () => { ...@@ -628,13 +629,13 @@ describe('Subscriptions Actions', () => {
}); });
describe('confirmOrderSuccess', () => { describe('confirmOrderSuccess', () => {
useMockLocationHelper();
const params = { location: 'http://example.com', plan_id: 'x', quantity: 10 }; const params = { location: 'http://example.com', plan_id: 'x', quantity: 10 };
it('changes the window location', done => { it('changes the window location', done => {
const spy = jest.spyOn(window.location, 'assign').mockImplementation();
testAction(actions.confirmOrderSuccess, params, {}, [], [], () => { testAction(actions.confirmOrderSuccess, params, {}, [], [], () => {
expect(spy).toHaveBeenCalledWith('http://example.com'); expect(window.location.assign).toHaveBeenCalledWith('http://example.com');
done(); done();
}); });
}); });
......
import testAction from 'helpers/vuex_action_helper'; import testAction from 'helpers/vuex_action_helper';
import { useMockLocationHelper } from 'helpers/mock_window_location_helper';
import MockAdapter from 'axios-mock-adapter'; import MockAdapter from 'axios-mock-adapter';
import createState from '~/create_cluster/eks_cluster/store/state'; import createState from '~/create_cluster/eks_cluster/store/state';
import * as actions from '~/create_cluster/eks_cluster/store/actions'; import * as actions from '~/create_cluster/eks_cluster/store/actions';
...@@ -251,12 +252,7 @@ describe('EKS Cluster Store Actions', () => { ...@@ -251,12 +252,7 @@ describe('EKS Cluster Store Actions', () => {
}); });
describe('createClusterSuccess', () => { describe('createClusterSuccess', () => {
beforeEach(() => { useMockLocationHelper();
jest.spyOn(window.location, 'assign').mockImplementation(() => {});
});
afterEach(() => {
window.location.assign.mockRestore();
});
it('redirects to the new cluster URL', () => { it('redirects to the new cluster URL', () => {
actions.createClusterSuccess(null, newClusterUrl); actions.createClusterSuccess(null, newClusterUrl);
......
...@@ -18,7 +18,7 @@ describe('Design reply form component', () => { ...@@ -18,7 +18,7 @@ describe('Design reply form component', () => {
const findCancelButton = () => wrapper.find({ ref: 'cancelButton' }); const findCancelButton = () => wrapper.find({ ref: 'cancelButton' });
const findModal = () => wrapper.find({ ref: 'cancelCommentModal' }); const findModal = () => wrapper.find({ ref: 'cancelCommentModal' });
function createComponent(props = {}) { function createComponent(props = {}, mountOptions = {}) {
wrapper = mount(DesignReplyForm, { wrapper = mount(DesignReplyForm, {
propsData: { propsData: {
value: '', value: '',
...@@ -26,6 +26,7 @@ describe('Design reply form component', () => { ...@@ -26,6 +26,7 @@ describe('Design reply form component', () => {
...props, ...props,
}, },
stubs: { GlModal }, stubs: { GlModal },
...mountOptions,
}); });
} }
...@@ -34,7 +35,8 @@ describe('Design reply form component', () => { ...@@ -34,7 +35,8 @@ describe('Design reply form component', () => {
}); });
it('textarea has focus after component mount', () => { it('textarea has focus after component mount', () => {
createComponent(); // We need to attach to document, so that `document.activeElement` is properly set in jsdom
createComponent({}, { attachToDocument: true });
expect(findTextarea().element).toEqual(document.activeElement); expect(findTextarea().element).toEqual(document.activeElement);
}); });
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
const path = require('path'); const path = require('path');
const { ErrorWithStack } = require('jest-util'); const { ErrorWithStack } = require('jest-util');
const JSDOMEnvironment = require('jest-environment-jsdom'); const JSDOMEnvironment = require('jest-environment-jsdom-sixteen');
const ROOT_PATH = path.resolve(__dirname, '../..'); const ROOT_PATH = path.resolve(__dirname, '../..');
......
/**
* Manage the instance of a custom `window.location`
*
* This only encapsulates the setup / teardown logic so that it can easily be
* reused with different implementations (i.e. a spy or a [fake][1])
*
* [1]: https://stackoverflow.com/a/41434763/1708147
*
* @param {() => any} fn Function that returns the object to use for window.location
*/
const useMockLocation = fn => {
const origWindowLocation = window.location;
let currentWindowLocation;
Object.defineProperty(window, 'location', {
get: () => currentWindowLocation,
});
beforeEach(() => {
currentWindowLocation = fn();
});
afterEach(() => {
currentWindowLocation = origWindowLocation;
});
};
/**
* Create an object with the location interface but `jest.fn()` implementations.
*/
export const createWindowLocationSpy = () => {
return {
assign: jest.fn(),
reload: jest.fn(),
replace: jest.fn(),
toString: jest.fn(),
};
};
/**
* Before each test, overwrite `window.location` with a spy implementation.
*/
export const useMockLocationHelper = () => useMockLocation(createWindowLocationSpy);
...@@ -33,7 +33,7 @@ describe('setWindowLocation', () => { ...@@ -33,7 +33,7 @@ describe('setWindowLocation', () => {
it.each([null, 1, undefined, false, '', 'gitlab.com'])( it.each([null, 1, undefined, false, '', 'gitlab.com'])(
'throws an error when called with an invalid url: "%s"', 'throws an error when called with an invalid url: "%s"',
invalidUrl => { invalidUrl => {
expect(() => setWindowLocation(invalidUrl)).toThrow(new TypeError('Invalid URL')); expect(() => setWindowLocation(invalidUrl)).toThrow(/Invalid URL/);
expect(window.location).toBe(originalLocation); expect(window.location).toBe(originalLocation);
}, },
); );
......
...@@ -12,7 +12,8 @@ import { ...@@ -12,7 +12,8 @@ import {
} from '~/ide/stores/actions'; } from '~/ide/stores/actions';
import service from '~/ide/services'; import service from '~/ide/services';
import api from '~/api'; import api from '~/api';
import testAction from '../../../helpers/vuex_action_helper'; import testAction from 'helpers/vuex_action_helper';
import { useMockLocationHelper } from 'helpers/mock_window_location_helper';
const TEST_PROJECT_ID = 'abc/def'; const TEST_PROJECT_ID = 'abc/def';
...@@ -116,6 +117,8 @@ describe('IDE store project actions', () => { ...@@ -116,6 +117,8 @@ describe('IDE store project actions', () => {
}); });
describe('createNewBranchFromDefault', () => { describe('createNewBranchFromDefault', () => {
useMockLocationHelper();
beforeEach(() => { beforeEach(() => {
jest.spyOn(api, 'createBranch').mockResolvedValue(); jest.spyOn(api, 'createBranch').mockResolvedValue();
}); });
...@@ -170,8 +173,6 @@ describe('IDE store project actions', () => { ...@@ -170,8 +173,6 @@ describe('IDE store project actions', () => {
}); });
it('reloads window', done => { it('reloads window', done => {
jest.spyOn(window.location, 'reload').mockImplementation();
createNewBranchFromDefault( createNewBranchFromDefault(
{ {
state: { state: {
......
...@@ -10,6 +10,8 @@ const createMountedWrapper = (props = {}) => { ...@@ -10,6 +10,8 @@ const createMountedWrapper = (props = {}) => {
wrapper = mount(DuplicateDashboardForm, { wrapper = mount(DuplicateDashboardForm, {
propsData: { ...props }, propsData: { ...props },
sync: false, sync: false,
// We need to attach to document, so that `document.activeElement` is properly set in jsdom
attachToDocument: true,
}); });
}; };
......
...@@ -6,11 +6,14 @@ import SidebarService from '~/sidebar/services/sidebar_service'; ...@@ -6,11 +6,14 @@ import SidebarService from '~/sidebar/services/sidebar_service';
import createFlash from '~/flash'; import createFlash from '~/flash';
import RecaptchaModal from '~/vue_shared/components/recaptcha_modal.vue'; import RecaptchaModal from '~/vue_shared/components/recaptcha_modal.vue';
import createStore from '~/notes/stores'; import createStore from '~/notes/stores';
import { useMockLocationHelper } from 'helpers/mock_window_location_helper';
jest.mock('~/flash'); jest.mock('~/flash');
jest.mock('~/sidebar/services/sidebar_service'); jest.mock('~/sidebar/services/sidebar_service');
describe('Confidential Issue Sidebar Block', () => { describe('Confidential Issue Sidebar Block', () => {
useMockLocationHelper();
let wrapper; let wrapper;
const findRecaptchaModal = () => wrapper.find(RecaptchaModal); const findRecaptchaModal = () => wrapper.find(RecaptchaModal);
...@@ -43,10 +46,6 @@ describe('Confidential Issue Sidebar Block', () => { ...@@ -43,10 +46,6 @@ describe('Confidential Issue Sidebar Block', () => {
}); });
}; };
beforeEach(() => {
jest.spyOn(window.location, 'reload').mockImplementation();
});
afterEach(() => { afterEach(() => {
wrapper.destroy(); wrapper.destroy();
}); });
......
...@@ -121,11 +121,6 @@ describe('Tracking', () => { ...@@ -121,11 +121,6 @@ describe('Tracking', () => {
describe('tracking interface events', () => { describe('tracking interface events', () => {
let eventSpy; let eventSpy;
const trigger = (selector, eventName = 'click') => {
const event = new Event(eventName, { bubbles: true });
document.querySelector(selector).dispatchEvent(event);
};
beforeEach(() => { beforeEach(() => {
eventSpy = jest.spyOn(Tracking, 'event'); eventSpy = jest.spyOn(Tracking, 'event');
Tracking.bindDocument('_category_'); // only happens once Tracking.bindDocument('_category_'); // only happens once
...@@ -140,7 +135,7 @@ describe('Tracking', () => { ...@@ -140,7 +135,7 @@ describe('Tracking', () => {
}); });
it('binds to clicks on elements matching [data-track-event]', () => { it('binds to clicks on elements matching [data-track-event]', () => {
trigger('[data-track-event="click_input1"]'); document.querySelector('[data-track-event="click_input1"]').click();
expect(eventSpy).toHaveBeenCalledWith('_category_', 'click_input1', { expect(eventSpy).toHaveBeenCalledWith('_category_', 'click_input1', {
label: '_label_', label: '_label_',
...@@ -149,13 +144,13 @@ describe('Tracking', () => { ...@@ -149,13 +144,13 @@ describe('Tracking', () => {
}); });
it('does not bind to clicks on elements without [data-track-event]', () => { it('does not bind to clicks on elements without [data-track-event]', () => {
trigger('[data-track-eventbogus="click_bogusinput"]'); document.querySelector('[data-track-eventbogus="click_bogusinput"]').click();
expect(eventSpy).not.toHaveBeenCalled(); expect(eventSpy).not.toHaveBeenCalled();
}); });
it('allows value override with the data-track-value attribute', () => { it('allows value override with the data-track-value attribute', () => {
trigger('[data-track-event="click_input2"]'); document.querySelector('[data-track-event="click_input2"]').click();
expect(eventSpy).toHaveBeenCalledWith('_category_', 'click_input2', { expect(eventSpy).toHaveBeenCalledWith('_category_', 'click_input2', {
value: '_value_override_', value: '_value_override_',
...@@ -163,13 +158,15 @@ describe('Tracking', () => { ...@@ -163,13 +158,15 @@ describe('Tracking', () => {
}); });
it('handles checkbox values correctly', () => { it('handles checkbox values correctly', () => {
trigger('[data-track-event="toggle_checkbox"]'); // checking const checkbox = document.querySelector('[data-track-event="toggle_checkbox"]');
checkbox.click(); // unchecking
expect(eventSpy).toHaveBeenCalledWith('_category_', 'toggle_checkbox', { expect(eventSpy).toHaveBeenCalledWith('_category_', 'toggle_checkbox', {
value: false, value: false,
}); });
trigger('[data-track-event="toggle_checkbox"]'); // unchecking checkbox.click(); // checking
expect(eventSpy).toHaveBeenCalledWith('_category_', 'toggle_checkbox', { expect(eventSpy).toHaveBeenCalledWith('_category_', 'toggle_checkbox', {
value: '_value_', value: '_value_',
...@@ -177,17 +174,19 @@ describe('Tracking', () => { ...@@ -177,17 +174,19 @@ describe('Tracking', () => {
}); });
it('handles bootstrap dropdowns', () => { it('handles bootstrap dropdowns', () => {
trigger('[data-track-event="toggle_dropdown"]', 'show.bs.dropdown'); // showing const dropdown = document.querySelector('[data-track-event="toggle_dropdown"]');
dropdown.dispatchEvent(new Event('show.bs.dropdown', { bubbles: true }));
expect(eventSpy).toHaveBeenCalledWith('_category_', 'toggle_dropdown_show', {}); expect(eventSpy).toHaveBeenCalledWith('_category_', 'toggle_dropdown_show', {});
trigger('[data-track-event="toggle_dropdown"]', 'hide.bs.dropdown'); // hiding dropdown.dispatchEvent(new Event('hide.bs.dropdown', { bubbles: true }));
expect(eventSpy).toHaveBeenCalledWith('_category_', 'toggle_dropdown_hide', {}); expect(eventSpy).toHaveBeenCalledWith('_category_', 'toggle_dropdown_hide', {});
}); });
it('handles nested elements inside an element with tracking', () => { it('handles nested elements inside an element with tracking', () => {
trigger('span.nested', 'click'); document.querySelector('span.nested').click();
expect(eventSpy).toHaveBeenCalledWith('_category_', 'nested_event', {}); expect(eventSpy).toHaveBeenCalledWith('_category_', 'nested_event', {});
}); });
......
...@@ -274,7 +274,7 @@ describe('Renamed Diff Viewer', () => { ...@@ -274,7 +274,7 @@ describe('Renamed Diff Viewer', () => {
expect(link.text()).toEqual(linkText); expect(link.text()).toEqual(linkText);
expect(link.attributes('href')).toEqual(DIFF_FILE_VIEW_PATH); expect(link.attributes('href')).toEqual(DIFF_FILE_VIEW_PATH);
link.trigger('click'); link.vm.$emit('click');
expect(clickMock).toHaveBeenCalled(); expect(clickMock).toHaveBeenCalled();
}, },
......
This diff is collapsed.
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