react-jitter 0.4.0 → 0.5.0

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/README.md CHANGED
@@ -256,6 +256,33 @@ Here is an example of the `change` object when `includeArguments` is enabled:
256
256
 
257
257
  In this example, the `arguments` field shows that the `UserContext` was used, and the `changedKeys` field shows that the `user` property has changed.
258
258
 
259
+ ### Detecting Unstable Hooks in Unit Tests
260
+
261
+ React Jitter can also be a powerful tool for improving code quality within your unit tests.
262
+
263
+ You can leverage this to write tests that fail if a hook becomes unstable, catching performance regressions early in the a testing setup where you might initialize React Jitter in a global setup file, you can easily override the `onHookChange` handler on a per-test basis.
264
+
265
+ ```javascript
266
+ // Example of a Vitest/Jest test
267
+ it('should not have unstable hooks', () => {
268
+ const unstableChanges = [];
269
+ // Initialize React Jitter in a global setup file (e.g., setupTests.js)
270
+ // Then, override the onHookChange handler for specific tests.
271
+ window.reactJitter.onHookChange = (change) => {
272
+ // You can ignore mocked hooks or handle them specifically
273
+ if (change.unstable && !change.isMocked) {
274
+ unstableChanges.push(change);
275
+ }
276
+ };
277
+
278
+ render(<MyComponent />);
279
+
280
+ // Assert that no unstable values were detected during the render
281
+ expect(unstableChanges).toHaveLength(0);
282
+ });
283
+ ```
284
+
285
+ The `onHookChange` callback's `change` object includes an `isMocked` boolean property. This is automatically set to `true` if React Jitter detects that the hook has been mocked (e.g., using `jest.fn()` or `vi.fn()`). This allows you to reliably identify and assert against unstable values in your test environment.
259
286
 
260
287
  ## How It Works
261
288
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-jitter",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "A developer tool for debugging React performance issues caused by hook changes and component re-renders.",
5
5
  "exports": {
6
6
  ".": {
@@ -40,6 +40,7 @@ type HookEndEvent = {
40
40
  line: number;
41
41
  offset: number;
42
42
  arguments?: string[];
43
+ isMocked?: boolean;
43
44
  };
44
45
  type HookAddress = Pick<HookEndEvent, 'hook' | 'file' | 'line' | 'offset' | 'arguments'>;
45
46
  type ReactJitterOptions = {
@@ -82,6 +83,7 @@ declare function useJitterScope(scope: Scope): {
82
83
  s: (id: string) => void;
83
84
  e: (hookResult: unknown, hookEndEvent: HookEndEvent) => unknown;
84
85
  re: <T>(renderResult: T) => T;
86
+ m: (value: unknown) => boolean;
85
87
  };
86
88
  declare function reactJitter(options: ReactJitterOptions): void;
87
89
 
@@ -40,6 +40,7 @@ type HookEndEvent = {
40
40
  line: number;
41
41
  offset: number;
42
42
  arguments?: string[];
43
+ isMocked?: boolean;
43
44
  };
44
45
  type HookAddress = Pick<HookEndEvent, 'hook' | 'file' | 'line' | 'offset' | 'arguments'>;
45
46
  type ReactJitterOptions = {
@@ -82,6 +83,7 @@ declare function useJitterScope(scope: Scope): {
82
83
  s: (id: string) => void;
83
84
  e: (hookResult: unknown, hookEndEvent: HookEndEvent) => unknown;
84
85
  re: <T>(renderResult: T) => T;
86
+ m: (value: unknown) => boolean;
85
87
  };
86
88
  declare function reactJitter(options: ReactJitterOptions): void;
87
89
 
@@ -592,6 +592,9 @@ function useJitterScope(scope) {
592
592
  if (hookEndEvent.arguments) {
593
593
  hookCall.arguments = hookEndEvent.arguments;
594
594
  }
595
+ if (hookEndEvent.isMocked) {
596
+ hookCall.isMocked = hookEndEvent.isMocked;
597
+ }
595
598
  scopes[scopeId].hookChanges.push(hookCall);
596
599
  callOnHookChange(hookCall);
597
600
  }
@@ -603,6 +606,12 @@ function useJitterScope(scope) {
603
606
  re: (renderResult) => {
604
607
  callOnRender(scopes[scopeId]);
605
608
  return renderResult;
609
+ },
610
+ m: (value) => {
611
+ if (typeof value !== "function") {
612
+ return false;
613
+ }
614
+ return "mockImplementation" in value || "mockReturnValue" in value;
606
615
  }
607
616
  };
608
617
  }
@@ -557,6 +557,9 @@ function useJitterScope(scope) {
557
557
  if (hookEndEvent.arguments) {
558
558
  hookCall.arguments = hookEndEvent.arguments;
559
559
  }
560
+ if (hookEndEvent.isMocked) {
561
+ hookCall.isMocked = hookEndEvent.isMocked;
562
+ }
560
563
  scopes[scopeId].hookChanges.push(hookCall);
561
564
  callOnHookChange(hookCall);
562
565
  }
@@ -568,6 +571,12 @@ function useJitterScope(scope) {
568
571
  re: (renderResult) => {
569
572
  callOnRender(scopes[scopeId]);
570
573
  return renderResult;
574
+ },
575
+ m: (value) => {
576
+ if (typeof value !== "function") {
577
+ return false;
578
+ }
579
+ return "mockImplementation" in value || "mockReturnValue" in value;
571
580
  }
572
581
  };
573
582
  }