-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathdid-intersect-mock.js
92 lines (83 loc) · 2.45 KB
/
did-intersect-mock.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
import { A as emberArray } from '@ember/array';
import { settled, find } from '@ember/test-helpers';
/**
* This replaces the browser's IntersectionObserver with a mocked one that is synchronous
* as opposed to asynchronous, and controllable by us.
*
* forceElement return `settled()` for convenience, so that
* any downstream side-effects can be awaited. This is also done for consistency with
* the rest of our test helpers.
*
* Usage:
*
* const didIntersectMock = mockDidIntersect(sinon);
* ...render logic...
* await didIntersectMock.enter();
*/
class MockIntersectionObserver {
static instances = [];
constructor(callback) {
this.callback = callback;
this._watchedElements = emberArray();
MockIntersectionObserver.instances.push(this);
}
observe(element) {
this._watchedElements.addObject(element);
}
unobserve(element) {
this._watchedElements.removeObject(element);
}
disconnect() {
this._watchedElements = [];
}
/**
* Force a single element to enter the viewport
* @param {String} el - a DOM selector string
*/
static enter(el) {
return MockIntersectionObserver.forceElement(find(el), {
isIntersecting: true,
intersectionRatio: 1,
});
}
/**
* Force a single element to exit the viewport
* @param {DomElement} el - a DOM Selector string
*/
static exit(el) {
return MockIntersectionObserver.forceElement(find(el), {
isIntersecting: false,
intersectionRatio: 0,
});
}
/**
* Force an IntersectionObserverEntry targeted at a specific DOM node.
* Useful when only triggering viewport state on certain elements.
*
* @param {DomElement} el
* @param {object} [state] Additional state to be passed as the IntersectionObserverEntry
*/
static forceElement(el, state) {
MockIntersectionObserver.instances.forEach((instance) => {
if (instance._watchedElements.includes(el)) {
instance.callback([
{
target: el,
...state,
},
]);
}
});
return settled();
}
}
/**
* Replaces the global IntersectionObserver with the MockIntersectionObserver class
*
* @param {Sinon} sinon Pass sinon as the argument to ensure the mock is undone at the end of the test
*/
export default function mockDidIntersect(sinon) {
sinon.replace(MockIntersectionObserver, 'instances', []);
sinon.replace(window, 'IntersectionObserver', MockIntersectionObserver);
return MockIntersectionObserver;
}