@describe-me/react 0.4.0 → 0.5.0-next.1
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 +5 -2
- package/dist/cleanup.d.ts +6 -0
- package/dist/cleanup.js +11 -0
- package/dist/fire-event-label.d.ts +6 -0
- package/dist/fire-event-label.js +22 -0
- package/dist/fire-event.d.ts +10 -0
- package/dist/fire-event.js +35 -0
- package/dist/record-sync-action.d.ts +7 -0
- package/dist/record-sync-action.js +27 -0
- package/dist/render-testing-library.d.ts +6 -4
- package/dist/render-testing-library.js +12 -8
- package/dist/testing-library.d.ts +2 -0
- package/dist/testing-library.js +4 -1
- package/package.json +8 -6
package/README.md
CHANGED
|
@@ -20,8 +20,11 @@ pnpm add -D @describe-me/react
|
|
|
20
20
|
|
|
21
21
|
## Usage
|
|
22
22
|
|
|
23
|
-
Each entry point re-exports everything its test library offers, with
|
|
24
|
-
|
|
23
|
+
Each entry point re-exports everything its test library offers, with the
|
|
24
|
+
recording functions swapped in, so switching an existing test is one import.
|
|
25
|
+
`@describe-me/react` swaps `render`. The Testing Library entry swaps `render`,
|
|
26
|
+
`fireEvent` and `cleanup`: every `fireEvent` call records a frame, and
|
|
27
|
+
`cleanup` takes the closing frame before it unmounts.
|
|
25
28
|
|
|
26
29
|
```tsx
|
|
27
30
|
import { render, step } from '@describe-me/react'
|
package/dist/cleanup.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { cleanup as unmountAll } from '@testing-library/react';
|
|
2
|
+
import { recorder } from '@describe-me/core';
|
|
3
|
+
/**
|
|
4
|
+
* Drop-in for `cleanup` from @testing-library/react. Takes the closing frame
|
|
5
|
+
* before it unmounts, so `afterEach(cleanup)` in a test file records the last
|
|
6
|
+
* state instead of an empty page.
|
|
7
|
+
*/
|
|
8
|
+
export function cleanup() {
|
|
9
|
+
void recorder.capture('end', 'end of test', undefined, { settle: false });
|
|
10
|
+
unmountAll();
|
|
11
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The frame label of one `fireEvent` call, e.g. `click(button "Save")`,
|
|
3
|
+
* `change(text "Name", "hello")` or `scroll()` for a target that is not an
|
|
4
|
+
* element, such as `window`.
|
|
5
|
+
*/
|
|
6
|
+
export declare function fireEventLabel(eventName: string, target: unknown, init?: unknown): string;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { elementLabel } from '@describe-me/core';
|
|
2
|
+
/**
|
|
3
|
+
* The frame label of one `fireEvent` call, e.g. `click(button "Save")`,
|
|
4
|
+
* `change(text "Name", "hello")` or `scroll()` for a target that is not an
|
|
5
|
+
* element, such as `window`.
|
|
6
|
+
*/
|
|
7
|
+
export function fireEventLabel(eventName, target, init) {
|
|
8
|
+
const parts = [];
|
|
9
|
+
const element = elementLabel(target);
|
|
10
|
+
if (element) {
|
|
11
|
+
parts.push(element);
|
|
12
|
+
}
|
|
13
|
+
const { target: initTarget, key } = (init ?? {});
|
|
14
|
+
const value = initTarget?.value;
|
|
15
|
+
if (typeof value === 'string') {
|
|
16
|
+
parts.push(JSON.stringify(value));
|
|
17
|
+
}
|
|
18
|
+
if (typeof key === 'string') {
|
|
19
|
+
parts.push(JSON.stringify(key));
|
|
20
|
+
}
|
|
21
|
+
return `${eventName}(${parts.join(', ')})`;
|
|
22
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { fireEvent as baseFireEvent } from '@testing-library/react';
|
|
2
|
+
type FireEvent = typeof baseFireEvent;
|
|
3
|
+
/**
|
|
4
|
+
* Drop-in for `fireEvent` from @testing-library/react: the function and every
|
|
5
|
+
* method record one frame per call, labelled like `click(button "Save")`.
|
|
6
|
+
* The label is read before the event fires, so it names the element the test
|
|
7
|
+
* targeted, not what the event turned it into.
|
|
8
|
+
*/
|
|
9
|
+
export declare const fireEvent: FireEvent;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { fireEvent as baseFireEvent } from '@testing-library/react';
|
|
2
|
+
import { recorder } from '@describe-me/core';
|
|
3
|
+
import { fireEventLabel } from './fire-event-label.js';
|
|
4
|
+
import { recordSyncAction } from './record-sync-action.js';
|
|
5
|
+
/** Fire through Testing Library, recording a frame only while a test is recorded. */
|
|
6
|
+
function fire(label, dispatch) {
|
|
7
|
+
if (!recorder.isActive) {
|
|
8
|
+
return dispatch();
|
|
9
|
+
}
|
|
10
|
+
return recordSyncAction(label(), dispatch);
|
|
11
|
+
}
|
|
12
|
+
/** `fireEvent(element, event)` is named after the event it dispatches. */
|
|
13
|
+
function eventName(event) {
|
|
14
|
+
const type = event?.type;
|
|
15
|
+
return typeof type === 'string' ? type : 'fireEvent';
|
|
16
|
+
}
|
|
17
|
+
/** Testing Library's `fireEvent` with the function and each of its methods wrapped. */
|
|
18
|
+
function recordingFireEvent() {
|
|
19
|
+
const methods = baseFireEvent;
|
|
20
|
+
const recording = (element, event) => fire(() => fireEventLabel(eventName(event), element), () => baseFireEvent(element, event));
|
|
21
|
+
const wrappers = recording;
|
|
22
|
+
// Composite events such as `mouseEnter` call Testing Library's own object,
|
|
23
|
+
// not these wrappers, so one call stays one frame.
|
|
24
|
+
for (const name of Object.keys(baseFireEvent)) {
|
|
25
|
+
wrappers[name] = (element, init) => fire(() => fireEventLabel(name, element, init), () => methods[name](element, init));
|
|
26
|
+
}
|
|
27
|
+
return recording;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Drop-in for `fireEvent` from @testing-library/react: the function and every
|
|
31
|
+
* method record one frame per call, labelled like `click(button "Save")`.
|
|
32
|
+
* The label is read before the event fires, so it names the element the test
|
|
33
|
+
* targeted, not what the event turned it into.
|
|
34
|
+
*/
|
|
35
|
+
export const fireEvent = recordingFireEvent();
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Run one synchronous interaction and record a frame for it, but only for the
|
|
3
|
+
* outermost call and only when it returns. Testing Library wraps every event
|
|
4
|
+
* in `act()`, so the DOM is committed by then and the frame is taken without
|
|
5
|
+
* settling.
|
|
6
|
+
*/
|
|
7
|
+
export declare function recordSyncAction<T>(label: string, run: () => T): T;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { recorder } from '@describe-me/core';
|
|
2
|
+
/**
|
|
3
|
+
* Nesting depth of synchronous interactions currently running. A handler
|
|
4
|
+
* that fires another event from inside the first would otherwise record two
|
|
5
|
+
* frames for one call.
|
|
6
|
+
*/
|
|
7
|
+
let depth = 0;
|
|
8
|
+
/**
|
|
9
|
+
* Run one synchronous interaction and record a frame for it, but only for the
|
|
10
|
+
* outermost call and only when it returns. Testing Library wraps every event
|
|
11
|
+
* in `act()`, so the DOM is committed by then and the frame is taken without
|
|
12
|
+
* settling.
|
|
13
|
+
*/
|
|
14
|
+
export function recordSyncAction(label, run) {
|
|
15
|
+
depth++;
|
|
16
|
+
let result;
|
|
17
|
+
try {
|
|
18
|
+
result = run();
|
|
19
|
+
}
|
|
20
|
+
finally {
|
|
21
|
+
depth--;
|
|
22
|
+
}
|
|
23
|
+
if (depth === 0) {
|
|
24
|
+
void recorder.capture('action', label, undefined, { settle: false });
|
|
25
|
+
}
|
|
26
|
+
return result;
|
|
27
|
+
}
|
|
@@ -4,16 +4,18 @@ type RenderOptions = Parameters<typeof baseRender>[1];
|
|
|
4
4
|
type RenderResult = ReturnType<typeof baseRender>;
|
|
5
5
|
/**
|
|
6
6
|
* Drop-in for `render` from @testing-library/react (jsdom and other DOM
|
|
7
|
-
* environments). Records a frame after mount and after every `rerender
|
|
7
|
+
* environments). Records a frame after mount and after every `rerender`
|
|
8
|
+
* while a test is being recorded.
|
|
8
9
|
*
|
|
9
10
|
* Synchronous like the original, so `const { asFragment } = render(...)`
|
|
10
11
|
* keeps working. Testing Library wraps mounting in `act()`, which means the DOM
|
|
11
12
|
* is committed when it returns, so frames are captured without settling: the
|
|
12
13
|
* snapshot is taken right here, before the test's next line can change the DOM.
|
|
13
14
|
*
|
|
14
|
-
*
|
|
15
|
-
* Library's own auto-cleanup, which would run before the
|
|
16
|
-
* taken. The plugin switches auto-cleanup off with
|
|
15
|
+
* Importing this module hands unmounting to the recorder's teardown rather
|
|
16
|
+
* than to Testing Library's own auto-cleanup, which would run before the
|
|
17
|
+
* closing frame is taken. The plugin switches auto-cleanup off with
|
|
18
|
+
* `RTL_SKIP_AUTO_CLEANUP`.
|
|
17
19
|
*/
|
|
18
20
|
export declare function render(ui: ReactElement, options?: RenderOptions): RenderResult;
|
|
19
21
|
export {};
|
|
@@ -1,25 +1,29 @@
|
|
|
1
|
-
import { cleanup, render as baseRender } from '@testing-library/react';
|
|
1
|
+
import { cleanup as unmountAll, render as baseRender } from '@testing-library/react';
|
|
2
2
|
import { isValidElement } from 'react';
|
|
3
3
|
import { recorder } from '@describe-me/core';
|
|
4
4
|
import { describeElement } from './describe-element.js';
|
|
5
5
|
import { labelFor } from './label-for.js';
|
|
6
|
+
// On import rather than in `render()`, so a file that only calls `renderHook`
|
|
7
|
+
// is unmounted after each test too.
|
|
8
|
+
recorder.onTeardown(unmountAll);
|
|
6
9
|
/**
|
|
7
10
|
* Drop-in for `render` from @testing-library/react (jsdom and other DOM
|
|
8
|
-
* environments). Records a frame after mount and after every `rerender
|
|
11
|
+
* environments). Records a frame after mount and after every `rerender`
|
|
12
|
+
* while a test is being recorded.
|
|
9
13
|
*
|
|
10
14
|
* Synchronous like the original, so `const { asFragment } = render(...)`
|
|
11
15
|
* keeps working. Testing Library wraps mounting in `act()`, which means the DOM
|
|
12
16
|
* is committed when it returns, so frames are captured without settling: the
|
|
13
17
|
* snapshot is taken right here, before the test's next line can change the DOM.
|
|
14
18
|
*
|
|
15
|
-
*
|
|
16
|
-
* Library's own auto-cleanup, which would run before the
|
|
17
|
-
* taken. The plugin switches auto-cleanup off with
|
|
19
|
+
* Importing this module hands unmounting to the recorder's teardown rather
|
|
20
|
+
* than to Testing Library's own auto-cleanup, which would run before the
|
|
21
|
+
* closing frame is taken. The plugin switches auto-cleanup off with
|
|
22
|
+
* `RTL_SKIP_AUTO_CLEANUP`.
|
|
18
23
|
*/
|
|
19
24
|
export function render(ui, options) {
|
|
20
|
-
recorder.onTeardown(cleanup);
|
|
21
25
|
const result = baseRender(ui, options);
|
|
22
|
-
if (isValidElement(ui)) {
|
|
26
|
+
if (recorder.isActive && isValidElement(ui)) {
|
|
23
27
|
const info = describeElement(ui);
|
|
24
28
|
recorder.setComponent(info);
|
|
25
29
|
void recorder.capture('render', labelFor(info), { props: info.props }, { settle: false });
|
|
@@ -27,7 +31,7 @@ export function render(ui, options) {
|
|
|
27
31
|
const originalRerender = result.rerender;
|
|
28
32
|
result.rerender = (next) => {
|
|
29
33
|
originalRerender(next);
|
|
30
|
-
if (isValidElement(next)) {
|
|
34
|
+
if (recorder.isActive && isValidElement(next)) {
|
|
31
35
|
const info = describeElement(next);
|
|
32
36
|
void recorder.capture('render', `rerender ${labelFor(info)}`, { props: info.props }, { settle: false });
|
|
33
37
|
}
|
package/dist/testing-library.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
// Everything @testing-library/react offers, with `render`
|
|
1
|
+
// Everything @testing-library/react offers, with `render`, `fireEvent` and
|
|
2
|
+
// `cleanup` swapped for recording ones.
|
|
2
3
|
export * from '@testing-library/react';
|
|
3
4
|
export { step } from '@describe-me/core';
|
|
5
|
+
export { cleanup } from './cleanup.js';
|
|
6
|
+
export { fireEvent } from './fire-event.js';
|
|
4
7
|
export { render } from './render-testing-library.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@describe-me/react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0-next.1",
|
|
4
4
|
"description": "React adapter for describe-me: drop-in render functions that record frames",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"vitest",
|
|
@@ -42,14 +42,16 @@
|
|
|
42
42
|
"README.md",
|
|
43
43
|
"LICENSE"
|
|
44
44
|
],
|
|
45
|
-
"sideEffects":
|
|
45
|
+
"sideEffects": [
|
|
46
|
+
"./dist/render-testing-library.js"
|
|
47
|
+
],
|
|
46
48
|
"dependencies": {
|
|
47
|
-
"@describe-me/core": "^0.
|
|
49
|
+
"@describe-me/core": "^0.5.0-next.1"
|
|
48
50
|
},
|
|
49
51
|
"peerDependencies": {
|
|
50
|
-
"@testing-library/react": "
|
|
51
|
-
"react": "
|
|
52
|
-
"vitest-browser-react": "
|
|
52
|
+
"@testing-library/react": "^16.0.0",
|
|
53
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
54
|
+
"vitest-browser-react": "^2.0.0"
|
|
53
55
|
},
|
|
54
56
|
"peerDependenciesMeta": {
|
|
55
57
|
"@testing-library/react": {
|