@react-native-harness/runtime 1.2.0 → 1.4.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/collector/functions.d.ts +3 -3
- package/dist/collector/functions.d.ts.map +1 -1
- package/dist/collector/functions.js +8 -0
- package/dist/collector/types.d.ts +3 -2
- package/dist/collector/types.d.ts.map +1 -1
- package/dist/collector/validation.d.ts +2 -2
- package/dist/collector/validation.d.ts.map +1 -1
- package/dist/device/index.d.ts +12 -0
- package/dist/device/index.d.ts.map +1 -0
- package/dist/device/index.js +62 -0
- package/dist/hmr.d.ts +2 -0
- package/dist/hmr.d.ts.map +1 -0
- package/dist/hmr.js +5 -0
- package/dist/logbox.d.ts +4 -0
- package/dist/logbox.d.ts.map +1 -0
- package/dist/logbox.js +18 -0
- package/dist/runner/hooks.d.ts +2 -1
- package/dist/runner/hooks.d.ts.map +1 -1
- package/dist/runner/hooks.js +27 -17
- package/dist/runner/runSuite.d.ts.map +1 -1
- package/dist/runner/runSuite.js +134 -35
- package/dist/runner/test-context.d.ts +16 -0
- package/dist/runner/test-context.d.ts.map +1 -0
- package/dist/runner/test-context.js +57 -0
- package/dist/runner/types.d.ts +2 -1
- package/dist/runner/types.d.ts.map +1 -1
- package/dist/test-utils/react-native-url-polyfill.d.ts +9 -0
- package/dist/test-utils/react-native-url-polyfill.d.ts.map +1 -0
- package/dist/test-utils/react-native-url-polyfill.js +1 -0
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/out-tsc/vitest/src/__tests__/device.test.d.ts +2 -0
- package/out-tsc/vitest/src/__tests__/device.test.d.ts.map +1 -0
- package/out-tsc/vitest/src/__tests__/logbox.test.d.ts +2 -0
- package/out-tsc/vitest/src/__tests__/logbox.test.d.ts.map +1 -0
- package/out-tsc/vitest/src/__tests__/runner-context.test.d.ts +2 -0
- package/out-tsc/vitest/src/__tests__/runner-context.test.d.ts.map +1 -0
- package/out-tsc/vitest/src/collector/functions.d.ts +3 -3
- package/out-tsc/vitest/src/collector/functions.d.ts.map +1 -1
- package/out-tsc/vitest/src/collector/types.d.ts +3 -2
- package/out-tsc/vitest/src/collector/types.d.ts.map +1 -1
- package/out-tsc/vitest/src/collector/validation.d.ts +2 -2
- package/out-tsc/vitest/src/collector/validation.d.ts.map +1 -1
- package/out-tsc/vitest/src/device/index.d.ts +12 -0
- package/out-tsc/vitest/src/device/index.d.ts.map +1 -0
- package/out-tsc/vitest/src/hmr.d.ts +2 -0
- package/out-tsc/vitest/src/hmr.d.ts.map +1 -0
- package/out-tsc/vitest/src/logbox.d.ts +4 -0
- package/out-tsc/vitest/src/logbox.d.ts.map +1 -0
- package/out-tsc/vitest/src/runner/hooks.d.ts +2 -1
- package/out-tsc/vitest/src/runner/hooks.d.ts.map +1 -1
- package/out-tsc/vitest/src/runner/runSuite.d.ts.map +1 -1
- package/out-tsc/vitest/src/runner/test-context.d.ts +16 -0
- package/out-tsc/vitest/src/runner/test-context.d.ts.map +1 -0
- package/out-tsc/vitest/src/runner/types.d.ts +2 -1
- package/out-tsc/vitest/src/runner/types.d.ts.map +1 -1
- package/out-tsc/vitest/src/test-utils/react-native-url-polyfill.d.ts +9 -0
- package/out-tsc/vitest/src/test-utils/react-native-url-polyfill.d.ts.map +1 -0
- package/out-tsc/vitest/src/ui/state.d.ts +1 -1
- package/out-tsc/vitest/tsconfig.spec.tsbuildinfo +1 -1
- package/out-tsc/vitest/vite.config.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/runner-context.test.ts +532 -0
- package/src/collector/functions.ts +14 -4
- package/src/collector/types.ts +4 -1
- package/src/collector/validation.ts +2 -2
- package/src/runner/hooks.ts +43 -19
- package/src/runner/runSuite.ts +178 -38
- package/src/runner/test-context.ts +84 -0
- package/src/runner/types.ts +3 -0
- package/src/test-utils/react-native-url-polyfill.ts +1 -0
- package/vite.config.ts +4 -0
package/src/runner/hooks.ts
CHANGED
|
@@ -1,12 +1,33 @@
|
|
|
1
|
-
import type { TestSuite } from '@react-native-harness/bridge';
|
|
1
|
+
import type { SuiteHookFn, TestFn, TestSuite } from '@react-native-harness/bridge';
|
|
2
|
+
import type { ActiveTestContext } from './types.js';
|
|
2
3
|
|
|
3
4
|
export type HookType = 'beforeEach' | 'afterEach' | 'beforeAll' | 'afterAll';
|
|
4
5
|
|
|
5
6
|
const collectInheritedHooks = (
|
|
6
7
|
suite: TestSuite,
|
|
7
|
-
hookType:
|
|
8
|
-
):
|
|
9
|
-
const hooks:
|
|
8
|
+
hookType: 'beforeEach' | 'afterEach'
|
|
9
|
+
): TestFn[] => {
|
|
10
|
+
const hooks: TestFn[] = [];
|
|
11
|
+
const suiteChain: TestSuite[] = [];
|
|
12
|
+
|
|
13
|
+
let current: TestSuite | undefined = suite;
|
|
14
|
+
while (current) {
|
|
15
|
+
suiteChain.unshift(current);
|
|
16
|
+
current = current.parent;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
for (const currentSuite of suiteChain) {
|
|
20
|
+
hooks.push(...currentSuite[hookType]);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return hooks;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const collectSuiteHooks = (
|
|
27
|
+
suite: TestSuite,
|
|
28
|
+
hookType: 'beforeAll' | 'afterAll'
|
|
29
|
+
): SuiteHookFn[] => {
|
|
30
|
+
const hooks: SuiteHookFn[] = [];
|
|
10
31
|
const suiteChain: TestSuite[] = [];
|
|
11
32
|
|
|
12
33
|
// Collect all suites from current to root
|
|
@@ -16,23 +37,15 @@ const collectInheritedHooks = (
|
|
|
16
37
|
currentSuite = currentSuite.parent;
|
|
17
38
|
}
|
|
18
39
|
|
|
19
|
-
if (hookType === '
|
|
20
|
-
//
|
|
40
|
+
if (hookType === 'beforeAll') {
|
|
41
|
+
// Run parent suite hooks before child suite hooks.
|
|
21
42
|
for (let i = suiteChain.length - 1; i >= 0; i--) {
|
|
22
|
-
|
|
23
|
-
hooks.push(...suiteChain[i].beforeEach);
|
|
24
|
-
} else {
|
|
25
|
-
hooks.push(...suiteChain[i].beforeAll);
|
|
26
|
-
}
|
|
43
|
+
hooks.push(...suiteChain[i].beforeAll);
|
|
27
44
|
}
|
|
28
45
|
} else {
|
|
29
|
-
//
|
|
46
|
+
// Run child suite hooks before parent suite hooks.
|
|
30
47
|
for (const suiteInChain of suiteChain) {
|
|
31
|
-
|
|
32
|
-
hooks.push(...suiteInChain.afterEach);
|
|
33
|
-
} else {
|
|
34
|
-
hooks.push(...suiteInChain.afterAll);
|
|
35
|
-
}
|
|
48
|
+
hooks.push(...suiteInChain.afterAll);
|
|
36
49
|
}
|
|
37
50
|
}
|
|
38
51
|
|
|
@@ -41,11 +54,22 @@ const collectInheritedHooks = (
|
|
|
41
54
|
|
|
42
55
|
export const runHooks = async (
|
|
43
56
|
suite: TestSuite,
|
|
44
|
-
hookType: HookType
|
|
57
|
+
hookType: HookType,
|
|
58
|
+
context?: ActiveTestContext,
|
|
45
59
|
): Promise<void> => {
|
|
60
|
+
if (hookType === 'beforeAll' || hookType === 'afterAll') {
|
|
61
|
+
const hooks = collectSuiteHooks(suite, hookType);
|
|
62
|
+
|
|
63
|
+
for (const hook of hooks) {
|
|
64
|
+
await hook();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
46
70
|
const hooks = collectInheritedHooks(suite, hookType);
|
|
47
71
|
|
|
48
72
|
for (const hook of hooks) {
|
|
49
|
-
await hook();
|
|
73
|
+
await hook(context as ActiveTestContext);
|
|
50
74
|
}
|
|
51
75
|
};
|
package/src/runner/runSuite.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type {
|
|
2
|
+
HarnessTaskContext,
|
|
2
3
|
TestCase,
|
|
3
4
|
TestResult,
|
|
4
5
|
TestSuite,
|
|
@@ -11,7 +12,63 @@ import {
|
|
|
11
12
|
import { flushExpectTestState } from '../expect/errors.js';
|
|
12
13
|
import { runHooks } from './hooks.js';
|
|
13
14
|
import { getTestExecutionError } from './errors.js';
|
|
14
|
-
import { TestRunnerContext } from './types.js';
|
|
15
|
+
import { ActiveTestContext, TestRunnerContext } from './types.js';
|
|
16
|
+
import {
|
|
17
|
+
createTestContext,
|
|
18
|
+
createTestLifecycleState,
|
|
19
|
+
isSkipTestError,
|
|
20
|
+
runOnTestFailed,
|
|
21
|
+
runOnTestFinished,
|
|
22
|
+
} from './test-context.js';
|
|
23
|
+
|
|
24
|
+
const getAncestorTitles = (suite: TestSuite): string[] => {
|
|
25
|
+
const ancestorTitles: string[] = [];
|
|
26
|
+
let currentSuite = suite.parent;
|
|
27
|
+
|
|
28
|
+
while (currentSuite) {
|
|
29
|
+
if (currentSuite.name !== 'root') {
|
|
30
|
+
ancestorTitles.unshift(currentSuite.name);
|
|
31
|
+
}
|
|
32
|
+
currentSuite = currentSuite.parent;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (suite.name !== 'root') {
|
|
36
|
+
ancestorTitles.push(suite.name);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return ancestorTitles;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const getFullName = (ancestorTitles: string[], testName: string): string =>
|
|
43
|
+
[...ancestorTitles, testName].join(' ');
|
|
44
|
+
|
|
45
|
+
const emitTestFinished = (
|
|
46
|
+
context: TestRunnerContext,
|
|
47
|
+
options: {
|
|
48
|
+
test: TestCase;
|
|
49
|
+
suite: TestSuite;
|
|
50
|
+
startedAt: number;
|
|
51
|
+
duration: number;
|
|
52
|
+
status: 'passed' | 'failed' | 'skipped' | 'todo';
|
|
53
|
+
error?: TestResult['error'];
|
|
54
|
+
},
|
|
55
|
+
) => {
|
|
56
|
+
const ancestorTitles = getAncestorTitles(options.suite);
|
|
57
|
+
|
|
58
|
+
context.events.emit({
|
|
59
|
+
type: 'test-finished',
|
|
60
|
+
file: context.testFilePath,
|
|
61
|
+
suite: options.suite.name,
|
|
62
|
+
name: options.test.name,
|
|
63
|
+
ancestorTitles,
|
|
64
|
+
fullName: getFullName(ancestorTitles, options.test.name),
|
|
65
|
+
startedAt: options.startedAt,
|
|
66
|
+
declarationMode: options.test.declarationMode,
|
|
67
|
+
duration: options.duration,
|
|
68
|
+
error: options.error,
|
|
69
|
+
status: options.status,
|
|
70
|
+
});
|
|
71
|
+
};
|
|
15
72
|
|
|
16
73
|
declare global {
|
|
17
74
|
var HARNESS_TEST_PATH: string;
|
|
@@ -22,14 +79,40 @@ const runTest = async (
|
|
|
22
79
|
suite: TestSuite,
|
|
23
80
|
context: TestRunnerContext,
|
|
24
81
|
): Promise<TestResult> => {
|
|
25
|
-
const
|
|
82
|
+
const startedAt = Date.now();
|
|
83
|
+
const task: HarnessTaskContext = {
|
|
84
|
+
name: test.name,
|
|
85
|
+
type: 'test',
|
|
86
|
+
mode:
|
|
87
|
+
test.status === 'active'
|
|
88
|
+
? 'run'
|
|
89
|
+
: test.status === 'skipped'
|
|
90
|
+
? 'skip'
|
|
91
|
+
: 'todo',
|
|
92
|
+
file: {
|
|
93
|
+
name: context.testFilePath,
|
|
94
|
+
},
|
|
95
|
+
suite: {
|
|
96
|
+
name: suite.name,
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
const lifecycleState = createTestLifecycleState();
|
|
100
|
+
const activeTestContext: ActiveTestContext = createTestContext(
|
|
101
|
+
task,
|
|
102
|
+
lifecycleState,
|
|
103
|
+
);
|
|
26
104
|
|
|
27
105
|
// Emit test-started event
|
|
106
|
+
const ancestorTitles = getAncestorTitles(suite);
|
|
28
107
|
context.events.emit({
|
|
29
108
|
type: 'test-started',
|
|
30
109
|
name: test.name,
|
|
31
110
|
suite: suite.name,
|
|
32
111
|
file: context.testFilePath,
|
|
112
|
+
ancestorTitles,
|
|
113
|
+
fullName: getFullName(ancestorTitles, test.name),
|
|
114
|
+
startedAt,
|
|
115
|
+
declarationMode: test.declarationMode,
|
|
33
116
|
});
|
|
34
117
|
|
|
35
118
|
try {
|
|
@@ -38,14 +121,16 @@ const runTest = async (
|
|
|
38
121
|
name: test.name,
|
|
39
122
|
status: 'skipped' as const,
|
|
40
123
|
duration: 0,
|
|
124
|
+
ancestorTitles,
|
|
125
|
+
fullName: getFullName(ancestorTitles, test.name),
|
|
126
|
+
startedAt,
|
|
127
|
+
declarationMode: test.declarationMode,
|
|
41
128
|
};
|
|
42
129
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
suite: suite.name,
|
|
48
|
-
file: context.testFilePath,
|
|
130
|
+
emitTestFinished(context, {
|
|
131
|
+
test,
|
|
132
|
+
suite,
|
|
133
|
+
startedAt,
|
|
49
134
|
duration: 0,
|
|
50
135
|
status: 'skipped',
|
|
51
136
|
});
|
|
@@ -59,14 +144,16 @@ const runTest = async (
|
|
|
59
144
|
name: test.name,
|
|
60
145
|
status: 'todo' as const,
|
|
61
146
|
duration: 0,
|
|
147
|
+
ancestorTitles,
|
|
148
|
+
fullName: getFullName(ancestorTitles, test.name),
|
|
149
|
+
startedAt,
|
|
150
|
+
declarationMode: test.declarationMode,
|
|
62
151
|
};
|
|
63
152
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
suite: suite.name,
|
|
69
|
-
file: context.testFilePath,
|
|
153
|
+
emitTestFinished(context, {
|
|
154
|
+
test,
|
|
155
|
+
suite,
|
|
156
|
+
startedAt,
|
|
70
157
|
duration: 0,
|
|
71
158
|
status: 'todo',
|
|
72
159
|
});
|
|
@@ -78,61 +165,105 @@ const runTest = async (
|
|
|
78
165
|
setCurrentExpectTestState(expectTestState);
|
|
79
166
|
|
|
80
167
|
try {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
168
|
+
let didSkip = false;
|
|
169
|
+
|
|
170
|
+
try {
|
|
171
|
+
// Run all beforeEach hooks from the current suite and its parents
|
|
172
|
+
await runHooks(suite, 'beforeEach', activeTestContext);
|
|
173
|
+
|
|
174
|
+
// Run the actual test
|
|
175
|
+
await test.fn(activeTestContext);
|
|
176
|
+
} catch (error) {
|
|
177
|
+
if (!isSkipTestError(error)) {
|
|
178
|
+
throw error;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
didSkip = true;
|
|
182
|
+
} finally {
|
|
183
|
+
// Run all afterEach hooks from the current suite and its parents
|
|
184
|
+
await runHooks(suite, 'afterEach', activeTestContext);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (didSkip) {
|
|
188
|
+
const duration = Date.now() - startedAt;
|
|
189
|
+
|
|
190
|
+
await runOnTestFinished(lifecycleState);
|
|
191
|
+
|
|
192
|
+
const result = {
|
|
193
|
+
name: test.name,
|
|
194
|
+
status: 'skipped' as const,
|
|
195
|
+
duration,
|
|
196
|
+
ancestorTitles,
|
|
197
|
+
fullName: getFullName(ancestorTitles, test.name),
|
|
198
|
+
startedAt,
|
|
199
|
+
declarationMode: test.declarationMode,
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
emitTestFinished(context, {
|
|
203
|
+
test,
|
|
204
|
+
suite,
|
|
205
|
+
startedAt,
|
|
206
|
+
duration,
|
|
207
|
+
status: 'skipped',
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
return result;
|
|
211
|
+
}
|
|
89
212
|
|
|
90
213
|
await flushExpectTestState(expectTestState);
|
|
214
|
+
await runOnTestFinished(lifecycleState);
|
|
91
215
|
} finally {
|
|
92
216
|
setCurrentExpectTestState(undefined);
|
|
93
217
|
}
|
|
94
218
|
|
|
95
|
-
const duration = Date.now() -
|
|
219
|
+
const duration = Date.now() - startedAt;
|
|
96
220
|
|
|
97
221
|
const result = {
|
|
98
222
|
name: test.name,
|
|
99
223
|
status: 'passed' as const,
|
|
100
224
|
duration,
|
|
225
|
+
ancestorTitles,
|
|
226
|
+
fullName: getFullName(ancestorTitles, test.name),
|
|
227
|
+
startedAt,
|
|
228
|
+
declarationMode: test.declarationMode,
|
|
101
229
|
};
|
|
102
230
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
suite: suite.name,
|
|
108
|
-
name: test.name,
|
|
231
|
+
emitTestFinished(context, {
|
|
232
|
+
test,
|
|
233
|
+
suite,
|
|
234
|
+
startedAt,
|
|
109
235
|
duration,
|
|
110
236
|
status: 'passed',
|
|
111
237
|
});
|
|
112
238
|
|
|
113
239
|
return result;
|
|
114
240
|
} catch (error) {
|
|
241
|
+
await runOnTestFailed(lifecycleState);
|
|
242
|
+
await runOnTestFinished(lifecycleState);
|
|
243
|
+
|
|
115
244
|
const testError = await getTestExecutionError(
|
|
116
245
|
error,
|
|
117
246
|
context.testFilePath,
|
|
118
247
|
suite.name,
|
|
119
248
|
test.name,
|
|
120
249
|
);
|
|
121
|
-
const duration = Date.now() -
|
|
250
|
+
const duration = Date.now() - startedAt;
|
|
122
251
|
|
|
123
252
|
const result = {
|
|
124
253
|
name: test.name,
|
|
125
254
|
status: 'failed' as const,
|
|
126
255
|
error: testError.toSerializedJSON(),
|
|
127
256
|
duration,
|
|
257
|
+
ancestorTitles,
|
|
258
|
+
fullName: getFullName(ancestorTitles, test.name),
|
|
259
|
+
startedAt,
|
|
260
|
+
declarationMode: test.declarationMode,
|
|
128
261
|
};
|
|
129
262
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
suite: suite.name,
|
|
135
|
-
name: test.name,
|
|
263
|
+
emitTestFinished(context, {
|
|
264
|
+
test,
|
|
265
|
+
suite,
|
|
266
|
+
startedAt,
|
|
136
267
|
duration,
|
|
137
268
|
error: testError.toSerializedJSON(),
|
|
138
269
|
status: 'failed',
|
|
@@ -157,10 +288,19 @@ export const runSuite = async (
|
|
|
157
288
|
|
|
158
289
|
// Check if suite should be skipped or is todo
|
|
159
290
|
if (suite.status === 'skipped') {
|
|
291
|
+
const testResults = await Promise.all(
|
|
292
|
+
suite.tests.map((test) => runTest({ ...test, status: 'skipped' }, suite, context)),
|
|
293
|
+
);
|
|
294
|
+
const suiteResults = await Promise.all(
|
|
295
|
+
suite.suites.map((childSuite) =>
|
|
296
|
+
runSuite({ ...childSuite, status: 'skipped' }, context),
|
|
297
|
+
),
|
|
298
|
+
);
|
|
299
|
+
|
|
160
300
|
const result = {
|
|
161
301
|
name: suite.name,
|
|
162
|
-
tests:
|
|
163
|
-
suites:
|
|
302
|
+
tests: testResults,
|
|
303
|
+
suites: suiteResults,
|
|
164
304
|
status: 'skipped' as const,
|
|
165
305
|
duration: 0,
|
|
166
306
|
};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import type { HarnessTaskContext } from '@react-native-harness/bridge';
|
|
2
|
+
import type { ActiveTestContext } from './types.js';
|
|
3
|
+
|
|
4
|
+
export type TestLifecycleState = {
|
|
5
|
+
onTestFailed: Array<() => void | Promise<void>>;
|
|
6
|
+
onTestFinished: Array<() => void | Promise<void>>;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export class SkipTestError extends Error {
|
|
10
|
+
note?: string;
|
|
11
|
+
|
|
12
|
+
constructor(note?: string) {
|
|
13
|
+
super(note ?? 'Test skipped');
|
|
14
|
+
this.name = 'SkipTestError';
|
|
15
|
+
this.note = note;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const isSkipTestError = (error: unknown): error is SkipTestError => {
|
|
20
|
+
return error instanceof SkipTestError;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const createSkip = () => {
|
|
24
|
+
function skip(noteOrCondition?: boolean | string, note?: string): void {
|
|
25
|
+
if (typeof noteOrCondition === 'boolean') {
|
|
26
|
+
if (!noteOrCondition) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
throw new SkipTestError(note);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
throw new SkipTestError(noteOrCondition);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return skip as ActiveTestContext['skip'];
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const createOnTestFinished = (state: TestLifecycleState) => {
|
|
40
|
+
return (fn: () => void | Promise<void>): void => {
|
|
41
|
+
state.onTestFinished.push(fn);
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const createOnTestFailed = (state: TestLifecycleState) => {
|
|
46
|
+
return (fn: () => void | Promise<void>): void => {
|
|
47
|
+
state.onTestFailed.push(fn);
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export const createTestLifecycleState = (): TestLifecycleState => {
|
|
52
|
+
return {
|
|
53
|
+
onTestFailed: [],
|
|
54
|
+
onTestFinished: [],
|
|
55
|
+
};
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export const runOnTestFailed = async (
|
|
59
|
+
state: TestLifecycleState,
|
|
60
|
+
): Promise<void> => {
|
|
61
|
+
for (let i = state.onTestFailed.length - 1; i >= 0; i--) {
|
|
62
|
+
await state.onTestFailed[i]();
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export const runOnTestFinished = async (
|
|
67
|
+
state: TestLifecycleState,
|
|
68
|
+
): Promise<void> => {
|
|
69
|
+
for (let i = state.onTestFinished.length - 1; i >= 0; i--) {
|
|
70
|
+
await state.onTestFinished[i]();
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export const createTestContext = (
|
|
75
|
+
task: HarnessTaskContext,
|
|
76
|
+
state: TestLifecycleState,
|
|
77
|
+
): ActiveTestContext => {
|
|
78
|
+
return {
|
|
79
|
+
task,
|
|
80
|
+
onTestFailed: createOnTestFailed(state),
|
|
81
|
+
onTestFinished: createOnTestFinished(state),
|
|
82
|
+
skip: createSkip(),
|
|
83
|
+
};
|
|
84
|
+
};
|
package/src/runner/types.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { EventEmitter } from '../utils/emitter.js';
|
|
2
2
|
import type {
|
|
3
|
+
HarnessTestContext,
|
|
3
4
|
TestRunnerEvents,
|
|
4
5
|
TestSuite,
|
|
5
6
|
TestSuiteResult,
|
|
@@ -12,6 +13,8 @@ export type TestRunnerContext = {
|
|
|
12
13
|
testFilePath: string;
|
|
13
14
|
};
|
|
14
15
|
|
|
16
|
+
export type ActiveTestContext = HarnessTestContext;
|
|
17
|
+
|
|
15
18
|
export type RunTestsOptions = {
|
|
16
19
|
testSuite: TestSuite;
|
|
17
20
|
testFilePath: string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const URL = globalThis.URL;
|
package/vite.config.ts
CHANGED
|
@@ -22,6 +22,10 @@ export default defineConfig(() => ({
|
|
|
22
22
|
alias: {
|
|
23
23
|
'@vitest/spy': path.resolve(__dirname, 'node_modules/@vitest/spy'),
|
|
24
24
|
'@vitest/expect': path.resolve(__dirname, 'node_modules/@vitest/expect'),
|
|
25
|
+
'react-native-url-polyfill': path.resolve(
|
|
26
|
+
__dirname,
|
|
27
|
+
'src/test-utils/react-native-url-polyfill.ts',
|
|
28
|
+
),
|
|
25
29
|
},
|
|
26
30
|
},
|
|
27
31
|
}));
|