@unrulysystems/rn-playwright-driver-runner 0.1.0
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/CHANGELOG.md +9 -0
- package/LICENSE +21 -0
- package/README.md +143 -0
- package/bin/rn-driver.ts +17 -0
- package/dist/cli.d.mts +3 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +1898 -0
- package/dist/cli.js.map +1 -0
- package/dist/cli.mjs +1891 -0
- package/dist/cli.mjs.map +1 -0
- package/dist/index.d.mts +281 -0
- package/dist/index.d.ts +281 -0
- package/dist/index.js +1063 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1057 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +67 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public configuration surface for the runner.
|
|
3
|
+
*
|
|
4
|
+
* A project provides these facts once in `rn-driver.config.ts`; the runner
|
|
5
|
+
* translates them into a deterministic native-lifecycle plan per platform. The
|
|
6
|
+
* shape intentionally separates the developer's *app-specific* facts (bundle
|
|
7
|
+
* id, schemes, Gradle tasks, launch kind) from the *generic* lifecycle the
|
|
8
|
+
* runner owns (simulator selection, Metro ownership, companion startup, token
|
|
9
|
+
* passing, Hermes target wait, cleanup).
|
|
10
|
+
*/
|
|
11
|
+
type Platform = 'ios' | 'android';
|
|
12
|
+
/**
|
|
13
|
+
* How the app process is brought up relative to the touch companion.
|
|
14
|
+
* - `launch`/`activate`: the iOS companion launches/foregrounds the app.
|
|
15
|
+
* - `attach`: the companion only injects; the host owns the launch. Required for
|
|
16
|
+
* `expo-dev-client`, where the host must point the dev launcher straight at
|
|
17
|
+
* Metro (see {@link LaunchKind}).
|
|
18
|
+
*/
|
|
19
|
+
type LaunchMode = 'launch' | 'activate' | 'attach';
|
|
20
|
+
/**
|
|
21
|
+
* The flavor of RN app being launched.
|
|
22
|
+
* - `plain`: a standard Expo/RN app that connects to Metro on cold launch.
|
|
23
|
+
* - `expo-dev-client`: a development build whose launcher must be handed the
|
|
24
|
+
* Metro URL at native launch (iOS: `simctl launch --initialUrl`), otherwise it
|
|
25
|
+
* lands on the dev-launcher screen and never registers a Hermes target.
|
|
26
|
+
*/
|
|
27
|
+
type LaunchKind = 'plain' | 'expo-dev-client';
|
|
28
|
+
interface MetroConfig {
|
|
29
|
+
/** Full Metro URL. When set, host/port are derived from it. */
|
|
30
|
+
url?: string;
|
|
31
|
+
/** Command used to start Metro when it is not already running. */
|
|
32
|
+
command?: string;
|
|
33
|
+
/** Host to bind/probe. Defaults to `127.0.0.1`. */
|
|
34
|
+
host?: string;
|
|
35
|
+
/**
|
|
36
|
+
* Metro port (default 8081). When the runner owns Metro it verifies this port
|
|
37
|
+
* is free and fails fast if occupied — it does NOT auto-probe a different port,
|
|
38
|
+
* because `command` pins the port the packager binds (REQ-METRO-003).
|
|
39
|
+
*/
|
|
40
|
+
port?: number;
|
|
41
|
+
/** Reuse an already-running packager at the resolved URL instead of starting one. */
|
|
42
|
+
reuseExisting?: boolean;
|
|
43
|
+
/** Bound for Metro `packager-status:running`. Defaults to 90_000ms. */
|
|
44
|
+
readyTimeoutMs?: number;
|
|
45
|
+
}
|
|
46
|
+
interface LaunchConfig {
|
|
47
|
+
mode: LaunchMode;
|
|
48
|
+
kind: LaunchKind;
|
|
49
|
+
/**
|
|
50
|
+
* Metro URL handed to the dev launcher for `expo-dev-client`. Defaults to the
|
|
51
|
+
* resolved Metro URL. Ignored for `plain`.
|
|
52
|
+
*/
|
|
53
|
+
initialUrl?: string;
|
|
54
|
+
}
|
|
55
|
+
interface CompanionConfig {
|
|
56
|
+
/** Local port the touch companion listens on. Defaults to 9999. */
|
|
57
|
+
port?: number;
|
|
58
|
+
/**
|
|
59
|
+
* Bound for the companion to accept its first authenticated request. Defaults
|
|
60
|
+
* to 300_000ms on iOS to cover a cold `xcodebuild test` build (FU-2).
|
|
61
|
+
*/
|
|
62
|
+
readyTimeoutMs?: number;
|
|
63
|
+
}
|
|
64
|
+
interface IosConfig {
|
|
65
|
+
/** App bundle identifier, e.g. `com.company.app`. */
|
|
66
|
+
bundleId: string;
|
|
67
|
+
/** Path to the `.xcworkspace`, e.g. `ios/App.xcworkspace`. */
|
|
68
|
+
workspace: string;
|
|
69
|
+
/** Scheme that builds the app, e.g. `App`. */
|
|
70
|
+
appScheme: string;
|
|
71
|
+
/** UI-test scheme. Defaults to `${appScheme}UITests`. */
|
|
72
|
+
uitestScheme?: string;
|
|
73
|
+
/**
|
|
74
|
+
* Explicit `xcodebuild` destination
|
|
75
|
+
* (`platform=iOS Simulator,id=<udid>`). When omitted the runner selects a
|
|
76
|
+
* booted iPhone, else the newest available iPhone runtime.
|
|
77
|
+
*/
|
|
78
|
+
destination?: string;
|
|
79
|
+
launch: LaunchConfig;
|
|
80
|
+
companion?: CompanionConfig;
|
|
81
|
+
/**
|
|
82
|
+
* App-specific pre-launch seeds written via `simctl spawn defaults write`
|
|
83
|
+
* (e.g. dev-menu onboarding flags). Keeps app-specific facts out of the
|
|
84
|
+
* generic lifecycle.
|
|
85
|
+
*/
|
|
86
|
+
defaults?: Record<string, string | number | boolean>;
|
|
87
|
+
}
|
|
88
|
+
interface AndroidConfig {
|
|
89
|
+
/** Android application id, e.g. `com.company.app`. */
|
|
90
|
+
packageName: string;
|
|
91
|
+
/** Launch activity, e.g. `.MainActivity`. */
|
|
92
|
+
activity: string;
|
|
93
|
+
/** Gradle tasks that build the app + androidTest APKs. */
|
|
94
|
+
gradleTasks?: string[];
|
|
95
|
+
/** Built app APK path. Defaults to the standard debug output path. */
|
|
96
|
+
appApkPath?: string;
|
|
97
|
+
/** Built androidTest APK path. Defaults to the standard debug output path. */
|
|
98
|
+
testApkPath?: string;
|
|
99
|
+
/**
|
|
100
|
+
* `am instrument` target, e.g.
|
|
101
|
+
* `com.company.app.test/com.rndriver.touchcompanion.RNDriverTouchCompanion`.
|
|
102
|
+
* Defaults to `${packageName}.test/com.rndriver.touchcompanion.RNDriverTouchCompanion`.
|
|
103
|
+
*/
|
|
104
|
+
instrumentationTarget?: string;
|
|
105
|
+
launch: LaunchConfig;
|
|
106
|
+
companion?: CompanionConfig;
|
|
107
|
+
}
|
|
108
|
+
interface PlaywrightConfig {
|
|
109
|
+
/** Playwright config path passed as `--config`. */
|
|
110
|
+
config?: string;
|
|
111
|
+
/** Default spec paths/globs when none are passed on the CLI. */
|
|
112
|
+
specs?: string[];
|
|
113
|
+
}
|
|
114
|
+
interface RnDriverConfig {
|
|
115
|
+
metro?: MetroConfig;
|
|
116
|
+
ios?: IosConfig;
|
|
117
|
+
android?: AndroidConfig;
|
|
118
|
+
playwright?: PlaywrightConfig;
|
|
119
|
+
/** Driver request timeout (`RN_TIMEOUT`). */
|
|
120
|
+
timeoutMs?: number;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Identity helper that provides editor/type checking for `rn-driver.config.ts`.
|
|
124
|
+
* Performs no I/O.
|
|
125
|
+
*/
|
|
126
|
+
declare function defineRnDriverConfig(config: RnDriverConfig): RnDriverConfig;
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* A single OS command. `args` and `env` MUST NEVER contain a secret value — a
|
|
130
|
+
* token reaches a process only via a file path (`stdinFromFile`, or a path in
|
|
131
|
+
* `args`/`env`), never as a literal.
|
|
132
|
+
*/
|
|
133
|
+
interface CommandSpec {
|
|
134
|
+
readonly command: string;
|
|
135
|
+
readonly args: readonly string[];
|
|
136
|
+
readonly env?: Readonly<Record<string, string>>;
|
|
137
|
+
readonly cwd?: string;
|
|
138
|
+
/** Pipe this file's bytes to stdin (keeps secret values out of argv). */
|
|
139
|
+
readonly stdinFromFile?: string;
|
|
140
|
+
/** Pipe this literal (non-secret) content to stdin (e.g. generated config XML). */
|
|
141
|
+
readonly stdinContents?: string;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* A poll-until-ready check with a bounded timeout. The executor polls; a probe
|
|
145
|
+
* that does not pass within `timeoutMs` is a stage failure.
|
|
146
|
+
*/
|
|
147
|
+
type ReadinessProbe = {
|
|
148
|
+
readonly kind: 'metro-status';
|
|
149
|
+
readonly metroUrl: string;
|
|
150
|
+
readonly timeoutMs: number;
|
|
151
|
+
} | {
|
|
152
|
+
readonly kind: 'hermes-target';
|
|
153
|
+
readonly platform: Platform;
|
|
154
|
+
readonly metroUrl: string;
|
|
155
|
+
readonly appId: string;
|
|
156
|
+
readonly deviceNameMatch?: string;
|
|
157
|
+
readonly timeoutMs: number;
|
|
158
|
+
} | {
|
|
159
|
+
readonly kind: 'xctest-hello';
|
|
160
|
+
readonly port: number;
|
|
161
|
+
readonly tokenFile: string;
|
|
162
|
+
readonly timeoutMs: number;
|
|
163
|
+
} | {
|
|
164
|
+
readonly kind: 'instrumentation-hello';
|
|
165
|
+
readonly port: number;
|
|
166
|
+
readonly tokenFile: string;
|
|
167
|
+
readonly timeoutMs: number;
|
|
168
|
+
};
|
|
169
|
+
type StepAction = {
|
|
170
|
+
readonly type: 'command';
|
|
171
|
+
readonly command: CommandSpec;
|
|
172
|
+
/** Long-lived process (Metro, companion). Tracked by `processKey` for cleanup. */
|
|
173
|
+
readonly background?: boolean;
|
|
174
|
+
readonly processKey?: string;
|
|
175
|
+
/** Best-effort command: a non-zero exit does not fail the run (bash `|| true`). */
|
|
176
|
+
readonly allowFailure?: boolean;
|
|
177
|
+
} | {
|
|
178
|
+
readonly type: 'write-file';
|
|
179
|
+
readonly path: string;
|
|
180
|
+
readonly contents: string;
|
|
181
|
+
readonly mode?: number;
|
|
182
|
+
} | {
|
|
183
|
+
readonly type: 'free-port';
|
|
184
|
+
readonly port: number;
|
|
185
|
+
} | {
|
|
186
|
+
readonly type: 'probe';
|
|
187
|
+
readonly probe: ReadinessProbe;
|
|
188
|
+
/**
|
|
189
|
+
* Bounded retry: if the probe times out, re-run `command` and probe again,
|
|
190
|
+
* up to `max` extra attempts (REQ-AND-005 — re-issue `am start` when the
|
|
191
|
+
* app loses a transient Hermes-registration race instead of failing).
|
|
192
|
+
*/
|
|
193
|
+
readonly retry?: {
|
|
194
|
+
readonly command: CommandSpec;
|
|
195
|
+
readonly max: number;
|
|
196
|
+
};
|
|
197
|
+
};
|
|
198
|
+
/** The lifecycle stage a step belongs to. A failure is attributed to its stage. */
|
|
199
|
+
type Stage = 'config' | 'metro' | 'device' | 'build' | 'companion' | 'app-launch' | 'hermes-target' | 'playwright' | 'cleanup';
|
|
200
|
+
interface Step {
|
|
201
|
+
readonly id: string;
|
|
202
|
+
readonly stage: Stage;
|
|
203
|
+
readonly description: string;
|
|
204
|
+
readonly action: StepAction;
|
|
205
|
+
/** Skipped when `--skip-build` reuses an already-built native project. */
|
|
206
|
+
readonly skippable?: boolean;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Teardown is emitted declaratively by the planner (so `--dry-run` shows it) and
|
|
210
|
+
* run defensively by the executor (so it is idempotent — REQ-CLEAN-001). Each
|
|
211
|
+
* action is a no-op when its precondition does not hold.
|
|
212
|
+
*/
|
|
213
|
+
type CleanupAction = {
|
|
214
|
+
readonly type: 'kill-process';
|
|
215
|
+
readonly processKey: string;
|
|
216
|
+
readonly description: string;
|
|
217
|
+
} | {
|
|
218
|
+
readonly type: 'free-port';
|
|
219
|
+
readonly port: number;
|
|
220
|
+
readonly description: string;
|
|
221
|
+
} | {
|
|
222
|
+
readonly type: 'remove-file';
|
|
223
|
+
readonly path: string;
|
|
224
|
+
readonly description: string;
|
|
225
|
+
} | {
|
|
226
|
+
readonly type: 'command';
|
|
227
|
+
readonly command: CommandSpec;
|
|
228
|
+
readonly description: string;
|
|
229
|
+
};
|
|
230
|
+
interface Plan {
|
|
231
|
+
readonly platform: Platform;
|
|
232
|
+
readonly steps: readonly Step[];
|
|
233
|
+
readonly cleanup: readonly CleanupAction[];
|
|
234
|
+
/** The driver env contract handed to Playwright — token files only, no values. */
|
|
235
|
+
readonly driverEnv: Readonly<Record<string, string>>;
|
|
236
|
+
/** The Playwright invocation run after the world is up. */
|
|
237
|
+
readonly playwright: CommandSpec;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
interface MetroOverrides {
|
|
241
|
+
readonly url?: string;
|
|
242
|
+
readonly host?: string;
|
|
243
|
+
readonly port?: number;
|
|
244
|
+
}
|
|
245
|
+
interface BuildPlanOptions {
|
|
246
|
+
readonly metroOverrides?: MetroOverrides;
|
|
247
|
+
/** Positional spec paths; override the config spec list when non-empty. */
|
|
248
|
+
readonly specs?: readonly string[];
|
|
249
|
+
/** Args after `--`; always appended to the Playwright invocation. */
|
|
250
|
+
readonly passthrough?: readonly string[];
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Build the plan for a platform using placeholder (no-I/O) resolution. This is
|
|
254
|
+
* the `--dry-run` path and the unit-test entry point: identical config yields an
|
|
255
|
+
* identical plan with zero side effects.
|
|
256
|
+
*/
|
|
257
|
+
declare function buildDryRunPlan(config: RnDriverConfig, platform: Platform, opts?: BuildPlanOptions): Plan;
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Render a {@link Plan} as auditable text for `--dry-run`. Token material appears
|
|
261
|
+
* only as the placeholder file path (dry-run resolution fills `tokenFile` with
|
|
262
|
+
* `<token-file>`), so this output never leaks a secret value.
|
|
263
|
+
*/
|
|
264
|
+
declare function renderPlan(plan: Plan): string;
|
|
265
|
+
|
|
266
|
+
interface ValidationResult {
|
|
267
|
+
readonly ok: boolean;
|
|
268
|
+
readonly errors: readonly string[];
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Validate a loaded config for the selected platforms. Runs before any side
|
|
272
|
+
* effect; failures name the offending field and the expected shape (REQ-CFG-003).
|
|
273
|
+
* Unknown keys are reported as typo protection (REQ-CFG-004).
|
|
274
|
+
*/
|
|
275
|
+
declare function validateConfig(config: unknown, platforms: readonly Platform[]): ValidationResult;
|
|
276
|
+
declare class ConfigValidationError extends Error {
|
|
277
|
+
readonly errors: readonly string[];
|
|
278
|
+
constructor(errors: readonly string[]);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
export { type AndroidConfig, type BuildPlanOptions, type CleanupAction, type CommandSpec, type CompanionConfig, ConfigValidationError, type IosConfig, type LaunchConfig, type LaunchKind, type LaunchMode, type MetroConfig, type MetroOverrides, type Plan, type Platform, type PlaywrightConfig, type ReadinessProbe, type RnDriverConfig, type Stage, type Step, type StepAction, type ValidationResult, buildDryRunPlan, defineRnDriverConfig, renderPlan, validateConfig };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public configuration surface for the runner.
|
|
3
|
+
*
|
|
4
|
+
* A project provides these facts once in `rn-driver.config.ts`; the runner
|
|
5
|
+
* translates them into a deterministic native-lifecycle plan per platform. The
|
|
6
|
+
* shape intentionally separates the developer's *app-specific* facts (bundle
|
|
7
|
+
* id, schemes, Gradle tasks, launch kind) from the *generic* lifecycle the
|
|
8
|
+
* runner owns (simulator selection, Metro ownership, companion startup, token
|
|
9
|
+
* passing, Hermes target wait, cleanup).
|
|
10
|
+
*/
|
|
11
|
+
type Platform = 'ios' | 'android';
|
|
12
|
+
/**
|
|
13
|
+
* How the app process is brought up relative to the touch companion.
|
|
14
|
+
* - `launch`/`activate`: the iOS companion launches/foregrounds the app.
|
|
15
|
+
* - `attach`: the companion only injects; the host owns the launch. Required for
|
|
16
|
+
* `expo-dev-client`, where the host must point the dev launcher straight at
|
|
17
|
+
* Metro (see {@link LaunchKind}).
|
|
18
|
+
*/
|
|
19
|
+
type LaunchMode = 'launch' | 'activate' | 'attach';
|
|
20
|
+
/**
|
|
21
|
+
* The flavor of RN app being launched.
|
|
22
|
+
* - `plain`: a standard Expo/RN app that connects to Metro on cold launch.
|
|
23
|
+
* - `expo-dev-client`: a development build whose launcher must be handed the
|
|
24
|
+
* Metro URL at native launch (iOS: `simctl launch --initialUrl`), otherwise it
|
|
25
|
+
* lands on the dev-launcher screen and never registers a Hermes target.
|
|
26
|
+
*/
|
|
27
|
+
type LaunchKind = 'plain' | 'expo-dev-client';
|
|
28
|
+
interface MetroConfig {
|
|
29
|
+
/** Full Metro URL. When set, host/port are derived from it. */
|
|
30
|
+
url?: string;
|
|
31
|
+
/** Command used to start Metro when it is not already running. */
|
|
32
|
+
command?: string;
|
|
33
|
+
/** Host to bind/probe. Defaults to `127.0.0.1`. */
|
|
34
|
+
host?: string;
|
|
35
|
+
/**
|
|
36
|
+
* Metro port (default 8081). When the runner owns Metro it verifies this port
|
|
37
|
+
* is free and fails fast if occupied — it does NOT auto-probe a different port,
|
|
38
|
+
* because `command` pins the port the packager binds (REQ-METRO-003).
|
|
39
|
+
*/
|
|
40
|
+
port?: number;
|
|
41
|
+
/** Reuse an already-running packager at the resolved URL instead of starting one. */
|
|
42
|
+
reuseExisting?: boolean;
|
|
43
|
+
/** Bound for Metro `packager-status:running`. Defaults to 90_000ms. */
|
|
44
|
+
readyTimeoutMs?: number;
|
|
45
|
+
}
|
|
46
|
+
interface LaunchConfig {
|
|
47
|
+
mode: LaunchMode;
|
|
48
|
+
kind: LaunchKind;
|
|
49
|
+
/**
|
|
50
|
+
* Metro URL handed to the dev launcher for `expo-dev-client`. Defaults to the
|
|
51
|
+
* resolved Metro URL. Ignored for `plain`.
|
|
52
|
+
*/
|
|
53
|
+
initialUrl?: string;
|
|
54
|
+
}
|
|
55
|
+
interface CompanionConfig {
|
|
56
|
+
/** Local port the touch companion listens on. Defaults to 9999. */
|
|
57
|
+
port?: number;
|
|
58
|
+
/**
|
|
59
|
+
* Bound for the companion to accept its first authenticated request. Defaults
|
|
60
|
+
* to 300_000ms on iOS to cover a cold `xcodebuild test` build (FU-2).
|
|
61
|
+
*/
|
|
62
|
+
readyTimeoutMs?: number;
|
|
63
|
+
}
|
|
64
|
+
interface IosConfig {
|
|
65
|
+
/** App bundle identifier, e.g. `com.company.app`. */
|
|
66
|
+
bundleId: string;
|
|
67
|
+
/** Path to the `.xcworkspace`, e.g. `ios/App.xcworkspace`. */
|
|
68
|
+
workspace: string;
|
|
69
|
+
/** Scheme that builds the app, e.g. `App`. */
|
|
70
|
+
appScheme: string;
|
|
71
|
+
/** UI-test scheme. Defaults to `${appScheme}UITests`. */
|
|
72
|
+
uitestScheme?: string;
|
|
73
|
+
/**
|
|
74
|
+
* Explicit `xcodebuild` destination
|
|
75
|
+
* (`platform=iOS Simulator,id=<udid>`). When omitted the runner selects a
|
|
76
|
+
* booted iPhone, else the newest available iPhone runtime.
|
|
77
|
+
*/
|
|
78
|
+
destination?: string;
|
|
79
|
+
launch: LaunchConfig;
|
|
80
|
+
companion?: CompanionConfig;
|
|
81
|
+
/**
|
|
82
|
+
* App-specific pre-launch seeds written via `simctl spawn defaults write`
|
|
83
|
+
* (e.g. dev-menu onboarding flags). Keeps app-specific facts out of the
|
|
84
|
+
* generic lifecycle.
|
|
85
|
+
*/
|
|
86
|
+
defaults?: Record<string, string | number | boolean>;
|
|
87
|
+
}
|
|
88
|
+
interface AndroidConfig {
|
|
89
|
+
/** Android application id, e.g. `com.company.app`. */
|
|
90
|
+
packageName: string;
|
|
91
|
+
/** Launch activity, e.g. `.MainActivity`. */
|
|
92
|
+
activity: string;
|
|
93
|
+
/** Gradle tasks that build the app + androidTest APKs. */
|
|
94
|
+
gradleTasks?: string[];
|
|
95
|
+
/** Built app APK path. Defaults to the standard debug output path. */
|
|
96
|
+
appApkPath?: string;
|
|
97
|
+
/** Built androidTest APK path. Defaults to the standard debug output path. */
|
|
98
|
+
testApkPath?: string;
|
|
99
|
+
/**
|
|
100
|
+
* `am instrument` target, e.g.
|
|
101
|
+
* `com.company.app.test/com.rndriver.touchcompanion.RNDriverTouchCompanion`.
|
|
102
|
+
* Defaults to `${packageName}.test/com.rndriver.touchcompanion.RNDriverTouchCompanion`.
|
|
103
|
+
*/
|
|
104
|
+
instrumentationTarget?: string;
|
|
105
|
+
launch: LaunchConfig;
|
|
106
|
+
companion?: CompanionConfig;
|
|
107
|
+
}
|
|
108
|
+
interface PlaywrightConfig {
|
|
109
|
+
/** Playwright config path passed as `--config`. */
|
|
110
|
+
config?: string;
|
|
111
|
+
/** Default spec paths/globs when none are passed on the CLI. */
|
|
112
|
+
specs?: string[];
|
|
113
|
+
}
|
|
114
|
+
interface RnDriverConfig {
|
|
115
|
+
metro?: MetroConfig;
|
|
116
|
+
ios?: IosConfig;
|
|
117
|
+
android?: AndroidConfig;
|
|
118
|
+
playwright?: PlaywrightConfig;
|
|
119
|
+
/** Driver request timeout (`RN_TIMEOUT`). */
|
|
120
|
+
timeoutMs?: number;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Identity helper that provides editor/type checking for `rn-driver.config.ts`.
|
|
124
|
+
* Performs no I/O.
|
|
125
|
+
*/
|
|
126
|
+
declare function defineRnDriverConfig(config: RnDriverConfig): RnDriverConfig;
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* A single OS command. `args` and `env` MUST NEVER contain a secret value — a
|
|
130
|
+
* token reaches a process only via a file path (`stdinFromFile`, or a path in
|
|
131
|
+
* `args`/`env`), never as a literal.
|
|
132
|
+
*/
|
|
133
|
+
interface CommandSpec {
|
|
134
|
+
readonly command: string;
|
|
135
|
+
readonly args: readonly string[];
|
|
136
|
+
readonly env?: Readonly<Record<string, string>>;
|
|
137
|
+
readonly cwd?: string;
|
|
138
|
+
/** Pipe this file's bytes to stdin (keeps secret values out of argv). */
|
|
139
|
+
readonly stdinFromFile?: string;
|
|
140
|
+
/** Pipe this literal (non-secret) content to stdin (e.g. generated config XML). */
|
|
141
|
+
readonly stdinContents?: string;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* A poll-until-ready check with a bounded timeout. The executor polls; a probe
|
|
145
|
+
* that does not pass within `timeoutMs` is a stage failure.
|
|
146
|
+
*/
|
|
147
|
+
type ReadinessProbe = {
|
|
148
|
+
readonly kind: 'metro-status';
|
|
149
|
+
readonly metroUrl: string;
|
|
150
|
+
readonly timeoutMs: number;
|
|
151
|
+
} | {
|
|
152
|
+
readonly kind: 'hermes-target';
|
|
153
|
+
readonly platform: Platform;
|
|
154
|
+
readonly metroUrl: string;
|
|
155
|
+
readonly appId: string;
|
|
156
|
+
readonly deviceNameMatch?: string;
|
|
157
|
+
readonly timeoutMs: number;
|
|
158
|
+
} | {
|
|
159
|
+
readonly kind: 'xctest-hello';
|
|
160
|
+
readonly port: number;
|
|
161
|
+
readonly tokenFile: string;
|
|
162
|
+
readonly timeoutMs: number;
|
|
163
|
+
} | {
|
|
164
|
+
readonly kind: 'instrumentation-hello';
|
|
165
|
+
readonly port: number;
|
|
166
|
+
readonly tokenFile: string;
|
|
167
|
+
readonly timeoutMs: number;
|
|
168
|
+
};
|
|
169
|
+
type StepAction = {
|
|
170
|
+
readonly type: 'command';
|
|
171
|
+
readonly command: CommandSpec;
|
|
172
|
+
/** Long-lived process (Metro, companion). Tracked by `processKey` for cleanup. */
|
|
173
|
+
readonly background?: boolean;
|
|
174
|
+
readonly processKey?: string;
|
|
175
|
+
/** Best-effort command: a non-zero exit does not fail the run (bash `|| true`). */
|
|
176
|
+
readonly allowFailure?: boolean;
|
|
177
|
+
} | {
|
|
178
|
+
readonly type: 'write-file';
|
|
179
|
+
readonly path: string;
|
|
180
|
+
readonly contents: string;
|
|
181
|
+
readonly mode?: number;
|
|
182
|
+
} | {
|
|
183
|
+
readonly type: 'free-port';
|
|
184
|
+
readonly port: number;
|
|
185
|
+
} | {
|
|
186
|
+
readonly type: 'probe';
|
|
187
|
+
readonly probe: ReadinessProbe;
|
|
188
|
+
/**
|
|
189
|
+
* Bounded retry: if the probe times out, re-run `command` and probe again,
|
|
190
|
+
* up to `max` extra attempts (REQ-AND-005 — re-issue `am start` when the
|
|
191
|
+
* app loses a transient Hermes-registration race instead of failing).
|
|
192
|
+
*/
|
|
193
|
+
readonly retry?: {
|
|
194
|
+
readonly command: CommandSpec;
|
|
195
|
+
readonly max: number;
|
|
196
|
+
};
|
|
197
|
+
};
|
|
198
|
+
/** The lifecycle stage a step belongs to. A failure is attributed to its stage. */
|
|
199
|
+
type Stage = 'config' | 'metro' | 'device' | 'build' | 'companion' | 'app-launch' | 'hermes-target' | 'playwright' | 'cleanup';
|
|
200
|
+
interface Step {
|
|
201
|
+
readonly id: string;
|
|
202
|
+
readonly stage: Stage;
|
|
203
|
+
readonly description: string;
|
|
204
|
+
readonly action: StepAction;
|
|
205
|
+
/** Skipped when `--skip-build` reuses an already-built native project. */
|
|
206
|
+
readonly skippable?: boolean;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Teardown is emitted declaratively by the planner (so `--dry-run` shows it) and
|
|
210
|
+
* run defensively by the executor (so it is idempotent — REQ-CLEAN-001). Each
|
|
211
|
+
* action is a no-op when its precondition does not hold.
|
|
212
|
+
*/
|
|
213
|
+
type CleanupAction = {
|
|
214
|
+
readonly type: 'kill-process';
|
|
215
|
+
readonly processKey: string;
|
|
216
|
+
readonly description: string;
|
|
217
|
+
} | {
|
|
218
|
+
readonly type: 'free-port';
|
|
219
|
+
readonly port: number;
|
|
220
|
+
readonly description: string;
|
|
221
|
+
} | {
|
|
222
|
+
readonly type: 'remove-file';
|
|
223
|
+
readonly path: string;
|
|
224
|
+
readonly description: string;
|
|
225
|
+
} | {
|
|
226
|
+
readonly type: 'command';
|
|
227
|
+
readonly command: CommandSpec;
|
|
228
|
+
readonly description: string;
|
|
229
|
+
};
|
|
230
|
+
interface Plan {
|
|
231
|
+
readonly platform: Platform;
|
|
232
|
+
readonly steps: readonly Step[];
|
|
233
|
+
readonly cleanup: readonly CleanupAction[];
|
|
234
|
+
/** The driver env contract handed to Playwright — token files only, no values. */
|
|
235
|
+
readonly driverEnv: Readonly<Record<string, string>>;
|
|
236
|
+
/** The Playwright invocation run after the world is up. */
|
|
237
|
+
readonly playwright: CommandSpec;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
interface MetroOverrides {
|
|
241
|
+
readonly url?: string;
|
|
242
|
+
readonly host?: string;
|
|
243
|
+
readonly port?: number;
|
|
244
|
+
}
|
|
245
|
+
interface BuildPlanOptions {
|
|
246
|
+
readonly metroOverrides?: MetroOverrides;
|
|
247
|
+
/** Positional spec paths; override the config spec list when non-empty. */
|
|
248
|
+
readonly specs?: readonly string[];
|
|
249
|
+
/** Args after `--`; always appended to the Playwright invocation. */
|
|
250
|
+
readonly passthrough?: readonly string[];
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Build the plan for a platform using placeholder (no-I/O) resolution. This is
|
|
254
|
+
* the `--dry-run` path and the unit-test entry point: identical config yields an
|
|
255
|
+
* identical plan with zero side effects.
|
|
256
|
+
*/
|
|
257
|
+
declare function buildDryRunPlan(config: RnDriverConfig, platform: Platform, opts?: BuildPlanOptions): Plan;
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Render a {@link Plan} as auditable text for `--dry-run`. Token material appears
|
|
261
|
+
* only as the placeholder file path (dry-run resolution fills `tokenFile` with
|
|
262
|
+
* `<token-file>`), so this output never leaks a secret value.
|
|
263
|
+
*/
|
|
264
|
+
declare function renderPlan(plan: Plan): string;
|
|
265
|
+
|
|
266
|
+
interface ValidationResult {
|
|
267
|
+
readonly ok: boolean;
|
|
268
|
+
readonly errors: readonly string[];
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Validate a loaded config for the selected platforms. Runs before any side
|
|
272
|
+
* effect; failures name the offending field and the expected shape (REQ-CFG-003).
|
|
273
|
+
* Unknown keys are reported as typo protection (REQ-CFG-004).
|
|
274
|
+
*/
|
|
275
|
+
declare function validateConfig(config: unknown, platforms: readonly Platform[]): ValidationResult;
|
|
276
|
+
declare class ConfigValidationError extends Error {
|
|
277
|
+
readonly errors: readonly string[];
|
|
278
|
+
constructor(errors: readonly string[]);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
export { type AndroidConfig, type BuildPlanOptions, type CleanupAction, type CommandSpec, type CompanionConfig, ConfigValidationError, type IosConfig, type LaunchConfig, type LaunchKind, type LaunchMode, type MetroConfig, type MetroOverrides, type Plan, type Platform, type PlaywrightConfig, type ReadinessProbe, type RnDriverConfig, type Stage, type Step, type StepAction, type ValidationResult, buildDryRunPlan, defineRnDriverConfig, renderPlan, validateConfig };
|