@nyby/detox-component-testing 1.4.2 → 1.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 +136 -47
- package/dist/ComponentHarness.d.ts.map +1 -1
- package/dist/ComponentHarness.js +19 -9
- package/dist/ComponentHarness.js.map +1 -1
- package/dist/configureHarness.d.ts +4 -1
- package/dist/configureHarness.d.ts.map +1 -1
- package/dist/configureHarness.js +15 -1
- package/dist/configureHarness.js.map +1 -1
- package/dist/environment.js +3 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/mount.d.ts.map +1 -1
- package/dist/mount.js +13 -27
- package/dist/mount.js.map +1 -1
- package/package.json +1 -1
- package/src/ComponentHarness.tsx +31 -13
- package/src/configureHarness.ts +16 -2
- package/src/environment.js +3 -1
- package/src/index.ts +2 -2
- package/src/mount.ts +13 -27
package/README.md
CHANGED
|
@@ -31,10 +31,18 @@ npm install @nyby/detox-component-testing react-native-launch-arguments
|
|
|
31
31
|
Create `app.component-test.js` alongside your app entry:
|
|
32
32
|
|
|
33
33
|
```js
|
|
34
|
-
import
|
|
35
|
-
import {
|
|
34
|
+
import React from 'react';
|
|
35
|
+
import {AppRegistry, View} from 'react-native';
|
|
36
|
+
import {ComponentHarness, configureHarness} from '@nyby/detox-component-testing';
|
|
36
37
|
import './component-tests/registry';
|
|
37
38
|
|
|
39
|
+
configureHarness({
|
|
40
|
+
wrapper: ({children}) => (
|
|
41
|
+
<View style={{flex: 1}}>{children}</View>
|
|
42
|
+
),
|
|
43
|
+
debugTree: true,
|
|
44
|
+
});
|
|
45
|
+
|
|
38
46
|
AppRegistry.registerComponent('example', () => ComponentHarness);
|
|
39
47
|
```
|
|
40
48
|
|
|
@@ -43,16 +51,16 @@ AppRegistry.registerComponent('example', () => ComponentHarness);
|
|
|
43
51
|
Create `component-tests/registry.ts` to register components for testing:
|
|
44
52
|
|
|
45
53
|
```ts
|
|
46
|
-
import {
|
|
47
|
-
import {
|
|
48
|
-
import {
|
|
54
|
+
import {registerComponent} from '@nyby/detox-component-testing';
|
|
55
|
+
import {Stepper} from '../src/components/Stepper';
|
|
56
|
+
import {LoginForm} from '../src/components/LoginForm';
|
|
49
57
|
|
|
50
58
|
// Auto-infer name from Component.name
|
|
51
|
-
registerComponent(Stepper, {
|
|
59
|
+
registerComponent(Stepper, {initial: 0});
|
|
52
60
|
registerComponent(LoginForm);
|
|
53
61
|
|
|
54
62
|
// Or use an explicit name
|
|
55
|
-
registerComponent('MyComponent', MyComponent, {
|
|
63
|
+
registerComponent('MyComponent', MyComponent, {someProp: 'default'});
|
|
56
64
|
```
|
|
57
65
|
|
|
58
66
|
### 4. Switch entry point for component tests
|
|
@@ -61,7 +69,7 @@ Update your index file to load the component test entry when running component t
|
|
|
61
69
|
|
|
62
70
|
```js
|
|
63
71
|
// index.js
|
|
64
|
-
const {
|
|
72
|
+
const {LaunchArguments} = require('react-native-launch-arguments');
|
|
65
73
|
|
|
66
74
|
if (LaunchArguments.value().detoxComponentName) {
|
|
67
75
|
require('./app.component-test');
|
|
@@ -81,27 +89,27 @@ module.exports = {
|
|
|
81
89
|
// ... existing app and device config
|
|
82
90
|
configurations: {
|
|
83
91
|
// ... existing configurations
|
|
84
|
-
|
|
85
|
-
device:
|
|
86
|
-
app:
|
|
92
|
+
'ios.sim.component': {
|
|
93
|
+
device: 'simulator',
|
|
94
|
+
app: 'ios',
|
|
87
95
|
testRunner: {
|
|
88
96
|
args: {
|
|
89
|
-
config:
|
|
90
|
-
_: [
|
|
91
|
-
}
|
|
92
|
-
}
|
|
97
|
+
config: 'component-tests/jest.config.js',
|
|
98
|
+
_: ['src/components'],
|
|
99
|
+
},
|
|
100
|
+
},
|
|
93
101
|
},
|
|
94
|
-
|
|
95
|
-
device:
|
|
96
|
-
app:
|
|
102
|
+
'android.emu.component': {
|
|
103
|
+
device: 'emulator',
|
|
104
|
+
app: 'android',
|
|
97
105
|
testRunner: {
|
|
98
106
|
args: {
|
|
99
|
-
config:
|
|
100
|
-
_: [
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
}
|
|
107
|
+
config: 'component-tests/jest.config.js',
|
|
108
|
+
_: ['src/components'],
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
},
|
|
105
113
|
};
|
|
106
114
|
```
|
|
107
115
|
|
|
@@ -112,19 +120,19 @@ Create `component-tests/jest.config.js`:
|
|
|
112
120
|
```js
|
|
113
121
|
module.exports = {
|
|
114
122
|
maxWorkers: 1,
|
|
115
|
-
globalSetup:
|
|
116
|
-
globalTeardown:
|
|
117
|
-
testEnvironment:
|
|
118
|
-
setupFilesAfterEnv: [
|
|
119
|
-
testRunner:
|
|
123
|
+
globalSetup: 'detox/runners/jest/globalSetup',
|
|
124
|
+
globalTeardown: 'detox/runners/jest/globalTeardown',
|
|
125
|
+
testEnvironment: '@nyby/detox-component-testing/environment',
|
|
126
|
+
setupFilesAfterEnv: ['./setup.ts'],
|
|
127
|
+
testRunner: 'jest-circus/runner',
|
|
120
128
|
testTimeout: 120000,
|
|
121
|
-
roots: [
|
|
122
|
-
testMatch: [
|
|
129
|
+
roots: ['<rootDir>/../src'],
|
|
130
|
+
testMatch: ['**/*.component.test.ts'],
|
|
123
131
|
transform: {
|
|
124
|
-
|
|
132
|
+
'\\.tsx?$': ['ts-jest', {tsconfig: '<rootDir>/../tsconfig.json'}],
|
|
125
133
|
},
|
|
126
|
-
reporters: [
|
|
127
|
-
verbose: true
|
|
134
|
+
reporters: ['detox/runners/jest/reporter'],
|
|
135
|
+
verbose: true,
|
|
128
136
|
};
|
|
129
137
|
```
|
|
130
138
|
|
|
@@ -133,8 +141,8 @@ module.exports = {
|
|
|
133
141
|
### Basic mounting
|
|
134
142
|
|
|
135
143
|
```ts
|
|
136
|
-
import {
|
|
137
|
-
import {
|
|
144
|
+
import {by, element, expect} from 'detox';
|
|
145
|
+
import {mount} from '@nyby/detox-component-testing/test';
|
|
138
146
|
|
|
139
147
|
describe('Stepper', () => {
|
|
140
148
|
it('renders with default props', async () => {
|
|
@@ -143,7 +151,7 @@ describe('Stepper', () => {
|
|
|
143
151
|
});
|
|
144
152
|
|
|
145
153
|
it('renders with custom props', async () => {
|
|
146
|
-
await mount('Stepper', {
|
|
154
|
+
await mount('Stepper', {initial: 100});
|
|
147
155
|
await expect(element(by.id('counter'))).toHaveText('100');
|
|
148
156
|
});
|
|
149
157
|
});
|
|
@@ -154,10 +162,10 @@ describe('Stepper', () => {
|
|
|
154
162
|
Use `spy()` to create a recording function for callback props, and `expectSpy()` to assert on it:
|
|
155
163
|
|
|
156
164
|
```ts
|
|
157
|
-
import {
|
|
165
|
+
import {mount, spy, expectSpy} from '@nyby/detox-component-testing/test';
|
|
158
166
|
|
|
159
167
|
it('fires onChange when incremented', async () => {
|
|
160
|
-
await mount('Stepper', {
|
|
168
|
+
await mount('Stepper', {initial: 0, onChange: spy('onChange')});
|
|
161
169
|
await element(by.id('increment')).tap();
|
|
162
170
|
|
|
163
171
|
await expectSpy('onChange').toHaveBeenCalled();
|
|
@@ -182,6 +190,7 @@ Subsequent mounts: ~100ms (in-place swap)
|
|
|
182
190
|
### App-side (import from `@nyby/detox-component-testing`)
|
|
183
191
|
|
|
184
192
|
#### `registerComponent(Component, defaultProps?)`
|
|
193
|
+
|
|
185
194
|
#### `registerComponent(name, Component, defaultProps?)`
|
|
186
195
|
|
|
187
196
|
Register a component for testing. When called with just a component, the name is inferred from `Component.name` or `Component.displayName`.
|
|
@@ -190,28 +199,31 @@ Register a component for testing. When called with just a component, the name is
|
|
|
190
199
|
|
|
191
200
|
Root component for the test harness. Register as your app's root component in the component test entry point.
|
|
192
201
|
|
|
193
|
-
#### `configureHarness({ wrapper })`
|
|
202
|
+
#### `configureHarness({ wrapper?, debugTree? })`
|
|
194
203
|
|
|
195
|
-
Set a global wrapper component
|
|
204
|
+
Set a global wrapper component and/or enable debug tree capture for all mounted components:
|
|
196
205
|
|
|
197
206
|
```js
|
|
198
|
-
import {
|
|
199
|
-
import {
|
|
200
|
-
import {
|
|
207
|
+
import {Provider} from 'react-redux';
|
|
208
|
+
import {configureHarness} from '@nyby/detox-component-testing';
|
|
209
|
+
import {createStore} from './store';
|
|
201
210
|
|
|
202
211
|
configureHarness({
|
|
203
|
-
wrapper: ({
|
|
212
|
+
wrapper: ({children, launchArgs}) => {
|
|
204
213
|
const store = createStore();
|
|
205
214
|
if (launchArgs.reduxState) {
|
|
206
215
|
store.dispatch(loadState(JSON.parse(launchArgs.reduxState)));
|
|
207
216
|
}
|
|
208
217
|
return <Provider store={store}>{children}</Provider>;
|
|
209
218
|
},
|
|
219
|
+
debugTree: true,
|
|
210
220
|
});
|
|
211
221
|
```
|
|
212
222
|
|
|
213
223
|
The wrapper receives `launchArgs` — the props passed to `mount()` — so you can configure per-test state.
|
|
214
224
|
|
|
225
|
+
The `debugTree` option enables component tree capture for `debug()` and the custom test environment. Pass `true` for defaults, or an object with `usefulProps`, `skipNames`, and/or `nativeDuplicates` to customize filtering (see [DebugTree](#debugtree) below).
|
|
226
|
+
|
|
215
227
|
### Test-side (import from `@nyby/detox-component-testing/test`)
|
|
216
228
|
|
|
217
229
|
#### `mount(componentName, props?)`
|
|
@@ -230,9 +242,86 @@ Returns an assertion object for a spy:
|
|
|
230
242
|
- `.toHaveBeenCalledTimes(n)` — spy was called exactly `n` times
|
|
231
243
|
- `.lastCalledWith(...args)` — the last call's arguments match
|
|
232
244
|
|
|
233
|
-
#### `
|
|
245
|
+
#### `debug(label?, outputDir?)`
|
|
246
|
+
|
|
247
|
+
Capture a screenshot, React component tree, and native view hierarchy for the current screen state. Useful for debugging test failures or inspecting what's on screen at any point in a test.
|
|
248
|
+
|
|
249
|
+
```ts
|
|
250
|
+
import {mount, debug} from '@nyby/detox-component-testing/test';
|
|
251
|
+
|
|
252
|
+
it('renders the event screen', async () => {
|
|
253
|
+
await mount('EventScreen', {eventId: 'event_1'});
|
|
254
|
+
await debug('after-mount'); // writes to artifacts/debug-after-mount.{png,json,xml}
|
|
255
|
+
});
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
Each call writes up to three files to the output directory (defaults to `<cwd>/artifacts`):
|
|
259
|
+
|
|
260
|
+
- `debug-<label>.png` — screenshot
|
|
261
|
+
- `debug-<label>-tree.json` — React component tree (requires `DebugTree` wrapper)
|
|
262
|
+
- `debug-<label>-view.xml` — native view hierarchy
|
|
263
|
+
|
|
264
|
+
If no label is provided, calls are numbered automatically (`1`, `2`, `3`, ...).
|
|
265
|
+
|
|
266
|
+
### Debugging
|
|
267
|
+
|
|
268
|
+
#### `DebugTree`
|
|
269
|
+
|
|
270
|
+
A component that captures the React fiber tree on demand. Enable it via `configureHarness({ debugTree: true })` to get component tree capture in `debug()` and the custom test environment.
|
|
271
|
+
|
|
272
|
+
```js
|
|
273
|
+
import {configureHarness} from '@nyby/detox-component-testing';
|
|
274
|
+
|
|
275
|
+
configureHarness({
|
|
276
|
+
wrapper: ({children}) => (
|
|
277
|
+
<Provider store={store}>{children}</Provider>
|
|
278
|
+
),
|
|
279
|
+
debugTree: true,
|
|
280
|
+
});
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
You can also pass filtering options directly:
|
|
284
|
+
|
|
285
|
+
```js
|
|
286
|
+
configureHarness({
|
|
287
|
+
debugTree: {
|
|
288
|
+
usefulProps: ['testID', 'accessibilityLabel', 'title'],
|
|
289
|
+
skipNames: ['MyInternalWrapper'],
|
|
290
|
+
},
|
|
291
|
+
});
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
`DebugTree` walks the React fiber tree and produces a JSON snapshot of component names, key props (`testID`, `accessibilityLabel`, `variant`, `onPress`, etc.), and text content. The output filters noise — internal wrappers and native duplicates are excluded by default.
|
|
295
|
+
|
|
296
|
+
The `DebugTree` component is also exported for advanced use cases where you need direct control.
|
|
297
|
+
|
|
298
|
+
All filtering lists are configurable:
|
|
299
|
+
|
|
300
|
+
| Prop | Type | Description |
|
|
301
|
+
| ------------------ | ---------- | ------------------------------------------------------------------------------------------------------------------------- |
|
|
302
|
+
| `usefulProps` | `string[]` | Props to include in output. Defaults to `testID`, `accessibilityLabel`, `title`, `value`, `placeholder`, `disabled`, etc. |
|
|
303
|
+
| `skipNames` | `string[]` | Component names to skip. Defaults to internal wrappers like `StaticContainer`, `PressabilityDebugView`, etc. |
|
|
304
|
+
| `nativeDuplicates` | `string[]` | Native components that duplicate their parent. Defaults to `RCTText`, `RCTView`, etc. |
|
|
305
|
+
|
|
306
|
+
#### Custom test environment
|
|
307
|
+
|
|
308
|
+
A Detox Jest environment that automatically captures debug artifacts when a test fails. Use it instead of the default Detox environment:
|
|
309
|
+
|
|
310
|
+
```js
|
|
311
|
+
// jest.config.js
|
|
312
|
+
module.exports = {
|
|
313
|
+
testEnvironment: '@nyby/detox-component-testing/environment',
|
|
314
|
+
// ...
|
|
315
|
+
};
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
On test failure, it captures:
|
|
319
|
+
|
|
320
|
+
- A screenshot
|
|
321
|
+
- The React component tree (if `DebugTree` is in the wrapper)
|
|
322
|
+
- The native view hierarchy
|
|
234
323
|
|
|
235
|
-
|
|
324
|
+
All artifacts are written to `<cwd>/artifacts/`.
|
|
236
325
|
|
|
237
326
|
## Limitations
|
|
238
327
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ComponentHarness.d.ts","sourceRoot":"","sources":["../src/ComponentHarness.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8E,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"ComponentHarness.d.ts","sourceRoot":"","sources":["../src/ComponentHarness.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8E,MAAM,OAAO,CAAC;AAwDnG,wBAAgB,gBAAgB,sBAkD/B"}
|
package/dist/ComponentHarness.js
CHANGED
|
@@ -39,6 +39,7 @@ const react_native_1 = require("react-native");
|
|
|
39
39
|
const react_native_launch_arguments_1 = require("react-native-launch-arguments");
|
|
40
40
|
const ComponentRegistry_1 = require("./ComponentRegistry");
|
|
41
41
|
const configureHarness_1 = require("./configureHarness");
|
|
42
|
+
const DebugTree_1 = require("./DebugTree");
|
|
42
43
|
class RenderErrorBoundary extends react_1.Component {
|
|
43
44
|
constructor() {
|
|
44
45
|
super(...arguments);
|
|
@@ -73,9 +74,9 @@ function parseLaunchArgs(args) {
|
|
|
73
74
|
function ComponentHarness() {
|
|
74
75
|
const launchArgs = react_native_launch_arguments_1.LaunchArguments.value();
|
|
75
76
|
const [mountPayload, setMountPayload] = (0, react_1.useState)(null);
|
|
76
|
-
const handleControl = (0, react_1.useCallback)((
|
|
77
|
+
const handleControl = (0, react_1.useCallback)((e) => {
|
|
77
78
|
try {
|
|
78
|
-
setMountPayload(JSON.parse(text));
|
|
79
|
+
setMountPayload(JSON.parse(e.nativeEvent.text));
|
|
79
80
|
}
|
|
80
81
|
catch (_e) { }
|
|
81
82
|
}, []);
|
|
@@ -93,7 +94,15 @@ function ComponentHarness() {
|
|
|
93
94
|
};
|
|
94
95
|
}
|
|
95
96
|
return (react_1.default.createElement(react_native_1.View, { style: { flex: 1 } },
|
|
96
|
-
react_1.default.createElement(react_native_1.TextInput, { testID: "detox-harness-control",
|
|
97
|
+
react_1.default.createElement(react_native_1.TextInput, { testID: "detox-harness-control", onEndEditing: handleControl, style: {
|
|
98
|
+
position: 'absolute',
|
|
99
|
+
bottom: 0,
|
|
100
|
+
left: 0,
|
|
101
|
+
right: 0,
|
|
102
|
+
height: 44,
|
|
103
|
+
opacity: 0.01,
|
|
104
|
+
zIndex: 9999,
|
|
105
|
+
} }),
|
|
97
106
|
activeMount && (react_1.default.createElement(react_1.default.Fragment, null,
|
|
98
107
|
react_1.default.createElement(react_native_1.Text, { testID: "detox-mount-id", style: { height: 1 } }, activeMount.id),
|
|
99
108
|
react_1.default.createElement(RenderErrorBoundary, null,
|
|
@@ -128,11 +137,12 @@ function ComponentRenderer({ mount }) {
|
|
|
128
137
|
});
|
|
129
138
|
const props = { ...defaultProps, ...(mount.props || {}), ...spyProps };
|
|
130
139
|
const Wrapper = (0, configureHarness_1.getWrapper)();
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
140
|
+
const debugTreeConfig = (0, configureHarness_1.getDebugTreeConfig)();
|
|
141
|
+
const content = (react_1.default.createElement(react_native_1.View, { testID: "component-harness-root", style: { flex: 1 } },
|
|
142
|
+
react_1.default.createElement(Component, { ...props }),
|
|
143
|
+
spyNames.map((name) => (react_1.default.createElement(react_native_1.View, { key: name },
|
|
144
|
+
react_1.default.createElement(react_native_1.Text, { testID: `spy-${name}-count` }, String(spyData[name].count)),
|
|
145
|
+
react_1.default.createElement(react_native_1.Text, { testID: `spy-${name}-lastArgs` }, JSON.stringify(spyData[name].lastArgs)))))));
|
|
146
|
+
return (react_1.default.createElement(Wrapper, { launchArgs: mount.props || {} }, debugTreeConfig ? react_1.default.createElement(DebugTree_1.DebugTree, { ...debugTreeConfig }, content) : content));
|
|
137
147
|
}
|
|
138
148
|
//# sourceMappingURL=ComponentHarness.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ComponentHarness.js","sourceRoot":"","sources":["../src/ComponentHarness.tsx"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"ComponentHarness.js","sourceRoot":"","sources":["../src/ComponentHarness.tsx"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwDA,4CAkDC;AA1GD,+CAAmG;AACnG,+CAA+D;AAC/D,iFAA8D;AAC9D,2DAAiD;AACjD,yDAAkE;AAClE,2CAAsC;AAMtC,MAAM,mBAAoB,SAAQ,iBAAyD;IAA3F;;QACE,UAAK,GAAuB,EAAC,KAAK,EAAE,IAAI,EAAC,CAAC;IAgB5C,CAAC;IAdC,MAAM,CAAC,wBAAwB,CAAC,KAAY;QAC1C,OAAO,EAAC,KAAK,EAAC,CAAC;IACjB,CAAC;IAED,MAAM;QACJ,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;YACrB,OAAO,CACL,8BAAC,yBAAU,IAAC,MAAM,EAAC,oBAAoB;gBACrC,8BAAC,mBAAI,IAAC,MAAM,EAAC,4BAA4B,IAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAQ,CAChE,CACd,CAAC;QACJ,CAAC;QACD,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC;IAC7B,CAAC;CACF;AAED,MAAM,WAAW,GAAG,YAAY,CAAC;AACjC,MAAM,UAAU,GAAG,WAAW,CAAC;AAS/B,SAAS,eAAe,CAAC,IAAyB;IAIhD,MAAM,KAAK,GAAwB,EAAE,CAAC;IACtC,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE;QAC5C,IAAI,GAAG,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;YAChC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,GAAG,KAAK,CAAC;QAC/C,CAAC;aAAM,IAAI,GAAG,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;YACtC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;QAC3C,CAAC;IACH,CAAC,CAAC,CAAC;IACH,OAAO,EAAC,KAAK,EAAE,KAAK,EAAC,CAAC;AACxB,CAAC;AAED,SAAgB,gBAAgB;IAC9B,MAAM,UAAU,GAAG,+CAAe,CAAC,KAAK,EAAyB,CAAC;IAClE,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,IAAA,gBAAQ,EAAsB,IAAI,CAAC,CAAC;IAE5E,MAAM,aAAa,GAAG,IAAA,mBAAW,EAAC,CAAC,CAAgC,EAAE,EAAE;QACrE,IAAI,CAAC;YACH,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC;QAClD,CAAC;QAAC,OAAO,EAAE,EAAE,CAAC,CAAA,CAAC;IACjB,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,IAAI,WAAW,GAAwB,IAAI,CAAC;IAC5C,IAAI,YAAY,EAAE,CAAC;QACjB,WAAW,GAAG,YAAY,CAAC;IAC7B,CAAC;SAAM,IAAI,UAAU,CAAC,kBAAkB,EAAE,CAAC;QACzC,MAAM,EAAC,KAAK,EAAE,KAAK,EAAC,GAAG,eAAe,CAAC,UAAU,CAAC,CAAC;QACnD,WAAW,GAAG;YACZ,EAAE,EAAE,GAAG;YACP,IAAI,EAAE,UAAU,CAAC,kBAA4B;YAC7C,KAAK;YACL,KAAK;SACN,CAAC;IACJ,CAAC;IAED,OAAO,CACL,8BAAC,mBAAI,IAAC,KAAK,EAAE,EAAC,IAAI,EAAE,CAAC,EAAC;QACpB,8BAAC,wBAAS,IACR,MAAM,EAAC,uBAAuB,EAC9B,YAAY,EAAE,aAAa,EAC3B,KAAK,EAAE;gBACL,QAAQ,EAAE,UAAU;gBACpB,MAAM,EAAE,CAAC;gBACT,IAAI,EAAE,CAAC;gBACP,KAAK,EAAE,CAAC;gBACR,MAAM,EAAE,EAAE;gBACV,OAAO,EAAE,IAAI;gBACb,MAAM,EAAE,IAAI;aACb,GACD;QACD,WAAW,IAAI,CACd;YACE,8BAAC,mBAAI,IAAC,MAAM,EAAC,gBAAgB,EAAC,KAAK,EAAE,EAAC,MAAM,EAAE,CAAC,EAAC,IAC7C,WAAW,CAAC,EAAE,CACV;YACP,8BAAC,mBAAmB;gBAClB,8BAAC,iBAAiB,IAAC,GAAG,EAAE,WAAW,CAAC,EAAE,EAAE,KAAK,EAAE,WAAW,GAAI,CAC1C,CACrB,CACJ,CACI,CACR,CAAC;AACJ,CAAC;AAOD,SAAS,iBAAiB,CAAC,EAAC,KAAK,EAAwB;IACvD,MAAM,EAAC,SAAS,EAAE,YAAY,EAAC,GAAG,IAAA,gCAAY,EAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC3D,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,IAAI,EAAE,CAAC;IAEnC,MAAM,WAAW,GAA4B,EAAE,CAAC;IAChD,QAAQ,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;QACxB,WAAW,CAAC,IAAI,CAAC,GAAG,EAAC,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAC,CAAC;IAC/C,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,IAAA,gBAAQ,EAAC,WAAW,CAAC,CAAC;IAEpD,MAAM,SAAS,GAAG,IAAA,cAAM,EAA2C,EAAE,CAAC,CAAC;IACvE,MAAM,QAAQ,GAA6C,EAAE,CAAC;IAC9D,QAAQ,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;QACxB,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;YAC7B,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,QAAe,EAAE,EAAE;gBAC/C,UAAU,CAAC,CAAC,IAAI,EAAE,EAAE;;oBAAC,OAAA,CAAC;wBACpB,GAAG,IAAI;wBACP,CAAC,IAAI,CAAC,EAAE;4BACN,KAAK,EAAE,CAAC,CAAA,MAAA,IAAI,CAAC,IAAI,CAAC,0CAAE,KAAK,KAAI,CAAC,CAAC,GAAG,CAAC;4BACnC,QAAQ,EAAE,QAAQ;yBACnB;qBACF,CAAC,CAAA;iBAAA,CAAC,CAAC;YACN,CAAC,CAAC;QACJ,CAAC;QACD,QAAQ,CAAC,IAAI,CAAC,GAAG,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;IAEH,MAAM,KAAK,GAAG,EAAC,GAAG,YAAY,EAAE,GAAG,CAAC,KAAK,CAAC,KAAK,IAAI,EAAE,CAAC,EAAE,GAAG,QAAQ,EAAC,CAAC;IACrE,MAAM,OAAO,GAAG,IAAA,6BAAU,GAAE,CAAC;IAC7B,MAAM,eAAe,GAAG,IAAA,qCAAkB,GAAE,CAAC;IAE7C,MAAM,OAAO,GAAG,CACd,8BAAC,mBAAI,IAAC,MAAM,EAAC,wBAAwB,EAAC,KAAK,EAAE,EAAC,IAAI,EAAE,CAAC,EAAC;QACpD,8BAAC,SAAS,OAAK,KAAK,GAAI;QACvB,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CACtB,8BAAC,mBAAI,IAAC,GAAG,EAAE,IAAI;YACb,8BAAC,mBAAI,IAAC,MAAM,EAAE,OAAO,IAAI,QAAQ,IAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAQ;YACvE,8BAAC,mBAAI,IAAC,MAAM,EAAE,OAAO,IAAI,WAAW,IAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,CAAQ,CAChF,CACR,CAAC,CACG,CACR,CAAC;IAEF,OAAO,CACL,8BAAC,OAAO,IAAC,UAAU,EAAE,KAAK,CAAC,KAAK,IAAI,EAAE,IACnC,eAAe,CAAC,CAAC,CAAC,8BAAC,qBAAS,OAAK,eAAe,IAAG,OAAO,CAAa,CAAC,CAAC,CAAC,OAAO,CAC1E,CACX,CAAC;AACJ,CAAC"}
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { ComponentType, ReactNode } from 'react';
|
|
2
|
+
import { DebugTreeProps } from './DebugTree';
|
|
2
3
|
export interface WrapperProps {
|
|
3
4
|
children: ReactNode;
|
|
4
5
|
launchArgs: Record<string, any>;
|
|
5
6
|
}
|
|
6
7
|
export interface HarnessConfig {
|
|
7
|
-
wrapper
|
|
8
|
+
wrapper?: ComponentType<WrapperProps>;
|
|
9
|
+
debugTree?: boolean | Omit<DebugTreeProps, 'children'>;
|
|
8
10
|
}
|
|
9
11
|
export declare function configureHarness(config: HarnessConfig): void;
|
|
10
12
|
export declare function getWrapper(): ComponentType<WrapperProps>;
|
|
13
|
+
export declare function getDebugTreeConfig(): Omit<DebugTreeProps, 'children'> | null;
|
|
11
14
|
//# sourceMappingURL=configureHarness.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"configureHarness.d.ts","sourceRoot":"","sources":["../src/configureHarness.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,aAAa,EAAE,SAAS,EAAC,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"configureHarness.d.ts","sourceRoot":"","sources":["../src/configureHarness.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,aAAa,EAAE,SAAS,EAAC,MAAM,OAAO,CAAC;AAC/C,OAAO,EAAC,cAAc,EAAC,MAAM,aAAa,CAAC;AAE3C,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,SAAS,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CACjC;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,EAAE,aAAa,CAAC,YAAY,CAAC,CAAC;IACtC,SAAS,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC,cAAc,EAAE,UAAU,CAAC,CAAC;CACxD;AAOD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI,CAO5D;AAED,wBAAgB,UAAU,IAAI,aAAa,CAAC,YAAY,CAAC,CAExD;AAED,wBAAgB,kBAAkB,IAAI,IAAI,CAAC,cAAc,EAAE,UAAU,CAAC,GAAG,IAAI,CAI5E"}
|
package/dist/configureHarness.js
CHANGED
|
@@ -2,12 +2,26 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.configureHarness = configureHarness;
|
|
4
4
|
exports.getWrapper = getWrapper;
|
|
5
|
+
exports.getDebugTreeConfig = getDebugTreeConfig;
|
|
5
6
|
const DefaultWrapper = ({ children }) => children;
|
|
6
7
|
let globalWrapper = null;
|
|
8
|
+
let globalDebugTree = null;
|
|
7
9
|
function configureHarness(config) {
|
|
8
|
-
|
|
10
|
+
if (config.wrapper) {
|
|
11
|
+
globalWrapper = config.wrapper;
|
|
12
|
+
}
|
|
13
|
+
if (config.debugTree !== undefined) {
|
|
14
|
+
globalDebugTree = config.debugTree;
|
|
15
|
+
}
|
|
9
16
|
}
|
|
10
17
|
function getWrapper() {
|
|
11
18
|
return globalWrapper || DefaultWrapper;
|
|
12
19
|
}
|
|
20
|
+
function getDebugTreeConfig() {
|
|
21
|
+
if (!globalDebugTree)
|
|
22
|
+
return null;
|
|
23
|
+
if (globalDebugTree === true)
|
|
24
|
+
return {};
|
|
25
|
+
return globalDebugTree;
|
|
26
|
+
}
|
|
13
27
|
//# sourceMappingURL=configureHarness.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"configureHarness.js","sourceRoot":"","sources":["../src/configureHarness.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"configureHarness.js","sourceRoot":"","sources":["../src/configureHarness.ts"],"names":[],"mappings":";;AAkBA,4CAOC;AAED,gCAEC;AAED,gDAIC;AAtBD,MAAM,cAAc,GAAG,CAAC,EAAC,QAAQ,EAAe,EAAE,EAAE,CAAC,QAAQ,CAAC;AAE9D,IAAI,aAAa,GAAuC,IAAI,CAAC;AAC7D,IAAI,eAAe,GAAsD,IAAI,CAAC;AAE9E,SAAgB,gBAAgB,CAAC,MAAqB;IACpD,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QACnB,aAAa,GAAG,MAAM,CAAC,OAAO,CAAC;IACjC,CAAC;IACD,IAAI,MAAM,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;QACnC,eAAe,GAAG,MAAM,CAAC,SAAS,CAAC;IACrC,CAAC;AACH,CAAC;AAED,SAAgB,UAAU;IACxB,OAAO,aAAa,IAAI,cAAc,CAAC;AACzC,CAAC;AAED,SAAgB,kBAAkB;IAChC,IAAI,CAAC,eAAe;QAAE,OAAO,IAAI,CAAC;IAClC,IAAI,eAAe,KAAK,IAAI;QAAE,OAAO,EAAE,CAAC;IACxC,OAAO,eAAe,CAAC;AACzB,CAAC"}
|
package/dist/environment.js
CHANGED
|
@@ -27,7 +27,9 @@ class CustomDetoxEnvironment extends DetoxCircusEnvironment {
|
|
|
27
27
|
const {element, by, waitFor} = this.global;
|
|
28
28
|
|
|
29
29
|
await element(by.id('debug-tree-control')).replaceText('dump');
|
|
30
|
-
await waitFor(element(by.id('debug-tree-output')))
|
|
30
|
+
await waitFor(element(by.id('debug-tree-output')))
|
|
31
|
+
.toExist()
|
|
32
|
+
.withTimeout(3000);
|
|
31
33
|
|
|
32
34
|
const attrs = await element(by.id('debug-tree-output')).getAttributes();
|
|
33
35
|
const tree = attrs.text || attrs.label || '[]';
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { registerComponent } from './ComponentRegistry';
|
|
2
2
|
export { ComponentHarness } from './ComponentHarness';
|
|
3
|
-
export { configureHarness, WrapperProps } from './configureHarness';
|
|
4
|
-
export { DebugTree } from './DebugTree';
|
|
3
|
+
export { configureHarness, WrapperProps, HarnessConfig } from './configureHarness';
|
|
4
|
+
export { DebugTree, DebugTreeProps } from './DebugTree';
|
|
5
5
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,iBAAiB,EAAC,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAC,gBAAgB,EAAC,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAC,gBAAgB,EAAE,YAAY,EAAC,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,iBAAiB,EAAC,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAC,gBAAgB,EAAC,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAC,gBAAgB,EAAE,YAAY,EAAE,aAAa,EAAC,MAAM,oBAAoB,CAAC;AACjF,OAAO,EAAC,SAAS,EAAE,cAAc,EAAC,MAAM,aAAa,CAAC"}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,yDAAsD;AAA9C,sHAAA,iBAAiB,OAAA;AACzB,uDAAoD;AAA5C,oHAAA,gBAAgB,OAAA;AACxB,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,yDAAsD;AAA9C,sHAAA,iBAAiB,OAAA;AACzB,uDAAoD;AAA5C,oHAAA,gBAAgB,OAAA;AACxB,uDAAiF;AAAzE,oHAAA,gBAAgB,OAAA;AACxB,yCAAsD;AAA9C,sGAAA,SAAS,OAAA"}
|
package/dist/mount.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mount.d.ts","sourceRoot":"","sources":["../src/mount.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,YAAY,EAAE,IAAI,CAAC;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,cAAc;IAC7B,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC,qBAAqB,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChD,cAAc,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/C;AAOD,wBAAgB,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAE3C;AAED,KAAK,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC,CAAC;AAExE,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC,CAYzD;AAED,wBAAsB,KAAK,CAAC,aAAa,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"mount.d.ts","sourceRoot":"","sources":["../src/mount.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,YAAY,EAAE,IAAI,CAAC;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,cAAc;IAC7B,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC,qBAAqB,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChD,cAAc,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/C;AAOD,wBAAgB,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAE3C;AAED,KAAK,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC,CAAC;AAExE,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC,CAYzD;AAED,wBAAsB,KAAK,CAAC,aAAa,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAqCpF;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,CAetD"}
|
package/dist/mount.js
CHANGED
|
@@ -41,38 +41,24 @@ async function mount(componentName, props) {
|
|
|
41
41
|
}
|
|
42
42
|
});
|
|
43
43
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
// Harness sets id '0' for the initial launch-args mount
|
|
55
|
-
try {
|
|
56
|
-
await waitFor(element(by.id('detox-mount-id')))
|
|
57
|
-
.toHaveText('0')
|
|
58
|
-
.withTimeout(5000);
|
|
59
|
-
}
|
|
60
|
-
catch (e) {
|
|
61
|
-
await assertNoRenderError(); // Throws with the actual error if one exists
|
|
62
|
-
throw e; // Re-throw original timeout if no render error found
|
|
63
|
-
}
|
|
64
|
-
await assertNoRenderError();
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
await element(by.id('detox-harness-control')).replaceText(JSON.stringify(payload));
|
|
44
|
+
const launchArgs = { detoxComponentName: componentName };
|
|
45
|
+
Object.entries(payload.props).forEach(([key, value]) => {
|
|
46
|
+
launchArgs[`detoxProp_${key}`] = value;
|
|
47
|
+
});
|
|
48
|
+
payload.spies.forEach((name) => {
|
|
49
|
+
launchArgs[`detoxSpy_${name}`] = true;
|
|
50
|
+
});
|
|
51
|
+
await device.launchApp({ newInstance: true, launchArgs });
|
|
52
|
+
appLaunched = true;
|
|
53
|
+
// Harness sets id '0' for the initial launch-args mount
|
|
68
54
|
try {
|
|
69
55
|
await waitFor(element(by.id('detox-mount-id')))
|
|
70
|
-
.toHaveText(
|
|
56
|
+
.toHaveText('0')
|
|
71
57
|
.withTimeout(5000);
|
|
72
58
|
}
|
|
73
59
|
catch (e) {
|
|
74
|
-
await assertNoRenderError();
|
|
75
|
-
throw e;
|
|
60
|
+
await assertNoRenderError(); // Throws with the actual error if one exists
|
|
61
|
+
throw e; // Re-throw original timeout if no render error found
|
|
76
62
|
}
|
|
77
63
|
await assertNoRenderError();
|
|
78
64
|
}
|
package/dist/mount.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mount.js","sourceRoot":"","sources":["../src/mount.ts"],"names":[],"mappings":";;AAgBA,kBAEC;AAID,kDAYC;AAED,
|
|
1
|
+
{"version":3,"file":"mount.js","sourceRoot":"","sources":["../src/mount.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/ComponentHarness.tsx
CHANGED
|
@@ -2,7 +2,8 @@ import React, {Component as ReactComponent, useState, useRef, useCallback, React
|
|
|
2
2
|
import {View, Text, TextInput, ScrollView} from 'react-native';
|
|
3
3
|
import {LaunchArguments} from 'react-native-launch-arguments';
|
|
4
4
|
import {getComponent} from './ComponentRegistry';
|
|
5
|
-
import {getWrapper} from './configureHarness';
|
|
5
|
+
import {getWrapper, getDebugTreeConfig} from './configureHarness';
|
|
6
|
+
import {DebugTree} from './DebugTree';
|
|
6
7
|
|
|
7
8
|
interface ErrorBoundaryState {
|
|
8
9
|
error: Error | null;
|
|
@@ -57,9 +58,9 @@ export function ComponentHarness() {
|
|
|
57
58
|
const launchArgs = LaunchArguments.value() as Record<string, any>;
|
|
58
59
|
const [mountPayload, setMountPayload] = useState<MountPayload | null>(null);
|
|
59
60
|
|
|
60
|
-
const handleControl = useCallback((text: string) => {
|
|
61
|
+
const handleControl = useCallback((e: {nativeEvent: {text: string}}) => {
|
|
61
62
|
try {
|
|
62
|
-
setMountPayload(JSON.parse(text));
|
|
63
|
+
setMountPayload(JSON.parse(e.nativeEvent.text));
|
|
63
64
|
} catch (_e) {}
|
|
64
65
|
}, []);
|
|
65
66
|
|
|
@@ -78,7 +79,19 @@ export function ComponentHarness() {
|
|
|
78
79
|
|
|
79
80
|
return (
|
|
80
81
|
<View style={{flex: 1}}>
|
|
81
|
-
<TextInput
|
|
82
|
+
<TextInput
|
|
83
|
+
testID="detox-harness-control"
|
|
84
|
+
onEndEditing={handleControl}
|
|
85
|
+
style={{
|
|
86
|
+
position: 'absolute',
|
|
87
|
+
bottom: 0,
|
|
88
|
+
left: 0,
|
|
89
|
+
right: 0,
|
|
90
|
+
height: 44,
|
|
91
|
+
opacity: 0.01,
|
|
92
|
+
zIndex: 9999,
|
|
93
|
+
}}
|
|
94
|
+
/>
|
|
82
95
|
{activeMount && (
|
|
83
96
|
<>
|
|
84
97
|
<Text testID="detox-mount-id" style={{height: 1}}>
|
|
@@ -128,18 +141,23 @@ function ComponentRenderer({mount}: {mount: MountPayload}) {
|
|
|
128
141
|
|
|
129
142
|
const props = {...defaultProps, ...(mount.props || {}), ...spyProps};
|
|
130
143
|
const Wrapper = getWrapper();
|
|
144
|
+
const debugTreeConfig = getDebugTreeConfig();
|
|
145
|
+
|
|
146
|
+
const content = (
|
|
147
|
+
<View testID="component-harness-root" style={{flex: 1}}>
|
|
148
|
+
<Component {...props} />
|
|
149
|
+
{spyNames.map((name) => (
|
|
150
|
+
<View key={name}>
|
|
151
|
+
<Text testID={`spy-${name}-count`}>{String(spyData[name].count)}</Text>
|
|
152
|
+
<Text testID={`spy-${name}-lastArgs`}>{JSON.stringify(spyData[name].lastArgs)}</Text>
|
|
153
|
+
</View>
|
|
154
|
+
))}
|
|
155
|
+
</View>
|
|
156
|
+
);
|
|
131
157
|
|
|
132
158
|
return (
|
|
133
159
|
<Wrapper launchArgs={mount.props || {}}>
|
|
134
|
-
<
|
|
135
|
-
<Component {...props} />
|
|
136
|
-
{spyNames.map((name) => (
|
|
137
|
-
<View key={name}>
|
|
138
|
-
<Text testID={`spy-${name}-count`}>{String(spyData[name].count)}</Text>
|
|
139
|
-
<Text testID={`spy-${name}-lastArgs`}>{JSON.stringify(spyData[name].lastArgs)}</Text>
|
|
140
|
-
</View>
|
|
141
|
-
))}
|
|
142
|
-
</View>
|
|
160
|
+
{debugTreeConfig ? <DebugTree {...debugTreeConfig}>{content}</DebugTree> : content}
|
|
143
161
|
</Wrapper>
|
|
144
162
|
);
|
|
145
163
|
}
|
package/src/configureHarness.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {ComponentType, ReactNode} from 'react';
|
|
2
|
+
import {DebugTreeProps} from './DebugTree';
|
|
2
3
|
|
|
3
4
|
export interface WrapperProps {
|
|
4
5
|
children: ReactNode;
|
|
@@ -6,17 +7,30 @@ export interface WrapperProps {
|
|
|
6
7
|
}
|
|
7
8
|
|
|
8
9
|
export interface HarnessConfig {
|
|
9
|
-
wrapper
|
|
10
|
+
wrapper?: ComponentType<WrapperProps>;
|
|
11
|
+
debugTree?: boolean | Omit<DebugTreeProps, 'children'>;
|
|
10
12
|
}
|
|
11
13
|
|
|
12
14
|
const DefaultWrapper = ({children}: WrapperProps) => children;
|
|
13
15
|
|
|
14
16
|
let globalWrapper: ComponentType<WrapperProps> | null = null;
|
|
17
|
+
let globalDebugTree: boolean | Omit<DebugTreeProps, 'children'> | null = null;
|
|
15
18
|
|
|
16
19
|
export function configureHarness(config: HarnessConfig): void {
|
|
17
|
-
|
|
20
|
+
if (config.wrapper) {
|
|
21
|
+
globalWrapper = config.wrapper;
|
|
22
|
+
}
|
|
23
|
+
if (config.debugTree !== undefined) {
|
|
24
|
+
globalDebugTree = config.debugTree;
|
|
25
|
+
}
|
|
18
26
|
}
|
|
19
27
|
|
|
20
28
|
export function getWrapper(): ComponentType<WrapperProps> {
|
|
21
29
|
return globalWrapper || DefaultWrapper;
|
|
22
30
|
}
|
|
31
|
+
|
|
32
|
+
export function getDebugTreeConfig(): Omit<DebugTreeProps, 'children'> | null {
|
|
33
|
+
if (!globalDebugTree) return null;
|
|
34
|
+
if (globalDebugTree === true) return {};
|
|
35
|
+
return globalDebugTree;
|
|
36
|
+
}
|
package/src/environment.js
CHANGED
|
@@ -27,7 +27,9 @@ class CustomDetoxEnvironment extends DetoxCircusEnvironment {
|
|
|
27
27
|
const {element, by, waitFor} = this.global;
|
|
28
28
|
|
|
29
29
|
await element(by.id('debug-tree-control')).replaceText('dump');
|
|
30
|
-
await waitFor(element(by.id('debug-tree-output')))
|
|
30
|
+
await waitFor(element(by.id('debug-tree-output')))
|
|
31
|
+
.toExist()
|
|
32
|
+
.withTimeout(3000);
|
|
31
33
|
|
|
32
34
|
const attrs = await element(by.id('debug-tree-output')).getAttributes();
|
|
33
35
|
const tree = attrs.text || attrs.label || '[]';
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export {registerComponent} from './ComponentRegistry';
|
|
2
2
|
export {ComponentHarness} from './ComponentHarness';
|
|
3
|
-
export {configureHarness, WrapperProps} from './configureHarness';
|
|
4
|
-
export {DebugTree} from './DebugTree';
|
|
3
|
+
export {configureHarness, WrapperProps, HarnessConfig} from './configureHarness';
|
|
4
|
+
export {DebugTree, DebugTreeProps} from './DebugTree';
|
package/src/mount.ts
CHANGED
|
@@ -52,37 +52,23 @@ export async function mount(componentName: string, props?: MountProps): Promise<
|
|
|
52
52
|
});
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
// Harness sets id '0' for the initial launch-args mount
|
|
66
|
-
try {
|
|
67
|
-
await waitFor(element(by.id('detox-mount-id')))
|
|
68
|
-
.toHaveText('0')
|
|
69
|
-
.withTimeout(5000);
|
|
70
|
-
} catch (e) {
|
|
71
|
-
await assertNoRenderError(); // Throws with the actual error if one exists
|
|
72
|
-
throw e; // Re-throw original timeout if no render error found
|
|
73
|
-
}
|
|
74
|
-
await assertNoRenderError();
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
await element(by.id('detox-harness-control')).replaceText(JSON.stringify(payload));
|
|
55
|
+
const launchArgs: Record<string, any> = {detoxComponentName: componentName};
|
|
56
|
+
Object.entries(payload.props).forEach(([key, value]) => {
|
|
57
|
+
launchArgs[`detoxProp_${key}`] = value;
|
|
58
|
+
});
|
|
59
|
+
payload.spies.forEach((name) => {
|
|
60
|
+
launchArgs[`detoxSpy_${name}`] = true;
|
|
61
|
+
});
|
|
62
|
+
await device.launchApp({newInstance: true, launchArgs});
|
|
63
|
+
appLaunched = true;
|
|
64
|
+
// Harness sets id '0' for the initial launch-args mount
|
|
79
65
|
try {
|
|
80
66
|
await waitFor(element(by.id('detox-mount-id')))
|
|
81
|
-
.toHaveText(
|
|
67
|
+
.toHaveText('0')
|
|
82
68
|
.withTimeout(5000);
|
|
83
69
|
} catch (e) {
|
|
84
|
-
await assertNoRenderError();
|
|
85
|
-
throw e;
|
|
70
|
+
await assertNoRenderError(); // Throws with the actual error if one exists
|
|
71
|
+
throw e; // Re-throw original timeout if no render error found
|
|
86
72
|
}
|
|
87
73
|
await assertNoRenderError();
|
|
88
74
|
}
|