-
Notifications
You must be signed in to change notification settings - Fork 31
/
setup.js
171 lines (143 loc) · 5.95 KB
/
setup.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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
// TODO: remove after JSDOM merges this PR: https://github.com/jsdom/jsdom/pull/3586
require('./aria-reflection-polyfill');
const config = global['lwc-jest'] || {};
const { nativeShadow } = config;
if (!nativeShadow) {
if (
ShadowRoot.prototype.constructor.toString().includes('function SyntheticShadowRoot') ||
EventTarget.prototype.addEventListener
.toString()
.includes('function patchedAddEventListener')
) {
throw new Error(
'@lwc/synthetic-shadow is being loaded twice. Please examine your jest/jsdom configuration.'
);
}
require('@lwc/synthetic-shadow');
}
// Provides temporary backward compatibility for wire-protocol reform: lwc > 1.5.0
global.wireAdaptersRegistryHack = new Map();
let originalRegisterDecorators;
function isValidWireAdapter(adapter) {
let isValid = false;
if (typeof adapter === 'function') {
// lets check, if it is a valid adapter
try {
const adapterInstance = new adapter(() => {});
isValid =
typeof adapterInstance.connect === 'function' &&
typeof adapterInstance.update === 'function' &&
typeof adapterInstance.disconnect === 'function';
} catch (e) {
isValid = false;
}
}
return isValid;
}
/**
* Returns a wire adapter mock in the shape of:
*
* fn : Used when adapter is invoked imperatively. It proxies to the original adapter function, if is callable.
* fn.adapter : A valid wire adapter class, consumable by @lwc/engine-dom.
* If the @originalAdapter.adapter or @originalAdapter is a valid wire adapter class, fn.adapter will
* act as a proxy on it until a spy is attached.
*
* @param originalAdapter
* @returns {function(...[*]): *}
*/
function createWireAdapterMockClass(originalAdapter) {
const noopAdapter = {
connect() {},
update() {},
disconnect() {},
};
let baseAdapter;
let baseAdapterFn = () => {};
const spies = [];
if (Object.prototype.hasOwnProperty.call(originalAdapter, 'adapter')) {
// Is more likely that the originalAdapter was registered with the wire service
// .adapter is the one that the engine will use, lets make it our base adapter
baseAdapter = originalAdapter.adapter;
} else if (isValidWireAdapter(originalAdapter)) {
// it may be the case that original adapter is a valid one, if is the case, lets use it as our base adapter
baseAdapter = originalAdapter;
}
if (typeof originalAdapter === 'function') {
// Mostly used in apex methods
baseAdapterFn = originalAdapter;
}
// Support for adapters to be called imperatively, mainly for apex.
const newAdapterMock = function (...args) {
return baseAdapterFn.call(this, ...args);
};
newAdapterMock.adapter = class WireAdapterMock {
constructor(dataCallback) {
// if a test is spying these adapter, it means is overriding the implementation
this._originalAdapter =
spies.length === 0 && baseAdapter ? new baseAdapter(dataCallback) : noopAdapter;
this._dataCallback = dataCallback;
spies.forEach((spy) => spy.createInstance(this));
}
connect() {
spies.forEach((spy) => spy.connect(this));
this._originalAdapter.connect();
}
update(config) {
spies.forEach((spy) => spy.update(this, config));
this._originalAdapter.update(config);
}
disconnect() {
spies.forEach((spy) => spy.disconnect(this));
this._originalAdapter.disconnect();
}
emit(value) {
this._dataCallback(value);
}
static spyAdapter(spy) {
// this function is meant to be used by wire-service-jest-util library.
// When this is used, register* was called, thus replacing the wire behaviour.
// As consequence, it will stop calling the originalAdapter on new instances.
spies.push(spy);
}
};
return newAdapterMock;
}
function overriddenRegisterDecorators(Ctor, decoratorsMeta) {
const wire = decoratorsMeta.wire || {};
Object.keys(wire).forEach((adapterName) => {
const adapter = wire[adapterName].adapter;
let wireAdapterMock = global.wireAdaptersRegistryHack.get(adapter);
if (!wireAdapterMock) {
// Checking if the adapter is extensible, since with the wire reform,
// the adapterId must be:
// a) An extensible object (so backward compatibility is provided via register)
// b) A valid class.
if (!Object.isExtensible(adapter)) {
// if we are in case of a) lets throw now so the developer knows that they need to migrate their
// adapters.
throw new TypeError('Invalid adapterId, it must be extensible.');
}
// Lets create a whole replacement for this adapter.
wireAdapterMock = createWireAdapterMockClass(adapter);
global.wireAdaptersRegistryHack.set(adapter, wireAdapterMock);
}
// we are entirely replacing the wire adapter with one that can be spied on.
wire[adapterName].adapter = wireAdapterMock;
});
return originalRegisterDecorators(Ctor, decoratorsMeta);
}
function installRegisterDecoratorsTrap(lwc) {
const originalDescriptor = Object.getOwnPropertyDescriptor(lwc, 'registerDecorators');
if (originalDescriptor.value === overriddenRegisterDecorators) {
return;
}
originalRegisterDecorators = originalDescriptor.value;
const newDescriptor = {
...originalDescriptor,
value: overriddenRegisterDecorators,
};
Object.defineProperty(lwc, 'registerDecorators', newDescriptor);
}
const lwc = require('@lwc/engine-dom');
installRegisterDecoratorsTrap(lwc);
require('./matchers/expect-throw-in-connected-callback');