Skip to content

Commit e4df47e

Browse files
committed
start organizing
1 parent 0f625a4 commit e4df47e

20 files changed

+3065
-328
lines changed

package.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,25 @@
1010
"exports": {
1111
".": "./src/index.js"
1212
},
13+
"dependencies": {
14+
"@testing-library/dom": "^10.4.0",
15+
"jsdom": "^25.0.1"
16+
},
17+
"devDependencies": {
18+
"@jest/globals": "^29.7.0",
19+
"@tsconfig/recommended": "^1.0.7",
20+
"@types/jsdom": "^21.1.7",
21+
"@types/react": "^18",
22+
"@types/react-dom": "^18",
23+
"expect": "^29.7.0",
24+
"prettier": "^3.3.3",
25+
"react": "^18.3.1",
26+
"react-dom": "^18.3.1",
27+
"typescript": "^5.6.2"
28+
},
1329
"peerDependencies": {
30+
"@jest/globals": "*",
31+
"expect": "*",
1432
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0",
1533
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0"
1634
},

src/testing/matchers/ProfiledComponent.ts renamed to src/jest/ProfiledComponent.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import type { MatcherFunction } from "expect";
2-
import { WaitForRenderTimeoutError } from "../internal/index.js";
2+
import { WaitForRenderTimeoutError } from "../profile/index.js";
33
import type {
44
NextRenderOptions,
55
Profiler,
66
ProfiledComponent,
77
ProfiledHook,
8-
} from "../internal/index.js";
8+
} from "../profile/index.js";
99

1010
export const toRerender: MatcherFunction<[options?: NextRenderOptions]> =
1111
async function (actual, options) {

src/jest/index.d.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import {
2+
NextRenderOptions,
3+
Profiler,
4+
ProfiledComponent,
5+
ProfiledHook,
6+
} from "../profile/index.js";
7+
8+
interface ApolloCustomMatchers<R = void, T = {}> {
9+
toRerender: T extends
10+
| Profiler<any>
11+
| ProfiledComponent<any, any>
12+
| ProfiledHook<any, any>
13+
? (options?: NextRenderOptions) => Promise<R>
14+
: { error: "matcher needs to be called on a ProfiledComponent instance" };
15+
16+
toRenderExactlyTimes: T extends
17+
| Profiler<any>
18+
| ProfiledComponent<any, any>
19+
| ProfiledHook<any, any>
20+
? (count: number, options?: NextRenderOptions) => Promise<R>
21+
: { error: "matcher needs to be called on a ProfiledComponent instance" };
22+
}
23+
24+
declare global {
25+
namespace jest {
26+
interface Matchers<R = void, T = {}> extends ApolloCustomMatchers<R, T> {}
27+
}
28+
}

src/jest/index.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { expect } from "@jest/globals";
2+
import { toRerender, toRenderExactlyTimes } from "./ProfiledComponent.js";
3+
4+
expect.extend({
5+
toRerender,
6+
toRenderExactlyTimes,
7+
});

src/testing/internal/profile/Render.tsx renamed to src/profile/Render.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,12 @@ export interface BaseRender {
3030
type Screen = typeof screen;
3131
/** @internal */
3232
export type SyncScreen = {
33-
[K in keyof Screen]: K extends `find${string}` ?
34-
{
35-
/** @deprecated A snapshot is static, so avoid async queries! */
36-
(...args: Parameters<Screen[K]>): ReturnType<Screen[K]>;
37-
}
38-
: Screen[K];
33+
[K in keyof Screen]: K extends `find${string}`
34+
? {
35+
/** @deprecated A snapshot is static, so avoid async queries! */
36+
(...args: Parameters<Screen[K]>): ReturnType<Screen[K]>;
37+
}
38+
: Screen[K];
3939
};
4040

4141
/** @internal */
@@ -102,7 +102,7 @@ export class RenderInstance<Snapshot> implements Render<Snapshot> {
102102

103103
const virtualConsole = new VirtualConsole();
104104
const stackTrace = captureStackTrace("RenderInstance.get");
105-
virtualConsole.on("jsdomError", (error) => {
105+
virtualConsole.on("jsdomError", (error: any) => {
106106
throw applyStackTrace(error, stackTrace);
107107
});
108108

File renamed without changes.

src/profile/disableActWarnings.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { withCleanup } from "./withCleanup.js";
2+
3+
/**
4+
* Temporarily disable act warnings.
5+
*
6+
* https://github.com/reactwg/react-18/discussions/102
7+
*/
8+
export function disableActWarnings() {
9+
const prev = { prevActEnv: (globalThis as any).IS_REACT_ACT_ENVIRONMENT };
10+
(globalThis as any).IS_REACT_ACT_ENVIRONMENT = false;
11+
12+
return withCleanup(prev, ({ prevActEnv }) => {
13+
(globalThis as any).IS_REACT_ACT_ENVIRONMENT = prevActEnv;
14+
});
15+
}
File renamed without changes.

src/testing/internal/profile/profile.tsx renamed to src/profile/profile.tsx

Lines changed: 25 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,11 @@
11
import * as React from "react";
22

3-
import { TextEncoder, TextDecoder } from "util";
4-
5-
global.TextEncoder ??= TextEncoder;
6-
// @ts-ignore
7-
global.TextDecoder ??= TextDecoder;
83
import type { Render, BaseRender } from "./Render.js";
94
import { RenderInstance } from "./Render.js";
105
import { applyStackTrace, captureStackTrace } from "./traces.js";
116
import type { ProfilerContextValue } from "./context.js";
127
import { ProfilerContextProvider, useProfilerContext } from "./context.js";
13-
import { disableActWarnings } from "../disposables/index.js";
8+
import { disableActWarnings } from "./disableActWarnings.js";
149

1510
type ValidSnapshot = void | (object & { /* not a function */ call?: never });
1611

@@ -163,10 +158,10 @@ export function createProfiler<Snapshot extends ValidSnapshot = void>({
163158
);
164159
}
165160
snapshotRef.current = snap(
166-
typeof snapshotRef.current === "object" ?
167-
// "cheap best effort" to prevent accidental mutation of the last snapshot
168-
{ ...snapshotRef.current! }
169-
: snapshotRef.current!
161+
typeof snapshotRef.current === "object"
162+
? // "cheap best effort" to prevent accidental mutation of the last snapshot
163+
{ ...snapshotRef.current! }
164+
: snapshotRef.current!
170165
);
171166
} else {
172167
snapshotRef.current = snap;
@@ -176,9 +171,9 @@ export function createProfiler<Snapshot extends ValidSnapshot = void>({
176171
const mergeSnapshot: MergeSnapshot<Snapshot> = (partialSnapshot) => {
177172
replaceSnapshot((snapshot) => ({
178173
...snapshot,
179-
...(typeof partialSnapshot === "function" ?
180-
partialSnapshot(snapshot)
181-
: partialSnapshot),
174+
...(typeof partialSnapshot === "function"
175+
? partialSnapshot(snapshot)
176+
: partialSnapshot),
182177
}));
183178
};
184179

@@ -225,8 +220,9 @@ export function createProfiler<Snapshot extends ValidSnapshot = void>({
225220
});
226221

227222
const snapshot = snapshotRef.current as Snapshot;
228-
const domSnapshot =
229-
snapshotDOM ? window.document.body.innerHTML : undefined;
223+
const domSnapshot = snapshotDOM
224+
? window.document.body.innerHTML
225+
: undefined;
230226
const render = new RenderInstance(
231227
baseRender,
232228
snapshot,
@@ -370,21 +366,22 @@ export class WaitForRenderTimeoutError extends Error {
370366
type StringReplaceRenderWithSnapshot<T extends string> =
371367
T extends `${infer Pre}Render${infer Post}` ? `${Pre}Snapshot${Post}` : T;
372368

373-
type ResultReplaceRenderWithSnapshot<T> =
374-
T extends (...args: infer Args) => Render<infer Snapshot> ?
375-
(...args: Args) => Snapshot
376-
: T extends (...args: infer Args) => Promise<Render<infer Snapshot>> ?
377-
(...args: Args) => Promise<Snapshot>
378-
: T;
369+
type ResultReplaceRenderWithSnapshot<T> = T extends (
370+
...args: infer Args
371+
) => Render<infer Snapshot>
372+
? (...args: Args) => Snapshot
373+
: T extends (...args: infer Args) => Promise<Render<infer Snapshot>>
374+
? (...args: Args) => Promise<Snapshot>
375+
: T;
379376

380377
type ProfiledHookFields<ReturnValue> =
381-
ProfiledComponentFields<ReturnValue> extends infer PC ?
382-
{
383-
[K in keyof PC as StringReplaceRenderWithSnapshot<
384-
K & string
385-
>]: ResultReplaceRenderWithSnapshot<PC[K]>;
386-
}
387-
: never;
378+
ProfiledComponentFields<ReturnValue> extends infer PC
379+
? {
380+
[K in keyof PC as StringReplaceRenderWithSnapshot<
381+
K & string
382+
>]: ResultReplaceRenderWithSnapshot<PC[K]>;
383+
}
384+
: never;
388385

389386
/** @internal */
390387
export interface ProfiledHook<Props, ReturnValue>

src/testing/internal/profile/traces.ts renamed to src/profile/traces.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,17 @@ export function captureStackTrace(callingFunction?: string | (() => {})) {
1111
}
1212

1313
const callerName =
14-
typeof callingFunction === "string" ? callingFunction
15-
: callingFunction ? callingFunction.name
16-
: undefined;
14+
typeof callingFunction === "string"
15+
? callingFunction
16+
: callingFunction
17+
? callingFunction.name
18+
: undefined;
1719

1820
if (callerName && stack.includes(callerName)) {
1921
const lines = stack.split("\n");
2022

2123
stack = lines
2224
.slice(
23-
// @ts-expect-error this is too old of a TS target, but node has it
2425
lines.findLastIndex((line: string) => line.includes(callerName)) + 1
2526
)
2627
.join("\n");

src/profile/withCleanup.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/** @internal */
2+
export function withCleanup<T extends object>(
3+
item: T,
4+
cleanup: (item: T) => void
5+
): T & Disposable {
6+
return {
7+
...item,
8+
[Symbol.dispose]() {
9+
cleanup(item);
10+
// if `item` already has a cleanup function, we also need to call the original cleanup function
11+
// (e.g. if something is wrapped in `withCleanup` twice)
12+
if (Symbol.dispose in item) {
13+
(item as Disposable)[Symbol.dispose]();
14+
}
15+
},
16+
};
17+
}

src/testing/internal/index.ts

Lines changed: 0 additions & 22 deletions
This file was deleted.

src/testing/matchers/index.d.ts

Lines changed: 0 additions & 59 deletions
This file was deleted.

src/testing/matchers/index.ts

Lines changed: 0 additions & 15 deletions
This file was deleted.

src/testing/matchers/toBeDisposed.ts

Lines changed: 0 additions & 26 deletions
This file was deleted.

0 commit comments

Comments
 (0)