Thursday, May 23, 2024
13
rated 0 times [  20] [ 7]  / answers: 1 / hits: 34804  / 7 Years ago, mon, may 29, 2017, 12:00:00

I have a JavaScript component in my application that handles infinite scroll pagination, and i'm trying to rewrite it to use the IntersectionObserver, as described here, however I'm having issues in testing it.


Is there a way to drive the behavior of the observer in a QUnit test, i.e. to trigger the observer callback with some entries described in my tests?


A possible solution I have come up with is to expose the callback function in the component's prototype and to invoke it directly in my test, something like this:


InfiniteScroll.prototype.observerCallback = function(entries) {
//handle the infinite scroll
}

InfiniteScroll.prototype.initObserver = function() {
var io = new IntersectionObserver(this.observerCallback);
io.observe(someElements);
}

//In my test
var component = new InfiniteScroll();
component.observerCallback(someEntries);
//Do some assertions about the state after the callback has been executed

I don't really like this approach since it's exposing the fact that the component uses an IntersectionObserver internally, which is an implementation detail that in my opinion should not be visible to client code, so is there any better way to test this (ideally not using jQuery)?


More From » unit-testing

 Answers
2

Here's another alternative based on previous answers, you can run it inside the beforeEach methods, or at the beginning of the .test.js file.


You could also pass parameters to the setupIntersectionObserverMock to mock the observe and/or unobserve methods to spy on them with a jest.fn() mock function.


/**
* Utility function that mocks the `IntersectionObserver` API. Necessary for components that rely
* on it, otherwise the tests will crash. Recommended to execute inside `beforeEach`.
* @param intersectionObserverMock - Parameter that is sent to the `Object.defineProperty`
* overwrite method. `jest.fn()` mock functions can be passed here if the goal is to not only
* mock the intersection observer, but its methods.
*/
export function setupIntersectionObserverMock({
root = null,
rootMargin = '',
thresholds = [],
disconnect = () => null,
observe = () => null,
takeRecords = () => [],
unobserve = () => null,
} = {}) {
class MockIntersectionObserver {
constructor() {
this.root = root;
this.rootMargin = rootMargin;
this.thresholds = thresholds;
this.disconnect = disconnect;
this.observe = observe;
this.takeRecords = takeRecords;
this.unobserve = unobserve;
}
}

Object.defineProperty(window, 'IntersectionObserver', {
writable: true,
configurable: true,
value: MockIntersectionObserver
});

Object.defineProperty(global, 'IntersectionObserver', {
writable: true,
configurable: true,
value: MockIntersectionObserver
});
}

And for TypeScript:


/**
* Utility function that mocks the `IntersectionObserver` API. Necessary for components that rely
* on it, otherwise the tests will crash. Recommended to execute inside `beforeEach`.
* @param intersectionObserverMock - Parameter that is sent to the `Object.defineProperty`
* overwrite method. `jest.fn()` mock functions can be passed here if the goal is to not only
* mock the intersection observer, but its methods.
*/
export function setupIntersectionObserverMock({
root = null,
rootMargin = '',
thresholds = [],
disconnect = () => null,
observe = () => null,
takeRecords = () => [],
unobserve = () => null,
} = {}): void {
class MockIntersectionObserver implements IntersectionObserver {
readonly root: Element | null = root;
readonly rootMargin: string = rootMargin;
readonly thresholds: ReadonlyArray < number > = thresholds;
disconnect: () => void = disconnect;
observe: (target: Element) => void = observe;
takeRecords: () => IntersectionObserverEntry[] = takeRecords;
unobserve: (target: Element) => void = unobserve;
}

Object.defineProperty(
window,
'IntersectionObserver', {
writable: true,
configurable: true,
value: MockIntersectionObserver
}
);

Object.defineProperty(
global,
'IntersectionObserver', {
writable: true,
configurable: true,
value: MockIntersectionObserver
}
);
}

[#57628] Friday, May 26, 2017, 7 Years  [reply] [flag answer]
Only authorized users can answer the question. Please sign in first, or register a free account.
kamryn

Total Points: 645
Total Questions: 100
Total Answers: 118

Location: Tanzania
Member since Wed, Feb 24, 2021
3 Years ago
kamryn questions
Sun, Apr 10, 22, 00:00, 2 Years ago
Sun, Oct 3, 21, 00:00, 3 Years ago
Thu, May 28, 20, 00:00, 4 Years ago
Tue, May 12, 20, 00:00, 4 Years ago
;