@react-native-harness/jest 1.0.0-alpha.17 → 1.0.0-alpha.19
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/errors.d.ts +12 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +21 -0
- package/dist/harness.d.ts +10 -0
- package/dist/harness.d.ts.map +1 -0
- package/dist/harness.js +72 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -8
- package/dist/logs.d.ts +5 -3
- package/dist/logs.d.ts.map +1 -1
- package/dist/logs.js +6 -0
- package/dist/run.d.ts +1 -1
- package/dist/run.d.ts.map +1 -1
- package/dist/run.js +1 -2
- package/dist/setup.d.ts.map +1 -1
- package/dist/setup.js +10 -19
- package/dist/teardown.d.ts.map +1 -1
- package/dist/teardown.js +1 -2
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/eslint.config.mjs +2 -1
- package/package.json +9 -6
- package/src/errors.ts +22 -0
- package/src/harness.ts +112 -0
- package/src/index.ts +23 -8
- package/src/logs.ts +12 -3
- package/src/run.ts +2 -3
- package/src/setup.ts +16 -28
- package/src/teardown.ts +1 -2
- package/tsconfig.json +9 -0
- package/tsconfig.lib.json +9 -0
- package/dist/cli-args.test.d.ts +0 -2
- package/dist/cli-args.test.d.ts.map +0 -1
- package/dist/cli-args.test.js +0 -1
- package/dist/event-mapper.d.ts +0 -16
- package/dist/event-mapper.d.ts.map +0 -1
- package/dist/event-mapper.js +0 -120
- package/dist/global-setup.d.ts +0 -3
- package/dist/global-setup.d.ts.map +0 -1
- package/dist/global-setup.js +0 -60
- package/dist/global-teardown.d.ts +0 -3
- package/dist/global-teardown.d.ts.map +0 -1
- package/dist/global-teardown.js +0 -11
- package/dist/harness-reporter.d.ts +0 -8
- package/dist/harness-reporter.d.ts.map +0 -1
- package/dist/harness-reporter.js +0 -12
- package/dist/jest-options.d.ts +0 -2
- package/dist/jest-options.d.ts.map +0 -1
- package/dist/jest-options.js +0 -1
- package/dist/types.d.ts +0 -17
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@react-native-harness/jest",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.19",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.js",
|
|
@@ -36,17 +36,20 @@
|
|
|
36
36
|
"@jest/test-result": "^30.2.0",
|
|
37
37
|
"chalk": "^4.1.2",
|
|
38
38
|
"jest-message-util": "^30.2.0",
|
|
39
|
-
"jest-runner": "^30.2.0",
|
|
40
39
|
"jest-util": "^30.2.0",
|
|
41
40
|
"p-limit": "^7.1.1",
|
|
41
|
+
"p-retry": "^7.1.0",
|
|
42
42
|
"tslib": "^2.3.0",
|
|
43
43
|
"yargs": "^17.7.2",
|
|
44
|
-
"@react-native-harness/bridge": "1.0.0-alpha.
|
|
45
|
-
"@react-native-harness/
|
|
46
|
-
"@react-native-harness/
|
|
44
|
+
"@react-native-harness/bridge": "1.0.0-alpha.19",
|
|
45
|
+
"@react-native-harness/config": "1.0.0-alpha.19",
|
|
46
|
+
"@react-native-harness/bundler-metro": "1.0.0-alpha.19",
|
|
47
|
+
"@react-native-harness/tools": "1.0.0-alpha.19",
|
|
48
|
+
"@react-native-harness/platforms": "1.0.0-alpha.19"
|
|
47
49
|
},
|
|
48
50
|
"devDependencies": {
|
|
49
|
-
"@types/yargs": "^17.0.32"
|
|
51
|
+
"@types/yargs": "^17.0.32",
|
|
52
|
+
"jest-runner": "^30.2.0"
|
|
50
53
|
},
|
|
51
54
|
"license": "MIT"
|
|
52
55
|
}
|
package/src/errors.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { HarnessError } from '@react-native-harness/tools';
|
|
2
|
+
|
|
3
|
+
export class NoRunnerSpecifiedError extends HarnessError {
|
|
4
|
+
constructor() {
|
|
5
|
+
super('No runner specified');
|
|
6
|
+
this.name = 'NoRunnerSpecifiedError';
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export class RunnerNotFoundError extends HarnessError {
|
|
11
|
+
constructor(public readonly runnerName: string) {
|
|
12
|
+
super(`Runner "${runnerName}" not found`);
|
|
13
|
+
this.name = 'RunnerNotFoundError';
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export class InitializationTimeoutError extends HarnessError {
|
|
18
|
+
constructor() {
|
|
19
|
+
super('The Harness did not become ready within the timeout period.');
|
|
20
|
+
this.name = 'InitializationTimeoutError';
|
|
21
|
+
}
|
|
22
|
+
}
|
package/src/harness.ts
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { getBridgeServer } from '@react-native-harness/bridge/server';
|
|
2
|
+
import { BridgeClientFunctions } from '@react-native-harness/bridge';
|
|
3
|
+
import { HarnessPlatform } from '@react-native-harness/platforms';
|
|
4
|
+
import { getMetroInstance } from '@react-native-harness/bundler-metro';
|
|
5
|
+
import { InitializationTimeoutError } from './errors.js';
|
|
6
|
+
import { Config as HarnessConfig } from '@react-native-harness/config';
|
|
7
|
+
import pRetry from 'p-retry';
|
|
8
|
+
|
|
9
|
+
const BRIDGE_READY_TIMEOUT = 10000;
|
|
10
|
+
|
|
11
|
+
export type Harness = {
|
|
12
|
+
runTests: BridgeClientFunctions['runTests'];
|
|
13
|
+
restart: () => Promise<void>;
|
|
14
|
+
dispose: () => Promise<void>;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const getHarnessInternal = async (
|
|
18
|
+
config: HarnessConfig,
|
|
19
|
+
platform: HarnessPlatform,
|
|
20
|
+
projectRoot: string,
|
|
21
|
+
signal: AbortSignal
|
|
22
|
+
): Promise<Harness> => {
|
|
23
|
+
const [metroInstance, platformInstance, serverBridge] = await Promise.all([
|
|
24
|
+
getMetroInstance({ projectRoot, harnessConfig: config }, signal),
|
|
25
|
+
import(platform.runner).then((module) => module.default(platform.config)),
|
|
26
|
+
getBridgeServer({
|
|
27
|
+
port: 3001,
|
|
28
|
+
timeout: config.bridgeTimeout,
|
|
29
|
+
}),
|
|
30
|
+
]);
|
|
31
|
+
|
|
32
|
+
const dispose = async () => {
|
|
33
|
+
await Promise.all([
|
|
34
|
+
serverBridge.dispose(),
|
|
35
|
+
platformInstance.dispose(),
|
|
36
|
+
metroInstance.dispose(),
|
|
37
|
+
]);
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
if (signal.aborted) {
|
|
41
|
+
await dispose();
|
|
42
|
+
|
|
43
|
+
throw new DOMException('The operation was aborted', 'AbortError');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
await pRetry(
|
|
48
|
+
() =>
|
|
49
|
+
new Promise<void>((resolve, reject) => {
|
|
50
|
+
signal.addEventListener('abort', () => {
|
|
51
|
+
reject(new DOMException('The operation was aborted', 'AbortError'));
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
serverBridge.once('ready', () => resolve());
|
|
55
|
+
platformInstance.restartApp().catch(reject);
|
|
56
|
+
}),
|
|
57
|
+
{
|
|
58
|
+
minTimeout: BRIDGE_READY_TIMEOUT,
|
|
59
|
+
maxTimeout: BRIDGE_READY_TIMEOUT,
|
|
60
|
+
retries: Infinity,
|
|
61
|
+
signal,
|
|
62
|
+
}
|
|
63
|
+
);
|
|
64
|
+
} catch (error) {
|
|
65
|
+
await dispose();
|
|
66
|
+
throw error;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const restart = () =>
|
|
70
|
+
new Promise<void>((resolve, reject) => {
|
|
71
|
+
serverBridge.once('ready', () => resolve());
|
|
72
|
+
platformInstance.restartApp().catch(reject);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
runTests: async (path, options) => {
|
|
77
|
+
const client = serverBridge.rpc.clients.at(-1);
|
|
78
|
+
|
|
79
|
+
if (!client) {
|
|
80
|
+
throw new Error('No client found');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return await client.runTests(path, options);
|
|
84
|
+
},
|
|
85
|
+
restart,
|
|
86
|
+
dispose,
|
|
87
|
+
};
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
export const getHarness = async (
|
|
91
|
+
config: HarnessConfig,
|
|
92
|
+
platform: HarnessPlatform,
|
|
93
|
+
projectRoot: string
|
|
94
|
+
): Promise<Harness> => {
|
|
95
|
+
const abortSignal = AbortSignal.timeout(config.bridgeTimeout);
|
|
96
|
+
|
|
97
|
+
try {
|
|
98
|
+
const harness = await getHarnessInternal(
|
|
99
|
+
config,
|
|
100
|
+
platform,
|
|
101
|
+
projectRoot,
|
|
102
|
+
abortSignal
|
|
103
|
+
);
|
|
104
|
+
return harness;
|
|
105
|
+
} catch (error) {
|
|
106
|
+
if (error instanceof DOMException && error.name === 'AbortError') {
|
|
107
|
+
throw new InitializationTimeoutError();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
throw error;
|
|
111
|
+
}
|
|
112
|
+
};
|
package/src/index.ts
CHANGED
|
@@ -11,9 +11,12 @@ import type {
|
|
|
11
11
|
import pLimit from 'p-limit';
|
|
12
12
|
import { runHarnessTestFile } from './run.js';
|
|
13
13
|
import { Config as HarnessConfig } from '@react-native-harness/config';
|
|
14
|
-
import {
|
|
14
|
+
import { type Harness } from './harness.js';
|
|
15
15
|
import { setup } from './setup.js';
|
|
16
16
|
import { teardown } from './teardown.js';
|
|
17
|
+
import { HarnessError } from '@react-native-harness/tools';
|
|
18
|
+
import { getErrorMessage } from './logs.js';
|
|
19
|
+
import { DeviceNotRespondingError } from '@react-native-harness/bridge';
|
|
17
20
|
|
|
18
21
|
class CancelRun extends Error {
|
|
19
22
|
constructor(message?: string) {
|
|
@@ -60,8 +63,12 @@ export default class JestHarness implements CallbackTestRunnerInterface {
|
|
|
60
63
|
onFailure
|
|
61
64
|
);
|
|
62
65
|
} catch (error) {
|
|
63
|
-
|
|
64
|
-
|
|
66
|
+
if (error instanceof HarnessError) {
|
|
67
|
+
// Jest will print strings as they are, without processing them further.
|
|
68
|
+
throw getErrorMessage(error);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
throw error;
|
|
65
72
|
} finally {
|
|
66
73
|
// This is necessary as Harness may throw and we want to catch it and display a helpful error message.
|
|
67
74
|
await teardown(this.#globalConfig);
|
|
@@ -93,10 +100,7 @@ export default class JestHarness implements CallbackTestRunnerInterface {
|
|
|
93
100
|
harnessConfig.resetEnvironmentBetweenTestFiles &&
|
|
94
101
|
!isFirstTest
|
|
95
102
|
) {
|
|
96
|
-
await
|
|
97
|
-
harness.bridge.once('ready', resolve);
|
|
98
|
-
harness.environment.restart();
|
|
99
|
-
});
|
|
103
|
+
await harness.restart();
|
|
100
104
|
}
|
|
101
105
|
isFirstTest = false;
|
|
102
106
|
|
|
@@ -110,7 +114,18 @@ export default class JestHarness implements CallbackTestRunnerInterface {
|
|
|
110
114
|
);
|
|
111
115
|
})
|
|
112
116
|
.then((result) => onResult(test, result))
|
|
113
|
-
.catch((err) =>
|
|
117
|
+
.catch((err) => {
|
|
118
|
+
if (err instanceof DeviceNotRespondingError) {
|
|
119
|
+
onFailure(test, {
|
|
120
|
+
message: err.message,
|
|
121
|
+
stack: '',
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
onFailure(test, err);
|
|
128
|
+
})
|
|
114
129
|
),
|
|
115
130
|
Promise.resolve()
|
|
116
131
|
);
|
package/src/logs.ts
CHANGED
|
@@ -1,21 +1,30 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { HarnessPlatform } from '@react-native-harness/platforms';
|
|
2
|
+
import { HarnessError } from '@react-native-harness/tools';
|
|
2
3
|
import chalk from 'chalk';
|
|
3
4
|
|
|
4
5
|
const TAG = chalk.supportsColor
|
|
5
6
|
? chalk.reset.inverse.bold.magenta(` HARNESS `)
|
|
6
7
|
: 'HARNESS';
|
|
7
8
|
|
|
9
|
+
const ERROR_TAG = chalk.supportsColor
|
|
10
|
+
? chalk.reset.inverse.bold.red(` HARNESS `)
|
|
11
|
+
: 'HARNESS';
|
|
12
|
+
|
|
8
13
|
// @see https://github.com/jestjs/jest/blob/main/packages/jest-reporters/src/BaseReporter.ts#L25
|
|
9
14
|
export const log = (message: string): void => {
|
|
10
15
|
process.stderr.write(`${message}\n`);
|
|
11
16
|
};
|
|
12
17
|
|
|
13
|
-
export const logTestRunHeader = (runner:
|
|
18
|
+
export const logTestRunHeader = (runner: HarnessPlatform): void => {
|
|
14
19
|
log(
|
|
15
20
|
`${TAG} Preparing to run tests using ${chalk.bold(runner.name)} runner\n`
|
|
16
21
|
);
|
|
17
22
|
};
|
|
18
23
|
|
|
19
|
-
export const logTestEnvironmentReady = (runner:
|
|
24
|
+
export const logTestEnvironmentReady = (runner: HarnessPlatform): void => {
|
|
20
25
|
log(`${TAG} Runner ${chalk.bold(runner.name)} ready\n`);
|
|
21
26
|
};
|
|
27
|
+
|
|
28
|
+
export const getErrorMessage = (error: HarnessError): string => {
|
|
29
|
+
return `${ERROR_TAG} ${error.message}\n`;
|
|
30
|
+
};
|
package/src/run.ts
CHANGED
|
@@ -5,7 +5,7 @@ import type {
|
|
|
5
5
|
TestSuiteResult as HarnessTestSuiteResult,
|
|
6
6
|
TestResult as HarnessTestResult,
|
|
7
7
|
} from '@react-native-harness/bridge';
|
|
8
|
-
import type { Harness } from '
|
|
8
|
+
import type { Harness } from './harness.js';
|
|
9
9
|
import { formatResultsErrors } from 'jest-message-util';
|
|
10
10
|
import { toTestResult } from './toTestResult.js';
|
|
11
11
|
|
|
@@ -84,8 +84,7 @@ export const runHarnessTestFile: RunHarnessTestFile = async ({
|
|
|
84
84
|
(setupFile) => path.relative(globalConfig.rootDir, setupFile)
|
|
85
85
|
);
|
|
86
86
|
|
|
87
|
-
const
|
|
88
|
-
const results = await client.runTests(relativeTestPath, {
|
|
87
|
+
const results = await harness.runTests(relativeTestPath, {
|
|
89
88
|
testNamePattern: globalConfig.testNamePattern,
|
|
90
89
|
setupFiles,
|
|
91
90
|
setupFilesAfterEnv,
|
package/src/setup.ts
CHANGED
|
@@ -1,19 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getConfig,
|
|
3
|
-
TestRunnerConfig,
|
|
4
3
|
type Config as HarnessConfig,
|
|
5
|
-
type TestRunnerConfig as HarnessTestRunnerConfig,
|
|
6
4
|
} from '@react-native-harness/config';
|
|
7
5
|
import type { Config as JestConfig } from 'jest-runner';
|
|
8
|
-
import {
|
|
9
|
-
getHarness as getHarnessExternal,
|
|
10
|
-
NoRunnerSpecifiedError,
|
|
11
|
-
RunnerNotFoundError,
|
|
12
|
-
type Harness,
|
|
13
|
-
} from '@react-native-harness/cli/external';
|
|
6
|
+
import { getHarness } from './harness.js';
|
|
14
7
|
import { preRunMessage } from 'jest-util';
|
|
15
8
|
import { getAdditionalCliArgs, HarnessCliArgs } from './cli-args.js';
|
|
16
9
|
import { logTestEnvironmentReady, logTestRunHeader } from './logs.js';
|
|
10
|
+
import { NoRunnerSpecifiedError, RunnerNotFoundError } from './errors.js';
|
|
11
|
+
import { HarnessPlatform } from '@react-native-harness/platforms';
|
|
17
12
|
|
|
18
13
|
const getHarnessConfig = async (
|
|
19
14
|
globalConfig: JestConfig.GlobalConfig
|
|
@@ -26,11 +21,11 @@ const getHarnessConfig = async (
|
|
|
26
21
|
const getHarnessRunner = (
|
|
27
22
|
config: HarnessConfig,
|
|
28
23
|
cliArgs: HarnessCliArgs
|
|
29
|
-
):
|
|
24
|
+
): HarnessPlatform => {
|
|
30
25
|
const selectedRunnerName = cliArgs.harnessRunner ?? config.defaultRunner;
|
|
31
26
|
|
|
32
27
|
if (!selectedRunnerName) {
|
|
33
|
-
throw new NoRunnerSpecifiedError(
|
|
28
|
+
throw new NoRunnerSpecifiedError();
|
|
34
29
|
}
|
|
35
30
|
|
|
36
31
|
const runner = config.runners.find(
|
|
@@ -38,21 +33,16 @@ const getHarnessRunner = (
|
|
|
38
33
|
);
|
|
39
34
|
|
|
40
35
|
if (!runner) {
|
|
41
|
-
throw new RunnerNotFoundError(selectedRunnerName
|
|
36
|
+
throw new RunnerNotFoundError(selectedRunnerName);
|
|
42
37
|
}
|
|
43
38
|
|
|
44
39
|
return runner;
|
|
45
40
|
};
|
|
46
41
|
|
|
47
|
-
const getHarness = async (runner: TestRunnerConfig): Promise<Harness> => {
|
|
48
|
-
return await getHarnessExternal(runner);
|
|
49
|
-
};
|
|
50
|
-
|
|
51
42
|
export const setup = async (globalConfig: JestConfig.GlobalConfig) => {
|
|
52
43
|
preRunMessage.remove(process.stderr);
|
|
53
44
|
const harnessConfig =
|
|
54
45
|
global.HARNESS_CONFIG ?? (await getHarnessConfig(globalConfig));
|
|
55
|
-
const isWatchMode = globalConfig.watch || globalConfig.watchAll;
|
|
56
46
|
|
|
57
47
|
if (global.HARNESS) {
|
|
58
48
|
// Do not setup again if HARNESS is already initialized
|
|
@@ -60,22 +50,16 @@ export const setup = async (globalConfig: JestConfig.GlobalConfig) => {
|
|
|
60
50
|
|
|
61
51
|
if (harnessConfig.resetEnvironmentBetweenTestFiles) {
|
|
62
52
|
// In watch mode, we want to restart the environment before each test run
|
|
63
|
-
await
|
|
64
|
-
global.HARNESS.bridge.once('ready', resolve);
|
|
65
|
-
global.HARNESS.environment.restart();
|
|
66
|
-
});
|
|
53
|
+
await global.HARNESS.restart();
|
|
67
54
|
}
|
|
68
55
|
|
|
69
56
|
return;
|
|
70
57
|
}
|
|
71
58
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
await global.HARNESS.environment.dispose();
|
|
77
|
-
});
|
|
78
|
-
}
|
|
59
|
+
// Gracefully dispose the Harness when the process exits.
|
|
60
|
+
process.on('exit', async () => {
|
|
61
|
+
await global.HARNESS.dispose();
|
|
62
|
+
});
|
|
79
63
|
|
|
80
64
|
const cliArgs = getAdditionalCliArgs();
|
|
81
65
|
const selectedRunner = getHarnessRunner(harnessConfig, cliArgs);
|
|
@@ -87,7 +71,11 @@ export const setup = async (globalConfig: JestConfig.GlobalConfig) => {
|
|
|
87
71
|
}
|
|
88
72
|
|
|
89
73
|
logTestRunHeader(selectedRunner);
|
|
90
|
-
const harness = await getHarness(
|
|
74
|
+
const harness = await getHarness(
|
|
75
|
+
harnessConfig,
|
|
76
|
+
selectedRunner,
|
|
77
|
+
globalConfig.rootDir
|
|
78
|
+
);
|
|
91
79
|
logTestEnvironmentReady(selectedRunner);
|
|
92
80
|
|
|
93
81
|
global.HARNESS_CONFIG = harnessConfig;
|
package/src/teardown.ts
CHANGED
package/tsconfig.json
CHANGED
package/tsconfig.lib.json
CHANGED
|
@@ -15,6 +15,15 @@
|
|
|
15
15
|
{
|
|
16
16
|
"path": "../cli/tsconfig.lib.json"
|
|
17
17
|
},
|
|
18
|
+
{
|
|
19
|
+
"path": "../bundler-metro/tsconfig.lib.json"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"path": "../tools/tsconfig.lib.json"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"path": "../platforms/tsconfig.lib.json"
|
|
26
|
+
},
|
|
18
27
|
{
|
|
19
28
|
"path": "../config/tsconfig.lib.json"
|
|
20
29
|
},
|
package/dist/cli-args.test.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cli-args.test.d.ts","sourceRoot":"","sources":["../src/cli-args.test.ts"],"names":[],"mappings":""}
|
package/dist/cli-args.test.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/event-mapper.d.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import type { TestEvents } from '@jest/test-result';
|
|
2
|
-
import type { Test } from 'jest-runner';
|
|
3
|
-
import type { BridgeEvents } from '@react-native-harness/bridge';
|
|
4
|
-
import type { TestCaseResult } from '@jest/test-result';
|
|
5
|
-
export type EventMapperState = {
|
|
6
|
-
currentTest?: Test;
|
|
7
|
-
suiteHierarchy: string[];
|
|
8
|
-
testResults: Map<string, TestCaseResult>;
|
|
9
|
-
fileStartTime: number;
|
|
10
|
-
};
|
|
11
|
-
export type HarnessEventMapper = {
|
|
12
|
-
handleBridgeEvent: (event: BridgeEvents, test: Test) => void;
|
|
13
|
-
reset: () => void;
|
|
14
|
-
};
|
|
15
|
-
export declare const getHarnessEventMapper: (emit: <T extends keyof TestEvents>(eventName: T, eventData: TestEvents[T]) => void) => HarnessEventMapper;
|
|
16
|
-
//# sourceMappingURL=event-mapper.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"event-mapper.d.ts","sourceRoot":"","sources":["../src/event-mapper.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,KAAK,EASV,YAAY,EACb,MAAM,8BAA8B,CAAC;AACtC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAGxD,MAAM,MAAM,gBAAgB,GAAG;IAC7B,WAAW,CAAC,EAAE,IAAI,CAAC;IACnB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IACzC,aAAa,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,iBAAiB,EAAE,CAAC,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;IAC7D,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAChC,MAAM,CAAC,CAAC,SAAS,MAAM,UAAU,EAC/B,SAAS,EAAE,CAAC,EACZ,SAAS,EAAE,UAAU,CAAC,CAAC,CAAC,KACrB,IAAI,KACR,kBA8JF,CAAC"}
|
package/dist/event-mapper.js
DELETED
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
export const getHarnessEventMapper = (emit) => {
|
|
2
|
-
const state = {
|
|
3
|
-
suiteHierarchy: [],
|
|
4
|
-
testResults: new Map(),
|
|
5
|
-
fileStartTime: 0,
|
|
6
|
-
};
|
|
7
|
-
const handleBridgeEvent = (event, test) => {
|
|
8
|
-
// Filter to only process test runner events
|
|
9
|
-
if (event.type.startsWith('test-') ||
|
|
10
|
-
event.type.startsWith('suite-') ||
|
|
11
|
-
event.type.startsWith('file-')) {
|
|
12
|
-
switch (event.type) {
|
|
13
|
-
case 'file-started':
|
|
14
|
-
handleFileStarted(event, test);
|
|
15
|
-
break;
|
|
16
|
-
case 'file-finished':
|
|
17
|
-
handleFileFinished(event, test);
|
|
18
|
-
break;
|
|
19
|
-
case 'suite-started':
|
|
20
|
-
handleSuiteStarted(event);
|
|
21
|
-
break;
|
|
22
|
-
case 'suite-finished':
|
|
23
|
-
handleSuiteFinished(event);
|
|
24
|
-
break;
|
|
25
|
-
case 'test-started':
|
|
26
|
-
handleTestStarted(event, test);
|
|
27
|
-
break;
|
|
28
|
-
case 'test-finished':
|
|
29
|
-
handleTestFinished(event, test);
|
|
30
|
-
break;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
};
|
|
34
|
-
const handleFileStarted = (event, test) => {
|
|
35
|
-
state.currentTest = test;
|
|
36
|
-
state.fileStartTime = Date.now();
|
|
37
|
-
state.suiteHierarchy = [];
|
|
38
|
-
state.testResults.clear();
|
|
39
|
-
// Don't emit test-file-start here - that should be handled by the Jest runner
|
|
40
|
-
// when it actually starts running the test file
|
|
41
|
-
};
|
|
42
|
-
const handleFileFinished = (_event, _test) => {
|
|
43
|
-
// File finished - all test results should already be emitted
|
|
44
|
-
// The final TestResult will be handled by the runHarnessTestFile function
|
|
45
|
-
};
|
|
46
|
-
const handleSuiteStarted = (event) => {
|
|
47
|
-
state.suiteHierarchy.push(event.name);
|
|
48
|
-
};
|
|
49
|
-
const handleSuiteFinished = (event) => {
|
|
50
|
-
// Remove the suite from hierarchy when it finishes
|
|
51
|
-
const index = state.suiteHierarchy.lastIndexOf(event.name);
|
|
52
|
-
if (index !== -1) {
|
|
53
|
-
state.suiteHierarchy.splice(index, 1);
|
|
54
|
-
}
|
|
55
|
-
};
|
|
56
|
-
const handleTestStarted = (event, test) => {
|
|
57
|
-
const testCaseStartInfo = {
|
|
58
|
-
ancestorTitles: [...state.suiteHierarchy],
|
|
59
|
-
fullName: `${state.suiteHierarchy.join(' ')} ${event.name}`.trim(),
|
|
60
|
-
title: event.name,
|
|
61
|
-
mode: 'skip', // Default mode, will be updated based on test status
|
|
62
|
-
};
|
|
63
|
-
emit('test-case-start', [test.path, testCaseStartInfo]);
|
|
64
|
-
};
|
|
65
|
-
const handleTestFinished = (event, test) => {
|
|
66
|
-
const testCaseResult = {
|
|
67
|
-
ancestorTitles: [...state.suiteHierarchy],
|
|
68
|
-
duration: event.duration,
|
|
69
|
-
failureDetails: [],
|
|
70
|
-
failureMessages: event.error ? [formatError(event.error)] : [],
|
|
71
|
-
fullName: `${state.suiteHierarchy.join(' ')} ${event.name}`.trim(),
|
|
72
|
-
numPassingAsserts: event.status === 'passed' ? 1 : 0,
|
|
73
|
-
status: mapStatus(event.status),
|
|
74
|
-
title: event.name,
|
|
75
|
-
startedAt: Date.now() - event.duration,
|
|
76
|
-
};
|
|
77
|
-
state.testResults.set(event.name, testCaseResult);
|
|
78
|
-
emit('test-case-result', [test.path, testCaseResult]);
|
|
79
|
-
};
|
|
80
|
-
const mapStatus = (status) => {
|
|
81
|
-
return status;
|
|
82
|
-
};
|
|
83
|
-
const formatError = (error) => {
|
|
84
|
-
let errorMessage = `${error.name}: ${error.message}`;
|
|
85
|
-
if (error.codeFrame) {
|
|
86
|
-
errorMessage += '\n\n' + formatCodeFrame(error.codeFrame);
|
|
87
|
-
}
|
|
88
|
-
return errorMessage;
|
|
89
|
-
};
|
|
90
|
-
const formatCodeFrame = (codeFrame) => {
|
|
91
|
-
const lines = codeFrame.content.split('\n');
|
|
92
|
-
const lineNumber = codeFrame.location?.row || 1;
|
|
93
|
-
const column = codeFrame.location?.column || 1;
|
|
94
|
-
let formatted = `\n${codeFrame.fileName}:${lineNumber}:${column}\n`;
|
|
95
|
-
// Add context lines around the error
|
|
96
|
-
const startLine = Math.max(0, lineNumber - 3);
|
|
97
|
-
const endLine = Math.min(lines.length, lineNumber + 2);
|
|
98
|
-
for (let i = startLine; i < endLine; i++) {
|
|
99
|
-
const lineNum = i + 1;
|
|
100
|
-
const line = lines[i];
|
|
101
|
-
const isErrorLine = lineNum === lineNumber;
|
|
102
|
-
formatted += `${lineNum.toString().padStart(3)} | ${line}\n`;
|
|
103
|
-
if (isErrorLine && column > 0) {
|
|
104
|
-
const pointer = ' '.repeat(column + 5) + '^';
|
|
105
|
-
formatted += pointer + '\n';
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
return formatted;
|
|
109
|
-
};
|
|
110
|
-
const reset = () => {
|
|
111
|
-
state.suiteHierarchy = [];
|
|
112
|
-
state.testResults.clear();
|
|
113
|
-
state.fileStartTime = 0;
|
|
114
|
-
state.currentTest = undefined;
|
|
115
|
-
};
|
|
116
|
-
return {
|
|
117
|
-
handleBridgeEvent,
|
|
118
|
-
reset,
|
|
119
|
-
};
|
|
120
|
-
};
|
package/dist/global-setup.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"global-setup.d.ts","sourceRoot":"","sources":["../src/global-setup.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,aAAa,CAAC;AA4CxD,yBAA+B,YAAY,EAAE,UAAU,CAAC,YAAY,iBA4CnE"}
|
package/dist/global-setup.js
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import { getConfig, } from '@react-native-harness/config';
|
|
2
|
-
import { getHarness as getHarnessExternal, NoRunnerSpecifiedError, RunnerNotFoundError, } from '@react-native-harness/cli/external';
|
|
3
|
-
import { preRunMessage } from 'jest-util';
|
|
4
|
-
import { getAdditionalCliArgs } from './cli-args.js';
|
|
5
|
-
import { logTestEnvironmentReady, logTestRunHeader } from './logs.js';
|
|
6
|
-
const getHarnessConfig = async (globalConfig) => {
|
|
7
|
-
const projectRoot = globalConfig.rootDir;
|
|
8
|
-
const { config: harnessConfig } = await getConfig(projectRoot);
|
|
9
|
-
return harnessConfig;
|
|
10
|
-
};
|
|
11
|
-
const getHarnessRunner = (config, cliArgs) => {
|
|
12
|
-
const selectedRunnerName = cliArgs.harnessRunner ?? config.defaultRunner;
|
|
13
|
-
if (!selectedRunnerName) {
|
|
14
|
-
throw new NoRunnerSpecifiedError(config.runners);
|
|
15
|
-
}
|
|
16
|
-
const runner = config.runners.find((runner) => runner.name === selectedRunnerName);
|
|
17
|
-
if (!runner) {
|
|
18
|
-
throw new RunnerNotFoundError(selectedRunnerName, config.runners);
|
|
19
|
-
}
|
|
20
|
-
return runner;
|
|
21
|
-
};
|
|
22
|
-
const getHarness = async (runner) => {
|
|
23
|
-
return await getHarnessExternal(runner);
|
|
24
|
-
};
|
|
25
|
-
export default async function (globalConfig) {
|
|
26
|
-
preRunMessage.remove(process.stderr);
|
|
27
|
-
const harnessConfig = global.HARNESS_CONFIG ?? (await getHarnessConfig(globalConfig));
|
|
28
|
-
const isWatchMode = globalConfig.watch || globalConfig.watchAll;
|
|
29
|
-
if (global.HARNESS) {
|
|
30
|
-
// Do not setup again if HARNESS is already initialized
|
|
31
|
-
// This is useful when running tests in watch mode
|
|
32
|
-
if (harnessConfig.resetEnvironmentBetweenTestFiles) {
|
|
33
|
-
// In watch mode, we want to restart the environment before each test run
|
|
34
|
-
await new Promise((resolve) => {
|
|
35
|
-
global.HARNESS.bridge.once('ready', resolve);
|
|
36
|
-
global.HARNESS.environment.restart();
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
41
|
-
if (isWatchMode) {
|
|
42
|
-
// In watch mode, we want to dispose the Harness when the process exits.
|
|
43
|
-
process.on('exit', async () => {
|
|
44
|
-
await global.HARNESS.bridge.dispose();
|
|
45
|
-
await global.HARNESS.environment.dispose();
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
const cliArgs = getAdditionalCliArgs();
|
|
49
|
-
const selectedRunner = getHarnessRunner(harnessConfig, cliArgs);
|
|
50
|
-
if (globalConfig.collectCoverage) {
|
|
51
|
-
// This is going to be used by @react-native-harness/babel-preset
|
|
52
|
-
// to enable instrumentation of test files.
|
|
53
|
-
process.env.RN_HARNESS_COLLECT_COVERAGE = 'true';
|
|
54
|
-
}
|
|
55
|
-
logTestRunHeader(selectedRunner);
|
|
56
|
-
const harness = await getHarness(selectedRunner);
|
|
57
|
-
logTestEnvironmentReady(selectedRunner);
|
|
58
|
-
global.HARNESS_CONFIG = harnessConfig;
|
|
59
|
-
global.HARNESS = harness;
|
|
60
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"global-teardown.d.ts","sourceRoot":"","sources":["../src/global-teardown.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,aAAa,CAAC;AAExD,yBAA+B,YAAY,EAAE,UAAU,CAAC,YAAY,iBAYnE"}
|
package/dist/global-teardown.js
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
export default async function (globalConfig) {
|
|
2
|
-
const isWatchMode = globalConfig.watch || globalConfig.watchAll;
|
|
3
|
-
if (isWatchMode) {
|
|
4
|
-
// In watch mode, we don't want to dispose the Harness.
|
|
5
|
-
return;
|
|
6
|
-
}
|
|
7
|
-
if (global.HARNESS) {
|
|
8
|
-
await global.HARNESS.bridge.dispose();
|
|
9
|
-
await global.HARNESS.environment.dispose();
|
|
10
|
-
}
|
|
11
|
-
}
|