@octanejs/testing-library 0.1.49 → 0.1.50
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 +22 -17
- package/package.json +2 -2
- package/src/dom-query-helpers.ts +45 -0
- package/src/fire-event.ts +26 -22
- package/src/pure.ts +91 -12
package/README.md
CHANGED
|
@@ -13,10 +13,11 @@ pnpm add @octanejs/testing-library
|
|
|
13
13
|
The split mirrors RTL's own architecture (and
|
|
14
14
|
`docs/react-library-compat-plan.md` §2): **`@testing-library/dom` is
|
|
15
15
|
framework-agnostic and reused verbatim** — every query, `screen`, `within`,
|
|
16
|
-
`waitFor`/`waitForElementToBeRemoved`, `findBy*`, `
|
|
16
|
+
`waitFor`/`waitForElementToBeRemoved`, `findBy*`, `prettyDOM`,
|
|
17
17
|
`configure` — while only react-testing-library's thin React layer is ported to
|
|
18
18
|
octane: `render`, `cleanup`, `renderHook`, the `act` re-export, and the
|
|
19
|
-
dom-testing-library config wiring
|
|
19
|
+
focus/blur event helpers and dom-testing-library config wiring
|
|
20
|
+
(`eventWrapper`/`asyncWrapper`) that makes
|
|
20
21
|
every dispatch/wait commit octane's scheduled work before your assertions run.
|
|
21
22
|
|
|
22
23
|
```ts
|
|
@@ -40,7 +41,8 @@ test('increments', () => {
|
|
|
40
41
|
`RTL_SKIP_AUTO_CLEANUP=true` or import `@octanejs/testing-library/pure`)
|
|
41
42
|
- `renderHook(callback, { initialProps, wrapper, ... }?)` → `{ result, rerender, unmount }`
|
|
42
43
|
- `act` — octane's `act`, re-exported (always async; always `await` it)
|
|
43
|
-
- `
|
|
44
|
+
- `screen`, `waitFor`, `within`, … — dom-testing-library, re-exported
|
|
45
|
+
- `fireEvent` — DOM event helpers, with focus/blur pairs for delegated handlers
|
|
44
46
|
|
|
45
47
|
## Octane-specific surface
|
|
46
48
|
|
|
@@ -60,8 +62,8 @@ remounts — exactly RTL's rerender semantics.
|
|
|
60
62
|
## Differences from react-testing-library
|
|
61
63
|
|
|
62
64
|
Octane dispatches **native, delegated DOM events — there is no synthetic event
|
|
63
|
-
layer
|
|
64
|
-
|
|
65
|
+
layer**. `fireEvent` uses DOM Testing Library with focus/blur pairs for
|
|
66
|
+
delegation. Other helpers keep native semantics:
|
|
65
67
|
|
|
66
68
|
- **`fireEvent.change` fires a native `change`; `fireEvent.input` a native
|
|
67
69
|
`input`.** In React, `onChange` handlers actually run off native `input`
|
|
@@ -85,18 +87,21 @@ React-specific remappings:
|
|
|
85
87
|
sequence and automatic checked-state transition. `fireEvent.change(checkbox)`
|
|
86
88
|
is only an explicit change dispatch; it does not model the click, activation,
|
|
87
89
|
cancellation/rollback, or full event ordering.
|
|
88
|
-
- **
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
`
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
90
|
+
- **Focus helpers emit both native focus events.** `fireEvent.focus` dispatches
|
|
91
|
+
`focus` then `focusin`; `fireEvent.blur` dispatches `blur` then `focusout`,
|
|
92
|
+
matching browser order.
|
|
93
|
+
Octane's delegated `onFocus`/`onBlur` handlers receive `focusin`/`focusout`.
|
|
94
|
+
Both events preserve the supplied options, including `relatedTarget`, except
|
|
95
|
+
that the paired `focusin`/`focusout` always bubbles. The return value is false
|
|
96
|
+
when the native `focus`/`blur` event was canceled.
|
|
97
|
+
Use `user.tab()` or native `focus()`/`blur()` to move actual focus.
|
|
98
|
+
- **No enter/leave remapping.** `fireEvent.mouseEnter` dispatches a real
|
|
99
|
+
`mouseenter`, which Octane's capture delegation delivers directly. It does
|
|
100
|
+
not also dispatch `mouseover` to drive React's synthetic event plugins.
|
|
101
|
+
- **Every dispatch commits before returning.** The DOM library's
|
|
102
|
+
`eventWrapper` runs every `fireEvent` dispatch in `flushSync` and then drains
|
|
103
|
+
effects, so the event's state updates and `useEffect` cascades are visible
|
|
104
|
+
before the helper returns.
|
|
100
105
|
- **Host elements at the root are `container.firstChild`, like RTL.**
|
|
101
106
|
`render(createElement('div', …))` goes through octane's value-position
|
|
102
107
|
renderer, which mounts a lone host element anchorless (the element
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@octanejs/testing-library",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.50",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"react": "^19.2.7",
|
|
49
49
|
"react-dom": "^19.2.7",
|
|
50
50
|
"vitest": "^4.1.10",
|
|
51
|
-
"octane": "0.2.
|
|
51
|
+
"octane": "0.2.3"
|
|
52
52
|
},
|
|
53
53
|
"scripts": {
|
|
54
54
|
"test": "vitest run"
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import * as dom from '@testing-library/dom';
|
|
2
|
+
import type { GetErrorFunction, QueryMethod, Variant, waitForOptions } from '@testing-library/dom';
|
|
3
|
+
|
|
4
|
+
// DOM Testing Library 10.4.1 exports these values but omits their declarations.
|
|
5
|
+
// Preserve the original functions and describe their pinned query contracts.
|
|
6
|
+
interface RuntimeQueryHelpers {
|
|
7
|
+
getMultipleElementsFoundError(message: string, container: Element): Error;
|
|
8
|
+
makeSingleQuery<Args extends unknown[], ElementType extends HTMLElement>(
|
|
9
|
+
query: QueryMethod<Args, ElementType[]>,
|
|
10
|
+
getMultipleError: GetErrorFunction<Args>,
|
|
11
|
+
): QueryMethod<Args, ElementType | null>;
|
|
12
|
+
makeGetAllQuery<Args extends unknown[], ElementType extends HTMLElement>(
|
|
13
|
+
query: QueryMethod<Args, ElementType[]>,
|
|
14
|
+
getMissingError: GetErrorFunction<Args>,
|
|
15
|
+
): QueryMethod<Args, ElementType[]>;
|
|
16
|
+
makeFindQuery<Text, Options, Result>(
|
|
17
|
+
query: (container: HTMLElement, text: Text, options?: Options) => Result,
|
|
18
|
+
): (
|
|
19
|
+
container: HTMLElement,
|
|
20
|
+
text: Text,
|
|
21
|
+
options?: Options,
|
|
22
|
+
waitOptions?: waitForOptions,
|
|
23
|
+
) => Promise<Awaited<Result>>;
|
|
24
|
+
wrapSingleQueryWithSuggestion<Args extends unknown[], Result extends HTMLElement | null>(
|
|
25
|
+
query: QueryMethod<Args, Result>,
|
|
26
|
+
queryName: string,
|
|
27
|
+
variant: Variant,
|
|
28
|
+
): QueryMethod<Args, Result>;
|
|
29
|
+
wrapAllByQueryWithSuggestion<Args extends unknown[], ElementType extends HTMLElement>(
|
|
30
|
+
query: QueryMethod<Args, ElementType[]>,
|
|
31
|
+
queryName: string,
|
|
32
|
+
variant: Variant,
|
|
33
|
+
): QueryMethod<Args, ElementType[]>;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const {
|
|
37
|
+
getMultipleElementsFoundError,
|
|
38
|
+
makeSingleQuery,
|
|
39
|
+
makeGetAllQuery,
|
|
40
|
+
makeFindQuery,
|
|
41
|
+
wrapSingleQueryWithSuggestion,
|
|
42
|
+
wrapAllByQueryWithSuggestion,
|
|
43
|
+
} = dom as typeof dom & RuntimeQueryHelpers;
|
|
44
|
+
|
|
45
|
+
export const queryHelpers = dom.queryHelpers as typeof dom.queryHelpers & RuntimeQueryHelpers;
|
package/src/fire-event.ts
CHANGED
|
@@ -1,22 +1,26 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
1
|
+
import { fireEvent as domFireEvent } from '@testing-library/dom';
|
|
2
|
+
|
|
3
|
+
// Keep the DOM helpers independent: callers importing @testing-library/dom
|
|
4
|
+
// directly still get one native event per helper. Octane's onFocus/onBlur
|
|
5
|
+
// delegation listens to focusin/focusout, so those two convenience helpers
|
|
6
|
+
// emit native focus/blur followed by the corresponding bubbling event.
|
|
7
|
+
export const fireEvent: typeof domFireEvent = Object.assign(
|
|
8
|
+
(...args: Parameters<typeof domFireEvent>) => domFireEvent(...args),
|
|
9
|
+
domFireEvent,
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
fireEvent.focus = (element, options) => {
|
|
13
|
+
const result = domFireEvent.focus(element, options);
|
|
14
|
+
domFireEvent.focusIn(element, { ...options, bubbles: true });
|
|
15
|
+
return result;
|
|
16
|
+
};
|
|
17
|
+
fireEvent.blur = (element, options) => {
|
|
18
|
+
const result = domFireEvent.blur(element, options);
|
|
19
|
+
domFireEvent.focusOut(element, { ...options, bubbles: true });
|
|
20
|
+
return result;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
// Other helpers retain DOM semantics: change dispatches an explicit native
|
|
24
|
+
// commit event; mouseEnter does not also dispatch mouseover. Use user-event
|
|
25
|
+
// for complete typing, click activation, and focus movement sequences.
|
|
26
|
+
// Commit/effect flushing uses the DOM library's eventWrapper (see pure.ts).
|
package/src/pure.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Strategy (docs/react-library-compat-plan.md §2): `@testing-library/dom` is
|
|
5
5
|
* framework-agnostic, so it is depended on VERBATIM and re-exported wholesale
|
|
6
|
-
* (queries, `screen`, `within`, `waitFor`, `
|
|
6
|
+
* (queries, `screen`, `within`, `waitFor`, `prettyDOM`, …). Only
|
|
7
7
|
* react-testing-library's thin React layer is ported here, onto octane:
|
|
8
8
|
*
|
|
9
9
|
* - `render` / `cleanup` / `renderHook` mount through octane's `createRoot`
|
|
@@ -43,6 +43,7 @@ import {
|
|
|
43
43
|
} from '@testing-library/dom';
|
|
44
44
|
import type { BoundFunctions, Queries } from '@testing-library/dom';
|
|
45
45
|
import { getIsOctaneActEnvironment, setOctaneActEnvironment } from './act-environment';
|
|
46
|
+
import { fireEvent as octaneFireEvent } from './fire-event';
|
|
46
47
|
|
|
47
48
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
48
49
|
// dom-testing-library config wiring (RTL pure.js does the same three hooks).
|
|
@@ -87,13 +88,8 @@ configureDTL({
|
|
|
87
88
|
setOctaneActEnvironment(previousActEnvironment);
|
|
88
89
|
}
|
|
89
90
|
},
|
|
90
|
-
// Every
|
|
91
|
-
//
|
|
92
|
-
// (maybeFlushDiscrete → flushSync), but fireEvent also dispatches event types
|
|
93
|
-
// with no discrete path — non-delegated or programmatic events whose updates
|
|
94
|
-
// would otherwise sit queued until the next microtask, i.e. after the test's
|
|
95
|
-
// assertion. flushSync commits those too, and the passive drain mirrors RTL's
|
|
96
|
-
// act(): effects scheduled by the event have run before fireEvent returns.
|
|
91
|
+
// Every fireEvent dispatch commits its updates and drains passive effects
|
|
92
|
+
// before returning, including programmatic and non-discrete events.
|
|
97
93
|
eventWrapper: (cb) => {
|
|
98
94
|
let result: unknown;
|
|
99
95
|
flushSync(() => {
|
|
@@ -407,10 +403,93 @@ export function renderHook<Result, Props = undefined>(
|
|
|
407
403
|
|
|
408
404
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
409
405
|
// Exports — everything dom-testing-library ships, plus the octane layer.
|
|
410
|
-
//
|
|
411
|
-
//
|
|
406
|
+
// fireEvent adds native focus/blur pairs for delegated focus handlers.
|
|
407
|
+
// Other convenience helpers retain DOM Testing Library's event semantics.
|
|
412
408
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
413
409
|
|
|
414
|
-
export
|
|
415
|
-
|
|
410
|
+
export const fireEvent = octaneFireEvent;
|
|
411
|
+
// Explicit value exports keep older SSR module runners from replacing our
|
|
412
|
+
// focus-aware fireEvent with the DOM helper during wildcard re-export.
|
|
413
|
+
export {
|
|
414
|
+
buildQueries,
|
|
415
|
+
configure,
|
|
416
|
+
createEvent,
|
|
417
|
+
findAllByAltText,
|
|
418
|
+
findAllByDisplayValue,
|
|
419
|
+
findAllByLabelText,
|
|
420
|
+
findAllByPlaceholderText,
|
|
421
|
+
findAllByRole,
|
|
422
|
+
findAllByTestId,
|
|
423
|
+
findAllByText,
|
|
424
|
+
findAllByTitle,
|
|
425
|
+
findByAltText,
|
|
426
|
+
findByDisplayValue,
|
|
427
|
+
findByLabelText,
|
|
428
|
+
findByPlaceholderText,
|
|
429
|
+
findByRole,
|
|
430
|
+
findByTestId,
|
|
431
|
+
findByText,
|
|
432
|
+
findByTitle,
|
|
433
|
+
getAllByAltText,
|
|
434
|
+
getAllByDisplayValue,
|
|
435
|
+
getAllByLabelText,
|
|
436
|
+
getAllByPlaceholderText,
|
|
437
|
+
getAllByRole,
|
|
438
|
+
getAllByTestId,
|
|
439
|
+
getAllByText,
|
|
440
|
+
getAllByTitle,
|
|
441
|
+
getByAltText,
|
|
442
|
+
getByDisplayValue,
|
|
443
|
+
getByLabelText,
|
|
444
|
+
getByPlaceholderText,
|
|
445
|
+
getByRole,
|
|
446
|
+
getByTestId,
|
|
447
|
+
getByText,
|
|
448
|
+
getByTitle,
|
|
449
|
+
getConfig,
|
|
450
|
+
getDefaultNormalizer,
|
|
451
|
+
getElementError,
|
|
452
|
+
getNodeText,
|
|
453
|
+
getQueriesForElement,
|
|
454
|
+
getRoles,
|
|
455
|
+
getSuggestedQuery,
|
|
456
|
+
isInaccessible,
|
|
457
|
+
logDOM,
|
|
458
|
+
logRoles,
|
|
459
|
+
prettyDOM,
|
|
460
|
+
prettyFormat,
|
|
461
|
+
queries,
|
|
462
|
+
queryAllByAltText,
|
|
463
|
+
queryAllByAttribute,
|
|
464
|
+
queryAllByDisplayValue,
|
|
465
|
+
queryAllByLabelText,
|
|
466
|
+
queryAllByPlaceholderText,
|
|
467
|
+
queryAllByRole,
|
|
468
|
+
queryAllByTestId,
|
|
469
|
+
queryAllByText,
|
|
470
|
+
queryAllByTitle,
|
|
471
|
+
queryByAltText,
|
|
472
|
+
queryByAttribute,
|
|
473
|
+
queryByDisplayValue,
|
|
474
|
+
queryByLabelText,
|
|
475
|
+
queryByPlaceholderText,
|
|
476
|
+
queryByRole,
|
|
477
|
+
queryByTestId,
|
|
478
|
+
queryByText,
|
|
479
|
+
queryByTitle,
|
|
480
|
+
screen,
|
|
481
|
+
waitFor,
|
|
482
|
+
waitForElementToBeRemoved,
|
|
483
|
+
within,
|
|
484
|
+
} from '@testing-library/dom';
|
|
485
|
+
export {
|
|
486
|
+
getMultipleElementsFoundError,
|
|
487
|
+
makeFindQuery,
|
|
488
|
+
makeGetAllQuery,
|
|
489
|
+
makeSingleQuery,
|
|
490
|
+
queryHelpers,
|
|
491
|
+
wrapAllByQueryWithSuggestion,
|
|
492
|
+
wrapSingleQueryWithSuggestion,
|
|
493
|
+
} from './dom-query-helpers';
|
|
494
|
+
export type * from '@testing-library/dom';
|
|
416
495
|
export { act } from 'octane';
|