hermes-test 1.0.1 → 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 +14 -14
- 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
|
@@ -2,15 +2,19 @@
|
|
|
2
2
|
// Provides stub implementations of commonly used RN APIs.
|
|
3
3
|
// Users can override via hermes-test.config.json "shims" or mockModule().
|
|
4
4
|
|
|
5
|
-
var noop = function() {};
|
|
6
|
-
var noopReturn = function(x) {
|
|
5
|
+
var noop = function () {};
|
|
6
|
+
var noopReturn = function (x) {
|
|
7
|
+
return x;
|
|
8
|
+
};
|
|
7
9
|
|
|
8
10
|
module.exports = {
|
|
9
11
|
// Platform
|
|
10
12
|
Platform: {
|
|
11
13
|
OS: 'ios',
|
|
12
14
|
Version: 19,
|
|
13
|
-
select: function(obj) {
|
|
15
|
+
select: function (obj) {
|
|
16
|
+
return obj.ios !== undefined ? obj.ios : obj.default;
|
|
17
|
+
},
|
|
14
18
|
isPad: false,
|
|
15
19
|
isTVOS: false,
|
|
16
20
|
isTV: false,
|
|
@@ -18,8 +22,10 @@ module.exports = {
|
|
|
18
22
|
|
|
19
23
|
// StyleSheet
|
|
20
24
|
StyleSheet: {
|
|
21
|
-
create: function(styles) {
|
|
22
|
-
|
|
25
|
+
create: function (styles) {
|
|
26
|
+
return styles;
|
|
27
|
+
},
|
|
28
|
+
flatten: function (style) {
|
|
23
29
|
if (!style) return {};
|
|
24
30
|
if (Array.isArray(style)) {
|
|
25
31
|
var result = {};
|
|
@@ -37,23 +43,35 @@ module.exports = {
|
|
|
37
43
|
|
|
38
44
|
// Dimensions
|
|
39
45
|
Dimensions: {
|
|
40
|
-
get: function() {
|
|
41
|
-
|
|
46
|
+
get: function () {
|
|
47
|
+
return { width: 375, height: 812, scale: 3, fontScale: 1 };
|
|
48
|
+
},
|
|
49
|
+
addEventListener: function () {
|
|
50
|
+
return { remove: noop };
|
|
51
|
+
},
|
|
42
52
|
removeEventListener: noop,
|
|
43
53
|
},
|
|
44
54
|
|
|
45
55
|
// PixelRatio
|
|
46
56
|
PixelRatio: {
|
|
47
|
-
get: function() {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
57
|
+
get: function () {
|
|
58
|
+
return 3;
|
|
59
|
+
},
|
|
60
|
+
getFontScale: function () {
|
|
61
|
+
return 1;
|
|
62
|
+
},
|
|
63
|
+
getPixelSizeForLayoutSize: function (size) {
|
|
64
|
+
return size * 3;
|
|
65
|
+
},
|
|
66
|
+
roundToNearestPixel: function (size) {
|
|
67
|
+
return Math.round(size * 3) / 3;
|
|
68
|
+
},
|
|
51
69
|
},
|
|
52
70
|
|
|
53
71
|
// AppState
|
|
54
72
|
AppState: {
|
|
55
73
|
currentState: 'active',
|
|
56
|
-
addEventListener: function(event, cb) {
|
|
74
|
+
addEventListener: function (event, cb) {
|
|
57
75
|
return { remove: noop };
|
|
58
76
|
},
|
|
59
77
|
removeEventListener: noop,
|
|
@@ -67,46 +85,106 @@ module.exports = {
|
|
|
67
85
|
|
|
68
86
|
// Linking
|
|
69
87
|
Linking: {
|
|
70
|
-
openURL: function() {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
88
|
+
openURL: function () {
|
|
89
|
+
return Promise.resolve();
|
|
90
|
+
},
|
|
91
|
+
canOpenURL: function () {
|
|
92
|
+
return Promise.resolve(true);
|
|
93
|
+
},
|
|
94
|
+
getInitialURL: function () {
|
|
95
|
+
return Promise.resolve(null);
|
|
96
|
+
},
|
|
97
|
+
addEventListener: function () {
|
|
98
|
+
return { remove: noop };
|
|
99
|
+
},
|
|
74
100
|
removeEventListener: noop,
|
|
75
101
|
},
|
|
76
102
|
|
|
77
103
|
// Keyboard
|
|
78
104
|
Keyboard: {
|
|
79
105
|
dismiss: noop,
|
|
80
|
-
addListener: function() {
|
|
106
|
+
addListener: function () {
|
|
107
|
+
return { remove: noop };
|
|
108
|
+
},
|
|
81
109
|
removeListener: noop,
|
|
82
110
|
removeAllListeners: noop,
|
|
83
111
|
},
|
|
84
112
|
|
|
85
113
|
// Animated
|
|
86
114
|
Animated: {
|
|
87
|
-
Value: function(val) {
|
|
115
|
+
Value: function (val) {
|
|
88
116
|
this._value = val;
|
|
89
|
-
this.setValue = function(v) {
|
|
90
|
-
|
|
91
|
-
|
|
117
|
+
this.setValue = function (v) {
|
|
118
|
+
this._value = v;
|
|
119
|
+
};
|
|
120
|
+
this.interpolate = function () {
|
|
121
|
+
return new module.exports.Animated.Value(0);
|
|
122
|
+
};
|
|
123
|
+
this.addListener = function () {
|
|
124
|
+
return { remove: noop };
|
|
125
|
+
};
|
|
92
126
|
this.removeListener = noop;
|
|
93
127
|
this.removeAllListeners = noop;
|
|
94
|
-
this.stopAnimation = function(cb) {
|
|
128
|
+
this.stopAnimation = function (cb) {
|
|
129
|
+
if (cb) cb(this._value);
|
|
130
|
+
};
|
|
95
131
|
},
|
|
96
|
-
ValueXY: function() {
|
|
132
|
+
ValueXY: function () {
|
|
97
133
|
this.x = new module.exports.Animated.Value(0);
|
|
98
134
|
this.y = new module.exports.Animated.Value(0);
|
|
99
135
|
this.setValue = noop;
|
|
100
|
-
this.getLayout = function() {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
136
|
+
this.getLayout = function () {
|
|
137
|
+
return { left: this.x, top: this.y };
|
|
138
|
+
};
|
|
139
|
+
},
|
|
140
|
+
timing: function () {
|
|
141
|
+
return {
|
|
142
|
+
start: function (cb) {
|
|
143
|
+
if (cb) cb({ finished: true });
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
},
|
|
147
|
+
spring: function () {
|
|
148
|
+
return {
|
|
149
|
+
start: function (cb) {
|
|
150
|
+
if (cb) cb({ finished: true });
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
},
|
|
154
|
+
decay: function () {
|
|
155
|
+
return {
|
|
156
|
+
start: function (cb) {
|
|
157
|
+
if (cb) cb({ finished: true });
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
},
|
|
161
|
+
parallel: function () {
|
|
162
|
+
return {
|
|
163
|
+
start: function (cb) {
|
|
164
|
+
if (cb) cb({ finished: true });
|
|
165
|
+
},
|
|
166
|
+
};
|
|
167
|
+
},
|
|
168
|
+
sequence: function () {
|
|
169
|
+
return {
|
|
170
|
+
start: function (cb) {
|
|
171
|
+
if (cb) cb({ finished: true });
|
|
172
|
+
},
|
|
173
|
+
};
|
|
174
|
+
},
|
|
175
|
+
stagger: function () {
|
|
176
|
+
return {
|
|
177
|
+
start: function (cb) {
|
|
178
|
+
if (cb) cb({ finished: true });
|
|
179
|
+
},
|
|
180
|
+
};
|
|
181
|
+
},
|
|
182
|
+
loop: function () {
|
|
183
|
+
return { start: noop, stop: noop };
|
|
184
|
+
},
|
|
185
|
+
event: function () {
|
|
186
|
+
return noop;
|
|
187
|
+
},
|
|
110
188
|
createAnimatedComponent: noopReturn,
|
|
111
189
|
View: 'Animated.View',
|
|
112
190
|
Text: 'Animated.Text',
|
|
@@ -116,8 +194,12 @@ module.exports = {
|
|
|
116
194
|
},
|
|
117
195
|
|
|
118
196
|
// Hooks
|
|
119
|
-
useWindowDimensions: function() {
|
|
120
|
-
|
|
197
|
+
useWindowDimensions: function () {
|
|
198
|
+
return { width: 375, height: 812, scale: 3, fontScale: 1 };
|
|
199
|
+
},
|
|
200
|
+
useColorScheme: function () {
|
|
201
|
+
return 'light';
|
|
202
|
+
},
|
|
121
203
|
|
|
122
204
|
// Components (string stubs — not rendered in test)
|
|
123
205
|
View: 'View',
|
|
@@ -147,22 +229,32 @@ module.exports = {
|
|
|
147
229
|
|
|
148
230
|
// Appearance
|
|
149
231
|
Appearance: {
|
|
150
|
-
getColorScheme: function() {
|
|
151
|
-
|
|
232
|
+
getColorScheme: function () {
|
|
233
|
+
return 'light';
|
|
234
|
+
},
|
|
235
|
+
addChangeListener: function () {
|
|
236
|
+
return { remove: noop };
|
|
237
|
+
},
|
|
152
238
|
},
|
|
153
239
|
|
|
154
240
|
// NativeModules fallback
|
|
155
241
|
NativeModules: {},
|
|
156
|
-
NativeEventEmitter: function() {
|
|
157
|
-
this.addListener = function() {
|
|
242
|
+
NativeEventEmitter: function () {
|
|
243
|
+
this.addListener = function () {
|
|
244
|
+
return { remove: noop };
|
|
245
|
+
};
|
|
158
246
|
this.removeListener = noop;
|
|
159
247
|
this.removeAllListeners = noop;
|
|
160
248
|
},
|
|
161
249
|
|
|
162
250
|
// AccessibilityInfo
|
|
163
251
|
AccessibilityInfo: {
|
|
164
|
-
isScreenReaderEnabled: function() {
|
|
165
|
-
|
|
252
|
+
isScreenReaderEnabled: function () {
|
|
253
|
+
return Promise.resolve(false);
|
|
254
|
+
},
|
|
255
|
+
addEventListener: function () {
|
|
256
|
+
return { remove: noop };
|
|
257
|
+
},
|
|
166
258
|
announceForAccessibility: noop,
|
|
167
259
|
},
|
|
168
260
|
};
|
package/src/shims/react.js
CHANGED
|
@@ -2,15 +2,15 @@
|
|
|
2
2
|
// react-reconciler imports 'react' — this shim delegates to globalThis.__HT_React.
|
|
3
3
|
// The Proxy ensures late-binding: properties are read at access time, not import time.
|
|
4
4
|
var handler = {
|
|
5
|
-
get: function(_, prop) {
|
|
5
|
+
get: function (_, prop) {
|
|
6
6
|
if (prop === Symbol.toPrimitive || prop === 'then') return undefined;
|
|
7
7
|
var R = globalThis.__HT_React;
|
|
8
8
|
return R ? R[prop] : undefined;
|
|
9
9
|
},
|
|
10
|
-
set: function(_, prop, value) {
|
|
10
|
+
set: function (_, prop, value) {
|
|
11
11
|
var R = globalThis.__HT_React;
|
|
12
12
|
if (R) R[prop] = value;
|
|
13
13
|
return true;
|
|
14
|
-
}
|
|
14
|
+
},
|
|
15
15
|
};
|
|
16
16
|
module.exports = new Proxy({}, handler);
|
|
@@ -12,10 +12,10 @@ var real = require('@__ht_real_pkg/@reduxjs/toolkit/query');
|
|
|
12
12
|
var _apiCache = {};
|
|
13
13
|
|
|
14
14
|
var handler = {
|
|
15
|
-
get: function(target, prop) {
|
|
15
|
+
get: function (target, prop) {
|
|
16
16
|
if (prop === 'createApi') {
|
|
17
17
|
return function createApi(opts) {
|
|
18
|
-
var key = opts && opts.reducerPath || 'api';
|
|
18
|
+
var key = (opts && opts.reducerPath) || 'api';
|
|
19
19
|
if (_apiCache[key]) return _apiCache[key];
|
|
20
20
|
var api = real.createApi(opts);
|
|
21
21
|
_apiCache[key] = api;
|
|
@@ -24,16 +24,26 @@ var handler = {
|
|
|
24
24
|
}
|
|
25
25
|
return real[prop];
|
|
26
26
|
},
|
|
27
|
-
ownKeys: function() {
|
|
28
|
-
try {
|
|
27
|
+
ownKeys: function () {
|
|
28
|
+
try {
|
|
29
|
+
return Object.getOwnPropertyNames(real);
|
|
30
|
+
} catch (e) {
|
|
31
|
+
return [];
|
|
32
|
+
}
|
|
29
33
|
},
|
|
30
|
-
getOwnPropertyDescriptor: function(target, prop) {
|
|
34
|
+
getOwnPropertyDescriptor: function (target, prop) {
|
|
31
35
|
try {
|
|
32
36
|
var d = Object.getOwnPropertyDescriptor(real, prop);
|
|
33
|
-
if (d)
|
|
34
|
-
|
|
37
|
+
if (d)
|
|
38
|
+
return {
|
|
39
|
+
configurable: true,
|
|
40
|
+
enumerable: d.enumerable,
|
|
41
|
+
writable: true,
|
|
42
|
+
value: d.get ? d.get() : d.value,
|
|
43
|
+
};
|
|
44
|
+
} catch (e) {}
|
|
35
45
|
return { configurable: true, enumerable: false, writable: true, value: undefined };
|
|
36
|
-
}
|
|
46
|
+
},
|
|
37
47
|
};
|
|
38
48
|
|
|
39
49
|
module.exports = new Proxy({}, handler);
|
package/src/shims/rtk-query.js
CHANGED
|
@@ -17,10 +17,10 @@ var _apiCache = {};
|
|
|
17
17
|
// Proxy delegates everything to the real module with late binding.
|
|
18
18
|
// This avoids eagerly copying ESM live bindings that may not be initialized yet.
|
|
19
19
|
var handler = {
|
|
20
|
-
get: function(target, prop) {
|
|
20
|
+
get: function (target, prop) {
|
|
21
21
|
if (prop === 'createApi') {
|
|
22
22
|
return function createApi(opts) {
|
|
23
|
-
var key = opts && opts.reducerPath || 'api';
|
|
23
|
+
var key = (opts && opts.reducerPath) || 'api';
|
|
24
24
|
if (_apiCache[key]) return _apiCache[key];
|
|
25
25
|
var api = real.createApi(opts);
|
|
26
26
|
_apiCache[key] = api;
|
|
@@ -29,16 +29,26 @@ var handler = {
|
|
|
29
29
|
}
|
|
30
30
|
return real[prop];
|
|
31
31
|
},
|
|
32
|
-
ownKeys: function() {
|
|
33
|
-
try {
|
|
32
|
+
ownKeys: function () {
|
|
33
|
+
try {
|
|
34
|
+
return Object.getOwnPropertyNames(real);
|
|
35
|
+
} catch (e) {
|
|
36
|
+
return [];
|
|
37
|
+
}
|
|
34
38
|
},
|
|
35
|
-
getOwnPropertyDescriptor: function(target, prop) {
|
|
39
|
+
getOwnPropertyDescriptor: function (target, prop) {
|
|
36
40
|
try {
|
|
37
41
|
var d = Object.getOwnPropertyDescriptor(real, prop);
|
|
38
|
-
if (d)
|
|
39
|
-
|
|
42
|
+
if (d)
|
|
43
|
+
return {
|
|
44
|
+
configurable: true,
|
|
45
|
+
enumerable: d.enumerable,
|
|
46
|
+
writable: true,
|
|
47
|
+
value: d.get ? d.get() : d.value,
|
|
48
|
+
};
|
|
49
|
+
} catch (e) {}
|
|
40
50
|
return { configurable: true, enumerable: false, writable: true, value: undefined };
|
|
41
|
-
}
|
|
51
|
+
},
|
|
42
52
|
};
|
|
43
53
|
|
|
44
54
|
module.exports = new Proxy({}, handler);
|
|
@@ -50,7 +50,7 @@ mod.withQueryClient = function withQueryClient(opts) {
|
|
|
50
50
|
var React = require('react');
|
|
51
51
|
var QCP = mod.QueryClientProvider;
|
|
52
52
|
|
|
53
|
-
var wrapper = function(props) {
|
|
53
|
+
var wrapper = function (props) {
|
|
54
54
|
return React.createElement(QCP, { client: client }, props.children);
|
|
55
55
|
};
|
|
56
56
|
|
|
@@ -59,7 +59,7 @@ mod.withQueryClient = function withQueryClient(opts) {
|
|
|
59
59
|
return {
|
|
60
60
|
queryClient: client,
|
|
61
61
|
wrapper: wrapper,
|
|
62
|
-
renderHookWithQuery: function(hookFn, hookOpts) {
|
|
62
|
+
renderHookWithQuery: function (hookFn, hookOpts) {
|
|
63
63
|
return renderHook(hookFn, Object.assign({}, hookOpts, { wrapper: wrapper }));
|
|
64
64
|
},
|
|
65
65
|
};
|
package/src/spy.ts
CHANGED
|
@@ -1,31 +1,30 @@
|
|
|
1
|
-
export type Spy<F extends (...args: any[]) => any = (...args: any[]) => any> =
|
|
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
|
-
};
|
|
1
|
+
export type Spy<F extends (...args: any[]) => any = (...args: any[]) => any> = F & {
|
|
2
|
+
readonly calls: ReadonlyArray<Parameters<F>>;
|
|
3
|
+
readonly callCount: number;
|
|
4
|
+
readonly returnValues: ReadonlyArray<ReturnType<F>>;
|
|
5
|
+
reset(): void;
|
|
6
|
+
|
|
7
|
+
// Core
|
|
8
|
+
setImpl(impl: F): void;
|
|
9
|
+
returns(value: ReturnType<F>): Spy<F>;
|
|
10
|
+
|
|
11
|
+
// Jest-compatible API
|
|
12
|
+
mockImplementation(fn: F): Spy<F>;
|
|
13
|
+
mockImplementationOnce(fn: F): Spy<F>;
|
|
14
|
+
mockReturnValue(value: ReturnType<F>): Spy<F>;
|
|
15
|
+
mockReturnValueOnce(value: ReturnType<F>): Spy<F>;
|
|
16
|
+
mockResolvedValue(value: any): Spy<F>;
|
|
17
|
+
mockResolvedValueOnce(value: any): Spy<F>;
|
|
18
|
+
mockRejectedValue(value: any): Spy<F>;
|
|
19
|
+
mockRejectedValueOnce(value: any): Spy<F>;
|
|
20
|
+
mockClear(): void;
|
|
21
|
+
mockReset(): void;
|
|
22
|
+
mockRestore(): void;
|
|
23
|
+
|
|
24
|
+
// spyOn support
|
|
25
|
+
_restore?: () => void;
|
|
26
|
+
_isSpy: true;
|
|
27
|
+
};
|
|
29
28
|
|
|
30
29
|
// Registry of all spies created — clearAllMocks() clears them all at once.
|
|
31
30
|
const _allSpies: Spy[] = [];
|
|
@@ -34,9 +33,7 @@ export function clearAllMocks(): void {
|
|
|
34
33
|
for (const s of _allSpies) s.mockClear();
|
|
35
34
|
}
|
|
36
35
|
|
|
37
|
-
export function spy<F extends (...args: any[]) => any = () => void>(
|
|
38
|
-
impl?: F
|
|
39
|
-
): Spy<F> {
|
|
36
|
+
export function spy<F extends (...args: any[]) => any = () => void>(impl?: F): Spy<F> {
|
|
40
37
|
let baseImpl: F | undefined = impl;
|
|
41
38
|
const onceImpls: F[] = []; // FIFO queue for mockImplementationOnce/mockReturnValueOnce
|
|
42
39
|
const calls: any[][] = [];
|
|
@@ -154,13 +151,12 @@ export function spy<F extends (...args: any[]) => any = () => void>(
|
|
|
154
151
|
}
|
|
155
152
|
|
|
156
153
|
// --- spyOn: replace a method on an object with a spy, preserving original ---
|
|
157
|
-
export function spyOn<T extends Record<string, any>>(
|
|
158
|
-
obj: T,
|
|
159
|
-
method: keyof T & string,
|
|
160
|
-
): Spy {
|
|
154
|
+
export function spyOn<T extends Record<string, any>>(obj: T, method: keyof T & string): Spy {
|
|
161
155
|
const original = obj[method];
|
|
162
156
|
const s = spy(typeof original === 'function' ? original.bind(obj) : undefined);
|
|
163
|
-
(s as any)._restore = () => {
|
|
157
|
+
(s as any)._restore = () => {
|
|
158
|
+
obj[method] = original;
|
|
159
|
+
};
|
|
164
160
|
obj[method] = s as any;
|
|
165
161
|
return s;
|
|
166
162
|
}
|
package/src/store.ts
CHANGED
|
@@ -9,7 +9,6 @@ import React from 'react';
|
|
|
9
9
|
import { Provider } from 'react-redux';
|
|
10
10
|
import { configureStore } from '@reduxjs/toolkit';
|
|
11
11
|
|
|
12
|
-
|
|
13
12
|
type StoreContext = {
|
|
14
13
|
store: any;
|
|
15
14
|
wrapper: any;
|
|
@@ -17,7 +16,10 @@ type StoreContext = {
|
|
|
17
16
|
getState: () => any;
|
|
18
17
|
setState: (state: Record<string, any>) => void;
|
|
19
18
|
patchState: (partial: Record<string, any>) => void;
|
|
20
|
-
renderHookWithReduxStore: <T>(
|
|
19
|
+
renderHookWithReduxStore: <T>(
|
|
20
|
+
hookFn: (props?: any) => T,
|
|
21
|
+
options?: { initialProps?: any },
|
|
22
|
+
) => any;
|
|
21
23
|
};
|
|
22
24
|
|
|
23
25
|
function withTestActions(reducer: (state: any, action: any) => any) {
|
|
@@ -39,8 +41,12 @@ function makeCtx(store: any): StoreContext {
|
|
|
39
41
|
wrapper,
|
|
40
42
|
dispatch: store.dispatch.bind(store),
|
|
41
43
|
getState: store.getState.bind(store),
|
|
42
|
-
setState(state: Record<string, any>) {
|
|
43
|
-
|
|
44
|
+
setState(state: Record<string, any>) {
|
|
45
|
+
store.dispatch({ type: '__SET_STATE__', payload: state });
|
|
46
|
+
},
|
|
47
|
+
patchState(partial: Record<string, any>) {
|
|
48
|
+
store.dispatch({ type: '__PATCH__', payload: partial });
|
|
49
|
+
},
|
|
44
50
|
renderHookWithReduxStore<T>(hookFn: (props?: any) => T, options?: { initialProps?: any }) {
|
|
45
51
|
return renderHook(hookFn, { ...options, wrapper });
|
|
46
52
|
},
|
|
@@ -49,11 +55,13 @@ function makeCtx(store: any): StoreContext {
|
|
|
49
55
|
|
|
50
56
|
/** Quick store from plain state object — identity reducer, any shape */
|
|
51
57
|
export function withStore(initialState: Record<string, any> = {}): StoreContext {
|
|
52
|
-
return makeCtx(
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
58
|
+
return makeCtx(
|
|
59
|
+
configureStore({
|
|
60
|
+
reducer: withTestActions((s: any = initialState) => s),
|
|
61
|
+
preloadedState: initialState,
|
|
62
|
+
middleware: gdm => gdm({ serializableCheck: false, immutableCheck: false }),
|
|
63
|
+
}),
|
|
64
|
+
);
|
|
57
65
|
}
|
|
58
66
|
|
|
59
67
|
/** Real app reducer — patchState + real actions both work */
|
|
@@ -61,11 +69,13 @@ export function withAppReducer(
|
|
|
61
69
|
reducer: (state: any, action: any) => any,
|
|
62
70
|
preloadedState?: Record<string, any>,
|
|
63
71
|
): StoreContext {
|
|
64
|
-
return makeCtx(
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
72
|
+
return makeCtx(
|
|
73
|
+
configureStore({
|
|
74
|
+
reducer: withTestActions(reducer),
|
|
75
|
+
preloadedState,
|
|
76
|
+
middleware: gdm => gdm({ serializableCheck: false, immutableCheck: false }),
|
|
77
|
+
}),
|
|
78
|
+
);
|
|
69
79
|
}
|
|
70
80
|
|
|
71
81
|
interface RtkQueryApi {
|
|
@@ -103,11 +113,11 @@ export function setupApiStore(
|
|
|
103
113
|
const store = configureStore({
|
|
104
114
|
reducer: reducerMap,
|
|
105
115
|
preloadedState: options?.preloadedState,
|
|
106
|
-
middleware:
|
|
116
|
+
middleware: gdm => {
|
|
107
117
|
let chain = gdm({ serializableCheck: false, immutableCheck: false });
|
|
108
118
|
for (const a of apis) chain = chain.concat(a.middleware);
|
|
109
|
-
for (const mw of
|
|
110
|
-
for (const mw of
|
|
119
|
+
for (const mw of options?.middleware?.concat ?? []) chain = chain.concat(mw);
|
|
120
|
+
for (const mw of options?.middleware?.prepend ?? []) chain = chain.prepend(mw);
|
|
111
121
|
return chain;
|
|
112
122
|
},
|
|
113
123
|
});
|
package/src/timers.ts
CHANGED
|
@@ -10,7 +10,9 @@
|
|
|
10
10
|
// useFakeTimers does `globalThis.Date.now = () => fakeNow` which mutates the original
|
|
11
11
|
// Date object's .now property. So we must save the function itself, not the constructor.
|
|
12
12
|
const _savedDateNow: () => number = Date.now;
|
|
13
|
-
export function realDateNow(): number {
|
|
13
|
+
export function realDateNow(): number {
|
|
14
|
+
return _savedDateNow();
|
|
15
|
+
}
|
|
14
16
|
|
|
15
17
|
interface PendingTimer {
|
|
16
18
|
id: number;
|
|
@@ -118,7 +120,7 @@ export function runAllTimers() {
|
|
|
118
120
|
if (!isFake) throw new Error('runAllTimers called without useFakeTimers()');
|
|
119
121
|
let safety = 1000;
|
|
120
122
|
while (pending.length > 0 && safety-- > 0) {
|
|
121
|
-
const next = pending.reduce((min, t) => t.fireAt < min.fireAt ? t : min);
|
|
123
|
+
const next = pending.reduce((min, t) => (t.fireAt < min.fireAt ? t : min));
|
|
122
124
|
fakeNow = next.fireAt;
|
|
123
125
|
if (next.type === 'timeout') {
|
|
124
126
|
pending = pending.filter(t => t.id !== next.id);
|
|
@@ -136,6 +138,6 @@ export function getTimerCount(): number {
|
|
|
136
138
|
|
|
137
139
|
export function advanceTimersToNextTimer() {
|
|
138
140
|
if (!isFake || pending.length === 0) return;
|
|
139
|
-
const next = pending.reduce((min, t) => t.fireAt < min.fireAt ? t : min);
|
|
141
|
+
const next = pending.reduce((min, t) => (t.fireAt < min.fireAt ? t : min));
|
|
140
142
|
advanceTimersByTime(next.fireAt - fakeNow);
|
|
141
143
|
}
|