Commit 139c8fb9 authored by Natalia Tepluhina's avatar Natalia Tepluhina

Merge branch 'leipert-rethink-observer-mock-implementation' into 'master'

Mock observers globally in Jest

See merge request gitlab-org/gitlab!67247
parents 0bb76140 ae0a4c9e
......@@ -44,7 +44,7 @@ export default class LazyLoader {
startContentObserver() {
const contentNode = document.querySelector(this.observerNode) || document.querySelector('body');
if (contentNode) {
if (contentNode && !this.mutationObserver) {
this.mutationObserver = new MutationObserver(() => this.searchLazyImages());
this.mutationObserver.observe(contentNode, {
......
......@@ -2,7 +2,6 @@ import { mount } from '@vue/test-utils';
import Vue, { nextTick } from 'vue';
import Vuex from 'vuex';
import BoardScope from 'ee/boards/components/board_scope.vue';
import { useMockIntersectionObserver } from 'helpers/mock_dom_observer';
import { TEST_HOST } from 'helpers/test_constants';
import LabelsSelect from '~/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue';
......@@ -11,7 +10,6 @@ Vue.use(Vuex);
describe('BoardScope', () => {
let wrapper;
let store;
useMockIntersectionObserver();
const createStore = () => {
return new Vuex.Store({
......
......@@ -52,7 +52,7 @@ class MockIntersectionObserver extends MockObserver {
* const { trigger: triggerMutate } = useMockMutationObserver();
*
* it('test', () => {
* trigger(el, { options: { childList: true }, entry: { } });
* triggerMutate(el, { options: { childList: true }, entry: { } });
* });
* })
* ```
......@@ -60,33 +60,31 @@ class MockIntersectionObserver extends MockObserver {
* @param {String} key
*/
const useMockObserver = (key, createMock) => {
let mockObserver;
let mockObservers = [];
let origObserver;
beforeEach(() => {
origObserver = global[key];
global[key] = jest.fn().mockImplementation((...args) => {
mockObserver = createMock(...args);
const mockObserver = createMock(...args);
mockObservers.push(mockObserver);
return mockObserver;
});
});
afterEach(() => {
mockObserver = null;
mockObservers.forEach((x) => x.disconnect());
mockObservers = [];
global[key] = origObserver;
});
const trigger = (...args) => {
if (!mockObserver) {
return;
}
mockObserver.$_triggerObserve(...args);
mockObservers.forEach((observer) => {
observer.$_triggerObserve(...args);
});
};
const observersCount = () => mockObserver.$_observers.length;
return { trigger, observersCount };
return { trigger };
};
export const useMockIntersectionObserver = () =>
......
......@@ -6,7 +6,6 @@ import VueApollo from 'vue-apollo';
import createHttpIntegrationMutation from 'ee_else_ce/alerts_settings/graphql/mutations/create_http_integration.mutation.graphql';
import updateHttpIntegrationMutation from 'ee_else_ce/alerts_settings/graphql/mutations/update_http_integration.mutation.graphql';
import createMockApollo from 'helpers/mock_apollo_helper';
import { useMockIntersectionObserver } from 'helpers/mock_dom_observer';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
import IntegrationsList from '~/alerts_settings/components/alerts_integrations_list.vue';
......@@ -57,7 +56,6 @@ describe('AlertsSettingsWrapper', () => {
let wrapper;
let fakeApollo;
let destroyIntegrationHandler;
useMockIntersectionObserver();
const httpMappingData = {
payloadExample: '{"test: : "field"}',
......
......@@ -88,13 +88,32 @@ class CustomEnvironment extends JSDOMEnvironment {
}),
});
this.global.PerformanceObserver = class {
/**
* JSDom doesn't have an own observer implementation, so this a Noop Observer.
* If you are testing functionality, related to observers, have a look at __helpers__/mock_dom_observer.js
*
* JSDom actually implements a _proper_ MutationObserver, so no need to mock it!
*/
class NoopObserver {
/* eslint-disable no-useless-constructor, no-unused-vars, no-empty-function, class-methods-use-this */
constructor(callback) {}
disconnect() {}
observe(element, initObject) {}
unobserve(element) {}
takeRecords() {
return [];
}
/* eslint-enable no-useless-constructor, no-unused-vars, no-empty-function, class-methods-use-this */
};
}
['IntersectionObserver', 'PerformanceObserver', 'ResizeObserver'].forEach((observer) => {
if (this.global[observer]) {
throw new Error(
`We overwrite an existing Observer in jsdom (${observer}), are you sure you want to do that?`,
);
}
this.global[observer] = NoopObserver;
});
}
async teardown() {
......
......@@ -2,7 +2,6 @@ import { GlIntersectionObserver } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import MockAdapter from 'axios-mock-adapter';
import { nextTick } from 'vue';
import { useMockIntersectionObserver } from 'helpers/mock_dom_observer';
import '~/behaviors/markdown/render_gfm';
import IssuableApp from '~/issue_show/components/app.vue';
import DescriptionComponent from '~/issue_show/components/description.vue';
......@@ -30,8 +29,6 @@ jest.mock('~/issue_show/event_hub');
const REALTIME_REQUEST_STACK = [initialRequest, secondRequest];
describe('Issuable output', () => {
useMockIntersectionObserver();
let mock;
let realtimeRequestCount = 0;
let wrapper;
......
import MockAdapter from 'axios-mock-adapter';
import { useMockIntersectionObserver } from 'helpers/mock_dom_observer';
import waitForPromises from 'helpers/wait_for_promises';
import { initIssuableApp } from '~/issue_show/issue';
import * as parseData from '~/issue_show/utils/parse_data';
......@@ -10,8 +9,6 @@ import { appProps } from './mock_data/mock_data';
const mock = new MockAdapter(axios);
mock.onGet().reply(200);
useMockIntersectionObserver();
jest.mock('~/lib/utils/poll');
const setupHTML = (initialData) => {
......
......@@ -4,7 +4,7 @@ import { useMockMutationObserver } from 'helpers/mock_dom_observer';
import Popovers from '~/popovers/components/popovers.vue';
describe('popovers/components/popovers.vue', () => {
const { trigger: triggerMutate, observersCount } = useMockMutationObserver();
const { trigger: triggerMutate } = useMockMutationObserver();
let wrapper;
const buildWrapper = (...targets) => {
......@@ -120,10 +120,13 @@ describe('popovers/components/popovers.vue', () => {
it('disconnects mutation observer on beforeDestroy', async () => {
await buildWrapper(createPopoverTarget());
const { observer } = wrapper.vm;
jest.spyOn(observer, 'disconnect');
expect(observersCount()).toBe(1);
expect(observer.disconnect).toHaveBeenCalledTimes(0);
wrapper.destroy();
expect(observersCount()).toBe(0);
expect(observer.disconnect).toHaveBeenCalledTimes(1);
});
});
......@@ -4,7 +4,7 @@ import { useMockMutationObserver } from 'helpers/mock_dom_observer';
import Tooltips from '~/tooltips/components/tooltips.vue';
describe('tooltips/components/tooltips.vue', () => {
const { trigger: triggerMutate, observersCount } = useMockMutationObserver();
const { trigger: triggerMutate } = useMockMutationObserver();
let wrapper;
const buildWrapper = () => {
......@@ -211,11 +211,14 @@ describe('tooltips/components/tooltips.vue', () => {
it('disconnects mutation observer on beforeDestroy', () => {
buildWrapper();
wrapper.vm.addTooltips([createTooltipTarget()]);
const { observer } = wrapper.vm;
jest.spyOn(observer, 'disconnect');
expect(observersCount()).toBe(1);
expect(observer.disconnect).toHaveBeenCalledTimes(0);
wrapper.destroy();
expect(observersCount()).toBe(0);
expect(observer.disconnect).toHaveBeenCalledTimes(1);
});
it('exposes hidden event', async () => {
......
import { useMockIntersectionObserver } from 'helpers/mock_dom_observer';
import autofocusonshow from '~/vue_shared/directives/autofocusonshow';
/**
......@@ -7,8 +6,6 @@ import autofocusonshow from '~/vue_shared/directives/autofocusonshow';
* on underlying DOM methods.
*/
describe('AutofocusOnShow directive', () => {
useMockIntersectionObserver();
describe('with input invisible on component render', () => {
let el;
......
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