@react-native-harness/cli 1.0.0-alpha.9 → 1.0.0-canary.1761046904277
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.map +1 -1
- package/dist/errors/errorHandler.js +27 -3
- package/dist/errors/errors.d.ts +6 -2
- package/dist/errors/errors.d.ts.map +1 -1
- package/dist/errors/errors.js +14 -1
- package/dist/external.d.ts +9 -0
- package/dist/external.d.ts.map +1 -0
- package/dist/external.js +25 -0
- package/dist/index.js +20 -4
- 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 +21 -6
- package/src/bundlers/metro.ts +17 -4
- package/src/commands/test.ts +39 -25
- package/src/discovery/index.ts +2 -0
- package/src/discovery/testDiscovery.ts +50 -0
- package/src/errors/errorHandler.ts +34 -4
- package/src/errors/errors.ts +16 -4
- package/src/external.ts +44 -0
- package/src/index.ts +33 -5
- package/src/platforms/android/emulator.ts +26 -28
- package/src/platforms/android/index.ts +1 -1
- package/src/platforms/ios/build.ts +3 -0
- package/src/platforms/ios/device.ts +5 -2
- package/src/platforms/ios/simulator.ts +8 -3
- package/src/platforms/platform-registry.ts +3 -1
- package/src/platforms/vega/build.ts +85 -0
- package/src/platforms/vega/device.ts +258 -0
- package/src/platforms/vega/index.ts +107 -0
- package/src/process.ts +1 -1
- package/src/utils.ts +17 -0
- package/tsconfig.json +3 -0
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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errorHandler.d.ts","sourceRoot":"","sources":["../../src/errors/errorHandler.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"errorHandler.d.ts","sourceRoot":"","sources":["../../src/errors/errorHandler.ts"],"names":[],"mappings":"AAkBA,eAAO,MAAM,WAAW,GAAI,OAAO,OAAO,KAAG,IA+M5C,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
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, AppNotInstalledError, BridgeTimeoutError, BundlingFailedError, } from './errors.js';
|
|
3
|
+
import { NoRunnerSpecifiedError, RunnerNotFoundError, EnvironmentInitializationError, TestExecutionError, RpcClientError, AppNotInstalledError, BridgeTimeoutError, BundlingFailedError, MetroPortUnavailableError, } from './errors.js';
|
|
4
4
|
export const handleError = (error) => {
|
|
5
5
|
if (error instanceof AssertionError) {
|
|
6
6
|
console.error(`\n❌ Assertion Error`);
|
|
@@ -104,16 +104,26 @@ export const handleError = (error) => {
|
|
|
104
104
|
}
|
|
105
105
|
else if (error instanceof AppNotInstalledError) {
|
|
106
106
|
console.error(`\n❌ App Not Installed`);
|
|
107
|
-
|
|
107
|
+
const deviceType = error.platform === 'ios'
|
|
108
|
+
? 'simulator'
|
|
109
|
+
: error.platform === 'android'
|
|
110
|
+
? 'emulator'
|
|
111
|
+
: 'virtual device';
|
|
112
|
+
console.error(`\nThe app "${error.bundleId}" is not installed on ${deviceType} "${error.deviceName}".`);
|
|
108
113
|
console.error(`\nTo resolve this issue:`);
|
|
109
114
|
if (error.platform === 'ios') {
|
|
110
115
|
console.error(` • Build and install the app: npx react-native run-ios --simulator="${error.deviceName}"`);
|
|
111
116
|
console.error(` • Or install from Xcode: Open ios/*.xcworkspace and run the project`);
|
|
112
117
|
}
|
|
113
|
-
else {
|
|
118
|
+
else if (error.platform === 'android') {
|
|
114
119
|
console.error(` • Build and install the app: npx react-native run-android`);
|
|
115
120
|
console.error(` • Or build manually: ./gradlew assembleDebug && adb install android/app/build/outputs/apk/debug/app-debug.apk`);
|
|
116
121
|
}
|
|
122
|
+
else if (error.platform === 'vega') {
|
|
123
|
+
console.error(` • Build the Vega app: npm run build:app`);
|
|
124
|
+
console.error(` • Install the app: kepler device install-app -p <path-to-vpkg> --device "${error.deviceName}"`);
|
|
125
|
+
console.error(` • Or use the combined command: kepler run-kepler <path-to-vpkg> "${error.bundleId}" -d "${error.deviceName}"`);
|
|
126
|
+
}
|
|
117
127
|
console.error(`\nPlease install the app and try running the tests again.`);
|
|
118
128
|
}
|
|
119
129
|
else if (error instanceof BundlingFailedError) {
|
|
@@ -143,6 +153,20 @@ export const handleError = (error) => {
|
|
|
143
153
|
console.error(` • Ensure the test harness bridge port (3001) is not blocked`);
|
|
144
154
|
console.error(`\nIf the app needs more time to start, consider increasing the timeout in the configuration.`);
|
|
145
155
|
}
|
|
156
|
+
else if (error instanceof MetroPortUnavailableError) {
|
|
157
|
+
console.error(`\n❌ Metro Port Unavailable`);
|
|
158
|
+
console.error(`\nPort ${error.port} is already in use or unavailable.`);
|
|
159
|
+
console.error(`\nThis usually indicates that:`);
|
|
160
|
+
console.error(` • Another Metro bundler instance is already running`);
|
|
161
|
+
console.error(` • Another application is using port ${error.port}`);
|
|
162
|
+
console.error(` • The port is blocked by a firewall or security software`);
|
|
163
|
+
console.error(`\nTo resolve this issue:`);
|
|
164
|
+
console.error(` • Stop any running Metro bundler instances`);
|
|
165
|
+
console.error(` • Check for other applications using port ${error.port}: lsof -i :${error.port}`);
|
|
166
|
+
console.error(` • Kill the process using the port: kill -9 <PID>`);
|
|
167
|
+
console.error(` • Or use a different port by updating your Metro configuration`);
|
|
168
|
+
console.error(`\nPlease free up the port and try again.`);
|
|
169
|
+
}
|
|
146
170
|
else {
|
|
147
171
|
console.error(`\n❌ Unexpected Error`);
|
|
148
172
|
console.error(error);
|
package/dist/errors/errors.d.ts
CHANGED
|
@@ -35,12 +35,16 @@ export declare class BridgeTimeoutError extends Error {
|
|
|
35
35
|
export declare class AppNotInstalledError extends Error {
|
|
36
36
|
readonly deviceName: string;
|
|
37
37
|
readonly bundleId: string;
|
|
38
|
-
readonly platform: 'ios' | 'android';
|
|
39
|
-
constructor(deviceName: string, bundleId: string, platform: 'ios' | 'android');
|
|
38
|
+
readonly platform: 'ios' | 'android' | 'vega';
|
|
39
|
+
constructor(deviceName: string, bundleId: string, platform: 'ios' | 'android' | 'vega');
|
|
40
40
|
}
|
|
41
41
|
export declare class BundlingFailedError extends Error {
|
|
42
42
|
readonly modulePath: string;
|
|
43
43
|
readonly reason: string;
|
|
44
44
|
constructor(modulePath: string, reason: string);
|
|
45
45
|
}
|
|
46
|
+
export declare class MetroPortUnavailableError extends Error {
|
|
47
|
+
readonly port: number;
|
|
48
|
+
constructor(port: number);
|
|
49
|
+
}
|
|
46
50
|
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/errors/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAEhE,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,oBAAqB,SAAQ,KAAK;aAE3B,UAAU,EAAE,MAAM;aAClB,QAAQ,EAAE,MAAM;aAChB,QAAQ,EAAE,KAAK,GAAG,SAAS;
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/errors/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAEhE,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,oBAAqB,SAAQ,KAAK;aAE3B,UAAU,EAAE,MAAM;aAClB,QAAQ,EAAE,MAAM;aAChB,QAAQ,EAAE,KAAK,GAAG,SAAS,GAAG,MAAM;gBAFpC,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,KAAK,GAAG,SAAS,GAAG,MAAM;CAcvD;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
|
@@ -74,7 +74,12 @@ export class AppNotInstalledError extends Error {
|
|
|
74
74
|
bundleId;
|
|
75
75
|
platform;
|
|
76
76
|
constructor(deviceName, bundleId, platform) {
|
|
77
|
-
|
|
77
|
+
const deviceType = platform === 'ios'
|
|
78
|
+
? 'simulator'
|
|
79
|
+
: platform === 'android'
|
|
80
|
+
? 'emulator'
|
|
81
|
+
: 'virtual device';
|
|
82
|
+
super(`App "${bundleId}" is not installed on ${deviceType} "${deviceName}"`);
|
|
78
83
|
this.deviceName = deviceName;
|
|
79
84
|
this.bundleId = bundleId;
|
|
80
85
|
this.platform = platform;
|
|
@@ -91,3 +96,11 @@ export class BundlingFailedError extends Error {
|
|
|
91
96
|
this.name = 'BundlingFailedError';
|
|
92
97
|
}
|
|
93
98
|
}
|
|
99
|
+
export class MetroPortUnavailableError extends Error {
|
|
100
|
+
port;
|
|
101
|
+
constructor(port) {
|
|
102
|
+
super(`Metro port ${port} is not available`);
|
|
103
|
+
this.port = port;
|
|
104
|
+
this.name = 'MetroPortUnavailableError';
|
|
105
|
+
}
|
|
106
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { TestRunnerConfig } from '@react-native-harness/config';
|
|
2
|
+
import { Environment } from './platforms/platform-adapter.js';
|
|
3
|
+
import { BridgeServer } from '@react-native-harness/bridge/server';
|
|
4
|
+
export type Harness = {
|
|
5
|
+
environment: Environment;
|
|
6
|
+
bridge: BridgeServer;
|
|
7
|
+
};
|
|
8
|
+
export declare const getHarness: (runner: TestRunnerConfig) => Promise<Harness>;
|
|
9
|
+
//# sourceMappingURL=external.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"external.d.ts","sourceRoot":"","sources":["../src/external.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAC9D,OAAO,EACL,YAAY,EAEb,MAAM,qCAAqC,CAAC;AAI7C,MAAM,MAAM,OAAO,GAAG;IACpB,WAAW,EAAE,WAAW,CAAC;IACzB,MAAM,EAAE,YAAY,CAAC;CACtB,CAAC;AAEF,eAAO,MAAM,UAAU,GACrB,QAAQ,gBAAgB,KACvB,OAAO,CAAC,OAAO,CA2BjB,CAAC"}
|
package/dist/external.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { getBridgeServer, } from '@react-native-harness/bridge/server';
|
|
2
|
+
import { BridgeTimeoutError } from './errors/errors.js';
|
|
3
|
+
import { getPlatformAdapter } from './platforms/platform-registry.js';
|
|
4
|
+
export const getHarness = async (runner) => {
|
|
5
|
+
const bridgeTimeout = 60000;
|
|
6
|
+
const platformAdapter = await getPlatformAdapter(runner.platform);
|
|
7
|
+
const serverBridge = await getBridgeServer({
|
|
8
|
+
port: 3001,
|
|
9
|
+
});
|
|
10
|
+
const readyPromise = new Promise((resolve, reject) => {
|
|
11
|
+
const timeout = setTimeout(() => {
|
|
12
|
+
reject(new BridgeTimeoutError(bridgeTimeout, runner.name, runner.platform));
|
|
13
|
+
}, bridgeTimeout);
|
|
14
|
+
serverBridge.once('ready', () => {
|
|
15
|
+
clearTimeout(timeout);
|
|
16
|
+
resolve();
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
const environment = await platformAdapter.getEnvironment(runner);
|
|
20
|
+
await readyPromise;
|
|
21
|
+
return {
|
|
22
|
+
environment,
|
|
23
|
+
bridge: serverBridge,
|
|
24
|
+
};
|
|
25
|
+
};
|
package/dist/index.js
CHANGED
|
@@ -10,19 +10,35 @@ const __dirname = dirname(__filename);
|
|
|
10
10
|
const packageJsonPath = join(__dirname, '../package.json');
|
|
11
11
|
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
|
12
12
|
const program = new Command();
|
|
13
|
-
logger.setVerbose(true);
|
|
14
13
|
program
|
|
15
14
|
.name('react-native-harness')
|
|
16
15
|
.description('React Native Test Harness - A comprehensive testing framework for React Native applications')
|
|
17
|
-
.version(packageJson.version)
|
|
16
|
+
.version(packageJson.version)
|
|
17
|
+
.option('-v, --verbose', 'Enable verbose logging')
|
|
18
|
+
.hook('preAction', (thisCommand) => {
|
|
19
|
+
// Handle global verbose option
|
|
20
|
+
const opts = thisCommand.optsWithGlobals();
|
|
21
|
+
if (opts.verbose) {
|
|
22
|
+
logger.setVerbose(true);
|
|
23
|
+
}
|
|
24
|
+
});
|
|
18
25
|
program
|
|
19
26
|
.command('test')
|
|
20
27
|
.description('Run tests using the specified runner')
|
|
21
28
|
.argument('[runner]', 'test runner name (uses defaultRunner from config if not specified)')
|
|
22
29
|
.argument('[pattern]', 'glob pattern to match test files (uses config.include if not specified)')
|
|
23
|
-
.
|
|
30
|
+
.option('-t, --testNamePattern <pattern>', 'Run only tests with names matching regex pattern')
|
|
31
|
+
.option('--testPathPattern <pattern>', 'Run only test files with paths matching regex pattern')
|
|
32
|
+
.option('--testPathIgnorePatterns <patterns...>', 'Ignore test files matching these patterns')
|
|
33
|
+
.option('--testMatch <patterns...>', 'Override config.include with these glob patterns')
|
|
34
|
+
.action(async (runner, pattern, options) => {
|
|
24
35
|
try {
|
|
25
|
-
|
|
36
|
+
// Convert CLI pattern argument to testMatch option
|
|
37
|
+
const mergedOptions = {
|
|
38
|
+
...options,
|
|
39
|
+
testMatch: pattern ? [pattern] : options.testMatch,
|
|
40
|
+
};
|
|
41
|
+
await testCommand(runner, mergedOptions);
|
|
26
42
|
}
|
|
27
43
|
catch (error) {
|
|
28
44
|
handleError(error);
|
|
@@ -7,5 +7,4 @@ export declare const runEmulator: (name: string) => Promise<ChildProcess>;
|
|
|
7
7
|
export declare const stopEmulator: (avdName: string) => Promise<void>;
|
|
8
8
|
export declare const isAppInstalled: (emulatorId: string, bundleId: string) => Promise<boolean>;
|
|
9
9
|
export declare const reversePort: (port: number) => Promise<void>;
|
|
10
|
-
export declare const getEmulatorScreenshot: (emulatorId: string, name?: string) => Promise<string>;
|
|
11
10
|
//# sourceMappingURL=emulator.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"emulator.d.ts","sourceRoot":"","sources":["../../../src/platforms/android/emulator.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,MAAM,MAAM,qBAAqB,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,CAAC;AAEtE,eAAO,MAAM,qBAAqB,GAChC,YAAY,MAAM,KACjB,OAAO,CAAC,MAAM,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"emulator.d.ts","sourceRoot":"","sources":["../../../src/platforms/android/emulator.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,MAAM,MAAM,qBAAqB,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,CAAC;AAEtE,eAAO,MAAM,qBAAqB,GAChC,YAAY,MAAM,KACjB,OAAO,CAAC,MAAM,GAAG,IAAI,CAcvB,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAC9B,SAAS,MAAM,KACd,OAAO,CAAC,MAAM,GAAG,IAAI,CAmBvB,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAC5B,SAAS,MAAM,KACd,OAAO,CAAC,qBAAqB,CAoB/B,CAAC;AAEF,eAAO,MAAM,WAAW,GAAU,MAAM,MAAM,KAAG,OAAO,CAAC,YAAY,CA2BpE,CAAC;AAEF,eAAO,MAAM,YAAY,GAAU,SAAS,MAAM,KAAG,OAAO,CAAC,IAAI,CAQhE,CAAC;AAOF,eAAO,MAAM,cAAc,GACzB,YAAY,MAAM,EAClB,UAAU,MAAM,KACf,OAAO,CAAC,OAAO,CAejB,CAAC;AAEF,eAAO,MAAM,WAAW,GAAU,MAAM,MAAM,KAAG,OAAO,CAAC,IAAI,CAE5D,CAAC"}
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import { spawn } from '@react-native-harness/tools';
|
|
2
2
|
export const getEmulatorNameFromId = async (emulatorId) => {
|
|
3
3
|
try {
|
|
4
|
-
const { stdout } = await spawn('adb', [
|
|
4
|
+
const { stdout } = await spawn('adb', [
|
|
5
|
+
'-s',
|
|
6
|
+
emulatorId,
|
|
7
|
+
'emu',
|
|
8
|
+
'avd',
|
|
9
|
+
'name',
|
|
10
|
+
]);
|
|
5
11
|
const avdName = stdout.split('\n')[0].trim();
|
|
6
12
|
return avdName || null;
|
|
7
13
|
}
|
|
@@ -36,7 +42,13 @@ export const getEmulatorStatus = async (avdName) => {
|
|
|
36
42
|
}
|
|
37
43
|
try {
|
|
38
44
|
// Check if device is fully booted by checking boot completion
|
|
39
|
-
const { stdout } = await spawn('adb', [
|
|
45
|
+
const { stdout } = await spawn('adb', [
|
|
46
|
+
'-s',
|
|
47
|
+
emulatorId,
|
|
48
|
+
'shell',
|
|
49
|
+
'getprop',
|
|
50
|
+
'sys.boot_completed',
|
|
51
|
+
]);
|
|
40
52
|
const bootCompleted = stdout.trim() === '1';
|
|
41
53
|
return bootCompleted ? 'running' : 'loading';
|
|
42
54
|
}
|
|
@@ -56,17 +68,17 @@ export const runEmulator = async (name) => {
|
|
|
56
68
|
}
|
|
57
69
|
else if (status === 'loading') {
|
|
58
70
|
// Check again in 2 seconds
|
|
59
|
-
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
71
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
60
72
|
await checkStatus();
|
|
61
73
|
}
|
|
62
74
|
else {
|
|
63
75
|
// Still stopped, check again in 1 second
|
|
64
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
76
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
65
77
|
await checkStatus();
|
|
66
78
|
}
|
|
67
79
|
};
|
|
68
80
|
// Start checking status after a brief delay to allow emulator to start
|
|
69
|
-
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
81
|
+
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
70
82
|
await checkStatus();
|
|
71
83
|
return nodeChildProcess;
|
|
72
84
|
};
|
|
@@ -84,7 +96,15 @@ const stopEmulatorById = async (emulatorId) => {
|
|
|
84
96
|
};
|
|
85
97
|
export const isAppInstalled = async (emulatorId, bundleId) => {
|
|
86
98
|
try {
|
|
87
|
-
const { stdout } = await spawn('adb', [
|
|
99
|
+
const { stdout } = await spawn('adb', [
|
|
100
|
+
'-s',
|
|
101
|
+
emulatorId,
|
|
102
|
+
'shell',
|
|
103
|
+
'pm',
|
|
104
|
+
'list',
|
|
105
|
+
'packages',
|
|
106
|
+
bundleId,
|
|
107
|
+
]);
|
|
88
108
|
return stdout.trim() !== '';
|
|
89
109
|
}
|
|
90
110
|
catch {
|
|
@@ -94,17 +114,3 @@ export const isAppInstalled = async (emulatorId, bundleId) => {
|
|
|
94
114
|
export const reversePort = async (port) => {
|
|
95
115
|
await spawn('adb', ['reverse', `tcp:${port}`, `tcp:${port}`]);
|
|
96
116
|
};
|
|
97
|
-
export const getEmulatorScreenshot = async (emulatorId, name = `${emulatorId}-${new Date()
|
|
98
|
-
.toISOString()
|
|
99
|
-
.replace(/:/g, '-')
|
|
100
|
-
.replace(/\//g, '-')}.png`) => {
|
|
101
|
-
// Use screencap to save directly to device, then pull the file
|
|
102
|
-
const devicePath = '/sdcard/screenshot.png';
|
|
103
|
-
// Take screenshot and save to device
|
|
104
|
-
await spawn('adb', ['-s', emulatorId, 'shell', 'screencap', '-p', devicePath]);
|
|
105
|
-
// Pull the file from device to local
|
|
106
|
-
await spawn('adb', ['-s', emulatorId, 'pull', devicePath, name]);
|
|
107
|
-
// Clean up the file on device
|
|
108
|
-
await spawn('adb', ['-s', emulatorId, 'shell', 'rm', devicePath]);
|
|
109
|
-
return name;
|
|
110
|
-
};
|
|
@@ -41,7 +41,7 @@ const androidPlatformAdapter = {
|
|
|
41
41
|
logger.debug('App running');
|
|
42
42
|
return {
|
|
43
43
|
restart: async () => {
|
|
44
|
-
await runApp(
|
|
44
|
+
await runApp(deviceId, runner.bundleId, runner.activityName);
|
|
45
45
|
},
|
|
46
46
|
dispose: async () => {
|
|
47
47
|
await killApp(deviceId, runner.bundleId);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../../src/platforms/ios/build.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../../src/platforms/ios/build.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,WAAW,QAAa,OAAO,CAAC,GAAG,CAQ/C,CAAC;AAEF,eAAO,MAAM,eAAe,GAC1B,eAAe,MAAM,EACrB,eAAe,MAAM,KAEpB,OAAO,CAAC,GAAG,GAAG,IAAI,CAsBpB,CAAC;AAEF,eAAO,MAAM,QAAQ,GAAU,MAAM,MAAM,KAAG,OAAO,CAAC,MAAM,EAAE,CAY7D,CAAC;AAEF,eAAO,MAAM,cAAc,GACzB,MAAM,MAAM,EACZ,UAAU,MAAM,KACf,OAAO,CAAC,OAAO,CAGjB,CAAC;AAEF,eAAO,MAAM,MAAM,GAAU,MAAM,MAAM,EAAE,SAAS,MAAM,KAAG,OAAO,CAAC,IAAI,CAGxE,CAAC;AAEF,eAAO,MAAM,OAAO,GAAU,MAAM,MAAM,EAAE,SAAS,MAAM,KAAG,OAAO,CAAC,IAAI,CAEzE,CAAC"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { spawn, spawnAndForget } from '@react-native-harness/tools';
|
|
2
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2
3
|
export const listDevices = async () => {
|
|
3
4
|
const { stdout } = await spawn('xcrun', [
|
|
4
5
|
'simctl',
|
|
@@ -8,7 +9,9 @@ export const listDevices = async () => {
|
|
|
8
9
|
]);
|
|
9
10
|
return JSON.parse(stdout);
|
|
10
11
|
};
|
|
11
|
-
export const getDeviceByName = async (simulatorName, systemVersion
|
|
12
|
+
export const getDeviceByName = async (simulatorName, systemVersion
|
|
13
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
14
|
+
) => {
|
|
12
15
|
const devices = await listDevices();
|
|
13
16
|
const expectedRuntimeId = `com.apple.CoreSimulator.SimRuntime.iOS-${systemVersion.replace(/\./, '-')}`;
|
|
14
17
|
const runtime = devices.devices[expectedRuntimeId];
|
|
@@ -16,6 +19,7 @@ export const getDeviceByName = async (simulatorName, systemVersion) => {
|
|
|
16
19
|
return null;
|
|
17
20
|
}
|
|
18
21
|
const runtimeDevices = devices.devices[runtime];
|
|
22
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
19
23
|
const device = runtimeDevices.find((d) => d.name === simulatorName);
|
|
20
24
|
if (device) {
|
|
21
25
|
return device;
|
|
@@ -1,7 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
export declare const
|
|
1
|
+
type Device = any;
|
|
2
|
+
export declare const listDevices: () => Promise<{
|
|
3
|
+
devices: Device[];
|
|
4
|
+
}>;
|
|
5
|
+
export declare const getDeviceByName: (simulatorName: string) => Promise<Device | null>;
|
|
3
6
|
export declare const listApps: (udid: string) => Promise<string[]>;
|
|
4
7
|
export declare const isAppInstalled: (simulatorName: string, bundleId: string) => Promise<boolean>;
|
|
5
8
|
export declare const runApp: (simulatorName: string, appName: string) => Promise<void>;
|
|
6
9
|
export declare const killApp: (simulatorName: string, appName: string) => Promise<void>;
|
|
10
|
+
export {};
|
|
7
11
|
//# sourceMappingURL=device.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"device.d.ts","sourceRoot":"","sources":["../../../src/platforms/ios/device.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"device.d.ts","sourceRoot":"","sources":["../../../src/platforms/ios/device.ts"],"names":[],"mappings":"AAGA,KAAK,MAAM,GAAG,GAAG,CAAC;AAElB,eAAO,MAAM,WAAW,QAAa,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAQjE,CAAC;AAEF,eAAO,MAAM,eAAe,GAC1B,eAAe,MAAM,KACpB,OAAO,CAAC,MAAM,GAAG,IAAI,CAavB,CAAC;AAEF,eAAO,MAAM,QAAQ,GAAU,MAAM,MAAM,KAAG,OAAO,CAAC,MAAM,EAAE,CAY7D,CAAC;AAEF,eAAO,MAAM,cAAc,GACzB,eAAe,MAAM,EACrB,UAAU,MAAM,KACf,OAAO,CAAC,OAAO,CASjB,CAAC;AAEF,eAAO,MAAM,MAAM,GACjB,eAAe,MAAM,EACrB,SAAS,MAAM,KACd,OAAO,CAAC,IAAI,CAGd,CAAC;AAEF,eAAO,MAAM,OAAO,GAClB,eAAe,MAAM,EACrB,SAAS,MAAM,KACd,OAAO,CAAC,IAAI,CAOd,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"simulator.d.ts","sourceRoot":"","sources":["../../../src/platforms/ios/simulator.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,kBAAkB,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,CAAC;AAEnE,eAAO,MAAM,oBAAoB,GAC/B,eAAe,MAAM,EACrB,eAAe,MAAM,KACpB,OAAO,CAAC,MAAM,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"simulator.d.ts","sourceRoot":"","sources":["../../../src/platforms/ios/simulator.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,kBAAkB,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,CAAC;AAEnE,eAAO,MAAM,oBAAoB,GAC/B,eAAe,MAAM,EACrB,eAAe,MAAM,KACpB,OAAO,CAAC,MAAM,GAAG,IAAI,CA+BvB,CAAC;AAEF,eAAO,MAAM,sBAAsB,QAAa,OAAO,CACrD,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAoCvD,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAC7B,MAAM,MAAM,KACX,OAAO,CAAC,kBAAkB,CAiC5B,CAAC;AAEF,eAAO,MAAM,YAAY,GAAU,MAAM,MAAM,KAAG,OAAO,CAAC,IAAI,CAkC7D,CAAC;AAEF,eAAO,MAAM,aAAa,GAAU,MAAM,MAAM,KAAG,OAAO,CAAC,IAAI,CAE9D,CAAC"}
|