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/src/fetch.ts CHANGED
@@ -75,13 +75,17 @@ function fakeFetch(input: any, init?: any): any {
75
75
  const method = (init?.method || 'GET').toUpperCase();
76
76
  let body = init?.body;
77
77
  if (typeof body === 'string') {
78
- try { body = JSON.parse(body); } catch {}
78
+ try {
79
+ body = JSON.parse(body);
80
+ } catch {}
79
81
  }
80
82
 
81
83
  const reqHeaders: Record<string, string> = {};
82
84
  if (init?.headers) {
83
85
  if (typeof init.headers.forEach === 'function') {
84
- init.headers.forEach((v: string, k: string) => { reqHeaders[k] = v; });
86
+ init.headers.forEach((v: string, k: string) => {
87
+ reqHeaders[k] = v;
88
+ });
85
89
  } else {
86
90
  Object.assign(reqHeaders, init.headers);
87
91
  }
@@ -122,14 +126,24 @@ function fakeFetch(input: any, init?: any): any {
122
126
  has: (k: string) => k.toLowerCase() in responseHeaders,
123
127
  },
124
128
  json: () => Promise.resolve(responseBody),
125
- text: () => Promise.resolve(typeof responseBody === 'string' ? responseBody : JSON.stringify(responseBody)),
126
- clone: function() { return this; },
129
+ text: () =>
130
+ Promise.resolve(
131
+ typeof responseBody === 'string' ? responseBody : JSON.stringify(responseBody),
132
+ ),
133
+ clone: function () {
134
+ return this;
135
+ },
127
136
  });
128
137
  }
129
138
 
130
139
  // --- Public API ---
131
140
 
132
- function createHandler(method: Method, url: string | RegExp, response: MockResponseInit | ((req: MockRequest) => MockResponseInit), once = false): MockHandler {
141
+ function createHandler(
142
+ method: Method,
143
+ url: string | RegExp,
144
+ response: MockResponseInit | ((req: MockRequest) => MockResponseInit),
145
+ once = false,
146
+ ): MockHandler {
133
147
  const handler = typeof response === 'function' ? response : () => response;
134
148
  return { method, url, handler, once };
135
149
  }
package/src/harness.ts CHANGED
@@ -4,13 +4,18 @@
4
4
  // Console interceptor — replace console with print()-based output.
5
5
  // Hermes's native print() writes to stdout and is always available.
6
6
  // This works in all bundle modes (single, split, watch).
7
- (function() {
7
+ (function () {
8
8
  const p = (globalThis as any).print || (() => {});
9
9
  function fmt(...args: any[]) {
10
- return args.map((a: any) => {
11
- try { return typeof a === 'string' ? a : JSON.stringify(a, null, 2); }
12
- catch { return String(a); }
13
- }).join(' ');
10
+ return args
11
+ .map((a: any) => {
12
+ try {
13
+ return typeof a === 'string' ? a : JSON.stringify(a, null, 2);
14
+ } catch {
15
+ return String(a);
16
+ }
17
+ })
18
+ .join(' ');
14
19
  }
15
20
  (globalThis as any).console = {
16
21
  log: (...args: any[]) => p(fmt(...args)),
@@ -32,8 +37,22 @@ import { spy, spyOn, clearAllMocks } from './spy';
32
37
  import { renderHook, act, waitFor } from './hooks';
33
38
  import { render, fireEvent } from './render';
34
39
  import { useMock, mockModule, resetMocks, resetMockModulePatches } from './mock';
35
- import { mockFetch, mockFetchUse, mockFetchReset, mockFetchClear, http, HttpResponse } from './fetch';
36
- import { useFakeTimers, useRealTimers, advanceTimersByTime, runAllTimers, getTimerCount, advanceTimersToNextTimer } from './timers';
40
+ import {
41
+ mockFetch,
42
+ mockFetchUse,
43
+ mockFetchReset,
44
+ mockFetchClear,
45
+ http,
46
+ HttpResponse,
47
+ } from './fetch';
48
+ import {
49
+ useFakeTimers,
50
+ useRealTimers,
51
+ advanceTimersByTime,
52
+ runAllTimers,
53
+ getTimerCount,
54
+ advanceTimersToNextTimer,
55
+ } from './timers';
37
56
 
38
57
  type TestFn = ((ctx: TestContext) => void | Promise<void>) | (() => void | Promise<void>);
39
58
  type TestContext = {
@@ -83,11 +102,11 @@ function test(name: string, fn: TestFn, options?: TestOptions): void {
83
102
  });
84
103
  }
85
104
 
86
- test.only = function(name: string, fn: TestFn): void {
105
+ test.only = function (name: string, fn: TestFn): void {
87
106
  test(name, fn, { only: true });
88
107
  };
89
108
 
90
- test.skip = function(name: string, fn: TestFn): void {
109
+ test.skip = function (name: string, fn: TestFn): void {
91
110
  test(name, fn, { skip: true });
92
111
  };
93
112
 
@@ -156,8 +175,14 @@ function flushAsync<T = any>(promise: Promise<T> | T): T {
156
175
  let error: any;
157
176
  let settled = false;
158
177
  (promise as Promise<T>).then(
159
- (v) => { result = v; settled = true; },
160
- (e) => { error = e; settled = true; }
178
+ v => {
179
+ result = v;
180
+ settled = true;
181
+ },
182
+ e => {
183
+ error = e;
184
+ settled = true;
185
+ },
161
186
  );
162
187
  // drainMicrotasks() flushes all pending microtasks. One call should settle
163
188
  // most promises. Loop only as safety net for edge cases (macrotask scheduling).
@@ -199,10 +224,14 @@ function _printFileResult(file: string, passed: number, failed: number, duration
199
224
  // Clear progress line before printing failure
200
225
  _print(`\r\x1b[K`);
201
226
  }
202
- _print(` \x1b[31mFAIL\x1b[0m ${file} \x1b[2m(${passed} passed, ${failed} failed)\x1b[0m${time}\n`);
227
+ _print(
228
+ ` \x1b[31mFAIL\x1b[0m ${file} \x1b[2m(${passed} passed, ${failed} failed)\x1b[0m${time}\n`,
229
+ );
203
230
  } else if ((globalThis as any).__HT_coverage) {
204
231
  // In-place progress counter
205
- _print(`\r\x1b[K \x1b[2mRunning...\x1b[0m ${_filesCompleted}/${_totalFiles} files (${_testsCompleted} tests)`);
232
+ _print(
233
+ `\r\x1b[K \x1b[2mRunning...\x1b[0m ${_filesCompleted}/${_totalFiles} files (${_testsCompleted} tests)`,
234
+ );
206
235
  } else {
207
236
  _print(` \x1b[32mPASS\x1b[0m ${file} \x1b[2m(${total} tests)\x1b[0m${time}\n`);
208
237
  }
@@ -225,18 +254,35 @@ function formatTestError(e: any): string {
225
254
 
226
255
  // Filter to application frames only (skip react internals, harness, native)
227
256
  const skipFn = new Set([
228
- 'anonymous', 'global', '__init', 'apply', 'map',
229
- 'react-stack-bottom-frame', 'proxy trap',
257
+ 'anonymous',
258
+ 'global',
259
+ '__init',
260
+ 'apply',
261
+ 'map',
262
+ 'react-stack-bottom-frame',
263
+ 'proxy trap',
230
264
  ]);
231
265
  const skipPrefix = [
232
- 'render', 'run', 'perform', 'work', 'flush', 'begin', 'update',
233
- 'reconcile', 'create', 'complete', 'commit', 'process',
266
+ 'render',
267
+ 'run',
268
+ 'perform',
269
+ 'work',
270
+ 'flush',
271
+ 'begin',
272
+ 'update',
273
+ 'reconcile',
274
+ 'create',
275
+ 'complete',
276
+ 'commit',
277
+ 'process',
234
278
  ];
235
279
  const appFrames = frames.filter(f => {
236
280
  if (skipFn.has(f.fn)) return false;
237
281
  if (f.file.includes('harness') || f.file.includes('runner')) return false;
238
282
  if (f.fn === '' && !f.file.includes('/src/') && !f.file.includes('packages/')) return false;
239
- for (const p of skipPrefix) { if (f.fn.startsWith(p)) return false; }
283
+ for (const p of skipPrefix) {
284
+ if (f.fn.startsWith(p)) return false;
285
+ }
240
286
  return true;
241
287
  });
242
288
 
@@ -246,7 +292,11 @@ function formatTestError(e: any): string {
246
292
  cleanStack += '\n';
247
293
  for (const f of appFrames.slice(0, 8)) {
248
294
  // esbuild names __esm blocks with source paths like "src/utils/string.ts"
249
- const loc = f.fn.includes('/') ? f.fn : (f.fn ? f.fn + ' (' + f.file + ':' + f.line + ')' : f.file + ':' + f.line);
295
+ const loc = f.fn.includes('/')
296
+ ? f.fn
297
+ : f.fn
298
+ ? f.fn + ' (' + f.file + ':' + f.line + ')'
299
+ : f.file + ':' + f.line;
250
300
  cleanStack += '\n at ' + loc;
251
301
  }
252
302
  }
@@ -265,15 +315,23 @@ function formatTestError(e: any): string {
265
315
  const nmMatch = srcPath.match(/node_modules\/((?:@[^/]+\/)?[^/]+)/);
266
316
  if (nmMatch) {
267
317
  const pkg = nmMatch[1];
268
- hint = '\n\n "' + pkg + '" crashed during initialization (native dependency).'
269
- + '\n Add to externals in hermes-test.config.json:\n\n'
270
- + ' { "externals": ["' + pkg + '"] }\n'
271
- + '\n Or mock the module that imports it with ht.mock().\n';
318
+ hint =
319
+ '\n\n "' +
320
+ pkg +
321
+ '" crashed during initialization (native dependency).' +
322
+ '\n Add to externals in hermes-test.config.json:\n\n' +
323
+ ' { "externals": ["' +
324
+ pkg +
325
+ '"] }\n' +
326
+ '\n Or mock the module that imports it with ht.mock().\n';
272
327
  } else {
273
328
  const cleanPath = srcPath.replace(/\/index\.(tsx?|jsx?)$/, '');
274
- hint = '\n\n Module "' + cleanPath + '" crashed during initialization.'
275
- + '\n A dependency uses an API not available in Hermes.'
276
- + '\n Mock it with ht.mock() or add the native dep to externals.\n';
329
+ hint =
330
+ '\n\n Module "' +
331
+ cleanPath +
332
+ '" crashed during initialization.' +
333
+ '\n A dependency uses an API not available in Hermes.' +
334
+ '\n Mock it with ht.mock() or add the native dep to externals.\n';
277
335
  }
278
336
  break;
279
337
  }
@@ -288,9 +346,18 @@ function formatTestError(e: any): string {
288
346
  if (importMap[k] === modPath && siblings.indexOf(k) === -1) siblings.push(k);
289
347
  }
290
348
  const mockBody = siblings.map(s => ' ' + s + ': () => {}').join(',\n');
291
- hint = '\n\n "' + fnName + '" from "' + modPath + '" failed.'
292
- + '\n Add this mock to your test file:\n\n'
293
- + " ht.mock('" + modPath + "', () => ({\n" + mockBody + '\n }));\n';
349
+ hint =
350
+ '\n\n "' +
351
+ fnName +
352
+ '" from "' +
353
+ modPath +
354
+ '" failed.' +
355
+ '\n Add this mock to your test file:\n\n' +
356
+ " ht.mock('" +
357
+ modPath +
358
+ "', () => ({\n" +
359
+ mockBody +
360
+ '\n }));\n';
294
361
  break;
295
362
  }
296
363
  }
@@ -301,7 +368,7 @@ function formatTestError(e: any): string {
301
368
 
302
369
  function runTests(): TestResult[] {
303
370
  const results: TestResult[] = [];
304
- const hasOnly = tests.some((t) => t.options.only);
371
+ const hasOnly = tests.some(t => t.options.only);
305
372
 
306
373
  // Count unique files for progress counter
307
374
  const uniqueFiles = new Set(tests.map(t => t.file));
@@ -317,7 +384,7 @@ function runTests(): TestResult[] {
317
384
  let _fileFailures: { name: string; error: string }[] = [];
318
385
 
319
386
  function _flushFileResult() {
320
- if (_currentFile && (_filePassed + _fileFailed) > 0) {
387
+ if (_currentFile && _filePassed + _fileFailed > 0) {
321
388
  _printFileResult(_currentFile, _filePassed, _fileFailed, Date.now() - _fileStart);
322
389
  // Print failure details
323
390
  for (const f of _fileFailures) {
@@ -476,7 +543,9 @@ function registerCrash(file: string, error: string): void {
476
543
  const formatted = formatTestError({ message: error.split('\n')[0], stack: error });
477
544
  tests.push({
478
545
  name: `[CRASH] ${file}`,
479
- fn: () => { throw new Error(formatted); },
546
+ fn: () => {
547
+ throw new Error(formatted);
548
+ },
480
549
  options: {},
481
550
  file,
482
551
  });
@@ -553,4 +622,31 @@ const unmock = (_modulePath: string) => {}; // Bundler directive — no runtime
553
622
  advanceTimersToNextTimer,
554
623
  };
555
624
 
556
- export { test, expect, spy, spyOn, clearAllMocks, group, describe, beforeEach, afterEach, beforeAll, afterAll, renderHook, act, waitFor, render, fireEvent, useMock, http, HttpResponse, flushAsync, useFakeTimers, useRealTimers, advanceTimersByTime, runAllTimers, getTimerCount, advanceTimersToNextTimer };
625
+ export {
626
+ test,
627
+ expect,
628
+ spy,
629
+ spyOn,
630
+ clearAllMocks,
631
+ group,
632
+ describe,
633
+ beforeEach,
634
+ afterEach,
635
+ beforeAll,
636
+ afterAll,
637
+ renderHook,
638
+ act,
639
+ waitFor,
640
+ render,
641
+ fireEvent,
642
+ useMock,
643
+ http,
644
+ HttpResponse,
645
+ flushAsync,
646
+ useFakeTimers,
647
+ useRealTimers,
648
+ advanceTimersByTime,
649
+ runAllTimers,
650
+ getTimerCount,
651
+ advanceTimersToNextTimer,
652
+ };
package/src/hooks.ts CHANGED
@@ -14,7 +14,10 @@ function getReact(): typeof import('react') {
14
14
 
15
15
  function getReconcilerModule(): any {
16
16
  const R = (globalThis as any).__HT_Reconciler;
17
- if (!R) throw new Error('react-reconciler not available. Make sure it is installed (it ships with hermes-test).');
17
+ if (!R)
18
+ throw new Error(
19
+ 'react-reconciler not available. Make sure it is installed (it ships with hermes-test).',
20
+ );
18
21
  return R;
19
22
  }
20
23
 
@@ -33,55 +36,129 @@ const hostConfig = {
33
36
  supportsMicrotasks: true,
34
37
  isPrimaryRenderer: true,
35
38
  warnsIfNotActing: true,
36
- createInstance(type: string, props: any) { const { children: _c, ...rest } = props; return { type, props: rest, children: [] }; },
37
- createTextInstance(text: string) { return { type: '__TEXT__', props: {}, text, children: [] }; },
38
- appendInitialChild(p: any, c: any) { p.children.push(c); c._parent = p; },
39
- appendChild(p: any, c: any) { p.children.push(c); c._parent = p; },
40
- appendChildToContainer(p: any, c: any) { p.children.push(c); c._parent = p; },
41
- removeChild(p: any, c: any) { const i = p.children.indexOf(c); if (i !== -1) p.children.splice(i, 1); },
42
- removeChildFromContainer(p: any, c: any) { const i = p.children.indexOf(c); if (i !== -1) p.children.splice(i, 1); },
43
- insertBefore(p: any, c: any, b: any) { const i = p.children.indexOf(b); p.children.splice(i, 0, c); c._parent = p; },
44
- insertInContainerBefore(p: any, c: any, b: any) { const i = p.children.indexOf(b); p.children.splice(i, 0, c); c._parent = p; },
45
- commitUpdate(inst: any, _type: any, _oldProps: any, newProps: any) { const { children: _c, ...rest } = newProps; inst.props = rest; },
46
- commitTextUpdate(inst: any, _oldText: string, newText: string) { inst.text = newText; },
39
+ createInstance(type: string, props: any) {
40
+ const { children: _c, ...rest } = props;
41
+ return { type, props: rest, children: [] };
42
+ },
43
+ createTextInstance(text: string) {
44
+ return { type: '__TEXT__', props: {}, text, children: [] };
45
+ },
46
+ appendInitialChild(p: any, c: any) {
47
+ p.children.push(c);
48
+ c._parent = p;
49
+ },
50
+ appendChild(p: any, c: any) {
51
+ p.children.push(c);
52
+ c._parent = p;
53
+ },
54
+ appendChildToContainer(p: any, c: any) {
55
+ p.children.push(c);
56
+ c._parent = p;
57
+ },
58
+ removeChild(p: any, c: any) {
59
+ const i = p.children.indexOf(c);
60
+ if (i !== -1) p.children.splice(i, 1);
61
+ },
62
+ removeChildFromContainer(p: any, c: any) {
63
+ const i = p.children.indexOf(c);
64
+ if (i !== -1) p.children.splice(i, 1);
65
+ },
66
+ insertBefore(p: any, c: any, b: any) {
67
+ const i = p.children.indexOf(b);
68
+ p.children.splice(i, 0, c);
69
+ c._parent = p;
70
+ },
71
+ insertInContainerBefore(p: any, c: any, b: any) {
72
+ const i = p.children.indexOf(b);
73
+ p.children.splice(i, 0, c);
74
+ c._parent = p;
75
+ },
76
+ commitUpdate(inst: any, _type: any, _oldProps: any, newProps: any) {
77
+ const { children: _c, ...rest } = newProps;
78
+ inst.props = rest;
79
+ },
80
+ commitTextUpdate(inst: any, _oldText: string, newText: string) {
81
+ inst.text = newText;
82
+ },
47
83
  commitMount() {},
48
- prepareForCommit() { return null; },
84
+ prepareForCommit() {
85
+ return null;
86
+ },
49
87
  resetAfterCommit() {},
50
88
  resetTextContent() {},
51
- finalizeInitialChildren() { return false; },
52
- shouldSetTextContent() { return false; },
53
- getRootHostContext() { return null; },
54
- getChildHostContext(ctx: any) { return ctx; },
55
- getPublicInstance(inst: any) { return inst; },
56
- prepareUpdate() { return {}; },
57
- clearContainer(c: any) { c.children = []; },
89
+ finalizeInitialChildren() {
90
+ return false;
91
+ },
92
+ shouldSetTextContent() {
93
+ return false;
94
+ },
95
+ getRootHostContext() {
96
+ return null;
97
+ },
98
+ getChildHostContext(ctx: any) {
99
+ return ctx;
100
+ },
101
+ getPublicInstance(inst: any) {
102
+ return inst;
103
+ },
104
+ prepareUpdate() {
105
+ return {};
106
+ },
107
+ clearContainer(c: any) {
108
+ c.children = [];
109
+ },
58
110
  scheduleTimeout: (globalThis as any).setTimeout || ((fn: any) => fn()),
59
111
  cancelTimeout: (globalThis as any).clearTimeout || (() => {}),
60
112
  noTimeout: -1,
61
- scheduleMicrotask: typeof queueMicrotask === 'function' ? queueMicrotask : (fn: any) => Promise.resolve().then(fn),
62
- getCurrentEventPriority() { return getReconcilerConstants().DefaultEventPriority ?? 0; },
63
- setCurrentUpdatePriority(priority: number) { currentUpdatePriority = priority; },
64
- getCurrentUpdatePriority() { return currentUpdatePriority; },
65
- resolveUpdatePriority() { return currentUpdatePriority || (getReconcilerConstants().DefaultEventPriority ?? 0); },
66
- shouldAttemptEagerTransition() { return false; },
113
+ scheduleMicrotask:
114
+ typeof queueMicrotask === 'function' ? queueMicrotask : (fn: any) => Promise.resolve().then(fn),
115
+ getCurrentEventPriority() {
116
+ return getReconcilerConstants().DefaultEventPriority ?? 0;
117
+ },
118
+ setCurrentUpdatePriority(priority: number) {
119
+ currentUpdatePriority = priority;
120
+ },
121
+ getCurrentUpdatePriority() {
122
+ return currentUpdatePriority;
123
+ },
124
+ resolveUpdatePriority() {
125
+ return currentUpdatePriority || (getReconcilerConstants().DefaultEventPriority ?? 0);
126
+ },
127
+ shouldAttemptEagerTransition() {
128
+ return false;
129
+ },
67
130
  trackSchedulerEvent() {},
68
- resolveEventType() { return ''; },
69
- resolveEventTimeStamp() { return -1.1; },
131
+ resolveEventType() {
132
+ return '';
133
+ },
134
+ resolveEventTimeStamp() {
135
+ return -1.1;
136
+ },
70
137
  requestPostPaintCallback() {},
71
- maySuspendCommit() { return false; },
72
- preloadInstance() { return true; },
138
+ maySuspendCommit() {
139
+ return false;
140
+ },
141
+ preloadInstance() {
142
+ return true;
143
+ },
73
144
  startSuspendingCommit() {},
74
145
  suspendInstance() {},
75
- waitForCommitToBeReady() { return null; },
146
+ waitForCommitToBeReady() {
147
+ return null;
148
+ },
76
149
  NotPendingTransition: null,
77
150
  resetFormInstance() {},
78
151
  hideInstance() {},
79
152
  unhideInstance() {},
80
153
  hideTextInstance() {},
81
154
  unhideTextInstance() {},
82
- getInstanceFromNode() { return null; },
155
+ getInstanceFromNode() {
156
+ return null;
157
+ },
83
158
  prepareScopeUpdate() {},
84
- getInstanceFromScope() { return null; },
159
+ getInstanceFromScope() {
160
+ return null;
161
+ },
85
162
  detachDeletedInstance() {},
86
163
  beforeActiveInstanceBlur() {},
87
164
  afterActiveInstanceBlur() {},
@@ -133,8 +210,13 @@ export function act(fn: () => void | Promise<void>): void {
133
210
  let settled = false;
134
211
  let error: any;
135
212
  (result as Promise<void>).then(
136
- () => { settled = true; },
137
- (e: any) => { settled = true; error = e; }
213
+ () => {
214
+ settled = true;
215
+ },
216
+ (e: any) => {
217
+ settled = true;
218
+ error = e;
219
+ },
138
220
  );
139
221
  drain();
140
222
  if (error) throw error;
@@ -152,7 +234,7 @@ export function act(fn: () => void | Promise<void>): void {
152
234
 
153
235
  export function renderHook<T>(
154
236
  hookFn: (props?: any) => T,
155
- options?: { initialProps?: any; wrapper?: any }
237
+ options?: { initialProps?: any; wrapper?: any },
156
238
  ): HookResult<T> {
157
239
  const history: T[] = [];
158
240
  let currentValue: T;
@@ -164,13 +246,17 @@ export function renderHook<T>(
164
246
  const root = reconciler.createContainer(
165
247
  container,
166
248
  0, // LegacyRoot — effects fire synchronously in act()
167
- null, // hydrationCallbacks
249
+ null, // hydrationCallbacks
168
250
  false, // isStrictMode
169
251
  false, // concurrentUpdatesByDefaultOverride
170
- '', // identifierPrefix
171
- (err: any) => { throw err; }, // onUncaughtError
172
- (err: any) => { throw err; }, // onCaughtError
173
- null, // onRecoverableError
252
+ '', // identifierPrefix
253
+ (err: any) => {
254
+ throw err;
255
+ }, // onUncaughtError
256
+ (err: any) => {
257
+ throw err;
258
+ }, // onCaughtError
259
+ null, // onRecoverableError
174
260
  () => {}, // onDefaultTransitionIndicator
175
261
  );
176
262
 
@@ -223,13 +309,15 @@ export function renderHook<T>(
223
309
 
224
310
  export function waitFor<T>(
225
311
  predicate: () => T | false | null | undefined,
226
- options?: { timeout?: number; interval?: number }
312
+ options?: { timeout?: number; interval?: number },
227
313
  ): T {
228
314
  const timeout = options?.timeout ?? 1000;
229
315
  const start = Date.now();
230
316
 
231
317
  for (let attempt = 0; attempt < 100; attempt++) {
232
- act(() => { drain(); });
318
+ act(() => {
319
+ drain();
320
+ });
233
321
  drain();
234
322
 
235
323
  const result = predicate();
package/src/index.ts CHANGED
@@ -28,8 +28,12 @@ function _makeCtx(store: any) {
28
28
  wrapper,
29
29
  dispatch: store.dispatch.bind(store),
30
30
  getState: store.getState.bind(store),
31
- setState(state: Record<string, any>) { store.dispatch({ type: '__SET_STATE__', payload: state }); },
32
- patchState(partial: Record<string, any>) { store.dispatch({ type: '__PATCH__', payload: partial }); },
31
+ setState(state: Record<string, any>) {
32
+ store.dispatch({ type: '__SET_STATE__', payload: state });
33
+ },
34
+ patchState(partial: Record<string, any>) {
35
+ store.dispatch({ type: '__PATCH__', payload: partial });
36
+ },
33
37
  renderHookWithReduxStore<T>(hookFn: (props?: any) => T, options?: { initialProps?: any }) {
34
38
  return ht.renderHook(hookFn, { ...options, wrapper });
35
39
  },
@@ -38,11 +42,13 @@ function _makeCtx(store: any) {
38
42
 
39
43
  export function withStore(initialState: Record<string, any> = {}) {
40
44
  const { configureStore } = require('@reduxjs/toolkit');
41
- return _makeCtx(configureStore({
42
- reducer: _withTestActions((s: any = initialState) => s),
43
- preloadedState: initialState,
44
- middleware: (gdm: any) => gdm({ serializableCheck: false, immutableCheck: false }),
45
- }));
45
+ return _makeCtx(
46
+ configureStore({
47
+ reducer: _withTestActions((s: any = initialState) => s),
48
+ preloadedState: initialState,
49
+ middleware: (gdm: any) => gdm({ serializableCheck: false, immutableCheck: false }),
50
+ }),
51
+ );
46
52
  }
47
53
 
48
54
  export function withAppReducer(
@@ -50,11 +56,13 @@ export function withAppReducer(
50
56
  preloadedState?: Record<string, any>,
51
57
  ) {
52
58
  const { configureStore } = require('@reduxjs/toolkit');
53
- return _makeCtx(configureStore({
54
- reducer: _withTestActions(reducer),
55
- preloadedState,
56
- middleware: (gdm: any) => gdm({ serializableCheck: false, immutableCheck: false }),
57
- }));
59
+ return _makeCtx(
60
+ configureStore({
61
+ reducer: _withTestActions(reducer),
62
+ preloadedState,
63
+ middleware: (gdm: any) => gdm({ serializableCheck: false, immutableCheck: false }),
64
+ }),
65
+ );
58
66
  }
59
67
 
60
68
  interface RtkQueryApi {
@@ -90,8 +98,8 @@ export function setupApiStore(
90
98
  middleware: (gdm: any) => {
91
99
  let chain = gdm({ serializableCheck: false, immutableCheck: false });
92
100
  for (const a of apis) chain = chain.concat(a.middleware);
93
- for (const mw of (options?.middleware?.concat ?? [])) chain = chain.concat(mw);
94
- for (const mw of (options?.middleware?.prepend ?? [])) chain = chain.prepend(mw);
101
+ for (const mw of options?.middleware?.concat ?? []) chain = chain.concat(mw);
102
+ for (const mw of options?.middleware?.prepend ?? []) chain = chain.prepend(mw);
95
103
  return chain;
96
104
  },
97
105
  });
package/src/mock.ts CHANGED
@@ -18,16 +18,13 @@ const mockRegistry: Record<string, Record<string, any>> = (globalThis as any).__
18
18
  (globalThis as any).__HT_mocks = mockRegistry;
19
19
 
20
20
  // Per-file mock scoping: __HT_file_mocks[filename][modulePath] = mock
21
- const fileMocks: Record<string, Record<string, any>> =
22
- (globalThis as any).__HT_file_mocks || ((globalThis as any).__HT_file_mocks = {});
21
+ const fileMocks: Record<string, Record<string, any>> = (globalThis as any).__HT_file_mocks ||
22
+ ((globalThis as any).__HT_file_mocks = {});
23
23
 
24
24
  // Track patches applied by mock() so they can be undone between files
25
25
  let mockModulePatches: { target: any; key: string; original: any }[] = [];
26
26
 
27
- export function mockModule(
28
- modulePath: string,
29
- factory: () => Record<string, any>
30
- ): void {
27
+ export function mockModule(modulePath: string, factory: () => Record<string, any>): void {
31
28
  const impl = factory();
32
29
  const value = typeof impl === 'function' ? impl : wrapWithSpies(impl);
33
30
 
@@ -55,7 +52,9 @@ export function mockModule(
55
52
  mockModulePatches.push({ target: globalMock, key, original: globalMock[key] });
56
53
  globalMock[key] = value[key];
57
54
  }
58
- } catch { /* frozen or non-configurable */ }
55
+ } catch {
56
+ /* frozen or non-configurable */
57
+ }
59
58
  }
60
59
  // Also patch 'default' export if mock provides it
61
60
  if ('default' in value && 'default' in globalMock) {
@@ -68,7 +67,9 @@ export function mockModule(
68
67
  mockModulePatches.push({ target: realDefault, key, original: realDefault[key] });
69
68
  realDefault[key] = mockDefault[key];
70
69
  }
71
- } catch { /* frozen */ }
70
+ } catch {
71
+ /* frozen */
72
+ }
72
73
  }
73
74
  }
74
75
  }
@@ -80,10 +81,13 @@ export function mockModule(
80
81
  // with the new __currentTestFile).
81
82
  export function resetMockModulePatches(): void {
82
83
  for (const { target, key, original } of mockModulePatches) {
83
- try { target[key] = original; } catch { /* best effort */ }
84
+ try {
85
+ target[key] = original;
86
+ } catch {
87
+ /* best effort */
88
+ }
84
89
  }
85
90
  mockModulePatches = [];
86
-
87
91
  }
88
92
 
89
93
  function wrapWithSpies<T extends Record<string, any>>(impl: T): T {
@@ -101,7 +105,7 @@ function wrapWithSpies<T extends Record<string, any>>(impl: T): T {
101
105
 
102
106
  export function useMock<T extends Record<string, any>>(
103
107
  moduleExports: T,
104
- implementation: Partial<T>
108
+ implementation: Partial<T>,
105
109
  ): { [K in keyof T]: T[K] extends (...args: any[]) => any ? Spy<T[K]> : T[K] } {
106
110
  const wrapped = wrapWithSpies(implementation as Record<string, any>);
107
111