hermes-test 1.0.0 → 1.0.2
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 +23 -11
- package/dist/harness.bundle.js +3 -0
- 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 +38 -36
- package/src/store.ts +27 -17
- package/src/timers.ts +5 -3
package/src/polyfills.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,7 +402,11 @@ 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
|
})();
|
package/src/render.ts
CHANGED
|
@@ -71,13 +71,16 @@ function makeQuery<T>(queryAll: (root: HTNode, arg: T) => HTNode[], label: strin
|
|
|
71
71
|
return {
|
|
72
72
|
getAll(root: HTNode, arg: T): HTNode[] {
|
|
73
73
|
const result = queryAll(root, arg);
|
|
74
|
-
if (result.length === 0)
|
|
74
|
+
if (result.length === 0)
|
|
75
|
+
throw new Error(`Unable to find element with ${label}: ${String(arg)}`);
|
|
75
76
|
return result;
|
|
76
77
|
},
|
|
77
78
|
get(root: HTNode, arg: T): HTNode {
|
|
78
79
|
const result = queryAll(root, arg);
|
|
79
|
-
if (result.length === 0)
|
|
80
|
-
|
|
80
|
+
if (result.length === 0)
|
|
81
|
+
throw new Error(`Unable to find element with ${label}: ${String(arg)}`);
|
|
82
|
+
if (result.length > 1)
|
|
83
|
+
throw new Error(`Found ${result.length} elements with ${label}: ${String(arg)}`);
|
|
81
84
|
return result[0];
|
|
82
85
|
},
|
|
83
86
|
queryAll(root: HTNode, arg: T): HTNode[] {
|
|
@@ -85,7 +88,8 @@ function makeQuery<T>(queryAll: (root: HTNode, arg: T) => HTNode[], label: strin
|
|
|
85
88
|
},
|
|
86
89
|
query(root: HTNode, arg: T): HTNode | null {
|
|
87
90
|
const result = queryAll(root, arg);
|
|
88
|
-
if (result.length > 1)
|
|
91
|
+
if (result.length > 1)
|
|
92
|
+
throw new Error(`Found ${result.length} elements with ${label}: ${String(arg)}`);
|
|
89
93
|
return result[0] || null;
|
|
90
94
|
},
|
|
91
95
|
};
|
|
@@ -123,7 +127,7 @@ function prettyPrint(json: any, indent: number = 0): string {
|
|
|
123
127
|
let propsStr = '';
|
|
124
128
|
if (props) {
|
|
125
129
|
const entries = Object.entries(props).map(([k, v]) =>
|
|
126
|
-
typeof v === 'string' ? `${k}="${v}"` : `${k}={${JSON.stringify(v)}}
|
|
130
|
+
typeof v === 'string' ? `${k}="${v}"` : `${k}={${JSON.stringify(v)}}`,
|
|
127
131
|
);
|
|
128
132
|
if (entries.length > 0) propsStr = ' ' + entries.join(' ');
|
|
129
133
|
}
|
|
@@ -133,7 +137,11 @@ function prettyPrint(json: any, indent: number = 0): string {
|
|
|
133
137
|
// Collapse adjacent string children into one
|
|
134
138
|
const merged: any[] = [];
|
|
135
139
|
for (const c of children) {
|
|
136
|
-
if (
|
|
140
|
+
if (
|
|
141
|
+
typeof c === 'string' &&
|
|
142
|
+
merged.length > 0 &&
|
|
143
|
+
typeof merged[merged.length - 1] === 'string'
|
|
144
|
+
) {
|
|
137
145
|
merged[merged.length - 1] += c;
|
|
138
146
|
} else {
|
|
139
147
|
merged.push(c);
|
|
@@ -154,7 +162,9 @@ export const fireEvent = Object.assign(
|
|
|
154
162
|
const handlerName = 'on' + eventName.charAt(0).toUpperCase() + eventName.slice(1);
|
|
155
163
|
const handler = node.props?.[handlerName];
|
|
156
164
|
if (!handler) throw new Error(`No handler "${handlerName}" on <${node.type}>`);
|
|
157
|
-
act(() => {
|
|
165
|
+
act(() => {
|
|
166
|
+
handler(...args);
|
|
167
|
+
});
|
|
158
168
|
},
|
|
159
169
|
{
|
|
160
170
|
press(node: HTNode, event?: any) {
|
|
@@ -166,19 +176,25 @@ export const fireEvent = Object.assign(
|
|
|
166
176
|
const handler = target?.props?.onPress || node.props?.onPress;
|
|
167
177
|
if (!handler) throw new Error(`No "onPress" handler on <${node.type}>`);
|
|
168
178
|
if ((target || node).props?.disabled) return; // Disabled elements don't fire press
|
|
169
|
-
act(() => {
|
|
179
|
+
act(() => {
|
|
180
|
+
handler(event);
|
|
181
|
+
});
|
|
170
182
|
},
|
|
171
183
|
changeText(node: HTNode, text: string) {
|
|
172
184
|
const handler = node.props?.onChangeText;
|
|
173
185
|
if (!handler) throw new Error(`No "onChangeText" handler on <${node.type}>`);
|
|
174
|
-
act(() => {
|
|
186
|
+
act(() => {
|
|
187
|
+
handler(text);
|
|
188
|
+
});
|
|
175
189
|
},
|
|
176
190
|
scroll(node: HTNode, event: any) {
|
|
177
191
|
const handler = node.props?.onScroll;
|
|
178
192
|
if (!handler) throw new Error(`No "onScroll" handler on <${node.type}>`);
|
|
179
|
-
act(() => {
|
|
193
|
+
act(() => {
|
|
194
|
+
handler(event);
|
|
195
|
+
});
|
|
180
196
|
},
|
|
181
|
-
}
|
|
197
|
+
},
|
|
182
198
|
);
|
|
183
199
|
|
|
184
200
|
// --- render() ---
|
|
@@ -213,11 +229,17 @@ export function render(element: any, options?: { shallow?: boolean }): RenderRes
|
|
|
213
229
|
const container: HTNode = { type: '__ROOT__', props: {}, children: [] };
|
|
214
230
|
const root = reconciler.createContainer(
|
|
215
231
|
container,
|
|
216
|
-
0,
|
|
232
|
+
0, // LegacyRoot
|
|
217
233
|
null, // hydrationCallbacks
|
|
218
|
-
false,
|
|
219
|
-
|
|
220
|
-
|
|
234
|
+
false,
|
|
235
|
+
false,
|
|
236
|
+
'',
|
|
237
|
+
(err: any) => {
|
|
238
|
+
throw err;
|
|
239
|
+
},
|
|
240
|
+
(err: any) => {
|
|
241
|
+
throw err;
|
|
242
|
+
},
|
|
221
243
|
null,
|
|
222
244
|
() => {},
|
|
223
245
|
);
|
|
@@ -232,7 +254,7 @@ export function render(element: any, options?: { shallow?: boolean }): RenderRes
|
|
|
232
254
|
const topType = element.type;
|
|
233
255
|
const origCE = React.createElement;
|
|
234
256
|
|
|
235
|
-
React.createElement = function(type: any, ...args: any[]) {
|
|
257
|
+
React.createElement = function (type: any, ...args: any[]) {
|
|
236
258
|
if (typeof type === 'function' && type !== topType) {
|
|
237
259
|
const name = type.displayName || type.name || 'Component';
|
|
238
260
|
return origCE.call(React, name, ...args);
|
|
@@ -253,22 +275,22 @@ export function render(element: any, options?: { shallow?: boolean }): RenderRes
|
|
|
253
275
|
|
|
254
276
|
const result: RenderResult = {
|
|
255
277
|
container,
|
|
256
|
-
getByText:
|
|
257
|
-
getAllByText:
|
|
258
|
-
queryByText:
|
|
259
|
-
queryAllByText:
|
|
260
|
-
getByTestId:
|
|
261
|
-
getAllByTestId:
|
|
262
|
-
queryByTestId:
|
|
263
|
-
queryAllByTestId:
|
|
264
|
-
getByProps:
|
|
265
|
-
getAllByProps:
|
|
266
|
-
queryByProps:
|
|
267
|
-
queryAllByProps:
|
|
268
|
-
getByType:
|
|
269
|
-
getAllByType:
|
|
270
|
-
queryByType:
|
|
271
|
-
queryAllByType:
|
|
278
|
+
getByText: t => textQ.get(container, t),
|
|
279
|
+
getAllByText: t => textQ.getAll(container, t),
|
|
280
|
+
queryByText: t => textQ.query(container, t),
|
|
281
|
+
queryAllByText: t => textQ.queryAll(container, t),
|
|
282
|
+
getByTestId: id => testIdQ.get(container, id),
|
|
283
|
+
getAllByTestId: id => testIdQ.getAll(container, id),
|
|
284
|
+
queryByTestId: id => testIdQ.query(container, id),
|
|
285
|
+
queryAllByTestId: id => testIdQ.queryAll(container, id),
|
|
286
|
+
getByProps: p => propsQ.get(container, p),
|
|
287
|
+
getAllByProps: p => propsQ.getAll(container, p),
|
|
288
|
+
queryByProps: p => propsQ.query(container, p),
|
|
289
|
+
queryAllByProps: p => propsQ.queryAll(container, p),
|
|
290
|
+
getByType: t => typeQ.get(container, t),
|
|
291
|
+
getAllByType: t => typeQ.getAll(container, t),
|
|
292
|
+
queryByType: t => typeQ.query(container, t),
|
|
293
|
+
queryAllByType: t => typeQ.queryAll(container, t),
|
|
272
294
|
toJSON() {
|
|
273
295
|
if (container.children.length === 0) return null;
|
|
274
296
|
if (container.children.length === 1) return toJSON(container.children[0]);
|
|
@@ -5,48 +5,48 @@
|
|
|
5
5
|
var _store = {};
|
|
6
6
|
|
|
7
7
|
var AsyncStorage = {
|
|
8
|
-
getItem: function(key) {
|
|
8
|
+
getItem: function (key) {
|
|
9
9
|
var val = _store[key] !== undefined ? _store[key] : null;
|
|
10
10
|
return Promise.resolve(val);
|
|
11
11
|
},
|
|
12
|
-
setItem: function(key, value) {
|
|
12
|
+
setItem: function (key, value) {
|
|
13
13
|
_store[key] = value;
|
|
14
14
|
return Promise.resolve();
|
|
15
15
|
},
|
|
16
|
-
removeItem: function(key) {
|
|
16
|
+
removeItem: function (key) {
|
|
17
17
|
delete _store[key];
|
|
18
18
|
return Promise.resolve();
|
|
19
19
|
},
|
|
20
|
-
multiRemove: function(keys) {
|
|
20
|
+
multiRemove: function (keys) {
|
|
21
21
|
for (var i = 0; i < keys.length; i++) {
|
|
22
22
|
delete _store[keys[i]];
|
|
23
23
|
}
|
|
24
24
|
return Promise.resolve();
|
|
25
25
|
},
|
|
26
|
-
multiGet: function(keys) {
|
|
26
|
+
multiGet: function (keys) {
|
|
27
27
|
var result = [];
|
|
28
28
|
for (var i = 0; i < keys.length; i++) {
|
|
29
29
|
result.push([keys[i], _store[keys[i]] !== undefined ? _store[keys[i]] : null]);
|
|
30
30
|
}
|
|
31
31
|
return Promise.resolve(result);
|
|
32
32
|
},
|
|
33
|
-
multiSet: function(pairs) {
|
|
33
|
+
multiSet: function (pairs) {
|
|
34
34
|
for (var i = 0; i < pairs.length; i++) {
|
|
35
35
|
_store[pairs[i][0]] = pairs[i][1];
|
|
36
36
|
}
|
|
37
37
|
return Promise.resolve();
|
|
38
38
|
},
|
|
39
|
-
getAllKeys: function() {
|
|
39
|
+
getAllKeys: function () {
|
|
40
40
|
return Promise.resolve(Object.keys(_store));
|
|
41
41
|
},
|
|
42
|
-
clear: function() {
|
|
42
|
+
clear: function () {
|
|
43
43
|
_store = {};
|
|
44
44
|
return Promise.resolve();
|
|
45
45
|
},
|
|
46
46
|
};
|
|
47
47
|
|
|
48
48
|
// Reset hook — called between test files by the harness
|
|
49
|
-
globalThis.__HT_resetAsyncStorage = function() {
|
|
49
|
+
globalThis.__HT_resetAsyncStorage = function () {
|
|
50
50
|
_store = {};
|
|
51
51
|
};
|
|
52
52
|
|
|
@@ -2,19 +2,39 @@
|
|
|
2
2
|
// Identity translation: t('key') returns 'key'.
|
|
3
3
|
// Covers useTranslation, withTranslation, Trans, initReactI18next.
|
|
4
4
|
|
|
5
|
-
var t = function(key) {
|
|
5
|
+
var t = function (key) {
|
|
6
|
+
return key;
|
|
7
|
+
};
|
|
6
8
|
var i18n = {
|
|
7
9
|
language: 'en',
|
|
8
|
-
changeLanguage: function() {
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
changeLanguage: function () {
|
|
11
|
+
return Promise.resolve();
|
|
12
|
+
},
|
|
13
|
+
use: function () {
|
|
14
|
+
return i18n;
|
|
15
|
+
},
|
|
16
|
+
init: function () {
|
|
17
|
+
return Promise.resolve();
|
|
18
|
+
},
|
|
11
19
|
};
|
|
12
20
|
|
|
13
21
|
module.exports = {
|
|
14
|
-
useTranslation: function() {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
22
|
+
useTranslation: function () {
|
|
23
|
+
return { t: t, i18n: i18n, ready: true };
|
|
24
|
+
},
|
|
25
|
+
withTranslation: function () {
|
|
26
|
+
return function (component) {
|
|
27
|
+
return component;
|
|
28
|
+
};
|
|
29
|
+
},
|
|
30
|
+
Trans: function (props) {
|
|
31
|
+
return props.children || null;
|
|
32
|
+
},
|
|
33
|
+
Translation: function (props) {
|
|
34
|
+
return props.children(t, { i18n: i18n });
|
|
35
|
+
},
|
|
36
|
+
initReactI18next: { type: '3rdParty', init: function () {} },
|
|
37
|
+
I18nextProvider: function (props) {
|
|
38
|
+
return props.children;
|
|
39
|
+
},
|
|
20
40
|
};
|