@atomic-testing/react-legacy 0.88.0 → 0.89.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
@@ -89,3 +89,10 @@ package for a dedicated example.
89
89
 
90
90
  For more in‑depth information, visit
91
91
  [https://atomic-testing.dev](https://atomic-testing.dev).
92
+
93
+ ## Public API & stability
94
+
95
+ The stable surface of this package is its `.` barrel exports, frozen under
96
+ SemVer and machine-checked by the committed [API Extractor](https://api-extractor.com/)
97
+ report at [`etc/react-legacy.api.md`](etc/react-legacy.api.md). Exports tagged `@internal` are
98
+ not part of that guarantee. See the [1.0 API freeze & evolution policy](../../agent-docs/adr/006-1.0-api-freeze-and-evolution.md).
package/dist/index.cjs CHANGED
@@ -22,10 +22,156 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
22
22
  }) : target, mod));
23
23
  //#endregion
24
24
  let _atomic_testing_core = require("@atomic-testing/core");
25
- let _atomic_testing_react_core = require("@atomic-testing/react-core");
26
25
  let react_dom = require("react-dom");
27
26
  react_dom = __toESM(react_dom);
28
27
  let react_dom_test_utils_js = require("react-dom/test-utils.js");
28
+ let _atomic_testing_dom_core = require("@atomic-testing/dom-core");
29
+ //#region src/LegacyReactInteractor.ts
30
+ /**
31
+ * React 16/17 counterpart of `@atomic-testing/react-core`'s `ReactInteractor`.
32
+ *
33
+ * It is a deliberate parallel implementation rather than a subclass of the
34
+ * react-core interactor: react-core wraps actions with `act` from
35
+ * `@testing-library/react@16`, whose peer range is React 18/19. Reusing it would
36
+ * drag that dependency into react-legacy and make the package impossible to
37
+ * install on React 16/17 (the very versions this adapter exists to support). By
38
+ * extending `DOMInteractor` directly and wrapping with `act` from
39
+ * `react-dom/test-utils` (the React ≤17 act), react-legacy stays on a coherent
40
+ * React-16/17 dependency graph.
41
+ */
42
+ var LegacyReactInteractor = class LegacyReactInteractor extends _atomic_testing_dom_core.DOMInteractor {
43
+ async enterText(locator, text, option) {
44
+ await (0, react_dom_test_utils_js.act)(async () => {
45
+ await super.enterText(locator, text, option);
46
+ });
47
+ }
48
+ async setRangeValue(locator, value) {
49
+ await (0, react_dom_test_utils_js.act)(async () => {
50
+ await super.setRangeValue(locator, value);
51
+ });
52
+ }
53
+ async click(locator, option) {
54
+ await (0, react_dom_test_utils_js.act)(async () => {
55
+ await super.click(locator, option);
56
+ });
57
+ }
58
+ async hover(locator, option) {
59
+ await (0, react_dom_test_utils_js.act)(async () => {
60
+ await super.hover(locator, option);
61
+ });
62
+ }
63
+ async mouseMove(locator, option) {
64
+ await (0, react_dom_test_utils_js.act)(async () => {
65
+ await super.mouseMove(locator, option);
66
+ });
67
+ }
68
+ async mouseDown(locator, option) {
69
+ await (0, react_dom_test_utils_js.act)(async () => {
70
+ await super.mouseDown(locator, option);
71
+ });
72
+ }
73
+ async mouseUp(locator, option) {
74
+ await (0, react_dom_test_utils_js.act)(async () => {
75
+ await super.mouseUp(locator, option);
76
+ });
77
+ }
78
+ async mouseOver(locator, option) {
79
+ await (0, react_dom_test_utils_js.act)(async () => {
80
+ await super.mouseOver(locator, option);
81
+ });
82
+ }
83
+ async mouseOut(locator, option) {
84
+ await (0, react_dom_test_utils_js.act)(async () => {
85
+ await super.mouseOut(locator, option);
86
+ });
87
+ }
88
+ async mouseEnter(locator, option) {
89
+ await (0, react_dom_test_utils_js.act)(async () => {
90
+ await super.mouseEnter(locator, option);
91
+ });
92
+ }
93
+ async mouseLeave(locator, option) {
94
+ await (0, react_dom_test_utils_js.act)(async () => {
95
+ await super.mouseLeave(locator, option);
96
+ });
97
+ }
98
+ async focus(locator, option) {
99
+ await (0, react_dom_test_utils_js.act)(async () => {
100
+ await super.focus(locator, option);
101
+ });
102
+ }
103
+ async blur(locator, option) {
104
+ await (0, react_dom_test_utils_js.act)(async () => {
105
+ await super.blur(locator, option);
106
+ });
107
+ }
108
+ async pressKey(locator, key, option) {
109
+ await (0, react_dom_test_utils_js.act)(async () => {
110
+ await super.pressKey(locator, key, option);
111
+ });
112
+ }
113
+ async contextMenu(locator) {
114
+ await (0, react_dom_test_utils_js.act)(async () => {
115
+ await super.contextMenu(locator);
116
+ });
117
+ }
118
+ async activate(locator) {
119
+ await (0, react_dom_test_utils_js.act)(async () => {
120
+ await super.activate(locator);
121
+ });
122
+ }
123
+ async selectOptionValue(locator, values) {
124
+ await (0, react_dom_test_utils_js.act)(async () => {
125
+ await super.selectOptionValue(locator, values);
126
+ });
127
+ }
128
+ async setInputFiles(locator, files) {
129
+ await (0, react_dom_test_utils_js.act)(async () => {
130
+ await super.setInputFiles(locator, files);
131
+ });
132
+ }
133
+ async scrollIntoView(locator) {
134
+ await (0, react_dom_test_utils_js.act)(async () => {
135
+ await super.scrollIntoView(locator);
136
+ });
137
+ }
138
+ async scrollBy(locator, delta) {
139
+ await (0, react_dom_test_utils_js.act)(async () => {
140
+ await super.scrollBy(locator, delta);
141
+ });
142
+ }
143
+ async dragTo(source, target) {
144
+ await (0, react_dom_test_utils_js.act)(async () => {
145
+ await super.dragTo(source, target);
146
+ });
147
+ }
148
+ async drag(locator, delta) {
149
+ await (0, react_dom_test_utils_js.act)(async () => {
150
+ await super.drag(locator, delta);
151
+ });
152
+ }
153
+ async wait(ms) {
154
+ await (0, react_dom_test_utils_js.act)(async () => {
155
+ await super.wait(ms);
156
+ });
157
+ }
158
+ async waitUntilComponentState(locator, option = _atomic_testing_core.defaultWaitForOption) {
159
+ await (0, react_dom_test_utils_js.act)(async () => {
160
+ await super.waitUntilComponentState(locator, option);
161
+ });
162
+ }
163
+ async waitUntil(option) {
164
+ let result;
165
+ await (0, react_dom_test_utils_js.act)(async () => {
166
+ result = await super.waitUntil(option);
167
+ });
168
+ return result;
169
+ }
170
+ clone() {
171
+ return new LegacyReactInteractor(this.rootEl);
172
+ }
173
+ };
174
+ //#endregion
29
175
  //#region src/createTestEngine.ts
30
176
  let _rootId = 0;
31
177
  function getNextRootElementId() {
@@ -54,7 +200,7 @@ function createTestEngine(node, partDefinitions, option) {
54
200
  rootEl.removeChild(container);
55
201
  return Promise.resolve();
56
202
  };
57
- return new _atomic_testing_core.TestEngine((0, _atomic_testing_core.byAttribute)(rootElementAttributeName, rootId), new _atomic_testing_react_core.ReactInteractor(), { parts: partDefinitions }, cleanup);
203
+ return new _atomic_testing_core.TestEngine((0, _atomic_testing_core.byAttribute)(rootElementAttributeName, rootId), new LegacyReactInteractor(), { parts: partDefinitions }, cleanup);
58
204
  }
59
205
  /**
60
206
  * Create test engine for React 17 or before, for React 18 or later, use @atomic-testing/react-18 or @atomic-testing/react-19
@@ -72,7 +218,7 @@ function createRenderedTestEngine(rootElement, partDefinitions, _option) {
72
218
  rootElement.removeAttribute(rootElementAttributeName);
73
219
  return Promise.resolve();
74
220
  };
75
- return new _atomic_testing_core.TestEngine((0, _atomic_testing_core.byAttribute)(rootElementAttributeName, rootId), new _atomic_testing_react_core.ReactInteractor(), { parts: partDefinitions }, cleanup);
221
+ return new _atomic_testing_core.TestEngine((0, _atomic_testing_core.byAttribute)(rootElementAttributeName, rootId), new LegacyReactInteractor(), { parts: partDefinitions }, cleanup);
76
222
  }
77
223
  //#endregion
78
224
  exports.createRenderedTestEngine = createRenderedTestEngine;
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["TestEngine","ReactInteractor"],"sources":["../src/createTestEngine.ts"],"sourcesContent":["import { byAttribute, ScenePart, TestEngine } from '@atomic-testing/core';\nimport { ReactInteractor } from '@atomic-testing/react-core';\nimport { ReactElement } from 'react';\nimport ReactDOM from 'react-dom';\nimport { act } from 'react-dom/test-utils';\n\nimport { IReactTestEngineOption } from './types';\n\nlet _rootId = 0;\nfunction getNextRootElementId() {\n return `${_rootId++}`;\n}\n\nconst rootElementAttributeName = 'data-atomic-testing-react-legacy';\n\n/**\n * Create test engine for React 17 or before, for React 18 or later, use @atomic-testing/react-18 or @atomic-testing/react-19\n * This function takes a react node and render it into a container element. For rendered\n * components, use createRenderedLegacyTestEngine\n * @param node The React node to render\n * @param partDefinitions The scene part definitions\n * @param option\n * @returns The test engine\n */\nexport function createTestEngine<T extends ScenePart>(\n node: ReactElement,\n partDefinitions: T,\n option?: Readonly<Partial<IReactTestEngineOption>>\n): TestEngine<T> {\n const rootEl = option?.rootElement ?? document.body;\n const container = rootEl.appendChild(document.createElement('div'));\n const rootId = getNextRootElementId();\n container.setAttribute(rootElementAttributeName, rootId);\n\n act(() => {\n ReactDOM.render(node, container);\n });\n const cleanup = () => {\n ReactDOM.unmountComponentAtNode(container);\n rootEl.removeChild(container);\n return Promise.resolve();\n };\n\n const engine = new TestEngine(\n byAttribute(rootElementAttributeName, rootId),\n new ReactInteractor(),\n {\n parts: partDefinitions,\n },\n cleanup\n );\n\n return engine;\n}\n\n/**\n * Create test engine for React 17 or before, for React 18 or later, use @atomic-testing/react-18 or @atomic-testing/react-19\n * This function takes an html element purportedly rendered by React and create a test engine for it, it\n * can be useful in environment such as Storybook where Storybook renders the component and the test\n * @param rootElement The React node to render\n * @param partDefinitions The scene part definitions\n * @param option\n * @returns The test engine\n */\nexport function createRenderedTestEngine<T extends ScenePart>(\n rootElement: HTMLElement,\n partDefinitions: T,\n _option?: Readonly<Partial<IReactTestEngineOption>>\n): TestEngine<T> {\n const rootId = getNextRootElementId();\n rootElement.setAttribute(rootElementAttributeName, rootId);\n\n const cleanup = () => {\n rootElement.removeAttribute(rootElementAttributeName);\n return Promise.resolve();\n };\n\n const engine = new TestEngine(\n byAttribute(rootElementAttributeName, rootId),\n new ReactInteractor(),\n {\n parts: partDefinitions,\n },\n cleanup\n );\n\n return engine;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAQA,IAAI,UAAU;AACd,SAAS,uBAAuB;CAC9B,OAAO,GAAG;AACZ;AAEA,MAAM,2BAA2B;;;;;;;;;;AAWjC,SAAgB,iBACd,MACA,iBACA,QACe;CACf,MAAM,SAAS,QAAQ,eAAe,SAAS;CAC/C,MAAM,YAAY,OAAO,YAAY,SAAS,cAAc,KAAK,CAAC;CAClE,MAAM,SAAS,qBAAqB;CACpC,UAAU,aAAa,0BAA0B,MAAM;CAEvD,CAAA,GAAA,wBAAA,IAAA,OAAU;EACR,UAAA,QAAS,OAAO,MAAM,SAAS;CACjC,CAAC;CACD,MAAM,gBAAgB;EACpB,UAAA,QAAS,uBAAuB,SAAS;EACzC,OAAO,YAAY,SAAS;EAC5B,OAAO,QAAQ,QAAQ;CACzB;CAWA,OAAO,IATYA,qBAAAA,YAAAA,GAAAA,qBAAAA,YAAAA,CACL,0BAA0B,MAAM,GAC5C,IAAIC,2BAAAA,gBAAgB,GACpB,EACE,OAAO,gBACT,GACA,OAGU;AACd;;;;;;;;;;AAWA,SAAgB,yBACd,aACA,iBACA,SACe;CACf,MAAM,SAAS,qBAAqB;CACpC,YAAY,aAAa,0BAA0B,MAAM;CAEzD,MAAM,gBAAgB;EACpB,YAAY,gBAAgB,wBAAwB;EACpD,OAAO,QAAQ,QAAQ;CACzB;CAWA,OAAO,IATYD,qBAAAA,YAAAA,GAAAA,qBAAAA,YAAAA,CACL,0BAA0B,MAAM,GAC5C,IAAIC,2BAAAA,gBAAgB,GACpB,EACE,OAAO,gBACT,GACA,OAGU;AACd"}
1
+ {"version":3,"file":"index.cjs","names":["DOMInteractor","defaultWaitForOption","TestEngine"],"sources":["../src/LegacyReactInteractor.ts","../src/createTestEngine.ts"],"sourcesContent":["import {\n BlurOption,\n ClickOption,\n defaultWaitForOption,\n EnterTextOption,\n FocusOption,\n HoverOption,\n Interactor,\n MouseDownOption,\n MouseEnterOption,\n MouseLeaveOption,\n MouseMoveOption,\n MouseOutOption,\n MouseUpOption,\n PartLocator,\n Point,\n PressKeyOption,\n WaitForOption,\n WaitUntilOption,\n} from '@atomic-testing/core';\nimport { DOMInteractor } from '@atomic-testing/dom-core';\nimport { act } from 'react-dom/test-utils';\n\n/**\n * React 16/17 counterpart of `@atomic-testing/react-core`'s `ReactInteractor`.\n *\n * It is a deliberate parallel implementation rather than a subclass of the\n * react-core interactor: react-core wraps actions with `act` from\n * `@testing-library/react@16`, whose peer range is React 18/19. Reusing it would\n * drag that dependency into react-legacy and make the package impossible to\n * install on React 16/17 (the very versions this adapter exists to support). By\n * extending `DOMInteractor` directly and wrapping with `act` from\n * `react-dom/test-utils` (the React ≤17 act), react-legacy stays on a coherent\n * React-16/17 dependency graph.\n */\nexport class LegacyReactInteractor extends DOMInteractor {\n override async enterText(locator: PartLocator, text: string, option?: Partial<EnterTextOption>): Promise<void> {\n await act(async () => {\n await super.enterText(locator, text, option);\n });\n }\n\n override async setRangeValue(locator: PartLocator, value: number): Promise<void> {\n await act(async () => {\n await super.setRangeValue(locator, value);\n });\n }\n\n override async click(locator: PartLocator, option?: Partial<ClickOption>): Promise<void> {\n await act(async () => {\n await super.click(locator, option);\n });\n }\n\n override async hover(locator: PartLocator, option?: Partial<HoverOption>): Promise<void> {\n await act(async () => {\n await super.hover(locator, option);\n });\n }\n\n override async mouseMove(locator: PartLocator, option?: Partial<MouseMoveOption>): Promise<void> {\n await act(async () => {\n await super.mouseMove(locator, option);\n });\n }\n\n override async mouseDown(locator: PartLocator, option?: Partial<MouseDownOption>): Promise<void> {\n await act(async () => {\n await super.mouseDown(locator, option);\n });\n }\n\n override async mouseUp(locator: PartLocator, option?: Partial<MouseUpOption>): Promise<void> {\n await act(async () => {\n await super.mouseUp(locator, option);\n });\n }\n\n override async mouseOver(locator: PartLocator, option?: Partial<HoverOption>): Promise<void> {\n await act(async () => {\n await super.mouseOver(locator, option);\n });\n }\n\n override async mouseOut(locator: PartLocator, option?: Partial<MouseOutOption>): Promise<void> {\n await act(async () => {\n await super.mouseOut(locator, option);\n });\n }\n\n override async mouseEnter(locator: PartLocator, option?: Partial<MouseEnterOption>): Promise<void> {\n await act(async () => {\n await super.mouseEnter(locator, option);\n });\n }\n\n override async mouseLeave(locator: PartLocator, option?: Partial<MouseLeaveOption>): Promise<void> {\n await act(async () => {\n await super.mouseLeave(locator, option);\n });\n }\n\n override async focus(locator: PartLocator, option?: Partial<FocusOption>): Promise<void> {\n await act(async () => {\n await super.focus(locator, option);\n });\n }\n\n override async blur(locator: PartLocator, option?: Partial<BlurOption>): Promise<void> {\n await act(async () => {\n await super.blur(locator, option);\n });\n }\n\n override async pressKey(locator: PartLocator, key: string, option?: Partial<PressKeyOption>): Promise<void> {\n await act(async () => {\n await super.pressKey(locator, key, option);\n });\n }\n\n override async contextMenu(locator: PartLocator): Promise<void> {\n await act(async () => {\n await super.contextMenu(locator);\n });\n }\n\n override async activate(locator: PartLocator): Promise<void> {\n await act(async () => {\n await super.activate(locator);\n });\n }\n\n override async selectOptionValue(locator: PartLocator, values: string[]): Promise<void> {\n await act(async () => {\n await super.selectOptionValue(locator, values);\n });\n }\n\n override async setInputFiles(locator: PartLocator, files: string | string[]): Promise<void> {\n await act(async () => {\n await super.setInputFiles(locator, files);\n });\n }\n\n override async scrollIntoView(locator: PartLocator): Promise<void> {\n await act(async () => {\n await super.scrollIntoView(locator);\n });\n }\n\n override async scrollBy(locator: PartLocator, delta: Point): Promise<void> {\n await act(async () => {\n await super.scrollBy(locator, delta);\n });\n }\n\n override async dragTo(source: PartLocator, target: PartLocator): Promise<void> {\n await act(async () => {\n await super.dragTo(source, target);\n });\n }\n\n override async drag(locator: PartLocator, delta: Point): Promise<void> {\n await act(async () => {\n await super.drag(locator, delta);\n });\n }\n\n //#region wait condition\n override async wait(ms: number): Promise<void> {\n await act(async () => {\n await super.wait(ms);\n });\n }\n\n override async waitUntilComponentState(\n locator: PartLocator,\n option: Partial<Readonly<WaitForOption>> = defaultWaitForOption\n ): Promise<void> {\n await act(async () => {\n await super.waitUntilComponentState(locator, option);\n });\n }\n\n override async waitUntil<T>(option: WaitUntilOption<T>): Promise<T> {\n // The React ≤17 `act` async overload resolves to `undefined`, not the\n // callback's value (unlike React 18's act), so capture the result via a\n // closure instead of returning it through `act`.\n let result!: T;\n await act(async () => {\n result = await super.waitUntil(option);\n });\n return result;\n }\n //#endregion\n\n override clone(): Interactor {\n return new LegacyReactInteractor(this.rootEl);\n }\n}\n","import { byAttribute, ScenePart, TestEngine } from '@atomic-testing/core';\nimport { ReactElement } from 'react';\nimport ReactDOM from 'react-dom';\nimport { act } from 'react-dom/test-utils';\n\nimport { LegacyReactInteractor } from './LegacyReactInteractor';\nimport { IReactTestEngineOption } from './types';\n\nlet _rootId = 0;\nfunction getNextRootElementId() {\n return `${_rootId++}`;\n}\n\nconst rootElementAttributeName = 'data-atomic-testing-react-legacy';\n\n/**\n * Create test engine for React 17 or before, for React 18 or later, use @atomic-testing/react-18 or @atomic-testing/react-19\n * This function takes a react node and render it into a container element. For rendered\n * components, use createRenderedLegacyTestEngine\n * @param node The React node to render\n * @param partDefinitions The scene part definitions\n * @param option\n * @returns The test engine\n */\nexport function createTestEngine<T extends ScenePart>(\n node: ReactElement,\n partDefinitions: T,\n option?: Readonly<Partial<IReactTestEngineOption>>\n): TestEngine<T> {\n const rootEl = option?.rootElement ?? document.body;\n const container = rootEl.appendChild(document.createElement('div'));\n const rootId = getNextRootElementId();\n container.setAttribute(rootElementAttributeName, rootId);\n\n act(() => {\n ReactDOM.render(node, container);\n });\n const cleanup = () => {\n ReactDOM.unmountComponentAtNode(container);\n rootEl.removeChild(container);\n return Promise.resolve();\n };\n\n const engine = new TestEngine(\n byAttribute(rootElementAttributeName, rootId),\n new LegacyReactInteractor(),\n {\n parts: partDefinitions,\n },\n cleanup\n );\n\n return engine;\n}\n\n/**\n * Create test engine for React 17 or before, for React 18 or later, use @atomic-testing/react-18 or @atomic-testing/react-19\n * This function takes an html element purportedly rendered by React and create a test engine for it, it\n * can be useful in environment such as Storybook where Storybook renders the component and the test\n * @param rootElement The React node to render\n * @param partDefinitions The scene part definitions\n * @param option\n * @returns The test engine\n */\nexport function createRenderedTestEngine<T extends ScenePart>(\n rootElement: HTMLElement,\n partDefinitions: T,\n _option?: Readonly<Partial<IReactTestEngineOption>>\n): TestEngine<T> {\n const rootId = getNextRootElementId();\n rootElement.setAttribute(rootElementAttributeName, rootId);\n\n const cleanup = () => {\n rootElement.removeAttribute(rootElementAttributeName);\n return Promise.resolve();\n };\n\n const engine = new TestEngine(\n byAttribute(rootElementAttributeName, rootId),\n new LegacyReactInteractor(),\n {\n parts: partDefinitions,\n },\n cleanup\n );\n\n return engine;\n}\n"],"mappings":"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"}
package/dist/index.mjs CHANGED
@@ -1,7 +1,153 @@
1
- import { TestEngine, byAttribute } from "@atomic-testing/core";
2
- import { ReactInteractor } from "@atomic-testing/react-core";
1
+ import { TestEngine, byAttribute, defaultWaitForOption } from "@atomic-testing/core";
3
2
  import ReactDOM from "react-dom";
4
3
  import { act } from "react-dom/test-utils.js";
4
+ import { DOMInteractor } from "@atomic-testing/dom-core";
5
+ //#region src/LegacyReactInteractor.ts
6
+ /**
7
+ * React 16/17 counterpart of `@atomic-testing/react-core`'s `ReactInteractor`.
8
+ *
9
+ * It is a deliberate parallel implementation rather than a subclass of the
10
+ * react-core interactor: react-core wraps actions with `act` from
11
+ * `@testing-library/react@16`, whose peer range is React 18/19. Reusing it would
12
+ * drag that dependency into react-legacy and make the package impossible to
13
+ * install on React 16/17 (the very versions this adapter exists to support). By
14
+ * extending `DOMInteractor` directly and wrapping with `act` from
15
+ * `react-dom/test-utils` (the React ≤17 act), react-legacy stays on a coherent
16
+ * React-16/17 dependency graph.
17
+ */
18
+ var LegacyReactInteractor = class LegacyReactInteractor extends DOMInteractor {
19
+ async enterText(locator, text, option) {
20
+ await act(async () => {
21
+ await super.enterText(locator, text, option);
22
+ });
23
+ }
24
+ async setRangeValue(locator, value) {
25
+ await act(async () => {
26
+ await super.setRangeValue(locator, value);
27
+ });
28
+ }
29
+ async click(locator, option) {
30
+ await act(async () => {
31
+ await super.click(locator, option);
32
+ });
33
+ }
34
+ async hover(locator, option) {
35
+ await act(async () => {
36
+ await super.hover(locator, option);
37
+ });
38
+ }
39
+ async mouseMove(locator, option) {
40
+ await act(async () => {
41
+ await super.mouseMove(locator, option);
42
+ });
43
+ }
44
+ async mouseDown(locator, option) {
45
+ await act(async () => {
46
+ await super.mouseDown(locator, option);
47
+ });
48
+ }
49
+ async mouseUp(locator, option) {
50
+ await act(async () => {
51
+ await super.mouseUp(locator, option);
52
+ });
53
+ }
54
+ async mouseOver(locator, option) {
55
+ await act(async () => {
56
+ await super.mouseOver(locator, option);
57
+ });
58
+ }
59
+ async mouseOut(locator, option) {
60
+ await act(async () => {
61
+ await super.mouseOut(locator, option);
62
+ });
63
+ }
64
+ async mouseEnter(locator, option) {
65
+ await act(async () => {
66
+ await super.mouseEnter(locator, option);
67
+ });
68
+ }
69
+ async mouseLeave(locator, option) {
70
+ await act(async () => {
71
+ await super.mouseLeave(locator, option);
72
+ });
73
+ }
74
+ async focus(locator, option) {
75
+ await act(async () => {
76
+ await super.focus(locator, option);
77
+ });
78
+ }
79
+ async blur(locator, option) {
80
+ await act(async () => {
81
+ await super.blur(locator, option);
82
+ });
83
+ }
84
+ async pressKey(locator, key, option) {
85
+ await act(async () => {
86
+ await super.pressKey(locator, key, option);
87
+ });
88
+ }
89
+ async contextMenu(locator) {
90
+ await act(async () => {
91
+ await super.contextMenu(locator);
92
+ });
93
+ }
94
+ async activate(locator) {
95
+ await act(async () => {
96
+ await super.activate(locator);
97
+ });
98
+ }
99
+ async selectOptionValue(locator, values) {
100
+ await act(async () => {
101
+ await super.selectOptionValue(locator, values);
102
+ });
103
+ }
104
+ async setInputFiles(locator, files) {
105
+ await act(async () => {
106
+ await super.setInputFiles(locator, files);
107
+ });
108
+ }
109
+ async scrollIntoView(locator) {
110
+ await act(async () => {
111
+ await super.scrollIntoView(locator);
112
+ });
113
+ }
114
+ async scrollBy(locator, delta) {
115
+ await act(async () => {
116
+ await super.scrollBy(locator, delta);
117
+ });
118
+ }
119
+ async dragTo(source, target) {
120
+ await act(async () => {
121
+ await super.dragTo(source, target);
122
+ });
123
+ }
124
+ async drag(locator, delta) {
125
+ await act(async () => {
126
+ await super.drag(locator, delta);
127
+ });
128
+ }
129
+ async wait(ms) {
130
+ await act(async () => {
131
+ await super.wait(ms);
132
+ });
133
+ }
134
+ async waitUntilComponentState(locator, option = defaultWaitForOption) {
135
+ await act(async () => {
136
+ await super.waitUntilComponentState(locator, option);
137
+ });
138
+ }
139
+ async waitUntil(option) {
140
+ let result;
141
+ await act(async () => {
142
+ result = await super.waitUntil(option);
143
+ });
144
+ return result;
145
+ }
146
+ clone() {
147
+ return new LegacyReactInteractor(this.rootEl);
148
+ }
149
+ };
150
+ //#endregion
5
151
  //#region src/createTestEngine.ts
6
152
  let _rootId = 0;
7
153
  function getNextRootElementId() {
@@ -30,7 +176,7 @@ function createTestEngine(node, partDefinitions, option) {
30
176
  rootEl.removeChild(container);
31
177
  return Promise.resolve();
32
178
  };
33
- return new TestEngine(byAttribute(rootElementAttributeName, rootId), new ReactInteractor(), { parts: partDefinitions }, cleanup);
179
+ return new TestEngine(byAttribute(rootElementAttributeName, rootId), new LegacyReactInteractor(), { parts: partDefinitions }, cleanup);
34
180
  }
35
181
  /**
36
182
  * Create test engine for React 17 or before, for React 18 or later, use @atomic-testing/react-18 or @atomic-testing/react-19
@@ -48,7 +194,7 @@ function createRenderedTestEngine(rootElement, partDefinitions, _option) {
48
194
  rootElement.removeAttribute(rootElementAttributeName);
49
195
  return Promise.resolve();
50
196
  };
51
- return new TestEngine(byAttribute(rootElementAttributeName, rootId), new ReactInteractor(), { parts: partDefinitions }, cleanup);
197
+ return new TestEngine(byAttribute(rootElementAttributeName, rootId), new LegacyReactInteractor(), { parts: partDefinitions }, cleanup);
52
198
  }
53
199
  //#endregion
54
200
  export { createRenderedTestEngine, createTestEngine };
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/createTestEngine.ts"],"sourcesContent":["import { byAttribute, ScenePart, TestEngine } from '@atomic-testing/core';\nimport { ReactInteractor } from '@atomic-testing/react-core';\nimport { ReactElement } from 'react';\nimport ReactDOM from 'react-dom';\nimport { act } from 'react-dom/test-utils';\n\nimport { IReactTestEngineOption } from './types';\n\nlet _rootId = 0;\nfunction getNextRootElementId() {\n return `${_rootId++}`;\n}\n\nconst rootElementAttributeName = 'data-atomic-testing-react-legacy';\n\n/**\n * Create test engine for React 17 or before, for React 18 or later, use @atomic-testing/react-18 or @atomic-testing/react-19\n * This function takes a react node and render it into a container element. For rendered\n * components, use createRenderedLegacyTestEngine\n * @param node The React node to render\n * @param partDefinitions The scene part definitions\n * @param option\n * @returns The test engine\n */\nexport function createTestEngine<T extends ScenePart>(\n node: ReactElement,\n partDefinitions: T,\n option?: Readonly<Partial<IReactTestEngineOption>>\n): TestEngine<T> {\n const rootEl = option?.rootElement ?? document.body;\n const container = rootEl.appendChild(document.createElement('div'));\n const rootId = getNextRootElementId();\n container.setAttribute(rootElementAttributeName, rootId);\n\n act(() => {\n ReactDOM.render(node, container);\n });\n const cleanup = () => {\n ReactDOM.unmountComponentAtNode(container);\n rootEl.removeChild(container);\n return Promise.resolve();\n };\n\n const engine = new TestEngine(\n byAttribute(rootElementAttributeName, rootId),\n new ReactInteractor(),\n {\n parts: partDefinitions,\n },\n cleanup\n );\n\n return engine;\n}\n\n/**\n * Create test engine for React 17 or before, for React 18 or later, use @atomic-testing/react-18 or @atomic-testing/react-19\n * This function takes an html element purportedly rendered by React and create a test engine for it, it\n * can be useful in environment such as Storybook where Storybook renders the component and the test\n * @param rootElement The React node to render\n * @param partDefinitions The scene part definitions\n * @param option\n * @returns The test engine\n */\nexport function createRenderedTestEngine<T extends ScenePart>(\n rootElement: HTMLElement,\n partDefinitions: T,\n _option?: Readonly<Partial<IReactTestEngineOption>>\n): TestEngine<T> {\n const rootId = getNextRootElementId();\n rootElement.setAttribute(rootElementAttributeName, rootId);\n\n const cleanup = () => {\n rootElement.removeAttribute(rootElementAttributeName);\n return Promise.resolve();\n };\n\n const engine = new TestEngine(\n byAttribute(rootElementAttributeName, rootId),\n new ReactInteractor(),\n {\n parts: partDefinitions,\n },\n cleanup\n );\n\n return engine;\n}\n"],"mappings":";;;;;AAQA,IAAI,UAAU;AACd,SAAS,uBAAuB;CAC9B,OAAO,GAAG;AACZ;AAEA,MAAM,2BAA2B;;;;;;;;;;AAWjC,SAAgB,iBACd,MACA,iBACA,QACe;CACf,MAAM,SAAS,QAAQ,eAAe,SAAS;CAC/C,MAAM,YAAY,OAAO,YAAY,SAAS,cAAc,KAAK,CAAC;CAClE,MAAM,SAAS,qBAAqB;CACpC,UAAU,aAAa,0BAA0B,MAAM;CAEvD,UAAU;EACR,SAAS,OAAO,MAAM,SAAS;CACjC,CAAC;CACD,MAAM,gBAAgB;EACpB,SAAS,uBAAuB,SAAS;EACzC,OAAO,YAAY,SAAS;EAC5B,OAAO,QAAQ,QAAQ;CACzB;CAWA,OAAO,IATY,WACjB,YAAY,0BAA0B,MAAM,GAC5C,IAAI,gBAAgB,GACpB,EACE,OAAO,gBACT,GACA,OAGU;AACd;;;;;;;;;;AAWA,SAAgB,yBACd,aACA,iBACA,SACe;CACf,MAAM,SAAS,qBAAqB;CACpC,YAAY,aAAa,0BAA0B,MAAM;CAEzD,MAAM,gBAAgB;EACpB,YAAY,gBAAgB,wBAAwB;EACpD,OAAO,QAAQ,QAAQ;CACzB;CAWA,OAAO,IATY,WACjB,YAAY,0BAA0B,MAAM,GAC5C,IAAI,gBAAgB,GACpB,EACE,OAAO,gBACT,GACA,OAGU;AACd"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/LegacyReactInteractor.ts","../src/createTestEngine.ts"],"sourcesContent":["import {\n BlurOption,\n ClickOption,\n defaultWaitForOption,\n EnterTextOption,\n FocusOption,\n HoverOption,\n Interactor,\n MouseDownOption,\n MouseEnterOption,\n MouseLeaveOption,\n MouseMoveOption,\n MouseOutOption,\n MouseUpOption,\n PartLocator,\n Point,\n PressKeyOption,\n WaitForOption,\n WaitUntilOption,\n} from '@atomic-testing/core';\nimport { DOMInteractor } from '@atomic-testing/dom-core';\nimport { act } from 'react-dom/test-utils';\n\n/**\n * React 16/17 counterpart of `@atomic-testing/react-core`'s `ReactInteractor`.\n *\n * It is a deliberate parallel implementation rather than a subclass of the\n * react-core interactor: react-core wraps actions with `act` from\n * `@testing-library/react@16`, whose peer range is React 18/19. Reusing it would\n * drag that dependency into react-legacy and make the package impossible to\n * install on React 16/17 (the very versions this adapter exists to support). By\n * extending `DOMInteractor` directly and wrapping with `act` from\n * `react-dom/test-utils` (the React ≤17 act), react-legacy stays on a coherent\n * React-16/17 dependency graph.\n */\nexport class LegacyReactInteractor extends DOMInteractor {\n override async enterText(locator: PartLocator, text: string, option?: Partial<EnterTextOption>): Promise<void> {\n await act(async () => {\n await super.enterText(locator, text, option);\n });\n }\n\n override async setRangeValue(locator: PartLocator, value: number): Promise<void> {\n await act(async () => {\n await super.setRangeValue(locator, value);\n });\n }\n\n override async click(locator: PartLocator, option?: Partial<ClickOption>): Promise<void> {\n await act(async () => {\n await super.click(locator, option);\n });\n }\n\n override async hover(locator: PartLocator, option?: Partial<HoverOption>): Promise<void> {\n await act(async () => {\n await super.hover(locator, option);\n });\n }\n\n override async mouseMove(locator: PartLocator, option?: Partial<MouseMoveOption>): Promise<void> {\n await act(async () => {\n await super.mouseMove(locator, option);\n });\n }\n\n override async mouseDown(locator: PartLocator, option?: Partial<MouseDownOption>): Promise<void> {\n await act(async () => {\n await super.mouseDown(locator, option);\n });\n }\n\n override async mouseUp(locator: PartLocator, option?: Partial<MouseUpOption>): Promise<void> {\n await act(async () => {\n await super.mouseUp(locator, option);\n });\n }\n\n override async mouseOver(locator: PartLocator, option?: Partial<HoverOption>): Promise<void> {\n await act(async () => {\n await super.mouseOver(locator, option);\n });\n }\n\n override async mouseOut(locator: PartLocator, option?: Partial<MouseOutOption>): Promise<void> {\n await act(async () => {\n await super.mouseOut(locator, option);\n });\n }\n\n override async mouseEnter(locator: PartLocator, option?: Partial<MouseEnterOption>): Promise<void> {\n await act(async () => {\n await super.mouseEnter(locator, option);\n });\n }\n\n override async mouseLeave(locator: PartLocator, option?: Partial<MouseLeaveOption>): Promise<void> {\n await act(async () => {\n await super.mouseLeave(locator, option);\n });\n }\n\n override async focus(locator: PartLocator, option?: Partial<FocusOption>): Promise<void> {\n await act(async () => {\n await super.focus(locator, option);\n });\n }\n\n override async blur(locator: PartLocator, option?: Partial<BlurOption>): Promise<void> {\n await act(async () => {\n await super.blur(locator, option);\n });\n }\n\n override async pressKey(locator: PartLocator, key: string, option?: Partial<PressKeyOption>): Promise<void> {\n await act(async () => {\n await super.pressKey(locator, key, option);\n });\n }\n\n override async contextMenu(locator: PartLocator): Promise<void> {\n await act(async () => {\n await super.contextMenu(locator);\n });\n }\n\n override async activate(locator: PartLocator): Promise<void> {\n await act(async () => {\n await super.activate(locator);\n });\n }\n\n override async selectOptionValue(locator: PartLocator, values: string[]): Promise<void> {\n await act(async () => {\n await super.selectOptionValue(locator, values);\n });\n }\n\n override async setInputFiles(locator: PartLocator, files: string | string[]): Promise<void> {\n await act(async () => {\n await super.setInputFiles(locator, files);\n });\n }\n\n override async scrollIntoView(locator: PartLocator): Promise<void> {\n await act(async () => {\n await super.scrollIntoView(locator);\n });\n }\n\n override async scrollBy(locator: PartLocator, delta: Point): Promise<void> {\n await act(async () => {\n await super.scrollBy(locator, delta);\n });\n }\n\n override async dragTo(source: PartLocator, target: PartLocator): Promise<void> {\n await act(async () => {\n await super.dragTo(source, target);\n });\n }\n\n override async drag(locator: PartLocator, delta: Point): Promise<void> {\n await act(async () => {\n await super.drag(locator, delta);\n });\n }\n\n //#region wait condition\n override async wait(ms: number): Promise<void> {\n await act(async () => {\n await super.wait(ms);\n });\n }\n\n override async waitUntilComponentState(\n locator: PartLocator,\n option: Partial<Readonly<WaitForOption>> = defaultWaitForOption\n ): Promise<void> {\n await act(async () => {\n await super.waitUntilComponentState(locator, option);\n });\n }\n\n override async waitUntil<T>(option: WaitUntilOption<T>): Promise<T> {\n // The React ≤17 `act` async overload resolves to `undefined`, not the\n // callback's value (unlike React 18's act), so capture the result via a\n // closure instead of returning it through `act`.\n let result!: T;\n await act(async () => {\n result = await super.waitUntil(option);\n });\n return result;\n }\n //#endregion\n\n override clone(): Interactor {\n return new LegacyReactInteractor(this.rootEl);\n }\n}\n","import { byAttribute, ScenePart, TestEngine } from '@atomic-testing/core';\nimport { ReactElement } from 'react';\nimport ReactDOM from 'react-dom';\nimport { act } from 'react-dom/test-utils';\n\nimport { LegacyReactInteractor } from './LegacyReactInteractor';\nimport { IReactTestEngineOption } from './types';\n\nlet _rootId = 0;\nfunction getNextRootElementId() {\n return `${_rootId++}`;\n}\n\nconst rootElementAttributeName = 'data-atomic-testing-react-legacy';\n\n/**\n * Create test engine for React 17 or before, for React 18 or later, use @atomic-testing/react-18 or @atomic-testing/react-19\n * This function takes a react node and render it into a container element. For rendered\n * components, use createRenderedLegacyTestEngine\n * @param node The React node to render\n * @param partDefinitions The scene part definitions\n * @param option\n * @returns The test engine\n */\nexport function createTestEngine<T extends ScenePart>(\n node: ReactElement,\n partDefinitions: T,\n option?: Readonly<Partial<IReactTestEngineOption>>\n): TestEngine<T> {\n const rootEl = option?.rootElement ?? document.body;\n const container = rootEl.appendChild(document.createElement('div'));\n const rootId = getNextRootElementId();\n container.setAttribute(rootElementAttributeName, rootId);\n\n act(() => {\n ReactDOM.render(node, container);\n });\n const cleanup = () => {\n ReactDOM.unmountComponentAtNode(container);\n rootEl.removeChild(container);\n return Promise.resolve();\n };\n\n const engine = new TestEngine(\n byAttribute(rootElementAttributeName, rootId),\n new LegacyReactInteractor(),\n {\n parts: partDefinitions,\n },\n cleanup\n );\n\n return engine;\n}\n\n/**\n * Create test engine for React 17 or before, for React 18 or later, use @atomic-testing/react-18 or @atomic-testing/react-19\n * This function takes an html element purportedly rendered by React and create a test engine for it, it\n * can be useful in environment such as Storybook where Storybook renders the component and the test\n * @param rootElement The React node to render\n * @param partDefinitions The scene part definitions\n * @param option\n * @returns The test engine\n */\nexport function createRenderedTestEngine<T extends ScenePart>(\n rootElement: HTMLElement,\n partDefinitions: T,\n _option?: Readonly<Partial<IReactTestEngineOption>>\n): TestEngine<T> {\n const rootId = getNextRootElementId();\n rootElement.setAttribute(rootElementAttributeName, rootId);\n\n const cleanup = () => {\n rootElement.removeAttribute(rootElementAttributeName);\n return Promise.resolve();\n };\n\n const engine = new TestEngine(\n byAttribute(rootElementAttributeName, rootId),\n new LegacyReactInteractor(),\n {\n parts: partDefinitions,\n },\n cleanup\n );\n\n return engine;\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atomic-testing/react-legacy",
3
- "version": "0.88.0",
3
+ "version": "0.89.0",
4
4
  "description": "Adapter for testing React 17 and earlier",
5
5
  "keywords": [
6
6
  "legacy",
@@ -29,26 +29,26 @@
29
29
  }
30
30
  },
31
31
  "dependencies": {
32
- "react-dom": "^17.0.0",
33
- "@atomic-testing/core": "0.88.0",
34
- "@atomic-testing/react-core": "0.88.0",
35
- "@atomic-testing/dom-core": "0.88.0"
32
+ "@atomic-testing/core": "0.89.0",
33
+ "@atomic-testing/dom-core": "0.89.0"
36
34
  },
37
35
  "devDependencies": {
38
36
  "@types/react": "^17.0.0",
39
37
  "@types/react-dom": "^17.0.0",
40
38
  "react": "^17.0.0",
41
- "react-dom": "^17.0.0"
39
+ "react-dom": "^17.0.0",
40
+ "@atomic-testing/component-driver-html": "0.89.0"
42
41
  },
43
42
  "peerDependencies": {
44
43
  "@testing-library/dom": ">=10.4.1",
45
- "@testing-library/react": ">=12.0.0",
46
44
  "@testing-library/user-event": ">=14.0.0",
47
45
  "react": "^16 || ^17",
48
46
  "react-dom": "^16 || ^17"
49
47
  },
50
48
  "scripts": {
51
49
  "build": "tsdown",
52
- "check:type": "tsgo --noEmit"
50
+ "check:type": "tsgo --noEmit",
51
+ "check:api": "api-extractor run",
52
+ "test:dom": "jest"
53
53
  }
54
54
  }
@@ -0,0 +1,200 @@
1
+ import {
2
+ BlurOption,
3
+ ClickOption,
4
+ defaultWaitForOption,
5
+ EnterTextOption,
6
+ FocusOption,
7
+ HoverOption,
8
+ Interactor,
9
+ MouseDownOption,
10
+ MouseEnterOption,
11
+ MouseLeaveOption,
12
+ MouseMoveOption,
13
+ MouseOutOption,
14
+ MouseUpOption,
15
+ PartLocator,
16
+ Point,
17
+ PressKeyOption,
18
+ WaitForOption,
19
+ WaitUntilOption,
20
+ } from '@atomic-testing/core';
21
+ import { DOMInteractor } from '@atomic-testing/dom-core';
22
+ import { act } from 'react-dom/test-utils';
23
+
24
+ /**
25
+ * React 16/17 counterpart of `@atomic-testing/react-core`'s `ReactInteractor`.
26
+ *
27
+ * It is a deliberate parallel implementation rather than a subclass of the
28
+ * react-core interactor: react-core wraps actions with `act` from
29
+ * `@testing-library/react@16`, whose peer range is React 18/19. Reusing it would
30
+ * drag that dependency into react-legacy and make the package impossible to
31
+ * install on React 16/17 (the very versions this adapter exists to support). By
32
+ * extending `DOMInteractor` directly and wrapping with `act` from
33
+ * `react-dom/test-utils` (the React ≤17 act), react-legacy stays on a coherent
34
+ * React-16/17 dependency graph.
35
+ */
36
+ export class LegacyReactInteractor extends DOMInteractor {
37
+ override async enterText(locator: PartLocator, text: string, option?: Partial<EnterTextOption>): Promise<void> {
38
+ await act(async () => {
39
+ await super.enterText(locator, text, option);
40
+ });
41
+ }
42
+
43
+ override async setRangeValue(locator: PartLocator, value: number): Promise<void> {
44
+ await act(async () => {
45
+ await super.setRangeValue(locator, value);
46
+ });
47
+ }
48
+
49
+ override async click(locator: PartLocator, option?: Partial<ClickOption>): Promise<void> {
50
+ await act(async () => {
51
+ await super.click(locator, option);
52
+ });
53
+ }
54
+
55
+ override async hover(locator: PartLocator, option?: Partial<HoverOption>): Promise<void> {
56
+ await act(async () => {
57
+ await super.hover(locator, option);
58
+ });
59
+ }
60
+
61
+ override async mouseMove(locator: PartLocator, option?: Partial<MouseMoveOption>): Promise<void> {
62
+ await act(async () => {
63
+ await super.mouseMove(locator, option);
64
+ });
65
+ }
66
+
67
+ override async mouseDown(locator: PartLocator, option?: Partial<MouseDownOption>): Promise<void> {
68
+ await act(async () => {
69
+ await super.mouseDown(locator, option);
70
+ });
71
+ }
72
+
73
+ override async mouseUp(locator: PartLocator, option?: Partial<MouseUpOption>): Promise<void> {
74
+ await act(async () => {
75
+ await super.mouseUp(locator, option);
76
+ });
77
+ }
78
+
79
+ override async mouseOver(locator: PartLocator, option?: Partial<HoverOption>): Promise<void> {
80
+ await act(async () => {
81
+ await super.mouseOver(locator, option);
82
+ });
83
+ }
84
+
85
+ override async mouseOut(locator: PartLocator, option?: Partial<MouseOutOption>): Promise<void> {
86
+ await act(async () => {
87
+ await super.mouseOut(locator, option);
88
+ });
89
+ }
90
+
91
+ override async mouseEnter(locator: PartLocator, option?: Partial<MouseEnterOption>): Promise<void> {
92
+ await act(async () => {
93
+ await super.mouseEnter(locator, option);
94
+ });
95
+ }
96
+
97
+ override async mouseLeave(locator: PartLocator, option?: Partial<MouseLeaveOption>): Promise<void> {
98
+ await act(async () => {
99
+ await super.mouseLeave(locator, option);
100
+ });
101
+ }
102
+
103
+ override async focus(locator: PartLocator, option?: Partial<FocusOption>): Promise<void> {
104
+ await act(async () => {
105
+ await super.focus(locator, option);
106
+ });
107
+ }
108
+
109
+ override async blur(locator: PartLocator, option?: Partial<BlurOption>): Promise<void> {
110
+ await act(async () => {
111
+ await super.blur(locator, option);
112
+ });
113
+ }
114
+
115
+ override async pressKey(locator: PartLocator, key: string, option?: Partial<PressKeyOption>): Promise<void> {
116
+ await act(async () => {
117
+ await super.pressKey(locator, key, option);
118
+ });
119
+ }
120
+
121
+ override async contextMenu(locator: PartLocator): Promise<void> {
122
+ await act(async () => {
123
+ await super.contextMenu(locator);
124
+ });
125
+ }
126
+
127
+ override async activate(locator: PartLocator): Promise<void> {
128
+ await act(async () => {
129
+ await super.activate(locator);
130
+ });
131
+ }
132
+
133
+ override async selectOptionValue(locator: PartLocator, values: string[]): Promise<void> {
134
+ await act(async () => {
135
+ await super.selectOptionValue(locator, values);
136
+ });
137
+ }
138
+
139
+ override async setInputFiles(locator: PartLocator, files: string | string[]): Promise<void> {
140
+ await act(async () => {
141
+ await super.setInputFiles(locator, files);
142
+ });
143
+ }
144
+
145
+ override async scrollIntoView(locator: PartLocator): Promise<void> {
146
+ await act(async () => {
147
+ await super.scrollIntoView(locator);
148
+ });
149
+ }
150
+
151
+ override async scrollBy(locator: PartLocator, delta: Point): Promise<void> {
152
+ await act(async () => {
153
+ await super.scrollBy(locator, delta);
154
+ });
155
+ }
156
+
157
+ override async dragTo(source: PartLocator, target: PartLocator): Promise<void> {
158
+ await act(async () => {
159
+ await super.dragTo(source, target);
160
+ });
161
+ }
162
+
163
+ override async drag(locator: PartLocator, delta: Point): Promise<void> {
164
+ await act(async () => {
165
+ await super.drag(locator, delta);
166
+ });
167
+ }
168
+
169
+ //#region wait condition
170
+ override async wait(ms: number): Promise<void> {
171
+ await act(async () => {
172
+ await super.wait(ms);
173
+ });
174
+ }
175
+
176
+ override async waitUntilComponentState(
177
+ locator: PartLocator,
178
+ option: Partial<Readonly<WaitForOption>> = defaultWaitForOption
179
+ ): Promise<void> {
180
+ await act(async () => {
181
+ await super.waitUntilComponentState(locator, option);
182
+ });
183
+ }
184
+
185
+ override async waitUntil<T>(option: WaitUntilOption<T>): Promise<T> {
186
+ // The React ≤17 `act` async overload resolves to `undefined`, not the
187
+ // callback's value (unlike React 18's act), so capture the result via a
188
+ // closure instead of returning it through `act`.
189
+ let result!: T;
190
+ await act(async () => {
191
+ result = await super.waitUntil(option);
192
+ });
193
+ return result;
194
+ }
195
+ //#endregion
196
+
197
+ override clone(): Interactor {
198
+ return new LegacyReactInteractor(this.rootEl);
199
+ }
200
+ }
@@ -1,9 +1,9 @@
1
1
  import { byAttribute, ScenePart, TestEngine } from '@atomic-testing/core';
2
- import { ReactInteractor } from '@atomic-testing/react-core';
3
2
  import { ReactElement } from 'react';
4
3
  import ReactDOM from 'react-dom';
5
4
  import { act } from 'react-dom/test-utils';
6
5
 
6
+ import { LegacyReactInteractor } from './LegacyReactInteractor';
7
7
  import { IReactTestEngineOption } from './types';
8
8
 
9
9
  let _rootId = 0;
@@ -43,7 +43,7 @@ export function createTestEngine<T extends ScenePart>(
43
43
 
44
44
  const engine = new TestEngine(
45
45
  byAttribute(rootElementAttributeName, rootId),
46
- new ReactInteractor(),
46
+ new LegacyReactInteractor(),
47
47
  {
48
48
  parts: partDefinitions,
49
49
  },
@@ -77,7 +77,7 @@ export function createRenderedTestEngine<T extends ScenePart>(
77
77
 
78
78
  const engine = new TestEngine(
79
79
  byAttribute(rootElementAttributeName, rootId),
80
- new ReactInteractor(),
80
+ new LegacyReactInteractor(),
81
81
  {
82
82
  parts: partDefinitions,
83
83
  },