hermes-test 1.0.1 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/hermes-test.js +14 -14
- package/dist/harness.bundle.js +212 -98
- package/globals.d.ts +12 -9
- package/index.d.ts +33 -26
- package/package.json +4 -4
- package/src/expect.ts +202 -114
- package/src/fetch.ts +19 -5
- package/src/harness.ts +129 -33
- package/src/hooks.ts +131 -43
- package/src/index.ts +22 -14
- package/src/mock.ts +15 -11
- package/src/polyfills.js +151 -73
- package/src/render.ts +54 -32
- package/src/shims/async-storage.js +9 -9
- package/src/shims/react-i18next.js +30 -10
- package/src/shims/react-native-launch-arguments.js +3 -1
- package/src/shims/react-native.js +133 -41
- package/src/shims/react.js +3 -3
- package/src/shims/rtk-query-core.js +18 -8
- package/src/shims/rtk-query.js +18 -8
- package/src/shims/tanstack-query.js +2 -2
- package/src/spy.ts +32 -36
- package/src/store.ts +27 -17
- package/src/timers.ts +5 -3
package/bin/hermes-test.js
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
2
|
+
'use strict';
|
|
3
3
|
|
|
4
|
-
const { execFileSync } = require(
|
|
5
|
-
const path = require(
|
|
6
|
-
const os = require(
|
|
7
|
-
const fs = require(
|
|
4
|
+
const { execFileSync } = require('child_process');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const os = require('os');
|
|
7
|
+
const fs = require('fs');
|
|
8
8
|
|
|
9
9
|
const platform = os.platform();
|
|
10
10
|
const arch = os.arch();
|
|
11
11
|
|
|
12
12
|
// Check for local development build first (target/release/hermes-test next to the package)
|
|
13
|
-
const localBin = path.join(__dirname,
|
|
13
|
+
const localBin = path.join(__dirname, '..', '..', '..', 'target', 'release', 'hermes-test');
|
|
14
14
|
if (fs.existsSync(localBin)) {
|
|
15
15
|
try {
|
|
16
|
-
execFileSync(localBin, process.argv.slice(2), { stdio:
|
|
16
|
+
execFileSync(localBin, process.argv.slice(2), { stdio: 'inherit' });
|
|
17
17
|
} catch (e) {
|
|
18
18
|
process.exit(e.status ?? 1);
|
|
19
19
|
}
|
|
@@ -21,9 +21,9 @@ if (fs.existsSync(localBin)) {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
const pkgMap = {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
'darwin-arm64': '@hermes-test/darwin-arm64',
|
|
25
|
+
'darwin-x64': '@hermes-test/darwin-x64',
|
|
26
|
+
'linux-x64': '@hermes-test/linux-x64',
|
|
27
27
|
};
|
|
28
28
|
|
|
29
29
|
const key = `${platform}-${arch}`;
|
|
@@ -31,7 +31,7 @@ const pkg = pkgMap[key];
|
|
|
31
31
|
|
|
32
32
|
if (!pkg) {
|
|
33
33
|
console.error(`hermes-test: unsupported platform ${key}`);
|
|
34
|
-
console.error(`Supported: ${Object.keys(pkgMap).join(
|
|
34
|
+
console.error(`Supported: ${Object.keys(pkgMap).join(', ')}`);
|
|
35
35
|
process.exit(1);
|
|
36
36
|
}
|
|
37
37
|
|
|
@@ -40,9 +40,9 @@ try {
|
|
|
40
40
|
// Search from cwd and script dir so the platform package is found
|
|
41
41
|
// even when hoisted to a different node_modules in monorepos (bun/pnpm/yarn workspaces)
|
|
42
42
|
const resolved = require.resolve(`${pkg}/package.json`, {
|
|
43
|
-
paths: [process.cwd(), path.join(__dirname,
|
|
43
|
+
paths: [process.cwd(), path.join(__dirname, '..')],
|
|
44
44
|
});
|
|
45
|
-
binPath = path.join(path.dirname(resolved),
|
|
45
|
+
binPath = path.join(path.dirname(resolved), 'bin', 'hermes-test');
|
|
46
46
|
} catch {
|
|
47
47
|
console.error(`hermes-test: platform package ${pkg} not installed.`);
|
|
48
48
|
console.error(`Run: npm install --save-dev ${pkg}`);
|
|
@@ -50,7 +50,7 @@ try {
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
try {
|
|
53
|
-
execFileSync(binPath, process.argv.slice(2), { stdio:
|
|
53
|
+
execFileSync(binPath, process.argv.slice(2), { stdio: 'inherit' });
|
|
54
54
|
} catch (e) {
|
|
55
55
|
process.exit(e.status ?? 1);
|
|
56
56
|
}
|
package/dist/harness.bundle.js
CHANGED
|
@@ -17,14 +17,14 @@ if (typeof globalThis.process === 'undefined') {
|
|
|
17
17
|
|
|
18
18
|
// process.nextTick — many Node.js-style tests use this
|
|
19
19
|
if (typeof globalThis.process.nextTick === 'undefined') {
|
|
20
|
-
globalThis.process.nextTick = function(fn) {
|
|
20
|
+
globalThis.process.nextTick = function (fn) {
|
|
21
21
|
Promise.resolve().then(fn);
|
|
22
22
|
};
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
// Object.fromEntries — ES2019, may not exist in older Hermes builds
|
|
26
26
|
if (typeof Object.fromEntries === 'undefined') {
|
|
27
|
-
Object.fromEntries = function(iterable) {
|
|
27
|
+
Object.fromEntries = function (iterable) {
|
|
28
28
|
var obj = {};
|
|
29
29
|
if (iterable && typeof iterable[Symbol.iterator] === 'function') {
|
|
30
30
|
var iter = iterable[Symbol.iterator]();
|
|
@@ -33,7 +33,9 @@ if (typeof Object.fromEntries === 'undefined') {
|
|
|
33
33
|
obj[next.value[0]] = next.value[1];
|
|
34
34
|
}
|
|
35
35
|
} else if (iterable && typeof iterable.forEach === 'function') {
|
|
36
|
-
iterable.forEach(function(pair) {
|
|
36
|
+
iterable.forEach(function (pair) {
|
|
37
|
+
obj[pair[0]] = pair[1];
|
|
38
|
+
});
|
|
37
39
|
}
|
|
38
40
|
return obj;
|
|
39
41
|
};
|
|
@@ -44,7 +46,7 @@ if (typeof globalThis.crypto === 'undefined') {
|
|
|
44
46
|
globalThis.crypto = {};
|
|
45
47
|
}
|
|
46
48
|
if (typeof globalThis.crypto.getRandomValues === 'undefined') {
|
|
47
|
-
globalThis.crypto.getRandomValues = function(arr) {
|
|
49
|
+
globalThis.crypto.getRandomValues = function (arr) {
|
|
48
50
|
for (var i = 0; i < arr.length; i++) {
|
|
49
51
|
arr[i] = Math.floor(Math.random() * 256);
|
|
50
52
|
}
|
|
@@ -54,54 +56,69 @@ if (typeof globalThis.crypto.getRandomValues === 'undefined') {
|
|
|
54
56
|
|
|
55
57
|
// MessageChannel polyfill — React 19's scheduler uses it for async work
|
|
56
58
|
if (typeof globalThis.MessageChannel === 'undefined') {
|
|
57
|
-
globalThis.MessageChannel = function() {
|
|
59
|
+
globalThis.MessageChannel = function () {
|
|
58
60
|
var cb = null;
|
|
59
61
|
this.port1 = { onmessage: null };
|
|
60
62
|
this.port2 = {
|
|
61
|
-
postMessage: function() {
|
|
62
|
-
if (cb) {
|
|
63
|
-
|
|
63
|
+
postMessage: function () {
|
|
64
|
+
if (cb) {
|
|
65
|
+
var fn = cb;
|
|
66
|
+
cb = null;
|
|
67
|
+
fn({ data: undefined });
|
|
68
|
+
}
|
|
69
|
+
},
|
|
64
70
|
};
|
|
65
71
|
var self = this;
|
|
66
72
|
Object.defineProperty(this.port1, 'onmessage', {
|
|
67
|
-
set: function(fn) {
|
|
68
|
-
|
|
73
|
+
set: function (fn) {
|
|
74
|
+
cb = fn;
|
|
75
|
+
},
|
|
76
|
+
get: function () {
|
|
77
|
+
return cb;
|
|
78
|
+
},
|
|
69
79
|
});
|
|
70
80
|
};
|
|
71
81
|
}
|
|
72
82
|
|
|
73
83
|
// Timer polyfills — React scheduler needs these
|
|
74
|
-
(function() {
|
|
84
|
+
(function () {
|
|
75
85
|
var queue = [];
|
|
76
86
|
var timerIdCounter = 1;
|
|
77
87
|
var timers = {};
|
|
78
88
|
|
|
79
89
|
if (typeof globalThis.setImmediate === 'undefined') {
|
|
80
|
-
globalThis.setImmediate = function(fn) {
|
|
90
|
+
globalThis.setImmediate = function (fn) {
|
|
91
|
+
queue.push(fn);
|
|
92
|
+
};
|
|
81
93
|
}
|
|
82
94
|
|
|
83
95
|
// Flush all async work: Hermes microtask queue (promises) + our polyfill queues (timers).
|
|
84
96
|
// The C++ bridge installs a native __HT_drain that calls Hermes's drainMicrotasks().
|
|
85
97
|
// We wrap it to also flush our setImmediate/setTimeout polyfill queues.
|
|
86
|
-
var nativeDrain = globalThis.__HT_drain || function() {};
|
|
87
|
-
globalThis.__HT_drain = function() {
|
|
98
|
+
var nativeDrain = globalThis.__HT_drain || function () {};
|
|
99
|
+
globalThis.__HT_drain = function () {
|
|
88
100
|
// 1. Drain Hermes's internal promise/microtask queue
|
|
89
101
|
nativeDrain();
|
|
90
102
|
// 2. Flush our setImmediate queue
|
|
91
103
|
var limit = 1000;
|
|
92
|
-
while (queue.length > 0 && limit-- > 0) {
|
|
104
|
+
while (queue.length > 0 && limit-- > 0) {
|
|
105
|
+
queue.shift()();
|
|
106
|
+
}
|
|
93
107
|
// 3. Flush pending timers
|
|
94
108
|
var ids = Object.keys(timers);
|
|
95
109
|
for (var i = 0; i < ids.length; i++) {
|
|
96
110
|
var t = timers[ids[i]];
|
|
97
|
-
if (t) {
|
|
111
|
+
if (t) {
|
|
112
|
+
delete timers[ids[i]];
|
|
113
|
+
t();
|
|
114
|
+
}
|
|
98
115
|
}
|
|
99
116
|
// 4. Drain again (timer callbacks may have queued more microtasks)
|
|
100
117
|
nativeDrain();
|
|
101
118
|
};
|
|
102
119
|
|
|
103
120
|
if (typeof globalThis.setTimeout === 'undefined') {
|
|
104
|
-
globalThis.setTimeout = function(fn, delay) {
|
|
121
|
+
globalThis.setTimeout = function (fn, delay) {
|
|
105
122
|
var id = timerIdCounter++;
|
|
106
123
|
if (!delay || delay <= 0) {
|
|
107
124
|
queue.push(fn);
|
|
@@ -113,35 +130,48 @@ if (typeof globalThis.MessageChannel === 'undefined') {
|
|
|
113
130
|
}
|
|
114
131
|
|
|
115
132
|
if (typeof globalThis.clearTimeout === 'undefined') {
|
|
116
|
-
globalThis.clearTimeout = function(id) {
|
|
133
|
+
globalThis.clearTimeout = function (id) {
|
|
134
|
+
delete timers[id];
|
|
135
|
+
};
|
|
117
136
|
}
|
|
118
137
|
|
|
119
138
|
if (typeof globalThis.console === 'undefined') {
|
|
120
139
|
globalThis.console = {
|
|
121
|
-
log: function() {},
|
|
122
|
-
warn: function() {},
|
|
123
|
-
error: function() {},
|
|
124
|
-
info: function() {},
|
|
125
|
-
debug: function() {},
|
|
140
|
+
log: function () {},
|
|
141
|
+
warn: function () {},
|
|
142
|
+
error: function () {},
|
|
143
|
+
info: function () {},
|
|
144
|
+
debug: function () {},
|
|
126
145
|
};
|
|
127
146
|
}
|
|
128
147
|
})();
|
|
129
148
|
|
|
130
149
|
// Web API polyfills — needed for RTK Query's fetchBaseQuery
|
|
131
|
-
(function() {
|
|
150
|
+
(function () {
|
|
132
151
|
// AbortController / AbortSignal
|
|
133
152
|
if (typeof globalThis.AbortController === 'undefined') {
|
|
134
|
-
function AbortSignal() {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
153
|
+
function AbortSignal() {
|
|
154
|
+
this.aborted = false;
|
|
155
|
+
this._listeners = [];
|
|
156
|
+
}
|
|
157
|
+
AbortSignal.prototype.addEventListener = function (type, fn) {
|
|
158
|
+
this._listeners.push(fn);
|
|
159
|
+
};
|
|
160
|
+
AbortSignal.prototype.removeEventListener = function (type, fn) {
|
|
161
|
+
this._listeners = this._listeners.filter(function (f) {
|
|
162
|
+
return f !== fn;
|
|
163
|
+
});
|
|
138
164
|
};
|
|
139
165
|
|
|
140
|
-
function AbortController() {
|
|
141
|
-
|
|
166
|
+
function AbortController() {
|
|
167
|
+
this.signal = new AbortSignal();
|
|
168
|
+
}
|
|
169
|
+
AbortController.prototype.abort = function () {
|
|
142
170
|
this.signal.aborted = true;
|
|
143
171
|
for (var i = 0; i < this.signal._listeners.length; i++) {
|
|
144
|
-
try {
|
|
172
|
+
try {
|
|
173
|
+
this.signal._listeners[i]();
|
|
174
|
+
} catch (e) {}
|
|
145
175
|
}
|
|
146
176
|
};
|
|
147
177
|
|
|
@@ -155,7 +185,11 @@ if (typeof globalThis.MessageChannel === 'undefined') {
|
|
|
155
185
|
this._map = {};
|
|
156
186
|
if (init) {
|
|
157
187
|
if (typeof init.forEach === 'function') {
|
|
158
|
-
init.forEach(
|
|
188
|
+
init.forEach(
|
|
189
|
+
function (v, k) {
|
|
190
|
+
this._map[k.toLowerCase()] = v;
|
|
191
|
+
}.bind(this),
|
|
192
|
+
);
|
|
159
193
|
} else {
|
|
160
194
|
var keys = Object.keys(init);
|
|
161
195
|
for (var i = 0; i < keys.length; i++) {
|
|
@@ -164,19 +198,29 @@ if (typeof globalThis.MessageChannel === 'undefined') {
|
|
|
164
198
|
}
|
|
165
199
|
}
|
|
166
200
|
}
|
|
167
|
-
Headers.prototype.get = function(k) {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
Headers.prototype.
|
|
201
|
+
Headers.prototype.get = function (k) {
|
|
202
|
+
return this._map[k.toLowerCase()] || null;
|
|
203
|
+
};
|
|
204
|
+
Headers.prototype.has = function (k) {
|
|
205
|
+
return k.toLowerCase() in this._map;
|
|
206
|
+
};
|
|
207
|
+
Headers.prototype.set = function (k, v) {
|
|
208
|
+
this._map[k.toLowerCase()] = v;
|
|
209
|
+
};
|
|
210
|
+
Headers.prototype.append = function (k, v) {
|
|
171
211
|
k = k.toLowerCase();
|
|
172
212
|
this._map[k] = this._map[k] ? this._map[k] + ', ' + v : v;
|
|
173
213
|
};
|
|
174
|
-
Headers.prototype.delete = function(k) {
|
|
175
|
-
|
|
214
|
+
Headers.prototype.delete = function (k) {
|
|
215
|
+
delete this._map[k.toLowerCase()];
|
|
216
|
+
};
|
|
217
|
+
Headers.prototype.forEach = function (fn) {
|
|
176
218
|
var keys = Object.keys(this._map);
|
|
177
219
|
for (var i = 0; i < keys.length; i++) fn(this._map[keys[i]], keys[i], this);
|
|
178
220
|
};
|
|
179
|
-
Headers.prototype.entries = function() {
|
|
221
|
+
Headers.prototype.entries = function () {
|
|
222
|
+
return Object.entries(this._map);
|
|
223
|
+
};
|
|
180
224
|
globalThis.Headers = Headers;
|
|
181
225
|
}
|
|
182
226
|
|
|
@@ -207,7 +251,11 @@ if (typeof globalThis.MessageChannel === 'undefined') {
|
|
|
207
251
|
}
|
|
208
252
|
} else if (typeof init.forEach === 'function') {
|
|
209
253
|
// URLSearchParams-like with forEach(value, key)
|
|
210
|
-
init.forEach(
|
|
254
|
+
init.forEach(
|
|
255
|
+
function (v, k) {
|
|
256
|
+
this._params.push([String(k), String(v)]);
|
|
257
|
+
}.bind(this),
|
|
258
|
+
);
|
|
211
259
|
} else {
|
|
212
260
|
// Plain object: { key: value }
|
|
213
261
|
var keys = Object.keys(init);
|
|
@@ -217,59 +265,76 @@ if (typeof globalThis.MessageChannel === 'undefined') {
|
|
|
217
265
|
}
|
|
218
266
|
}
|
|
219
267
|
}
|
|
220
|
-
URLSearchParams.prototype.get = function(k) {
|
|
268
|
+
URLSearchParams.prototype.get = function (k) {
|
|
221
269
|
for (var i = 0; i < this._params.length; i++) {
|
|
222
270
|
if (this._params[i][0] === k) return this._params[i][1];
|
|
223
271
|
}
|
|
224
272
|
return null;
|
|
225
273
|
};
|
|
226
|
-
URLSearchParams.prototype.has = function(k) {
|
|
274
|
+
URLSearchParams.prototype.has = function (k) {
|
|
227
275
|
for (var i = 0; i < this._params.length; i++) {
|
|
228
276
|
if (this._params[i][0] === k) return true;
|
|
229
277
|
}
|
|
230
278
|
return false;
|
|
231
279
|
};
|
|
232
|
-
URLSearchParams.prototype.set = function(k, v) {
|
|
280
|
+
URLSearchParams.prototype.set = function (k, v) {
|
|
233
281
|
for (var i = 0; i < this._params.length; i++) {
|
|
234
|
-
if (this._params[i][0] === k) {
|
|
282
|
+
if (this._params[i][0] === k) {
|
|
283
|
+
this._params[i][1] = String(v);
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
235
286
|
}
|
|
236
287
|
this._params.push([k, String(v)]);
|
|
237
288
|
};
|
|
238
|
-
URLSearchParams.prototype.append = function(k, v) {
|
|
239
|
-
|
|
240
|
-
|
|
289
|
+
URLSearchParams.prototype.append = function (k, v) {
|
|
290
|
+
this._params.push([k, String(v)]);
|
|
291
|
+
};
|
|
292
|
+
URLSearchParams.prototype['delete'] = function (k) {
|
|
293
|
+
this._params = this._params.filter(function (p) {
|
|
294
|
+
return p[0] !== k;
|
|
295
|
+
});
|
|
241
296
|
};
|
|
242
|
-
URLSearchParams.prototype.entries = function() {
|
|
297
|
+
URLSearchParams.prototype.entries = function () {
|
|
243
298
|
var params = this._params;
|
|
244
299
|
var i = 0;
|
|
245
|
-
return {
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
300
|
+
return {
|
|
301
|
+
next: function () {
|
|
302
|
+
if (i < params.length) return { value: [params[i][0], params[i++][1]], done: false };
|
|
303
|
+
return { value: undefined, done: true };
|
|
304
|
+
},
|
|
305
|
+
};
|
|
249
306
|
};
|
|
250
|
-
URLSearchParams.prototype.keys = function() {
|
|
307
|
+
URLSearchParams.prototype.keys = function () {
|
|
251
308
|
var params = this._params;
|
|
252
309
|
var i = 0;
|
|
253
|
-
return {
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
310
|
+
return {
|
|
311
|
+
next: function () {
|
|
312
|
+
if (i < params.length) return { value: params[i++][0], done: false };
|
|
313
|
+
return { value: undefined, done: true };
|
|
314
|
+
},
|
|
315
|
+
};
|
|
257
316
|
};
|
|
258
|
-
URLSearchParams.prototype.values = function() {
|
|
317
|
+
URLSearchParams.prototype.values = function () {
|
|
259
318
|
var params = this._params;
|
|
260
319
|
var i = 0;
|
|
261
|
-
return {
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
320
|
+
return {
|
|
321
|
+
next: function () {
|
|
322
|
+
if (i < params.length) return { value: params[i++][1], done: false };
|
|
323
|
+
return { value: undefined, done: true };
|
|
324
|
+
},
|
|
325
|
+
};
|
|
265
326
|
};
|
|
266
|
-
URLSearchParams.prototype.forEach = function(fn, thisArg) {
|
|
327
|
+
URLSearchParams.prototype.forEach = function (fn, thisArg) {
|
|
267
328
|
for (var i = 0; i < this._params.length; i++) {
|
|
268
329
|
fn.call(thisArg, this._params[i][1], this._params[i][0], this);
|
|
269
330
|
}
|
|
270
331
|
};
|
|
271
|
-
URLSearchParams.prototype.toString = function() {
|
|
272
|
-
return this._params
|
|
332
|
+
URLSearchParams.prototype.toString = function () {
|
|
333
|
+
return this._params
|
|
334
|
+
.map(function (p) {
|
|
335
|
+
return encodeURIComponent(p[0]) + '=' + encodeURIComponent(p[1]);
|
|
336
|
+
})
|
|
337
|
+
.join('&');
|
|
273
338
|
};
|
|
274
339
|
URLSearchParams.prototype[Symbol.iterator] = URLSearchParams.prototype.entries;
|
|
275
340
|
globalThis.URLSearchParams = URLSearchParams;
|
|
@@ -293,13 +358,20 @@ if (typeof globalThis.MessageChannel === 'undefined') {
|
|
|
293
358
|
this.host = this.hostname + (this.port ? ':' + this.port : '');
|
|
294
359
|
this.origin = this.protocol + '//' + this.host;
|
|
295
360
|
} else {
|
|
296
|
-
this.protocol = '';
|
|
297
|
-
this.
|
|
298
|
-
this.
|
|
361
|
+
this.protocol = '';
|
|
362
|
+
this.hostname = '';
|
|
363
|
+
this.port = '';
|
|
364
|
+
this.pathname = url;
|
|
365
|
+
this.search = '';
|
|
366
|
+
this.hash = '';
|
|
367
|
+
this.host = '';
|
|
368
|
+
this.origin = '';
|
|
299
369
|
}
|
|
300
370
|
this.searchParams = new globalThis.URLSearchParams(this.search);
|
|
301
371
|
}
|
|
302
|
-
URL.prototype.toString = function() {
|
|
372
|
+
URL.prototype.toString = function () {
|
|
373
|
+
return this.href;
|
|
374
|
+
};
|
|
303
375
|
globalThis.URL = URL;
|
|
304
376
|
}
|
|
305
377
|
|
|
@@ -315,8 +387,10 @@ if (typeof globalThis.MessageChannel === 'undefined') {
|
|
|
315
387
|
|
|
316
388
|
// Stub fetch (mockFetch will override with handler-based implementation)
|
|
317
389
|
if (typeof globalThis.fetch === 'undefined') {
|
|
318
|
-
globalThis.fetch = function() {
|
|
319
|
-
return Promise.reject(
|
|
390
|
+
globalThis.fetch = function () {
|
|
391
|
+
return Promise.reject(
|
|
392
|
+
new Error('fetch not configured — use mockFetch() to register handlers'),
|
|
393
|
+
);
|
|
320
394
|
};
|
|
321
395
|
}
|
|
322
396
|
|
|
@@ -328,8 +402,12 @@ if (typeof globalThis.MessageChannel === 'undefined') {
|
|
|
328
402
|
this.ok = this.status >= 200 && this.status < 300;
|
|
329
403
|
this.headers = new globalThis.Headers(init && init.headers);
|
|
330
404
|
};
|
|
331
|
-
globalThis.Response.prototype.json = function() {
|
|
332
|
-
|
|
405
|
+
globalThis.Response.prototype.json = function () {
|
|
406
|
+
return Promise.resolve(JSON.parse(this.body));
|
|
407
|
+
};
|
|
408
|
+
globalThis.Response.prototype.text = function () {
|
|
409
|
+
return Promise.resolve(String(this.body));
|
|
410
|
+
};
|
|
333
411
|
}
|
|
334
412
|
})();
|
|
335
413
|
|
|
@@ -402,10 +480,14 @@ var __metroTestHarness = (() => {
|
|
|
402
480
|
_updateSnapshots = update;
|
|
403
481
|
}
|
|
404
482
|
function _serializeSnapshot(value) {
|
|
405
|
-
return JSON.stringify(
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
483
|
+
return JSON.stringify(
|
|
484
|
+
value,
|
|
485
|
+
(_key, val) => {
|
|
486
|
+
if (typeof val === "function") return "[Function]";
|
|
487
|
+
return val;
|
|
488
|
+
},
|
|
489
|
+
2
|
|
490
|
+
);
|
|
409
491
|
}
|
|
410
492
|
function _loadSnapshots(path) {
|
|
411
493
|
if (_snapshotCache[path]) return _snapshotCache[path];
|
|
@@ -458,8 +540,10 @@ Run with --update-snapshots to update.`
|
|
|
458
540
|
}
|
|
459
541
|
}
|
|
460
542
|
function deepEqual(a, b) {
|
|
461
|
-
if (b != null && typeof b === "object" && b.__htMatcher && typeof b.matches === "function")
|
|
462
|
-
|
|
543
|
+
if (b != null && typeof b === "object" && b.__htMatcher && typeof b.matches === "function")
|
|
544
|
+
return b.matches(a);
|
|
545
|
+
if (a != null && typeof a === "object" && a.__htMatcher && typeof a.matches === "function")
|
|
546
|
+
return a.matches(b);
|
|
463
547
|
if (a === b) return true;
|
|
464
548
|
if (a == null || b == null) return a === b;
|
|
465
549
|
if (typeof a !== typeof b) return false;
|
|
@@ -512,10 +596,14 @@ Run with --update-snapshots to update.`
|
|
|
512
596
|
actual === expected,
|
|
513
597
|
negated ? `expect(received).not.toBe(expected)
|
|
514
598
|
|
|
515
|
-
Expected: not ${formatValue(
|
|
599
|
+
Expected: not ${formatValue(
|
|
600
|
+
expected
|
|
601
|
+
)}
|
|
516
602
|
Received: ${formatValue(actual)}` : `expect(received).toBe(expected)
|
|
517
603
|
|
|
518
|
-
Expected: ${formatValue(
|
|
604
|
+
Expected: ${formatValue(
|
|
605
|
+
expected
|
|
606
|
+
)}
|
|
519
607
|
Received: ${formatValue(actual)}`
|
|
520
608
|
);
|
|
521
609
|
},
|
|
@@ -524,16 +612,21 @@ Run with --update-snapshots to update.`
|
|
|
524
612
|
deepEqual(actual, expected),
|
|
525
613
|
negated ? `expect(received).not.toEqual(expected)
|
|
526
614
|
|
|
527
|
-
Expected: not ${formatValue(
|
|
615
|
+
Expected: not ${formatValue(
|
|
616
|
+
expected
|
|
617
|
+
)}
|
|
528
618
|
Received: ${formatValue(actual)}` : `expect(received).toEqual(expected)
|
|
529
619
|
|
|
530
|
-
Expected: ${formatValue(
|
|
620
|
+
Expected: ${formatValue(
|
|
621
|
+
expected
|
|
622
|
+
)}
|
|
531
623
|
Received: ${formatValue(actual)}`
|
|
532
624
|
);
|
|
533
625
|
},
|
|
534
626
|
toMatchObject(expected) {
|
|
535
627
|
function matchesObject(a, b) {
|
|
536
|
-
if (b != null && typeof b === "object" && b.__htMatcher && typeof b.matches === "function")
|
|
628
|
+
if (b != null && typeof b === "object" && b.__htMatcher && typeof b.matches === "function")
|
|
629
|
+
return b.matches(a);
|
|
537
630
|
if (typeof b !== "object" || b === null) return a === b;
|
|
538
631
|
if (Array.isArray(b)) {
|
|
539
632
|
if (!Array.isArray(a) || a.length < b.length) return false;
|
|
@@ -548,10 +641,14 @@ Run with --update-snapshots to update.`
|
|
|
548
641
|
matchesObject(actual, expected),
|
|
549
642
|
negated ? `expect(received).not.toMatchObject(expected)
|
|
550
643
|
|
|
551
|
-
Expected: not ${formatValue(
|
|
644
|
+
Expected: not ${formatValue(
|
|
645
|
+
expected
|
|
646
|
+
)}
|
|
552
647
|
Received: ${formatValue(actual)}` : `expect(received).toMatchObject(expected)
|
|
553
648
|
|
|
554
|
-
Expected: ${formatValue(
|
|
649
|
+
Expected: ${formatValue(
|
|
650
|
+
expected
|
|
651
|
+
)}
|
|
555
652
|
Received: ${formatValue(actual)}`
|
|
556
653
|
);
|
|
557
654
|
},
|
|
@@ -716,7 +813,9 @@ Run with --update-snapshots to update.`
|
|
|
716
813
|
const match = s.calls.some((call) => deepEqual(call, args));
|
|
717
814
|
assert(
|
|
718
815
|
match,
|
|
719
|
-
negated ? `Expected spy not to have been called with ${formatValue(args)}` : `Expected spy to have been called with ${formatValue(args)}, calls: ${formatValue(
|
|
816
|
+
negated ? `Expected spy not to have been called with ${formatValue(args)}` : `Expected spy to have been called with ${formatValue(args)}, calls: ${formatValue(
|
|
817
|
+
s.calls
|
|
818
|
+
)}`
|
|
720
819
|
);
|
|
721
820
|
},
|
|
722
821
|
wasLastCalledWith(...args) {
|
|
@@ -867,11 +966,13 @@ Run with --update-snapshots to update.`
|
|
|
867
966
|
},
|
|
868
967
|
toBe: async (expected) => {
|
|
869
968
|
const r = await actual;
|
|
870
|
-
if (r !== expected)
|
|
969
|
+
if (r !== expected)
|
|
970
|
+
throw new Error(`Expected ${formatValue(expected)}, got ${formatValue(r)}`);
|
|
871
971
|
},
|
|
872
972
|
toEqual: async (expected) => {
|
|
873
973
|
const r = await actual;
|
|
874
|
-
if (!deepEqual(r, expected))
|
|
974
|
+
if (!deepEqual(r, expected))
|
|
975
|
+
throw new Error(`Expected deep equal to ${formatValue(expected)}, got ${formatValue(r)}`);
|
|
875
976
|
},
|
|
876
977
|
toBeDefined: async () => {
|
|
877
978
|
const r = await actual;
|
|
@@ -1040,7 +1141,10 @@ Run with --update-snapshots to update.`
|
|
|
1040
1141
|
}
|
|
1041
1142
|
function getReconcilerModule() {
|
|
1042
1143
|
const R = globalThis.__HT_Reconciler;
|
|
1043
|
-
if (!R)
|
|
1144
|
+
if (!R)
|
|
1145
|
+
throw new Error(
|
|
1146
|
+
"react-reconciler not available. Make sure it is installed (it ships with hermes-test)."
|
|
1147
|
+
);
|
|
1044
1148
|
return R;
|
|
1045
1149
|
}
|
|
1046
1150
|
function getReconcilerConstants() {
|
|
@@ -1387,13 +1491,16 @@ Run with --update-snapshots to update.`
|
|
|
1387
1491
|
return {
|
|
1388
1492
|
getAll(root, arg) {
|
|
1389
1493
|
const result = queryAll(root, arg);
|
|
1390
|
-
if (result.length === 0)
|
|
1494
|
+
if (result.length === 0)
|
|
1495
|
+
throw new Error(`Unable to find element with ${label}: ${String(arg)}`);
|
|
1391
1496
|
return result;
|
|
1392
1497
|
},
|
|
1393
1498
|
get(root, arg) {
|
|
1394
1499
|
const result = queryAll(root, arg);
|
|
1395
|
-
if (result.length === 0)
|
|
1396
|
-
|
|
1500
|
+
if (result.length === 0)
|
|
1501
|
+
throw new Error(`Unable to find element with ${label}: ${String(arg)}`);
|
|
1502
|
+
if (result.length > 1)
|
|
1503
|
+
throw new Error(`Found ${result.length} elements with ${label}: ${String(arg)}`);
|
|
1397
1504
|
return result[0];
|
|
1398
1505
|
},
|
|
1399
1506
|
queryAll(root, arg) {
|
|
@@ -1401,7 +1508,8 @@ Run with --update-snapshots to update.`
|
|
|
1401
1508
|
},
|
|
1402
1509
|
query(root, arg) {
|
|
1403
1510
|
const result = queryAll(root, arg);
|
|
1404
|
-
if (result.length > 1)
|
|
1511
|
+
if (result.length > 1)
|
|
1512
|
+
throw new Error(`Found ${result.length} elements with ${label}: ${String(arg)}`);
|
|
1405
1513
|
return result[0] || null;
|
|
1406
1514
|
}
|
|
1407
1515
|
};
|
|
@@ -1761,7 +1869,9 @@ ${pad}</${type}>`;
|
|
|
1761
1869
|
has: (k) => k.toLowerCase() in responseHeaders
|
|
1762
1870
|
},
|
|
1763
1871
|
json: () => Promise.resolve(responseBody),
|
|
1764
|
-
text: () => Promise.resolve(
|
|
1872
|
+
text: () => Promise.resolve(
|
|
1873
|
+
typeof responseBody === "string" ? responseBody : JSON.stringify(responseBody)
|
|
1874
|
+
),
|
|
1765
1875
|
clone: function() {
|
|
1766
1876
|
return this;
|
|
1767
1877
|
}
|
|
@@ -2059,10 +2169,14 @@ ${pad}</${type}>`;
|
|
|
2059
2169
|
if (globalThis.__HT_coverage) {
|
|
2060
2170
|
_print(`\r\x1B[K`);
|
|
2061
2171
|
}
|
|
2062
|
-
_print(
|
|
2063
|
-
`)
|
|
2172
|
+
_print(
|
|
2173
|
+
` \x1B[31mFAIL\x1B[0m ${file} \x1B[2m(${passed} passed, ${failed} failed)\x1B[0m${time}
|
|
2174
|
+
`
|
|
2175
|
+
);
|
|
2064
2176
|
} else if (globalThis.__HT_coverage) {
|
|
2065
|
-
_print(
|
|
2177
|
+
_print(
|
|
2178
|
+
`\r\x1B[K \x1B[2mRunning...\x1B[0m ${_filesCompleted}/${_totalFiles} files (${_testsCompleted} tests)`
|
|
2179
|
+
);
|
|
2066
2180
|
} else {
|
|
2067
2181
|
_print(` \x1B[32mPASS\x1B[0m ${file} \x1B[2m(${total} tests)\x1B[0m${time}
|
|
2068
2182
|
`);
|