@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
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CollectionResult } from '@react-native-harness/bridge';
|
|
1
|
+
import type { CollectionResult, SuiteHookFn } from '@react-native-harness/bridge';
|
|
2
2
|
import type { TestFn } from './types.js';
|
|
3
3
|
export declare const describe: ((name: string, fn: () => void) => void) & {
|
|
4
4
|
skip: (name: string, fn: () => void) => void;
|
|
@@ -14,8 +14,8 @@ export declare const it: ((name: string, fn: TestFn) => void) & {
|
|
|
14
14
|
only: (name: string, fn: TestFn) => void;
|
|
15
15
|
todo: (name: string) => void;
|
|
16
16
|
};
|
|
17
|
-
export declare function beforeAll(fn:
|
|
18
|
-
export declare function afterAll(fn:
|
|
17
|
+
export declare function beforeAll(fn: SuiteHookFn): void;
|
|
18
|
+
export declare function afterAll(fn: SuiteHookFn): void;
|
|
19
19
|
export declare function beforeEach(fn: TestFn): void;
|
|
20
20
|
export declare function afterEach(fn: TestFn): void;
|
|
21
21
|
export declare const collectTests: (fn: () => void | Promise<void>) => Promise<CollectionResult>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"functions.d.ts","sourceRoot":"","sources":["../../src/collector/functions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAGV,gBAAgB,
|
|
1
|
+
{"version":3,"file":"functions.d.ts","sourceRoot":"","sources":["../../src/collector/functions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAGV,gBAAgB,EAChB,WAAW,EACZ,MAAM,8BAA8B,CAAC;AACtC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAqMzC,eAAO,MAAM,QAAQ,UACZ,MAAM,MAAM,MAAM,IAAI;iBAsBd,MAAM,MAAM,MAAM,IAAI;iBAqBtB,MAAM,MAAM,MAAM,IAAI;CAsBtC,CAAC;AAEF,eAAO,MAAM,IAAI,UACR,MAAM,MAAM,MAAM;iBAaV,MAAM,MAAM,MAAM;iBAWlB,MAAM,MAAM,MAAM;iBAWlB,MAAM;CAiBtB,CAAC;AAEF,eAAO,MAAM,EAAE,UAtDN,MAAM,MAAM,MAAM;iBAaV,MAAM,MAAM,MAAM;iBAWlB,MAAM,MAAM,MAAM;iBAWlB,MAAM;CAmBD,CAAC;AAEvB,wBAAgB,SAAS,CAAC,EAAE,EAAE,WAAW,QAQxC;AAED,wBAAgB,QAAQ,CAAC,EAAE,EAAE,WAAW,QAQvC;AAED,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM,QAQpC;AAED,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,QAQnC;AAgBD,eAAO,MAAM,YAAY,GACvB,IAAI,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAC7B,OAAO,CAAC,gBAAgB,CAiB1B,CAAC"}
|
|
@@ -30,10 +30,18 @@ const computeSuiteStatus = (suite, parentContext) => {
|
|
|
30
30
|
return 'active';
|
|
31
31
|
};
|
|
32
32
|
const convertRawTestCaseToTestCase = (rawTest, suiteContext) => {
|
|
33
|
+
const declarationMode = rawTest.options.todo
|
|
34
|
+
? 'todo'
|
|
35
|
+
: rawTest.options.skip
|
|
36
|
+
? 'skip'
|
|
37
|
+
: rawTest.options.only
|
|
38
|
+
? 'only'
|
|
39
|
+
: undefined;
|
|
33
40
|
return {
|
|
34
41
|
name: rawTest.name,
|
|
35
42
|
fn: rawTest.fn,
|
|
36
43
|
status: computeTestStatus(rawTest, suiteContext),
|
|
44
|
+
declarationMode,
|
|
37
45
|
};
|
|
38
46
|
};
|
|
39
47
|
const convertRawTestSuiteToTestSuite = (rawSuite, parentContext = {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { EventEmitter } from '../utils/emitter.js';
|
|
2
|
-
import { TestCollectorEvents, CollectionResult } from '@react-native-harness/bridge';
|
|
3
|
-
export type TestFn = () => void | Promise<void>;
|
|
2
|
+
import { TestCollectorEvents, CollectionResult, type HarnessTestContext } from '@react-native-harness/bridge';
|
|
3
|
+
export type TestFn = (context: HarnessTestContext) => void | Promise<void>;
|
|
4
|
+
export type SuiteHookFn = () => void | Promise<void>;
|
|
4
5
|
export type TestCollectorEventsEmitter = EventEmitter<TestCollectorEvents>;
|
|
5
6
|
export type TestCollector = {
|
|
6
7
|
events: TestCollectorEventsEmitter;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/collector/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EACL,mBAAmB,EACnB,gBAAgB,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/collector/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EACL,mBAAmB,EACnB,gBAAgB,EAChB,KAAK,kBAAkB,EACxB,MAAM,8BAA8B,CAAC;AAEtC,MAAM,MAAM,MAAM,GAAG,CAAC,OAAO,EAAE,kBAAkB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAE3E,MAAM,MAAM,WAAW,GAAG,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAErD,MAAM,MAAM,0BAA0B,GAAG,YAAY,CAAC,mBAAmB,CAAC,CAAC;AAE3E,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,EAAE,0BAA0B,CAAC;IACnC,OAAO,EAAE,CACP,EAAE,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,EAC9B,YAAY,EAAE,MAAM,KACjB,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAC/B,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { TestFn } from './types.js';
|
|
1
|
+
import { TestFn, SuiteHookFn } from './types.js';
|
|
2
2
|
export declare const validateTestName: (name: string, functionName: string) => void;
|
|
3
|
-
export declare const validateTestFunction: (fn: TestFn, functionName: string) => void;
|
|
3
|
+
export declare const validateTestFunction: (fn: TestFn | SuiteHookFn, functionName: string) => void;
|
|
4
4
|
//# sourceMappingURL=validation.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/collector/validation.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/collector/validation.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEjD,eAAO,MAAM,gBAAgB,GAAI,MAAM,MAAM,EAAE,cAAc,MAAM,KAAG,IAMrE,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAC/B,IAAI,MAAM,GAAG,WAAW,EACxB,cAAc,MAAM,KACnB,IAMF,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { DeviceStateMutation, PermissionName } from '@react-native-harness/platforms';
|
|
2
|
+
export declare const device: {
|
|
3
|
+
permissions: {
|
|
4
|
+
grant: (permission: PermissionName) => Promise<DeviceStateMutation | null>;
|
|
5
|
+
revoke: (permission: PermissionName) => Promise<DeviceStateMutation | null>;
|
|
6
|
+
reset: (permission: PermissionName) => Promise<DeviceStateMutation | null>;
|
|
7
|
+
grantAll: () => Promise<DeviceStateMutation | null>;
|
|
8
|
+
denyAll: () => Promise<DeviceStateMutation | null>;
|
|
9
|
+
resetAll: () => Promise<DeviceStateMutation | null>;
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/device/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,mBAAmB,EACnB,cAAc,EACf,MAAM,iCAAiC,CAAC;AA2BzC,eAAO,MAAM,MAAM;;4BAEW,cAAc,KAAG,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC;6BAOnD,cAAc,KAAG,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC;4BAOrD,cAAc,KAAG,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC;wBAO1D,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC;uBAMpC,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC;wBAMlC,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC;;CAO1D,CAAC"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { getHandle } from '../client/store.js';
|
|
2
|
+
const warnIfNeeded = (warning) => {
|
|
3
|
+
if (warning) {
|
|
4
|
+
console.warn(warning);
|
|
5
|
+
}
|
|
6
|
+
};
|
|
7
|
+
const applyPermissionCommand = async (command) => {
|
|
8
|
+
const result = await getHandle().applyDevicePermission(command);
|
|
9
|
+
warnIfNeeded(result.warning);
|
|
10
|
+
if (!result.mutationId) {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
return {
|
|
14
|
+
id: result.mutationId,
|
|
15
|
+
revert: async () => {
|
|
16
|
+
await getHandle().revertDevicePermission(result.mutationId);
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
export const device = {
|
|
21
|
+
permissions: {
|
|
22
|
+
grant: async (permission) => {
|
|
23
|
+
return await applyPermissionCommand({
|
|
24
|
+
kind: 'permission',
|
|
25
|
+
permission,
|
|
26
|
+
decision: 'grant',
|
|
27
|
+
});
|
|
28
|
+
},
|
|
29
|
+
revoke: async (permission) => {
|
|
30
|
+
return await applyPermissionCommand({
|
|
31
|
+
kind: 'permission',
|
|
32
|
+
permission,
|
|
33
|
+
decision: 'deny',
|
|
34
|
+
});
|
|
35
|
+
},
|
|
36
|
+
reset: async (permission) => {
|
|
37
|
+
return await applyPermissionCommand({
|
|
38
|
+
kind: 'permission',
|
|
39
|
+
permission,
|
|
40
|
+
decision: 'reset',
|
|
41
|
+
});
|
|
42
|
+
},
|
|
43
|
+
grantAll: async () => {
|
|
44
|
+
return await applyPermissionCommand({
|
|
45
|
+
kind: 'permission-all',
|
|
46
|
+
decision: 'grant',
|
|
47
|
+
});
|
|
48
|
+
},
|
|
49
|
+
denyAll: async () => {
|
|
50
|
+
return await applyPermissionCommand({
|
|
51
|
+
kind: 'permission-all',
|
|
52
|
+
decision: 'deny',
|
|
53
|
+
});
|
|
54
|
+
},
|
|
55
|
+
resetAll: async () => {
|
|
56
|
+
return await applyPermissionCommand({
|
|
57
|
+
kind: 'permission-all',
|
|
58
|
+
decision: 'reset',
|
|
59
|
+
});
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
};
|
package/dist/hmr.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hmr.d.ts","sourceRoot":"","sources":["../src/hmr.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,UAAU,QAAO,IAM7B,CAAC"}
|
package/dist/hmr.js
ADDED
package/dist/logbox.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logbox.d.ts","sourceRoot":"","sources":["../src/logbox.ts"],"names":[],"mappings":"AAqBA,eAAO,MAAM,kBAAkB,QAAO,OAA2B,CAAC;AAElE,qEAAqE;AACrE,eAAO,MAAM,eAAe,QAAO,IAelC,CAAC"}
|
package/dist/logbox.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { LogBox, TurboModuleRegistry, } from 'react-native';
|
|
2
|
+
let logBoxSuppressed = false;
|
|
3
|
+
const noop = () => undefined;
|
|
4
|
+
export const isLogBoxSuppressed = () => logBoxSuppressed;
|
|
5
|
+
/** Hide LogBox UI while keeping console.error → Metro forwarding. */
|
|
6
|
+
export const disableLogBoxUI = () => {
|
|
7
|
+
const harnessLogBox = LogBox;
|
|
8
|
+
harnessLogBox.ignoreAllLogs(true);
|
|
9
|
+
harnessLogBox.addException = noop;
|
|
10
|
+
harnessLogBox.addLog = noop;
|
|
11
|
+
harnessLogBox.addConsoleLog = noop;
|
|
12
|
+
const nativeLogBox = TurboModuleRegistry?.get('LogBox');
|
|
13
|
+
if (nativeLogBox != null) {
|
|
14
|
+
nativeLogBox.show = noop;
|
|
15
|
+
nativeLogBox.hide = noop;
|
|
16
|
+
}
|
|
17
|
+
logBoxSuppressed = true;
|
|
18
|
+
};
|
package/dist/runner/hooks.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { TestSuite } from '@react-native-harness/bridge';
|
|
2
|
+
import type { ActiveTestContext } from './types.js';
|
|
2
3
|
export type HookType = 'beforeEach' | 'afterEach' | 'beforeAll' | 'afterAll';
|
|
3
|
-
export declare const runHooks: (suite: TestSuite, hookType: HookType) => Promise<void>;
|
|
4
|
+
export declare const runHooks: (suite: TestSuite, hookType: HookType, context?: ActiveTestContext) => Promise<void>;
|
|
4
5
|
//# sourceMappingURL=hooks.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../src/runner/hooks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../src/runner/hooks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAuB,SAAS,EAAE,MAAM,8BAA8B,CAAC;AACnF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEpD,MAAM,MAAM,QAAQ,GAAG,YAAY,GAAG,WAAW,GAAG,WAAW,GAAG,UAAU,CAAC;AAmD7E,eAAO,MAAM,QAAQ,GACnB,OAAO,SAAS,EAChB,UAAU,QAAQ,EAClB,UAAU,iBAAiB,KAC1B,OAAO,CAAC,IAAI,CAgBd,CAAC"}
|
package/dist/runner/hooks.js
CHANGED
|
@@ -1,4 +1,17 @@
|
|
|
1
1
|
const collectInheritedHooks = (suite, hookType) => {
|
|
2
|
+
const hooks = [];
|
|
3
|
+
const suiteChain = [];
|
|
4
|
+
let current = suite;
|
|
5
|
+
while (current) {
|
|
6
|
+
suiteChain.unshift(current);
|
|
7
|
+
current = current.parent;
|
|
8
|
+
}
|
|
9
|
+
for (const currentSuite of suiteChain) {
|
|
10
|
+
hooks.push(...currentSuite[hookType]);
|
|
11
|
+
}
|
|
12
|
+
return hooks;
|
|
13
|
+
};
|
|
14
|
+
const collectSuiteHooks = (suite, hookType) => {
|
|
2
15
|
const hooks = [];
|
|
3
16
|
const suiteChain = [];
|
|
4
17
|
// Collect all suites from current to root
|
|
@@ -7,33 +20,30 @@ const collectInheritedHooks = (suite, hookType) => {
|
|
|
7
20
|
suiteChain.push(currentSuite);
|
|
8
21
|
currentSuite = currentSuite.parent;
|
|
9
22
|
}
|
|
10
|
-
if (hookType === '
|
|
11
|
-
//
|
|
23
|
+
if (hookType === 'beforeAll') {
|
|
24
|
+
// Run parent suite hooks before child suite hooks.
|
|
12
25
|
for (let i = suiteChain.length - 1; i >= 0; i--) {
|
|
13
|
-
|
|
14
|
-
hooks.push(...suiteChain[i].beforeEach);
|
|
15
|
-
}
|
|
16
|
-
else {
|
|
17
|
-
hooks.push(...suiteChain[i].beforeAll);
|
|
18
|
-
}
|
|
26
|
+
hooks.push(...suiteChain[i].beforeAll);
|
|
19
27
|
}
|
|
20
28
|
}
|
|
21
29
|
else {
|
|
22
|
-
//
|
|
30
|
+
// Run child suite hooks before parent suite hooks.
|
|
23
31
|
for (const suiteInChain of suiteChain) {
|
|
24
|
-
|
|
25
|
-
hooks.push(...suiteInChain.afterEach);
|
|
26
|
-
}
|
|
27
|
-
else {
|
|
28
|
-
hooks.push(...suiteInChain.afterAll);
|
|
29
|
-
}
|
|
32
|
+
hooks.push(...suiteInChain.afterAll);
|
|
30
33
|
}
|
|
31
34
|
}
|
|
32
35
|
return hooks;
|
|
33
36
|
};
|
|
34
|
-
export const runHooks = async (suite, hookType) => {
|
|
37
|
+
export const runHooks = async (suite, hookType, context) => {
|
|
38
|
+
if (hookType === 'beforeAll' || hookType === 'afterAll') {
|
|
39
|
+
const hooks = collectSuiteHooks(suite, hookType);
|
|
40
|
+
for (const hook of hooks) {
|
|
41
|
+
await hook();
|
|
42
|
+
}
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
35
45
|
const hooks = collectInheritedHooks(suite, hookType);
|
|
36
46
|
for (const hook of hooks) {
|
|
37
|
-
await hook();
|
|
47
|
+
await hook(context);
|
|
38
48
|
}
|
|
39
49
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runSuite.d.ts","sourceRoot":"","sources":["../../src/runner/runSuite.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"runSuite.d.ts","sourceRoot":"","sources":["../../src/runner/runSuite.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAIV,SAAS,EACT,eAAe,EAChB,MAAM,8BAA8B,CAAC;AAQtC,OAAO,EAAqB,iBAAiB,EAAE,MAAM,YAAY,CAAC;AA0DlE,OAAO,CAAC,MAAM,CAAC;IACb,IAAI,iBAAiB,EAAE,MAAM,CAAC;CAC/B;AAyMD,eAAO,MAAM,QAAQ,GACnB,OAAO,SAAS,EAChB,SAAS,iBAAiB,KACzB,OAAO,CAAC,eAAe,CA6IzB,CAAC"}
|
package/dist/runner/runSuite.js
CHANGED
|
@@ -2,14 +2,68 @@ import { setCurrentExpectTestState, } from '../expect/context.js';
|
|
|
2
2
|
import { flushExpectTestState } from '../expect/errors.js';
|
|
3
3
|
import { runHooks } from './hooks.js';
|
|
4
4
|
import { getTestExecutionError } from './errors.js';
|
|
5
|
+
import { createTestContext, createTestLifecycleState, isSkipTestError, runOnTestFailed, runOnTestFinished, } from './test-context.js';
|
|
6
|
+
const getAncestorTitles = (suite) => {
|
|
7
|
+
const ancestorTitles = [];
|
|
8
|
+
let currentSuite = suite.parent;
|
|
9
|
+
while (currentSuite) {
|
|
10
|
+
if (currentSuite.name !== 'root') {
|
|
11
|
+
ancestorTitles.unshift(currentSuite.name);
|
|
12
|
+
}
|
|
13
|
+
currentSuite = currentSuite.parent;
|
|
14
|
+
}
|
|
15
|
+
if (suite.name !== 'root') {
|
|
16
|
+
ancestorTitles.push(suite.name);
|
|
17
|
+
}
|
|
18
|
+
return ancestorTitles;
|
|
19
|
+
};
|
|
20
|
+
const getFullName = (ancestorTitles, testName) => [...ancestorTitles, testName].join(' ');
|
|
21
|
+
const emitTestFinished = (context, options) => {
|
|
22
|
+
const ancestorTitles = getAncestorTitles(options.suite);
|
|
23
|
+
context.events.emit({
|
|
24
|
+
type: 'test-finished',
|
|
25
|
+
file: context.testFilePath,
|
|
26
|
+
suite: options.suite.name,
|
|
27
|
+
name: options.test.name,
|
|
28
|
+
ancestorTitles,
|
|
29
|
+
fullName: getFullName(ancestorTitles, options.test.name),
|
|
30
|
+
startedAt: options.startedAt,
|
|
31
|
+
declarationMode: options.test.declarationMode,
|
|
32
|
+
duration: options.duration,
|
|
33
|
+
error: options.error,
|
|
34
|
+
status: options.status,
|
|
35
|
+
});
|
|
36
|
+
};
|
|
5
37
|
const runTest = async (test, suite, context) => {
|
|
6
|
-
const
|
|
38
|
+
const startedAt = Date.now();
|
|
39
|
+
const task = {
|
|
40
|
+
name: test.name,
|
|
41
|
+
type: 'test',
|
|
42
|
+
mode: test.status === 'active'
|
|
43
|
+
? 'run'
|
|
44
|
+
: test.status === 'skipped'
|
|
45
|
+
? 'skip'
|
|
46
|
+
: 'todo',
|
|
47
|
+
file: {
|
|
48
|
+
name: context.testFilePath,
|
|
49
|
+
},
|
|
50
|
+
suite: {
|
|
51
|
+
name: suite.name,
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
const lifecycleState = createTestLifecycleState();
|
|
55
|
+
const activeTestContext = createTestContext(task, lifecycleState);
|
|
7
56
|
// Emit test-started event
|
|
57
|
+
const ancestorTitles = getAncestorTitles(suite);
|
|
8
58
|
context.events.emit({
|
|
9
59
|
type: 'test-started',
|
|
10
60
|
name: test.name,
|
|
11
61
|
suite: suite.name,
|
|
12
62
|
file: context.testFilePath,
|
|
63
|
+
ancestorTitles,
|
|
64
|
+
fullName: getFullName(ancestorTitles, test.name),
|
|
65
|
+
startedAt,
|
|
66
|
+
declarationMode: test.declarationMode,
|
|
13
67
|
});
|
|
14
68
|
try {
|
|
15
69
|
if (test.status === 'skipped') {
|
|
@@ -17,13 +71,15 @@ const runTest = async (test, suite, context) => {
|
|
|
17
71
|
name: test.name,
|
|
18
72
|
status: 'skipped',
|
|
19
73
|
duration: 0,
|
|
74
|
+
ancestorTitles,
|
|
75
|
+
fullName: getFullName(ancestorTitles, test.name),
|
|
76
|
+
startedAt,
|
|
77
|
+
declarationMode: test.declarationMode,
|
|
20
78
|
};
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
suite: suite.name,
|
|
26
|
-
file: context.testFilePath,
|
|
79
|
+
emitTestFinished(context, {
|
|
80
|
+
test,
|
|
81
|
+
suite,
|
|
82
|
+
startedAt,
|
|
27
83
|
duration: 0,
|
|
28
84
|
status: 'skipped',
|
|
29
85
|
});
|
|
@@ -35,13 +91,15 @@ const runTest = async (test, suite, context) => {
|
|
|
35
91
|
name: test.name,
|
|
36
92
|
status: 'todo',
|
|
37
93
|
duration: 0,
|
|
94
|
+
ancestorTitles,
|
|
95
|
+
fullName: getFullName(ancestorTitles, test.name),
|
|
96
|
+
startedAt,
|
|
97
|
+
declarationMode: test.declarationMode,
|
|
38
98
|
};
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
suite: suite.name,
|
|
44
|
-
file: context.testFilePath,
|
|
99
|
+
emitTestFinished(context, {
|
|
100
|
+
test,
|
|
101
|
+
suite,
|
|
102
|
+
startedAt,
|
|
45
103
|
duration: 0,
|
|
46
104
|
status: 'todo',
|
|
47
105
|
});
|
|
@@ -50,49 +108,88 @@ const runTest = async (test, suite, context) => {
|
|
|
50
108
|
const expectTestState = {};
|
|
51
109
|
setCurrentExpectTestState(expectTestState);
|
|
52
110
|
try {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
111
|
+
let didSkip = false;
|
|
112
|
+
try {
|
|
113
|
+
// Run all beforeEach hooks from the current suite and its parents
|
|
114
|
+
await runHooks(suite, 'beforeEach', activeTestContext);
|
|
115
|
+
// Run the actual test
|
|
116
|
+
await test.fn(activeTestContext);
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
if (!isSkipTestError(error)) {
|
|
120
|
+
throw error;
|
|
121
|
+
}
|
|
122
|
+
didSkip = true;
|
|
123
|
+
}
|
|
124
|
+
finally {
|
|
125
|
+
// Run all afterEach hooks from the current suite and its parents
|
|
126
|
+
await runHooks(suite, 'afterEach', activeTestContext);
|
|
127
|
+
}
|
|
128
|
+
if (didSkip) {
|
|
129
|
+
const duration = Date.now() - startedAt;
|
|
130
|
+
await runOnTestFinished(lifecycleState);
|
|
131
|
+
const result = {
|
|
132
|
+
name: test.name,
|
|
133
|
+
status: 'skipped',
|
|
134
|
+
duration,
|
|
135
|
+
ancestorTitles,
|
|
136
|
+
fullName: getFullName(ancestorTitles, test.name),
|
|
137
|
+
startedAt,
|
|
138
|
+
declarationMode: test.declarationMode,
|
|
139
|
+
};
|
|
140
|
+
emitTestFinished(context, {
|
|
141
|
+
test,
|
|
142
|
+
suite,
|
|
143
|
+
startedAt,
|
|
144
|
+
duration,
|
|
145
|
+
status: 'skipped',
|
|
146
|
+
});
|
|
147
|
+
return result;
|
|
148
|
+
}
|
|
59
149
|
await flushExpectTestState(expectTestState);
|
|
150
|
+
await runOnTestFinished(lifecycleState);
|
|
60
151
|
}
|
|
61
152
|
finally {
|
|
62
153
|
setCurrentExpectTestState(undefined);
|
|
63
154
|
}
|
|
64
|
-
const duration = Date.now() -
|
|
155
|
+
const duration = Date.now() - startedAt;
|
|
65
156
|
const result = {
|
|
66
157
|
name: test.name,
|
|
67
158
|
status: 'passed',
|
|
68
159
|
duration,
|
|
160
|
+
ancestorTitles,
|
|
161
|
+
fullName: getFullName(ancestorTitles, test.name),
|
|
162
|
+
startedAt,
|
|
163
|
+
declarationMode: test.declarationMode,
|
|
69
164
|
};
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
suite: suite.name,
|
|
75
|
-
name: test.name,
|
|
165
|
+
emitTestFinished(context, {
|
|
166
|
+
test,
|
|
167
|
+
suite,
|
|
168
|
+
startedAt,
|
|
76
169
|
duration,
|
|
77
170
|
status: 'passed',
|
|
78
171
|
});
|
|
79
172
|
return result;
|
|
80
173
|
}
|
|
81
174
|
catch (error) {
|
|
175
|
+
await runOnTestFailed(lifecycleState);
|
|
176
|
+
await runOnTestFinished(lifecycleState);
|
|
82
177
|
const testError = await getTestExecutionError(error, context.testFilePath, suite.name, test.name);
|
|
83
|
-
const duration = Date.now() -
|
|
178
|
+
const duration = Date.now() - startedAt;
|
|
84
179
|
const result = {
|
|
85
180
|
name: test.name,
|
|
86
181
|
status: 'failed',
|
|
87
182
|
error: testError.toSerializedJSON(),
|
|
88
183
|
duration,
|
|
184
|
+
ancestorTitles,
|
|
185
|
+
fullName: getFullName(ancestorTitles, test.name),
|
|
186
|
+
startedAt,
|
|
187
|
+
declarationMode: test.declarationMode,
|
|
89
188
|
};
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
suite: suite.name,
|
|
95
|
-
name: test.name,
|
|
189
|
+
emitTestFinished(context, {
|
|
190
|
+
test,
|
|
191
|
+
suite,
|
|
192
|
+
startedAt,
|
|
96
193
|
duration,
|
|
97
194
|
error: testError.toSerializedJSON(),
|
|
98
195
|
status: 'failed',
|
|
@@ -110,10 +207,12 @@ export const runSuite = async (suite, context) => {
|
|
|
110
207
|
});
|
|
111
208
|
// Check if suite should be skipped or is todo
|
|
112
209
|
if (suite.status === 'skipped') {
|
|
210
|
+
const testResults = await Promise.all(suite.tests.map((test) => runTest({ ...test, status: 'skipped' }, suite, context)));
|
|
211
|
+
const suiteResults = await Promise.all(suite.suites.map((childSuite) => runSuite({ ...childSuite, status: 'skipped' }, context)));
|
|
113
212
|
const result = {
|
|
114
213
|
name: suite.name,
|
|
115
|
-
tests:
|
|
116
|
-
suites:
|
|
214
|
+
tests: testResults,
|
|
215
|
+
suites: suiteResults,
|
|
117
216
|
status: 'skipped',
|
|
118
217
|
duration: 0,
|
|
119
218
|
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { HarnessTaskContext } from '@react-native-harness/bridge';
|
|
2
|
+
import type { ActiveTestContext } from './types.js';
|
|
3
|
+
export type TestLifecycleState = {
|
|
4
|
+
onTestFailed: Array<() => void | Promise<void>>;
|
|
5
|
+
onTestFinished: Array<() => void | Promise<void>>;
|
|
6
|
+
};
|
|
7
|
+
export declare class SkipTestError extends Error {
|
|
8
|
+
note?: string;
|
|
9
|
+
constructor(note?: string);
|
|
10
|
+
}
|
|
11
|
+
export declare const isSkipTestError: (error: unknown) => error is SkipTestError;
|
|
12
|
+
export declare const createTestLifecycleState: () => TestLifecycleState;
|
|
13
|
+
export declare const runOnTestFailed: (state: TestLifecycleState) => Promise<void>;
|
|
14
|
+
export declare const runOnTestFinished: (state: TestLifecycleState) => Promise<void>;
|
|
15
|
+
export declare const createTestContext: (task: HarnessTaskContext, state: TestLifecycleState) => ActiveTestContext;
|
|
16
|
+
//# sourceMappingURL=test-context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-context.d.ts","sourceRoot":"","sources":["../../src/runner/test-context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AACvE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEpD,MAAM,MAAM,kBAAkB,GAAG;IAC/B,YAAY,EAAE,KAAK,CAAC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IAChD,cAAc,EAAE,KAAK,CAAC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;CACnD,CAAC;AAEF,qBAAa,aAAc,SAAQ,KAAK;IACtC,IAAI,CAAC,EAAE,MAAM,CAAC;gBAEF,IAAI,CAAC,EAAE,MAAM;CAK1B;AAED,eAAO,MAAM,eAAe,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,aAEzD,CAAC;AA8BF,eAAO,MAAM,wBAAwB,QAAO,kBAK3C,CAAC;AAEF,eAAO,MAAM,eAAe,GAC1B,OAAO,kBAAkB,KACxB,OAAO,CAAC,IAAI,CAId,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAC5B,OAAO,kBAAkB,KACxB,OAAO,CAAC,IAAI,CAId,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAC5B,MAAM,kBAAkB,EACxB,OAAO,kBAAkB,KACxB,iBAOF,CAAC"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
export class SkipTestError extends Error {
|
|
2
|
+
note;
|
|
3
|
+
constructor(note) {
|
|
4
|
+
super(note ?? 'Test skipped');
|
|
5
|
+
this.name = 'SkipTestError';
|
|
6
|
+
this.note = note;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
export const isSkipTestError = (error) => {
|
|
10
|
+
return error instanceof SkipTestError;
|
|
11
|
+
};
|
|
12
|
+
const createSkip = () => {
|
|
13
|
+
function skip(noteOrCondition, note) {
|
|
14
|
+
if (typeof noteOrCondition === 'boolean') {
|
|
15
|
+
if (!noteOrCondition) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
throw new SkipTestError(note);
|
|
19
|
+
}
|
|
20
|
+
throw new SkipTestError(noteOrCondition);
|
|
21
|
+
}
|
|
22
|
+
return skip;
|
|
23
|
+
};
|
|
24
|
+
const createOnTestFinished = (state) => {
|
|
25
|
+
return (fn) => {
|
|
26
|
+
state.onTestFinished.push(fn);
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
const createOnTestFailed = (state) => {
|
|
30
|
+
return (fn) => {
|
|
31
|
+
state.onTestFailed.push(fn);
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
export const createTestLifecycleState = () => {
|
|
35
|
+
return {
|
|
36
|
+
onTestFailed: [],
|
|
37
|
+
onTestFinished: [],
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
export const runOnTestFailed = async (state) => {
|
|
41
|
+
for (let i = state.onTestFailed.length - 1; i >= 0; i--) {
|
|
42
|
+
await state.onTestFailed[i]();
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
export const runOnTestFinished = async (state) => {
|
|
46
|
+
for (let i = state.onTestFinished.length - 1; i >= 0; i--) {
|
|
47
|
+
await state.onTestFinished[i]();
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
export const createTestContext = (task, state) => {
|
|
51
|
+
return {
|
|
52
|
+
task,
|
|
53
|
+
onTestFailed: createOnTestFailed(state),
|
|
54
|
+
onTestFinished: createOnTestFinished(state),
|
|
55
|
+
skip: createSkip(),
|
|
56
|
+
};
|
|
57
|
+
};
|
package/dist/runner/types.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { EventEmitter } from '../utils/emitter.js';
|
|
2
|
-
import type { TestRunnerEvents, TestSuite, TestSuiteResult } from '@react-native-harness/bridge';
|
|
2
|
+
import type { HarnessTestContext, TestRunnerEvents, TestSuite, TestSuiteResult } from '@react-native-harness/bridge';
|
|
3
3
|
export type TestRunnerEventsEmitter = EventEmitter<TestRunnerEvents>;
|
|
4
4
|
export type TestRunnerContext = {
|
|
5
5
|
events: TestRunnerEventsEmitter;
|
|
6
6
|
testFilePath: string;
|
|
7
7
|
};
|
|
8
|
+
export type ActiveTestContext = HarnessTestContext;
|
|
8
9
|
export type RunTestsOptions = {
|
|
9
10
|
testSuite: TestSuite;
|
|
10
11
|
testFilePath: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/runner/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,KAAK,EACV,gBAAgB,EAChB,SAAS,EACT,eAAe,EAChB,MAAM,8BAA8B,CAAC;AAEtC,MAAM,MAAM,uBAAuB,GAAG,YAAY,CAAC,gBAAgB,CAAC,CAAC;AAErE,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,EAAE,uBAAuB,CAAC;IAChC,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,SAAS,EAAE,SAAS,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,MAAM,EAAE,uBAAuB,CAAC;IAChC,GAAG,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,OAAO,CAAC,eAAe,CAAC,CAAC;IAC5D,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,CAAC"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/runner/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,KAAK,EACV,kBAAkB,EAClB,gBAAgB,EAChB,SAAS,EACT,eAAe,EAChB,MAAM,8BAA8B,CAAC;AAEtC,MAAM,MAAM,uBAAuB,GAAG,YAAY,CAAC,gBAAgB,CAAC,CAAC;AAErE,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,EAAE,uBAAuB,CAAC;IAChC,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG,kBAAkB,CAAC;AAEnD,MAAM,MAAM,eAAe,GAAG;IAC5B,SAAS,EAAE,SAAS,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,MAAM,EAAE,uBAAuB,CAAC;IAChC,GAAG,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,OAAO,CAAC,eAAe,CAAC,CAAC;IAC5D,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare const URL: {
|
|
2
|
+
new (url: string | URL, base?: string | URL): URL;
|
|
3
|
+
prototype: URL;
|
|
4
|
+
canParse(url: string | URL, base?: string | URL): boolean;
|
|
5
|
+
createObjectURL(obj: Blob | MediaSource): string;
|
|
6
|
+
parse(url: string | URL, base?: string | URL): URL | null;
|
|
7
|
+
revokeObjectURL(url: string): void;
|
|
8
|
+
};
|
|
9
|
+
//# sourceMappingURL=react-native-url-polyfill.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react-native-url-polyfill.d.ts","sourceRoot":"","sources":["../../src/test-utils/react-native-url-polyfill.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,GAAG;;;;;;;CAAiB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const URL = globalThis.URL;
|