@react-native-harness/cli 1.0.0-alpha.9 → 1.0.0-canary.1761729829908
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 +23 -4
- package/dist/bundlers/metro.d.ts.map +1 -1
- package/dist/bundlers/metro.js +15 -5
- package/dist/commands/test.d.ts +2 -1
- package/dist/commands/test.d.ts.map +1 -1
- package/dist/commands/test.js +23 -19
- package/dist/discovery/index.d.ts +3 -0
- package/dist/discovery/index.d.ts.map +1 -0
- package/dist/discovery/index.js +1 -0
- package/dist/discovery/testDiscovery.d.ts +11 -0
- package/dist/discovery/testDiscovery.d.ts.map +1 -0
- package/dist/discovery/testDiscovery.js +29 -0
- package/dist/errors/errorHandler.d.ts +1 -1
- package/dist/errors/errorHandler.d.ts.map +1 -1
- package/dist/errors/errorHandler.js +103 -101
- package/dist/errors/errors.d.ts +6 -7
- package/dist/errors/errors.d.ts.map +1 -1
- package/dist/errors/errors.js +8 -12
- package/dist/external.d.ts +2 -0
- package/dist/external.d.ts.map +1 -0
- package/dist/external.js +1 -0
- package/dist/index.js +58 -33
- package/dist/jest.d.ts +2 -0
- package/dist/jest.d.ts.map +1 -0
- package/dist/jest.js +7 -0
- package/dist/platforms/android/emulator.d.ts +0 -1
- package/dist/platforms/android/emulator.d.ts.map +1 -1
- package/dist/platforms/android/emulator.js +26 -20
- package/dist/platforms/android/index.js +1 -1
- package/dist/platforms/ios/build.d.ts.map +1 -1
- package/dist/platforms/ios/build.js +5 -1
- package/dist/platforms/ios/device.d.ts +6 -2
- package/dist/platforms/ios/device.d.ts.map +1 -1
- package/dist/platforms/ios/simulator.d.ts.map +1 -1
- package/dist/platforms/ios/simulator.js +8 -3
- package/dist/platforms/platform-registry.d.ts.map +1 -1
- package/dist/platforms/platform-registry.js +3 -1
- package/dist/platforms/vega/build.d.ts +23 -0
- package/dist/platforms/vega/build.d.ts.map +1 -0
- package/dist/platforms/vega/build.js +55 -0
- package/dist/platforms/vega/device.d.ts +57 -0
- package/dist/platforms/vega/device.d.ts.map +1 -0
- package/dist/platforms/vega/device.js +206 -0
- package/dist/platforms/vega/index.d.ts +4 -0
- package/dist/platforms/vega/index.d.ts.map +1 -0
- package/dist/platforms/vega/index.js +75 -0
- package/dist/process.js +1 -1
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/dist/utils/status-formatter.d.ts +27 -0
- package/dist/utils/status-formatter.d.ts.map +1 -0
- package/dist/utils/status-formatter.js +54 -0
- package/dist/utils.d.ts +1 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +15 -0
- package/eslint.config.mjs +1 -0
- package/package.json +25 -9
- package/src/external.ts +0 -0
- package/src/index.ts +65 -44
- package/tsconfig.lib.json +1 -12
- package/src/bundlers/metro.ts +0 -89
- package/src/commands/test.ts +0 -218
- package/src/errors/errorHandler.ts +0 -196
- package/src/errors/errors.ts +0 -119
- package/src/platforms/android/build.ts +0 -49
- package/src/platforms/android/device.ts +0 -48
- package/src/platforms/android/emulator.ts +0 -139
- package/src/platforms/android/index.ts +0 -87
- package/src/platforms/ios/build.ts +0 -68
- package/src/platforms/ios/device.ts +0 -76
- package/src/platforms/ios/index.ts +0 -66
- package/src/platforms/ios/simulator.ts +0 -166
- package/src/platforms/platform-adapter.ts +0 -11
- package/src/platforms/platform-registry.ts +0 -24
- package/src/platforms/web/index.ts +0 -16
- package/src/process.ts +0 -33
- package/src/utils.ts +0 -12
package/README.md
CHANGED
|
@@ -1,7 +1,26 @@
|
|
|
1
|
-
|
|
1
|
+

|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
### Command Line Interface for React Native Harness
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
[![mit licence][license-badge]][license]
|
|
6
|
+
[![npm downloads][npm-downloads-badge]][npm-downloads]
|
|
7
|
+
[![Chat][chat-badge]][chat]
|
|
8
|
+
[![PRs Welcome][prs-welcome-badge]][prs-welcome]
|
|
6
9
|
|
|
7
|
-
|
|
10
|
+
Command-line interface that orchestrates test execution across iOS simulators and Android emulators, managing the entire testing workflow from bundling to result reporting.
|
|
11
|
+
|
|
12
|
+
## Made with ❤️ at Callstack
|
|
13
|
+
|
|
14
|
+
`@react-native-harness/cli` is an open source project and will always remain free to use. If you think it's cool, please star it 🌟. [Callstack][callstack-readme-with-love] is a group of React and React Native geeks, contact us at [hello@callstack.com](mailto:hello@callstack.com) if you need any help with these or just want to say hi!
|
|
15
|
+
|
|
16
|
+
Like the project? ⚛️ [Join the team](https://callstack.com/careers/?utm_campaign=Senior_RN&utm_source=github&utm_medium=readme) who does amazing stuff for clients and drives React Native Open Source! 🔥
|
|
17
|
+
|
|
18
|
+
[callstack-readme-with-love]: https://callstack.com/?utm_source=github.com&utm_medium=referral&utm_campaign=react-native-harness&utm_term=readme-with-love
|
|
19
|
+
[license-badge]: https://img.shields.io/npm/l/@react-native-harness/cli?style=for-the-badge
|
|
20
|
+
[license]: https://github.com/callstackincubator/react-native-harness/blob/main/LICENSE
|
|
21
|
+
[npm-downloads-badge]: https://img.shields.io/npm/dm/@react-native-harness/cli?style=for-the-badge
|
|
22
|
+
[npm-downloads]: https://www.npmjs.com/package/@react-native-harness/cli
|
|
23
|
+
[prs-welcome-badge]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=for-the-badge
|
|
24
|
+
[prs-welcome]: ../../CONTRIBUTING.md
|
|
25
|
+
[chat-badge]: https://img.shields.io/discord/426714625279524876.svg?style=for-the-badge
|
|
26
|
+
[chat]: https://discord.gg/xgGt7KAjxv
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"metro.d.ts","sourceRoot":"","sources":["../../src/bundlers/metro.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"metro.d.ts","sourceRoot":"","sources":["../../src/bundlers/metro.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAcvD,eAAO,MAAM,QAAQ,GAAU,gBAAc,KAAG,OAAO,CAAC,YAAY,CAiDnE,CAAC;AAEF,eAAO,MAAM,YAAY,GACvB,aAAW,EACX,mBAAe,EACf,mBAAiB,KAChB,OAAO,CAAC,IAAI,CA4Bd,CAAC;AAEF,eAAO,MAAM,SAAS,QAAa,OAAO,CAAC,IAAI,CAE9C,CAAC"}
|
package/dist/bundlers/metro.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import { getReactNativeCliPath, getExpoCliPath, getTimeoutSignal, spawn, SubprocessError, } from '@react-native-harness/tools';
|
|
1
|
+
import { getReactNativeCliPath, getExpoCliPath, getTimeoutSignal, spawn, logger, SubprocessError, } from '@react-native-harness/tools';
|
|
2
|
+
import { isPortAvailable } from '../utils.js';
|
|
3
|
+
import { MetroPortUnavailableError } from '../errors/errors.js';
|
|
2
4
|
const METRO_PORT = 8081;
|
|
3
5
|
export const runMetro = async (isExpo = false) => {
|
|
4
6
|
const metro = spawn('node', [
|
|
@@ -13,13 +15,17 @@ export const runMetro = async (isExpo = false) => {
|
|
|
13
15
|
...(isExpo && { EXPO_NO_METRO_WORKSPACE_ROOT: 'true' }),
|
|
14
16
|
},
|
|
15
17
|
});
|
|
16
|
-
// Forward metro output to
|
|
18
|
+
// Forward metro output to logger
|
|
17
19
|
metro.nodeChildProcess.then((childProcess) => {
|
|
18
20
|
if (childProcess.stdout) {
|
|
19
|
-
childProcess.stdout.
|
|
21
|
+
childProcess.stdout.on('data', (data) => {
|
|
22
|
+
logger.debug(data.toString().trim());
|
|
23
|
+
});
|
|
20
24
|
}
|
|
21
25
|
if (childProcess.stderr) {
|
|
22
|
-
childProcess.stderr.
|
|
26
|
+
childProcess.stderr.on('data', (data) => {
|
|
27
|
+
logger.debug(data.toString().trim());
|
|
28
|
+
});
|
|
23
29
|
}
|
|
24
30
|
});
|
|
25
31
|
metro.catch((error) => {
|
|
@@ -27,8 +33,12 @@ export const runMetro = async (isExpo = false) => {
|
|
|
27
33
|
if (error instanceof SubprocessError && error.signalName === 'SIGTERM') {
|
|
28
34
|
return;
|
|
29
35
|
}
|
|
30
|
-
|
|
36
|
+
logger.error('Metro crashed unexpectedly', error);
|
|
31
37
|
});
|
|
38
|
+
const isDefaultPortAvailable = await isPortAvailable(METRO_PORT);
|
|
39
|
+
if (!isDefaultPortAvailable) {
|
|
40
|
+
throw new MetroPortUnavailableError(METRO_PORT);
|
|
41
|
+
}
|
|
32
42
|
await waitForMetro();
|
|
33
43
|
return metro.nodeChildProcess;
|
|
34
44
|
};
|
package/dist/commands/test.d.ts
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
|
|
1
|
+
import { type TestFilterOptions } from '../discovery/index.js';
|
|
2
|
+
export declare const testCommand: (runnerName?: string, options?: TestFilterOptions) => Promise<void>;
|
|
2
3
|
//# sourceMappingURL=test.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test.d.ts","sourceRoot":"","sources":["../../src/commands/test.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"test.d.ts","sourceRoot":"","sources":["../../src/commands/test.ts"],"names":[],"mappings":"AAsBA,OAAO,EAEL,KAAK,iBAAiB,EACvB,MAAM,uBAAuB,CAAC;AAiK/B,eAAO,MAAM,WAAW,GACtB,aAAa,MAAM,EACnB,UAAS,iBAAsB,KAC9B,OAAO,CAAC,IAAI,CA0Cd,CAAC"}
|
package/dist/commands/test.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { getBridgeServer, } from '@react-native-harness/bridge/server';
|
|
2
2
|
import { getConfig, } from '@react-native-harness/config';
|
|
3
3
|
import { getPlatformAdapter } from '../platforms/platform-registry.js';
|
|
4
|
-
import {
|
|
5
|
-
import { intro, logger, outro, spinner, progress, } from '@react-native-harness/tools';
|
|
4
|
+
import { intro, logger, outro, spinner } from '@react-native-harness/tools';
|
|
6
5
|
import { BridgeTimeoutError } from '../errors/errors.js';
|
|
7
6
|
import { assert } from '../utils.js';
|
|
8
7
|
import { EnvironmentInitializationError, NoRunnerSpecifiedError, RpcClientError, RunnerNotFoundError, } from '../errors/errors.js';
|
|
8
|
+
import { discoverTestFiles, } from '../discovery/index.js';
|
|
9
9
|
const setupEnvironment = async (context) => {
|
|
10
10
|
const startSpinner = spinner();
|
|
11
11
|
const platform = context.runner.platform;
|
|
@@ -33,28 +33,24 @@ const setupEnvironment = async (context) => {
|
|
|
33
33
|
}
|
|
34
34
|
startSpinner.stop(`"${context.runner.name}" (${platform}) runner started`);
|
|
35
35
|
};
|
|
36
|
-
const findTestFiles = async (context,
|
|
36
|
+
const findTestFiles = async (context, options = {}) => {
|
|
37
37
|
const discoverSpinner = spinner();
|
|
38
38
|
discoverSpinner.start('Discovering tests');
|
|
39
|
-
|
|
40
|
-
const glob = new Glob(globPattern, {
|
|
41
|
-
cwd: context.projectRoot,
|
|
42
|
-
});
|
|
43
|
-
context.testFiles = await glob.walk();
|
|
39
|
+
context.testFiles = await discoverTestFiles(context.projectRoot, context.config.include, options);
|
|
44
40
|
discoverSpinner.stop(`Found ${context.testFiles.length} test files`);
|
|
45
41
|
};
|
|
46
|
-
const runTests = async (context) => {
|
|
47
|
-
const { bridge, environment, testFiles } = context;
|
|
42
|
+
const runTests = async (context, options = {}) => {
|
|
43
|
+
const { bridge, environment, testFiles, config } = context;
|
|
48
44
|
assert(bridge != null, 'Bridge not initialized');
|
|
49
45
|
assert(environment != null, 'Environment not initialized');
|
|
50
46
|
assert(testFiles != null, 'Test files not initialized');
|
|
51
|
-
let runSpinner =
|
|
47
|
+
let runSpinner = spinner();
|
|
52
48
|
runSpinner.start('Running tests');
|
|
53
49
|
let shouldRestart = false;
|
|
54
50
|
for (const testFile of testFiles) {
|
|
55
51
|
if (shouldRestart) {
|
|
56
|
-
runSpinner =
|
|
57
|
-
runSpinner.
|
|
52
|
+
runSpinner = spinner();
|
|
53
|
+
runSpinner.start(`Restarting environment for next test file`);
|
|
58
54
|
await new Promise((resolve) => {
|
|
59
55
|
bridge.once('ready', resolve);
|
|
60
56
|
environment.restart();
|
|
@@ -65,14 +61,22 @@ const runTests = async (context) => {
|
|
|
65
61
|
if (!client) {
|
|
66
62
|
throw new RpcClientError('No RPC client available', 3001, 'No clients connected');
|
|
67
63
|
}
|
|
68
|
-
|
|
64
|
+
// Pass only testNamePattern to runtime (file filtering already done)
|
|
65
|
+
const executionOptions = {
|
|
66
|
+
testNamePattern: options.testNamePattern,
|
|
67
|
+
};
|
|
68
|
+
const result = await client.runTests(testFile, executionOptions);
|
|
69
69
|
context.results = [...(context.results ?? []), ...result.suites];
|
|
70
|
-
|
|
70
|
+
if (config.resetEnvironmentBetweenTestFiles) {
|
|
71
|
+
shouldRestart = true;
|
|
72
|
+
}
|
|
73
|
+
runSpinner.stop(`Test file ${testFile} completed`);
|
|
71
74
|
}
|
|
75
|
+
runSpinner.stop('Tests completed');
|
|
72
76
|
};
|
|
73
77
|
const cleanUp = async (context) => {
|
|
74
78
|
if (context.bridge) {
|
|
75
|
-
context.bridge.
|
|
79
|
+
context.bridge.dispose();
|
|
76
80
|
}
|
|
77
81
|
if (context.environment) {
|
|
78
82
|
await context.environment.dispose();
|
|
@@ -97,7 +101,7 @@ const hasFailedTests = (results) => {
|
|
|
97
101
|
}
|
|
98
102
|
return false;
|
|
99
103
|
};
|
|
100
|
-
export const testCommand = async (runnerName,
|
|
104
|
+
export const testCommand = async (runnerName, options = {}) => {
|
|
101
105
|
intro('React Native Test Harness');
|
|
102
106
|
const { config, projectRoot } = await getConfig(process.cwd());
|
|
103
107
|
const selectedRunnerName = runnerName ?? config.defaultRunner;
|
|
@@ -117,8 +121,8 @@ export const testCommand = async (runnerName, pattern) => {
|
|
|
117
121
|
};
|
|
118
122
|
try {
|
|
119
123
|
await setupEnvironment(context);
|
|
120
|
-
await findTestFiles(context,
|
|
121
|
-
await runTests(context);
|
|
124
|
+
await findTestFiles(context, options);
|
|
125
|
+
await runTests(context, options);
|
|
122
126
|
assert(context.results != null, 'Results not initialized');
|
|
123
127
|
config.reporter?.report(context.results);
|
|
124
128
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/discovery/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,YAAY,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { discoverTestFiles } from './testDiscovery.js';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type TestFilterOptions = {
|
|
2
|
+
testNamePattern?: string;
|
|
3
|
+
testPathPattern?: string;
|
|
4
|
+
testPathIgnorePatterns?: string[];
|
|
5
|
+
testMatch?: string[];
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Discovers test files based on patterns and filtering options
|
|
9
|
+
*/
|
|
10
|
+
export declare const discoverTestFiles: (projectRoot: string, configInclude: string | string[], options?: TestFilterOptions) => Promise<string[]>;
|
|
11
|
+
//# sourceMappingURL=testDiscovery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testDiscovery.d.ts","sourceRoot":"","sources":["../../src/discovery/testDiscovery.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,iBAAiB,GAAG;IAC9B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,sBAAsB,CAAC,EAAE,MAAM,EAAE,CAAC;IAClC,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,iBAAiB,GAC5B,aAAa,MAAM,EACnB,eAAe,MAAM,GAAG,MAAM,EAAE,EAChC,UAAS,iBAAsB,KAC9B,OAAO,CAAC,MAAM,EAAE,CAiClB,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Glob } from 'glob';
|
|
2
|
+
/**
|
|
3
|
+
* Discovers test files based on patterns and filtering options
|
|
4
|
+
*/
|
|
5
|
+
export const discoverTestFiles = async (projectRoot, configInclude, options = {}) => {
|
|
6
|
+
// Priority: testMatch > configInclude
|
|
7
|
+
const patterns = options.testMatch || configInclude;
|
|
8
|
+
const patternArray = Array.isArray(patterns) ? patterns : [patterns];
|
|
9
|
+
// Glob discovery
|
|
10
|
+
const allFiles = [];
|
|
11
|
+
for (const pattern of patternArray) {
|
|
12
|
+
const glob = new Glob(pattern, { cwd: projectRoot, nodir: true });
|
|
13
|
+
const files = await glob.walk();
|
|
14
|
+
allFiles.push(...files);
|
|
15
|
+
}
|
|
16
|
+
// Remove duplicates
|
|
17
|
+
let uniqueFiles = [...new Set(allFiles)];
|
|
18
|
+
// Apply testPathPattern filtering
|
|
19
|
+
if (options.testPathPattern) {
|
|
20
|
+
const regex = new RegExp(options.testPathPattern);
|
|
21
|
+
uniqueFiles = uniqueFiles.filter((file) => regex.test(file));
|
|
22
|
+
}
|
|
23
|
+
// Apply testPathIgnorePatterns filtering
|
|
24
|
+
if (options.testPathIgnorePatterns?.length) {
|
|
25
|
+
const ignoreRegexes = options.testPathIgnorePatterns.map((p) => new RegExp(p));
|
|
26
|
+
uniqueFiles = uniqueFiles.filter((file) => !ignoreRegexes.some((regex) => regex.test(file)));
|
|
27
|
+
}
|
|
28
|
+
return uniqueFiles;
|
|
29
|
+
};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const
|
|
1
|
+
export declare const formatError: (error: unknown) => string;
|
|
2
2
|
//# sourceMappingURL=errorHandler.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errorHandler.d.ts","sourceRoot":"","sources":["../../src/errors/errorHandler.ts"],"names":[],"mappings":"AAiBA,eAAO,MAAM,WAAW,GAAI,OAAO,OAAO,KAAG,
|
|
1
|
+
{"version":3,"file":"errorHandler.d.ts","sourceRoot":"","sources":["../../src/errors/errorHandler.ts"],"names":[],"mappings":"AAiBA,eAAO,MAAM,WAAW,GAAI,OAAO,OAAO,KAAG,MAqK5C,CAAC"}
|
|
@@ -1,150 +1,152 @@
|
|
|
1
1
|
import { ConfigLoadError, ConfigNotFoundError, ConfigValidationError, } from '@react-native-harness/config';
|
|
2
2
|
import { AssertionError } from '../utils.js';
|
|
3
|
-
import { NoRunnerSpecifiedError, RunnerNotFoundError, EnvironmentInitializationError, TestExecutionError, RpcClientError,
|
|
4
|
-
export const
|
|
3
|
+
import { NoRunnerSpecifiedError, RunnerNotFoundError, EnvironmentInitializationError, TestExecutionError, RpcClientError, BridgeTimeoutError, BundlingFailedError, MetroPortUnavailableError, } from './errors.js';
|
|
4
|
+
export const formatError = (error) => {
|
|
5
|
+
const lines = [];
|
|
5
6
|
if (error instanceof AssertionError) {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
lines.push(`\n❌ Assertion Error`);
|
|
8
|
+
lines.push(`\nError: ${error.message}`);
|
|
9
|
+
lines.push(`\nPlease check your configuration and try again.`);
|
|
9
10
|
}
|
|
10
11
|
else if (error instanceof ConfigValidationError) {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
lines.push(`\n❌ Configuration Error`);
|
|
13
|
+
lines.push(`\nFile: ${error.filePath}`);
|
|
14
|
+
lines.push(`\nValidation errors:`);
|
|
14
15
|
error.validationErrors.forEach((err) => {
|
|
15
|
-
|
|
16
|
+
lines.push(` • ${err}`);
|
|
16
17
|
});
|
|
17
|
-
|
|
18
|
+
lines.push(`\nPlease fix the configuration errors and try again.`);
|
|
18
19
|
}
|
|
19
20
|
else if (error instanceof ConfigNotFoundError) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
lines.push(`\n❌ Configuration Not Found`);
|
|
22
|
+
lines.push(`\nCould not find 'rn-harness.config' in '${error.searchPath}' or any parent directories.`);
|
|
23
|
+
lines.push(`\nSupported file extensions: .js, .mjs, .cjs, .json`);
|
|
24
|
+
lines.push(`\nPlease create a configuration file or run from a directory that contains one.`);
|
|
24
25
|
}
|
|
25
26
|
else if (error instanceof ConfigLoadError) {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
lines.push(`\n❌ Configuration Load Error`);
|
|
28
|
+
lines.push(`\nFile: ${error.filePath}`);
|
|
29
|
+
lines.push(`Error: ${error.message}`);
|
|
29
30
|
if (error.cause) {
|
|
30
|
-
|
|
31
|
+
lines.push(`\nCause: ${error.cause.message}`);
|
|
31
32
|
}
|
|
32
|
-
|
|
33
|
+
lines.push(`\nPlease check your configuration file syntax and try again.`);
|
|
33
34
|
}
|
|
34
35
|
else if (error instanceof NoRunnerSpecifiedError) {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
lines.push('\n❌ No runner specified');
|
|
37
|
+
lines.push('\nPlease specify a runner name or set a defaultRunner in your config.');
|
|
38
|
+
lines.push('\nUsage: react-native-harness test [runner-name] [pattern]');
|
|
39
|
+
lines.push('\nAvailable runners:');
|
|
39
40
|
error.availableRunners.forEach((r) => {
|
|
40
|
-
|
|
41
|
+
lines.push(` • ${r.name} (${r.platform})`);
|
|
41
42
|
});
|
|
42
|
-
|
|
43
|
-
|
|
43
|
+
lines.push('\nTo set a default runner, add "defaultRunner" to your config:');
|
|
44
|
+
lines.push(' { "defaultRunner": "your-runner-name" }');
|
|
44
45
|
}
|
|
45
46
|
else if (error instanceof RunnerNotFoundError) {
|
|
46
|
-
|
|
47
|
-
|
|
47
|
+
lines.push(`\n❌ Runner "${error.runnerName}" not found`);
|
|
48
|
+
lines.push('\nAvailable runners:');
|
|
48
49
|
error.availableRunners.forEach((r) => {
|
|
49
|
-
|
|
50
|
+
lines.push(` • ${r.name} (${r.platform})`);
|
|
50
51
|
});
|
|
51
|
-
|
|
52
|
+
lines.push('\nTo add a new runner, update your rn-harness.config file.');
|
|
52
53
|
}
|
|
53
54
|
else if (error instanceof EnvironmentInitializationError) {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
lines.push(`\n❌ Environment Initialization Error`);
|
|
56
|
+
lines.push(`\nRunner: ${error.runnerName} (${error.platform})`);
|
|
57
|
+
lines.push(`\nError: ${error.message}`);
|
|
57
58
|
if (error.details) {
|
|
58
|
-
|
|
59
|
+
lines.push(`\nDetails: ${error.details}`);
|
|
59
60
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
61
|
+
lines.push(`\nTroubleshooting steps:`);
|
|
62
|
+
lines.push(` • Verify that ${error.platform} development environment is properly set up`);
|
|
63
|
+
lines.push(` • Check that the app is built and ready for testing`);
|
|
64
|
+
lines.push(` • Ensure all required dependencies are installed`);
|
|
64
65
|
if (error.platform === 'ios') {
|
|
65
|
-
|
|
66
|
+
lines.push(` • Verify Xcode and iOS Simulator are working correctly`);
|
|
66
67
|
}
|
|
67
68
|
else if (error.platform === 'android') {
|
|
68
|
-
|
|
69
|
+
lines.push(` • Verify Android SDK and emulator are working correctly`);
|
|
69
70
|
}
|
|
70
|
-
|
|
71
|
+
lines.push(`\nPlease check your environment configuration and try again.`);
|
|
71
72
|
}
|
|
72
73
|
else if (error instanceof TestExecutionError) {
|
|
73
|
-
|
|
74
|
-
|
|
74
|
+
lines.push(`\n❌ Test Execution Error`);
|
|
75
|
+
lines.push(`\nFile: ${error.testFile}`);
|
|
75
76
|
if (error.testSuite) {
|
|
76
|
-
|
|
77
|
+
lines.push(`\nSuite: ${error.testSuite}`);
|
|
77
78
|
}
|
|
78
79
|
if (error.testName) {
|
|
79
|
-
|
|
80
|
+
lines.push(`\nTest: ${error.testName}`);
|
|
80
81
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
82
|
+
lines.push(`\nError: ${error.message}`);
|
|
83
|
+
lines.push(`\nTroubleshooting steps:`);
|
|
84
|
+
lines.push(` • Check the test file syntax and logic`);
|
|
85
|
+
lines.push(` • Verify all test dependencies are available`);
|
|
86
|
+
lines.push(` • Ensure the app is in the expected state for the test`);
|
|
87
|
+
lines.push(` • Check device/emulator logs for additional error details`);
|
|
88
|
+
lines.push(`\nPlease check your test file and try again.`);
|
|
88
89
|
}
|
|
89
90
|
else if (error instanceof RpcClientError) {
|
|
90
|
-
|
|
91
|
-
|
|
91
|
+
lines.push(`\n❌ RPC Client Error`);
|
|
92
|
+
lines.push(`\nError: ${error.message}`);
|
|
92
93
|
if (error.bridgePort) {
|
|
93
|
-
|
|
94
|
+
lines.push(`\nBridge Port: ${error.bridgePort}`);
|
|
94
95
|
}
|
|
95
96
|
if (error.connectionStatus) {
|
|
96
|
-
|
|
97
|
+
lines.push(`\nConnection Status: ${error.connectionStatus}`);
|
|
97
98
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
}
|
|
105
|
-
else if (error instanceof AppNotInstalledError) {
|
|
106
|
-
console.error(`\n❌ App Not Installed`);
|
|
107
|
-
console.error(`\nThe app "${error.bundleId}" is not installed on ${error.platform === 'ios' ? 'simulator' : 'emulator'} "${error.deviceName}".`);
|
|
108
|
-
console.error(`\nTo resolve this issue:`);
|
|
109
|
-
if (error.platform === 'ios') {
|
|
110
|
-
console.error(` • Build and install the app: npx react-native run-ios --simulator="${error.deviceName}"`);
|
|
111
|
-
console.error(` • Or install from Xcode: Open ios/*.xcworkspace and run the project`);
|
|
112
|
-
}
|
|
113
|
-
else {
|
|
114
|
-
console.error(` • Build and install the app: npx react-native run-android`);
|
|
115
|
-
console.error(` • Or build manually: ./gradlew assembleDebug && adb install android/app/build/outputs/apk/debug/app-debug.apk`);
|
|
116
|
-
}
|
|
117
|
-
console.error(`\nPlease install the app and try running the tests again.`);
|
|
99
|
+
lines.push(`\nTroubleshooting steps:`);
|
|
100
|
+
lines.push(` • Verify the React Native app is running and connected`);
|
|
101
|
+
lines.push(` • Check that the bridge port is not blocked by firewall`);
|
|
102
|
+
lines.push(` • Ensure the app has the React Native Harness runtime integrated`);
|
|
103
|
+
lines.push(` • Try restarting the app and test harness`);
|
|
104
|
+
lines.push(`\nPlease check your bridge connection and try again.`);
|
|
118
105
|
}
|
|
119
106
|
else if (error instanceof BundlingFailedError) {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
107
|
+
lines.push(`\n❌ Test File Bundling Error`);
|
|
108
|
+
lines.push(`\nFile: ${error.modulePath}`);
|
|
109
|
+
lines.push(`\nError: ${error.reason}`);
|
|
110
|
+
lines.push(`\nTroubleshooting steps:`);
|
|
111
|
+
lines.push(` • Check the test file syntax and imports`);
|
|
112
|
+
lines.push(` • Verify all imported modules exist and are accessible`);
|
|
113
|
+
lines.push(` • Ensure the Metro bundler configuration is correct`);
|
|
114
|
+
lines.push(` • Check for any circular dependencies in the test file`);
|
|
115
|
+
lines.push(` • Verify that all required packages are installed`);
|
|
116
|
+
lines.push(`\nPlease fix the bundling issues and try again.`);
|
|
130
117
|
}
|
|
131
118
|
else if (error instanceof BridgeTimeoutError) {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
119
|
+
lines.push(`\n❌ Bridge Connection Timeout`);
|
|
120
|
+
lines.push(`\nThe bridge connection timed out after ${error.timeout}ms while waiting for the "${error.runnerName}" (${error.platform}) runner to be ready.`);
|
|
121
|
+
lines.push(`\nThis usually indicates that:`);
|
|
122
|
+
lines.push(` • The React Native app failed to load or connect to the bridge`);
|
|
123
|
+
lines.push(` • The app crashed during startup`);
|
|
124
|
+
lines.push(` • Network connectivity issues between the app and the test harness`);
|
|
125
|
+
lines.push(` • The app is taking longer than expected to initialize`);
|
|
126
|
+
lines.push(`\nTo resolve this issue:`);
|
|
127
|
+
lines.push(` • Check that the app is properly installed and can start normally`);
|
|
128
|
+
lines.push(` • Verify that the app has the React Native Harness runtime integrated`);
|
|
129
|
+
lines.push(` • Check device/emulator logs for any startup errors`);
|
|
130
|
+
lines.push(` • Ensure the test harness bridge port (3001) is not blocked`);
|
|
131
|
+
lines.push(`\nIf the app needs more time to start, consider increasing the timeout in the configuration.`);
|
|
132
|
+
}
|
|
133
|
+
else if (error instanceof MetroPortUnavailableError) {
|
|
134
|
+
lines.push(`\n❌ Metro Port Unavailable`);
|
|
135
|
+
lines.push(`\nPort ${error.port} is already in use or unavailable.`);
|
|
136
|
+
lines.push(`\nThis usually indicates that:`);
|
|
137
|
+
lines.push(` • Another Metro bundler instance is already running`);
|
|
138
|
+
lines.push(` • Another application is using port ${error.port}`);
|
|
139
|
+
lines.push(` • The port is blocked by a firewall or security software`);
|
|
140
|
+
lines.push(`\nTo resolve this issue:`);
|
|
141
|
+
lines.push(` • Stop any running Metro bundler instances`);
|
|
142
|
+
lines.push(` • Check for other applications using port ${error.port}: lsof -i :${error.port}`);
|
|
143
|
+
lines.push(` • Kill the process using the port: kill -9 <PID>`);
|
|
144
|
+
lines.push(` • Or use a different port by updating your Metro configuration`);
|
|
145
|
+
lines.push(`\nPlease free up the port and try again.`);
|
|
145
146
|
}
|
|
146
147
|
else {
|
|
147
|
-
|
|
148
|
-
|
|
148
|
+
// Re-throw the error to be handled by the caller
|
|
149
|
+
throw error;
|
|
149
150
|
}
|
|
151
|
+
return lines.join('');
|
|
150
152
|
};
|
package/dist/errors/errors.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
type TestRunnerConfig = any;
|
|
2
2
|
export declare class NoRunnerSpecifiedError extends Error {
|
|
3
3
|
constructor(availableRunners: TestRunnerConfig[]);
|
|
4
4
|
availableRunners: TestRunnerConfig[];
|
|
@@ -32,15 +32,14 @@ export declare class BridgeTimeoutError extends Error {
|
|
|
32
32
|
readonly platform: string;
|
|
33
33
|
constructor(timeout: number, runnerName: string, platform: string);
|
|
34
34
|
}
|
|
35
|
-
export declare class AppNotInstalledError extends Error {
|
|
36
|
-
readonly deviceName: string;
|
|
37
|
-
readonly bundleId: string;
|
|
38
|
-
readonly platform: 'ios' | 'android';
|
|
39
|
-
constructor(deviceName: string, bundleId: string, platform: 'ios' | 'android');
|
|
40
|
-
}
|
|
41
35
|
export declare class BundlingFailedError extends Error {
|
|
42
36
|
readonly modulePath: string;
|
|
43
37
|
readonly reason: string;
|
|
44
38
|
constructor(modulePath: string, reason: string);
|
|
45
39
|
}
|
|
40
|
+
export declare class MetroPortUnavailableError extends Error {
|
|
41
|
+
readonly port: number;
|
|
42
|
+
constructor(port: number);
|
|
43
|
+
}
|
|
44
|
+
export {};
|
|
46
45
|
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/errors/errors.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/errors/errors.ts"],"names":[],"mappings":"AAAA,KAAK,gBAAgB,GAAG,GAAG,CAAC;AAE5B,qBAAa,sBAAuB,SAAQ,KAAK;gBACnC,gBAAgB,EAAE,gBAAgB,EAAE;IAKhD,gBAAgB,EAAE,gBAAgB,EAAE,CAAC;CACtC;AAED,qBAAa,mBAAoB,SAAQ,KAAK;gBAChC,UAAU,EAAE,MAAM,EAAE,gBAAgB,EAAE,gBAAgB,EAAE;IAMpE,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,gBAAgB,EAAE,CAAC;CACtC;AAED,qBAAa,8BAA+B,SAAQ,KAAK;gBAErD,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,MAAM;IAUlB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,kBAAmB,SAAQ,KAAK;gBAEzC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,OAAO,EACd,SAAS,CAAC,EAAE,MAAM,EAClB,QAAQ,CAAC,EAAE,MAAM;IAenB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,OAAO,CAAC;CACxB;AAED,qBAAa,cAAe,SAAQ,KAAK;gBAC3B,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,gBAAgB,CAAC,EAAE,MAAM;IAS3E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,qBAAa,kBAAmB,SAAQ,KAAK;aAEzB,OAAO,EAAE,MAAM;aACf,UAAU,EAAE,MAAM;aAClB,QAAQ,EAAE,MAAM;gBAFhB,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM;CAOnC;AAED,qBAAa,mBAAoB,SAAQ,KAAK;aAE1B,UAAU,EAAE,MAAM;aAClB,MAAM,EAAE,MAAM;gBADd,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM;CAKjC;AAED,qBAAa,yBAA0B,SAAQ,KAAK;aACtB,IAAI,EAAE,MAAM;gBAAZ,IAAI,EAAE,MAAM;CAIzC"}
|
package/dist/errors/errors.js
CHANGED
|
@@ -69,18 +69,6 @@ export class BridgeTimeoutError extends Error {
|
|
|
69
69
|
this.name = 'BridgeTimeoutError';
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
|
-
export class AppNotInstalledError extends Error {
|
|
73
|
-
deviceName;
|
|
74
|
-
bundleId;
|
|
75
|
-
platform;
|
|
76
|
-
constructor(deviceName, bundleId, platform) {
|
|
77
|
-
super(`App "${bundleId}" is not installed on ${platform === 'ios' ? 'simulator' : 'emulator'} "${deviceName}"`);
|
|
78
|
-
this.deviceName = deviceName;
|
|
79
|
-
this.bundleId = bundleId;
|
|
80
|
-
this.platform = platform;
|
|
81
|
-
this.name = 'AppNotInstalledError';
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
72
|
export class BundlingFailedError extends Error {
|
|
85
73
|
modulePath;
|
|
86
74
|
reason;
|
|
@@ -91,3 +79,11 @@ export class BundlingFailedError extends Error {
|
|
|
91
79
|
this.name = 'BundlingFailedError';
|
|
92
80
|
}
|
|
93
81
|
}
|
|
82
|
+
export class MetroPortUnavailableError extends Error {
|
|
83
|
+
port;
|
|
84
|
+
constructor(port) {
|
|
85
|
+
super(`Metro port ${port} is not available`);
|
|
86
|
+
this.port = port;
|
|
87
|
+
this.name = 'MetroPortUnavailableError';
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"external.d.ts","sourceRoot":"","sources":["../src/external.ts"],"names":[],"mappings":""}
|
package/dist/external.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|