@delacour/warden-e2e 0.3.1 → 0.5.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/{chunk-8em9kgsk.mjs → chunk-j9b17ade.mjs} +200 -37
- package/index.mjs +1 -1
- package/package.json +2 -2
- package/preload.mjs +61 -10
- package/types/core/src/affected/affected.schema.d.ts +185 -0
- package/types/core/src/batch/preset.schema.d.ts +59 -0
- package/types/core/src/builds/app-option.schema.d.ts +9 -1
- package/types/core/src/builds/builds.defaults.d.ts +62 -0
- package/types/core/src/builds/config.d.ts +122 -0
- package/types/core/src/builds/config.schema.d.ts +392 -0
- package/types/core/src/builds/define-config.d.ts +719 -0
- package/types/core/src/builds/kinds.d.ts +56 -0
- package/types/core/src/builds/native-ids.d.ts +14 -0
- package/types/core/src/builds/resolve.d.ts +26 -0
- package/types/core/src/duration.d.ts +5 -0
- package/types/core/src/exec.d.ts +2 -0
- package/types/core/src/ports.d.ts +76 -0
- package/types/core/src/providers/provider.types.d.ts +7 -0
- package/types/core/src/sims/slim.profile.d.ts +54 -0
- package/types/e2e/src/devtools/cdp.d.ts +7 -2
- package/types/e2e/src/devtools/devtools.types.d.ts +13 -0
- package/types/e2e/src/devtools/facet.d.ts +2 -0
- package/types/e2e/src/devtools/logbox.d.ts +27 -0
- package/types/e2e/src/devtools/logbox.script.d.ts +17 -0
- package/types/e2e/src/devtools/session.d.ts +8 -1
- package/types/e2e/src/driver/ios/ios-driver.d.ts +7 -6
- package/types/e2e/src/driver/ios/simd-protocol.d.ts +23 -0
- package/types/e2e/src/driver/ios/type-budget.d.ts +8 -4
- package/types/e2e/src/driver/observation-cache.d.ts +3 -1
- package/types/e2e/src/driver/typing.d.ts +10 -0
- package/types/e2e/src/preload.d.ts +5 -0
- package/types/e2e/src/screen/field.d.ts +11 -1
- package/types/e2e/src/screen/locator.d.ts +24 -5
- package/types/e2e/src/session.d.ts +4 -1
|
@@ -0,0 +1,719 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The public, documented shape of `warden.config.ts`, published as `@delacour/warden/config`.
|
|
3
|
+
*
|
|
4
|
+
* Self-contained on purpose (no imports): `apps/cli/scripts/npm-packages.ts` emits this file as a
|
|
5
|
+
* single `config.d.ts` + `config.mjs`. `define-config.test.ts` asserts every type here accepts exactly
|
|
6
|
+
* what the zod schemas in `config.ts` / `preset.schema.ts` / `affected.schema.ts` accept.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* // warden.config.ts
|
|
11
|
+
* import { defineConfig } from "@delacour/warden/config";
|
|
12
|
+
*
|
|
13
|
+
* export default defineConfig({
|
|
14
|
+
* projects: [{ name: "salient", root: "apps/salient/app", bundleId: { ios: "com.example.salient" } }],
|
|
15
|
+
* });
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* @packageDocumentation
|
|
19
|
+
*/
|
|
20
|
+
/** A device platform warden can lease. */
|
|
21
|
+
export type WardenPlatform = "ios" | "android";
|
|
22
|
+
/**
|
|
23
|
+
* Install the project's app on every leased device before the first job.
|
|
24
|
+
*
|
|
25
|
+
* - `true` installs the cached / built app (`WARDEN_APP_PATH` / `WARDEN_APP_HASH` are exported).
|
|
26
|
+
* - `{ installMode: "clean" }` gives each run fresh app data without reinstalling an unchanged build (`--install-mode clean`).
|
|
27
|
+
* - `{ installMode: "reinstall" }` uninstalls the app first, then installs it, every run (`--install-mode reinstall`).
|
|
28
|
+
* - `{ installMode, resetKeychain: true }` also resets the iOS simulator's keychain (`--reset-keychain`); needs `installMode`.
|
|
29
|
+
* - `{ clean: true }` is the deprecated spelling of `{ installMode: "reinstall" }`.
|
|
30
|
+
* - `{ variant: "e2e" }` installs that {@link WardenProject.variants} build instead of the project's own.
|
|
31
|
+
* - `{ kind: "simulator" }` installs that {@link WardenBuildKind} instead (not together with `variant`).
|
|
32
|
+
* - `{}` is the same as `true`.
|
|
33
|
+
*/
|
|
34
|
+
export type WardenAppOption = boolean | {
|
|
35
|
+
installMode?: "clean" | "reinstall" | undefined;
|
|
36
|
+
resetKeychain?: boolean | undefined;
|
|
37
|
+
/** @deprecated use `installMode: "reinstall"` */
|
|
38
|
+
clean?: boolean | undefined;
|
|
39
|
+
variant?: string | undefined;
|
|
40
|
+
kind?: WardenBuildKind | undefined;
|
|
41
|
+
};
|
|
42
|
+
/** A value per platform, e.g. a bundle id or a build command. */
|
|
43
|
+
export interface WardenPerPlatform {
|
|
44
|
+
/** iOS value (bundle identifier, build command…). */
|
|
45
|
+
ios?: string | undefined;
|
|
46
|
+
/** Android value (application id / package, build command…). */
|
|
47
|
+
android?: string | undefined;
|
|
48
|
+
}
|
|
49
|
+
/** How the build cache / install key is computed. */
|
|
50
|
+
export interface WardenFingerprint {
|
|
51
|
+
/**
|
|
52
|
+
* Own fingerprint command; `{platform}` is substituted. Stdout is JSON `{ "hash": "…" }` or a bare hash.
|
|
53
|
+
*
|
|
54
|
+
* @default `@expo/fingerprint`
|
|
55
|
+
* @example "bun run --silent fingerprint:{platform}"
|
|
56
|
+
*/
|
|
57
|
+
command?: string | undefined;
|
|
58
|
+
/**
|
|
59
|
+
* What the key covers.
|
|
60
|
+
*
|
|
61
|
+
* - `"native"`: the native fingerprint only.
|
|
62
|
+
* - `"native+js"`: also a content hash of {@link WardenFingerprint.jsInputs}, for Release builds that embed the JS bundle.
|
|
63
|
+
*
|
|
64
|
+
* @default "native"
|
|
65
|
+
*/
|
|
66
|
+
include?: "native" | "native+js" | undefined;
|
|
67
|
+
/**
|
|
68
|
+
* Required with `include: "native+js"`, not allowed otherwise. Globs, relative to the project root,
|
|
69
|
+
* of the JS sources in the bundle. A glob starting with `../` reaches outside the project (a workspace
|
|
70
|
+
* package the bundle embeds). Only git-tracked and untracked-not-ignored files count.
|
|
71
|
+
*
|
|
72
|
+
* @example ["src/**", "../../../packages/engine/src/**"]
|
|
73
|
+
*/
|
|
74
|
+
jsInputs?: string[] | undefined;
|
|
75
|
+
}
|
|
76
|
+
/** EAS build lookup for a project. */
|
|
77
|
+
export interface WardenEas {
|
|
78
|
+
/**
|
|
79
|
+
* EAS build profile to look builds up under.
|
|
80
|
+
*
|
|
81
|
+
* @default "development-simulator"
|
|
82
|
+
*/
|
|
83
|
+
profile?: string | undefined;
|
|
84
|
+
/**
|
|
85
|
+
* EAS workflow file to trigger on a cache miss.
|
|
86
|
+
*
|
|
87
|
+
* @example ".eas/workflows/dev-build.yml"
|
|
88
|
+
*/
|
|
89
|
+
workflow?: string | undefined;
|
|
90
|
+
/**
|
|
91
|
+
* Trigger {@link WardenEas.workflow} when no EAS build matches.
|
|
92
|
+
*
|
|
93
|
+
* @default false
|
|
94
|
+
*/
|
|
95
|
+
trigger?: boolean | undefined;
|
|
96
|
+
}
|
|
97
|
+
/** Local build, the resolver's last resort. */
|
|
98
|
+
export interface WardenBuild extends WardenPerPlatform {
|
|
99
|
+
/**
|
|
100
|
+
* Which build a local build produces. Sets where the artifact is searched (`<Config>-iphonesimulator`,
|
|
101
|
+
* `apk/release`) and, unless `ios` / `android` is set, the default command
|
|
102
|
+
* (`--configuration Release` / `--variant release`).
|
|
103
|
+
*
|
|
104
|
+
* @default "Debug"
|
|
105
|
+
*/
|
|
106
|
+
configuration?: "Debug" | "Release" | undefined;
|
|
107
|
+
/**
|
|
108
|
+
* Inject shared compiler caches into local builds so a native rebuild in one worktree reuses another's
|
|
109
|
+
* compiled objects: ccache (`$WARDEN_HOME/ccache`, `CCACHE_BASEDIR` = git toplevel), Xcode 26
|
|
110
|
+
* compilation caching, and the Gradle build cache. `false` turns them off.
|
|
111
|
+
*
|
|
112
|
+
* @default true
|
|
113
|
+
*/
|
|
114
|
+
cache?: boolean | undefined;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* A named build of a project (`dev`, `e2e`…), picked with `--variant`, `app: { variant }` or `warden dev`
|
|
118
|
+
* (which uses `dev` when it exists). Each key set here **replaces** the project's own, wholesale.
|
|
119
|
+
*
|
|
120
|
+
* @example { dev: { build: { configuration: "Debug" }, fingerprint: { include: "native" } } }
|
|
121
|
+
*/
|
|
122
|
+
export interface WardenVariant {
|
|
123
|
+
/** Replaces {@link WardenProject.fingerprint}. */
|
|
124
|
+
fingerprint?: WardenFingerprint | undefined;
|
|
125
|
+
/** Replaces {@link WardenProject.eas}. */
|
|
126
|
+
eas?: WardenEas | false | undefined;
|
|
127
|
+
/** Replaces {@link WardenProject.build}. */
|
|
128
|
+
build?: WardenBuild | undefined;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* What a cached build is for.
|
|
132
|
+
*
|
|
133
|
+
* - `"dev"`: Debug, JS from Metro at runtime. The default of `warden ui open` / `warden dev`.
|
|
134
|
+
* - `"simulator"`: Release for a simulator / emulator whose JS is swapped by a repack (iOS `SKIP_BUNDLING=1`, Android
|
|
135
|
+
* plain `assembleRelease`; its embedded bundle is replaced). The app's JS is repacked into a clone of this build, keyed by the content
|
|
136
|
+
* of the JS sources, so a JS edit costs seconds, not a native rebuild. The default of `warden e2e`.
|
|
137
|
+
* - `"release"`: Release with the JS bundle embedded at build time; the key also covers the JS sources.
|
|
138
|
+
*/
|
|
139
|
+
export type WardenBuildKind = "dev" | "simulator" | "release";
|
|
140
|
+
/** Overrides of one {@link WardenBuildKind}'s built-in defaults. */
|
|
141
|
+
export interface WardenKind {
|
|
142
|
+
/**
|
|
143
|
+
* Replaces the kind's built-in build command, per platform. The project's own top-level `build` is not read
|
|
144
|
+
* for a kind.
|
|
145
|
+
*
|
|
146
|
+
* @example { ios: "SKIP_BUNDLING=1 bunx expo run:ios --configuration Release --no-install --no-bundler" }
|
|
147
|
+
*/
|
|
148
|
+
build?: WardenPerPlatform | undefined;
|
|
149
|
+
/**
|
|
150
|
+
* Env of the build and of the JS bundle step (`EXPO_PUBLIC_*`, `NODE_ENV`…). Part of the repack key: another
|
|
151
|
+
* value is another repack.
|
|
152
|
+
*/
|
|
153
|
+
env?: Record<string, string> | undefined;
|
|
154
|
+
/**
|
|
155
|
+
* Globs, relative to the project root, of the JS sources the JS key covers (`simulator`: the repack key,
|
|
156
|
+
* `release`: the build key). A `!` prefix excludes (`"!docs/**"`); a glob starting with `../` reaches a workspace
|
|
157
|
+
* package the bundle embeds. Ignored by `dev`.
|
|
158
|
+
*
|
|
159
|
+
* @default every file git tracks (or would) under the project root except `ios/`, `android/` and `node_modules`
|
|
160
|
+
* @example ["src/**", "assets/**", "app.config.ts", "../../packages/ui/src/**"]
|
|
161
|
+
*/
|
|
162
|
+
jsInputs?: string[] | undefined;
|
|
163
|
+
}
|
|
164
|
+
/** Per-kind overrides, `projects[].kinds`. */
|
|
165
|
+
export interface WardenKinds {
|
|
166
|
+
dev?: WardenKind | undefined;
|
|
167
|
+
simulator?: WardenKind | undefined;
|
|
168
|
+
release?: WardenKind | undefined;
|
|
169
|
+
}
|
|
170
|
+
/** An app warden builds, caches and installs. */
|
|
171
|
+
export interface WardenProject {
|
|
172
|
+
/** Project name, used in the cache key and referenced by `batches.*.project` / `e2e.*.project`. */
|
|
173
|
+
name: string;
|
|
174
|
+
/**
|
|
175
|
+
* Project directory, relative to the config file.
|
|
176
|
+
*
|
|
177
|
+
* @default "."
|
|
178
|
+
*/
|
|
179
|
+
root?: string | undefined;
|
|
180
|
+
/** Bundle id / package per platform. Required when the project has a dynamic `app.config.*`. */
|
|
181
|
+
bundleId: WardenPerPlatform;
|
|
182
|
+
/**
|
|
183
|
+
* How the app is run: `"expo"` (`expo start` + a dev client) or bare `"react-native"` (`react-native start`,
|
|
184
|
+
* `react-native build-ios` / `build-android`, the app itself pointed at Metro).
|
|
185
|
+
*
|
|
186
|
+
* @default `"expo"` when the project's package.json depends on `expo`, `"react-native"` when only on `react-native`
|
|
187
|
+
*/
|
|
188
|
+
framework?: "expo" | "react-native" | undefined;
|
|
189
|
+
/** How the build cache key is computed. */
|
|
190
|
+
fingerprint?: WardenFingerprint | undefined;
|
|
191
|
+
/**
|
|
192
|
+
* EAS build lookup. Without it, EAS is used only when the project has an `eas.json`.
|
|
193
|
+
* `false`: never consult eas-cli, even with an `eas.json` present.
|
|
194
|
+
*/
|
|
195
|
+
eas?: WardenEas | false | undefined;
|
|
196
|
+
/**
|
|
197
|
+
* Local build command per platform, used as the last resort.
|
|
198
|
+
*
|
|
199
|
+
* @example { ios: "bunx expo run:ios --no-install --no-bundler" }
|
|
200
|
+
*/
|
|
201
|
+
build?: WardenBuild | undefined;
|
|
202
|
+
/**
|
|
203
|
+
* Named builds, e.g. a Debug dev client next to a Release e2e build. Each has its own cache key, so
|
|
204
|
+
* every worktree with the same fingerprint reuses one build per variant.
|
|
205
|
+
*/
|
|
206
|
+
variants?: Record<string, WardenVariant> | undefined;
|
|
207
|
+
/**
|
|
208
|
+
* Per-kind overrides of the built-in `dev` / `simulator` / `release` builds, picked with `app: { kind }` or
|
|
209
|
+
* `--kind`. Kinds sit on top of {@link WardenProject.variants}: both keep working, but a build is one or the other.
|
|
210
|
+
*/
|
|
211
|
+
kinds?: WardenKinds | undefined;
|
|
212
|
+
/** Extra (legacy) cache roots laid out as `<dir>/<hash>/*.app|*.apk`, imported on demand. `~` expands to `$HOME`. */
|
|
213
|
+
cacheDirs?: string[] | undefined;
|
|
214
|
+
}
|
|
215
|
+
/** A file of jobs, `-` for stdin, or a `sh -c` command whose stdout lines are the jobs. */
|
|
216
|
+
export type WardenJobsFrom = string | {
|
|
217
|
+
/** `sh -c` command whose stdout lines are the jobs; a nonzero exit fails the batch. */
|
|
218
|
+
command: string;
|
|
219
|
+
};
|
|
220
|
+
/**
|
|
221
|
+
* A saved `warden batch` invocation, run with `warden batch <name>`. Each field mirrors the flag of the
|
|
222
|
+
* same name; flags on the command line override it and `-- <cmd>` overrides `cmd`.
|
|
223
|
+
*
|
|
224
|
+
* Runs in its project's root, or the config file's directory when there is no `project`.
|
|
225
|
+
*/
|
|
226
|
+
export interface WardenBatchPreset {
|
|
227
|
+
/** A `projects[].name`: its root is the preset's cwd, and `app` installs its build. */
|
|
228
|
+
project?: string | undefined;
|
|
229
|
+
/** Platform of the devices to claim. */
|
|
230
|
+
platform: WardenPlatform;
|
|
231
|
+
/**
|
|
232
|
+
* Devices to claim.
|
|
233
|
+
*
|
|
234
|
+
* @default 1
|
|
235
|
+
*/
|
|
236
|
+
count?: number | undefined;
|
|
237
|
+
/** Max warden devices of this profile. */
|
|
238
|
+
max?: number | undefined;
|
|
239
|
+
/**
|
|
240
|
+
* Device profile.
|
|
241
|
+
*
|
|
242
|
+
* @example "iphone-17"
|
|
243
|
+
*/
|
|
244
|
+
profile?: string | undefined;
|
|
245
|
+
/**
|
|
246
|
+
* Runtime.
|
|
247
|
+
*
|
|
248
|
+
* @example "latest"
|
|
249
|
+
*/
|
|
250
|
+
runtime?: string | undefined;
|
|
251
|
+
/**
|
|
252
|
+
* Label on the device and port leases.
|
|
253
|
+
*
|
|
254
|
+
* @default the preset name
|
|
255
|
+
*/
|
|
256
|
+
label?: string | undefined;
|
|
257
|
+
/**
|
|
258
|
+
* Lease TTL without heartbeat (`30s`, `10m`, `2h`).
|
|
259
|
+
*
|
|
260
|
+
* @default "30m"
|
|
261
|
+
*/
|
|
262
|
+
ttl?: string | undefined;
|
|
263
|
+
/** Wait this long for free devices (`30s`, `10m`, `2h`). */
|
|
264
|
+
wait?: string | undefined;
|
|
265
|
+
/**
|
|
266
|
+
* `FROM[:SPAN]` port specs, exported as `WARDEN_PORT_<i>`.
|
|
267
|
+
*
|
|
268
|
+
* @example ["8091:20"]
|
|
269
|
+
*/
|
|
270
|
+
ports?: string[] | undefined;
|
|
271
|
+
/** Ensure the app on every device first. */
|
|
272
|
+
app?: WardenAppOption | undefined;
|
|
273
|
+
/**
|
|
274
|
+
* Re-run a failed job up to N times on the same device.
|
|
275
|
+
*
|
|
276
|
+
* @default 0
|
|
277
|
+
*/
|
|
278
|
+
retry?: number | undefined;
|
|
279
|
+
/**
|
|
280
|
+
* A job passes only after N consecutive green runs on its device.
|
|
281
|
+
*
|
|
282
|
+
* @default 1
|
|
283
|
+
*/
|
|
284
|
+
passes?: number | undefined;
|
|
285
|
+
/** Added to the serve and job env. */
|
|
286
|
+
env?: Record<string, string> | undefined;
|
|
287
|
+
/** Long-lived `sh -c` command started before the jobs. */
|
|
288
|
+
serve?: string | undefined;
|
|
289
|
+
/**
|
|
290
|
+
* Wait for `http://…`, `tcp:PORT` or `file:PATH` (relative to the preset's cwd) before the jobs.
|
|
291
|
+
*
|
|
292
|
+
* @example "file:e2e-artifacts/batch/session.json"
|
|
293
|
+
*/
|
|
294
|
+
serveReady?: string | undefined;
|
|
295
|
+
/**
|
|
296
|
+
* Give up waiting for `serveReady` after this long.
|
|
297
|
+
*
|
|
298
|
+
* @default "10m"
|
|
299
|
+
*/
|
|
300
|
+
serveTimeout?: string | undefined;
|
|
301
|
+
/** The jobs. At most one of `jobs` / `jobsFrom`. */
|
|
302
|
+
jobs?: string[] | undefined;
|
|
303
|
+
/** Where the jobs come from. At most one of `jobs` / `jobsFrom`. */
|
|
304
|
+
jobsFrom?: WardenJobsFrom | undefined;
|
|
305
|
+
/** iOS: record every simulator and the TUI into this dir. */
|
|
306
|
+
record?: string | undefined;
|
|
307
|
+
/**
|
|
308
|
+
* iOS: close the Simulator.app windows of this run's devices. Overrides the top-level `headless`;
|
|
309
|
+
* `--headless` / `--no-headless` override both.
|
|
310
|
+
*/
|
|
311
|
+
headless?: boolean | undefined;
|
|
312
|
+
/**
|
|
313
|
+
* iOS: slim the simulator daemons of this run's devices (on by default). `false` turns it off; an object keeps
|
|
314
|
+
* parts running (`except`: category ids, `keep`: single `com.apple.*` labels). Overrides the top-level `slim`;
|
|
315
|
+
* `--slim` / `--no-slim` override both.
|
|
316
|
+
*/
|
|
317
|
+
slim?: boolean | {
|
|
318
|
+
except?: string[] | undefined;
|
|
319
|
+
keep?: string[] | undefined;
|
|
320
|
+
} | undefined;
|
|
321
|
+
/** Per-job log dir. */
|
|
322
|
+
logs?: string | undefined;
|
|
323
|
+
/**
|
|
324
|
+
* Per-job argv; `{job}` `{udid}` `{worker}` `{seq}` are substituted.
|
|
325
|
+
*
|
|
326
|
+
* @example ["bun", "scripts/e2e/run-ios.ts", "--attach", "{job}", "--device", "{udid}"]
|
|
327
|
+
*/
|
|
328
|
+
cmd: string[];
|
|
329
|
+
}
|
|
330
|
+
/** `e2e.<suite>.specs.<id|glob>`: what a spec covers and whether it gates. */
|
|
331
|
+
export interface WardenE2eSpecEntry {
|
|
332
|
+
/**
|
|
333
|
+
* Files the spec drives (screens / routes); everything they import, transitively, selects it.
|
|
334
|
+
*
|
|
335
|
+
* @default []
|
|
336
|
+
*/
|
|
337
|
+
entries?: string[] | undefined;
|
|
338
|
+
/**
|
|
339
|
+
* Extra globs that select the spec when a changed file matches.
|
|
340
|
+
*
|
|
341
|
+
* @default []
|
|
342
|
+
*/
|
|
343
|
+
paths?: string[] | undefined;
|
|
344
|
+
/**
|
|
345
|
+
* Platforms the spec runs on.
|
|
346
|
+
*
|
|
347
|
+
* @default from the id (`-ios-` / `-android-`), else both
|
|
348
|
+
*/
|
|
349
|
+
platforms?: WardenPlatform[] | undefined;
|
|
350
|
+
/** Overrides the suite's `maxDepth`. */
|
|
351
|
+
maxDepth?: number | undefined;
|
|
352
|
+
/** A failure fails `warden e2e`; `false` = reported only. Defaults to the suite's `required`. */
|
|
353
|
+
required?: boolean | undefined;
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* `e2e.<suite>.metro`: one warden-owned Metro per run. It is started in the project root on a leased
|
|
357
|
+
* port, counts as ready only once that port is verified to serve this checkout (never another
|
|
358
|
+
* worktree's Metro), and is stopped at the end. Specs get `WARDEN_METRO_URL` / `WARDEN_PORT`.
|
|
359
|
+
*/
|
|
360
|
+
export interface WardenE2eMetro {
|
|
361
|
+
/** @default true */
|
|
362
|
+
enabled?: boolean | undefined;
|
|
363
|
+
/**
|
|
364
|
+
* Where the Metro port is leased from (`<from>[:<span>]`).
|
|
365
|
+
*
|
|
366
|
+
* @default "8081:100"
|
|
367
|
+
*/
|
|
368
|
+
port?: string | undefined;
|
|
369
|
+
/**
|
|
370
|
+
* Extra launch arguments when warden opens the app on each device (iOS: after `--initialUrl <metroUrl>`).
|
|
371
|
+
*
|
|
372
|
+
* @default []
|
|
373
|
+
*/
|
|
374
|
+
launchArgs?: string[] | undefined;
|
|
375
|
+
/**
|
|
376
|
+
* Open the app on each device once Metro is ready. `false` for a spec that installs the app
|
|
377
|
+
* itself and so has to launch it too (`WARDEN_METRO_URL` is what it points the dev client at).
|
|
378
|
+
*
|
|
379
|
+
* @default true
|
|
380
|
+
*/
|
|
381
|
+
open?: boolean | undefined;
|
|
382
|
+
/** Added to Metro's env only. */
|
|
383
|
+
env?: Record<string, string> | undefined;
|
|
384
|
+
/**
|
|
385
|
+
* Build the bundle once before the first spec, so no spec pays for (or times out on) the cold build.
|
|
386
|
+
*
|
|
387
|
+
* @default true
|
|
388
|
+
*/
|
|
389
|
+
prewarm?: boolean | undefined;
|
|
390
|
+
/**
|
|
391
|
+
* Give up waiting for Metro after this long.
|
|
392
|
+
*
|
|
393
|
+
* @default "2m"
|
|
394
|
+
*/
|
|
395
|
+
readyTimeout?: string | undefined;
|
|
396
|
+
}
|
|
397
|
+
/** Ceilings `device.perf.stop()` holds a perf report to; a breach fails the test with `PERF_BUDGET`. All lower-is-better. */
|
|
398
|
+
export interface WardenPerfBudgets {
|
|
399
|
+
/** Cold start, ms. */
|
|
400
|
+
startupColdMs?: number | undefined;
|
|
401
|
+
/** Share of janky frames, percent. */
|
|
402
|
+
jankPct?: number | undefined;
|
|
403
|
+
/** The slowest React commit, ms. */
|
|
404
|
+
maxCommitMs?: number | undefined;
|
|
405
|
+
/** 95th percentile app CPU, percent. */
|
|
406
|
+
p95CpuPct?: number | undefined;
|
|
407
|
+
/** Peak memory (PSS on Android, RSS on iOS), MB. */
|
|
408
|
+
peakMemoryMb?: number | undefined;
|
|
409
|
+
}
|
|
410
|
+
/** `perf` (top level, or `e2e.<suite>.perf`): profiling settings for specs that use `device.perf`. */
|
|
411
|
+
export interface WardenPerfConfig {
|
|
412
|
+
/** Budgets; a metric the report has no number for (its capture did not run) cannot breach. */
|
|
413
|
+
budgets?: WardenPerfBudgets | undefined;
|
|
414
|
+
}
|
|
415
|
+
/** `e2e.<suite>.spec`: how `warden e2e` runs the `*.e2e.ts` specs (`bun test` with `@delacour/warden-e2e`). */
|
|
416
|
+
export interface WardenE2eSpec {
|
|
417
|
+
/** The app under test. Default: the suite project's `bundleId` for the platform. */
|
|
418
|
+
bundleId?: string | undefined;
|
|
419
|
+
/** Launch arguments `app.open()` passes, after `--initialUrl <metro url>` and `metro.launchArgs`. */
|
|
420
|
+
launchArgs?: string[] | undefined;
|
|
421
|
+
/**
|
|
422
|
+
* Per-test timeout.
|
|
423
|
+
*
|
|
424
|
+
* @default "2m"
|
|
425
|
+
*/
|
|
426
|
+
timeout?: string | undefined;
|
|
427
|
+
/**
|
|
428
|
+
* Per-action timeout (tap, fill, waiting for an element).
|
|
429
|
+
*
|
|
430
|
+
* @default "30s"
|
|
431
|
+
*/
|
|
432
|
+
actionTimeout?: string | undefined;
|
|
433
|
+
/**
|
|
434
|
+
* Polling timeout of `expect(locator)`.
|
|
435
|
+
*
|
|
436
|
+
* @default "5s"
|
|
437
|
+
*/
|
|
438
|
+
assertionTimeout?: string | undefined;
|
|
439
|
+
}
|
|
440
|
+
/** `e2e.<suite>.devices[]`: one leg of the run with its own device pool. */
|
|
441
|
+
export interface WardenE2eDeviceLeg {
|
|
442
|
+
/** Device profile; also the leg's name (`{leg}`, `WARDEN_LEG`, `--devices <profile>`). */
|
|
443
|
+
profile: string;
|
|
444
|
+
/**
|
|
445
|
+
* Devices to claim for this leg.
|
|
446
|
+
*
|
|
447
|
+
* @default 1
|
|
448
|
+
*/
|
|
449
|
+
count?: number | undefined;
|
|
450
|
+
/** Added to this leg's setup and spec env. */
|
|
451
|
+
env?: Record<string, string> | undefined;
|
|
452
|
+
/**
|
|
453
|
+
* `false`: the leg only runs with `--devices all` or `--devices <profile>`.
|
|
454
|
+
*
|
|
455
|
+
* @default true
|
|
456
|
+
*/
|
|
457
|
+
default?: boolean | undefined;
|
|
458
|
+
}
|
|
459
|
+
/**
|
|
460
|
+
* `e2e.<suite>`: the specs `warden affected` selects and `warden e2e` runs. Every path and glob is
|
|
461
|
+
* relative to the config file.
|
|
462
|
+
*/
|
|
463
|
+
export interface WardenE2eSuite {
|
|
464
|
+
/**
|
|
465
|
+
* `projects[].name`: the specs, `serve` and `setup` run in its root and `app` installs its build.
|
|
466
|
+
* Needed when the config has several projects and warden runs from the repo root.
|
|
467
|
+
*/
|
|
468
|
+
project?: string | undefined;
|
|
469
|
+
/** Perf budgets for this suite; each key replaces the top-level `perf.budgets` one. */
|
|
470
|
+
perf?: WardenPerfConfig | undefined;
|
|
471
|
+
/** Directory of specs: `*.e2e.ts(x)` specs whose other `.ts` files are helpers (searched recursively; dot dirs, `node_modules` and `__baselines__` skipped). */
|
|
472
|
+
specsDir: string;
|
|
473
|
+
/** How the specs run: the app under test, its launch arguments and the timeouts. */
|
|
474
|
+
spec?: WardenE2eSpec | undefined;
|
|
475
|
+
/**
|
|
476
|
+
* `"device"`: one `bun test` process per device runs its share of the specs (sharded round-robin by
|
|
477
|
+
* spec id), so a device's specs run in order in one process and the devices run in parallel.
|
|
478
|
+
* `"spec"`: one process per spec, one worker per device: slower (each process pays bun's start,
|
|
479
|
+
* the preload and a driver connect), but a spec can never see what the one before left behind and
|
|
480
|
+
* the devices share the specs dynamically. With `passes` above 1 the default is `"spec"`.
|
|
481
|
+
*
|
|
482
|
+
* @default "device"
|
|
483
|
+
*/
|
|
484
|
+
isolate?: "device" | "spec" | undefined;
|
|
485
|
+
/**
|
|
486
|
+
* What happens to the devices warden booted when the gate passes. `"always"`: shut them down.
|
|
487
|
+
* `"idle"`: leave them booted, so the next run skips the 10-30 s boot, and let `warden gc` shut
|
|
488
|
+
* them down once they have sat idle. A failed run always leaves them up. `--no-shutdown` wins.
|
|
489
|
+
*
|
|
490
|
+
* @default "always"
|
|
491
|
+
*/
|
|
492
|
+
shutdown?: "always" | "idle" | undefined;
|
|
493
|
+
/**
|
|
494
|
+
* Per spec: a spec process still going after this many milliseconds for each spec it runs (one
|
|
495
|
+
* spec, or a device's share of them with `isolate: "device"`) is killed (screenshot first) and
|
|
496
|
+
* counts as failed.
|
|
497
|
+
*
|
|
498
|
+
* @default 600000 (10 min)
|
|
499
|
+
*/
|
|
500
|
+
jobTimeoutMs?: number | undefined;
|
|
501
|
+
/**
|
|
502
|
+
* Device legs: each claims its own pool and runs the selected specs, concurrently with the others.
|
|
503
|
+
* Replaces `profile` / `count`.
|
|
504
|
+
*
|
|
505
|
+
* @example [{ profile: "iphone-17", count: 2 }, { profile: "ipad-pro-11-inch-m5", default: false, env: { E2E_LAYOUT: "regular" } }]
|
|
506
|
+
*/
|
|
507
|
+
devices?: WardenE2eDeviceLeg[] | undefined;
|
|
508
|
+
/** Dev-client suites: warden starts one Metro for the run on a leased port and points the app at it. */
|
|
509
|
+
metro?: WardenE2eMetro | undefined;
|
|
510
|
+
/** tsconfig for files with no `tsconfig.json` between them and the config file. */
|
|
511
|
+
tsconfig?: string | undefined;
|
|
512
|
+
/**
|
|
513
|
+
* File-based router directory: `_layout` files above an entry count as entries.
|
|
514
|
+
*
|
|
515
|
+
* @example "apps/mobile/src/app"
|
|
516
|
+
*/
|
|
517
|
+
routerRoot?: string | undefined;
|
|
518
|
+
/** Default platform. Without it `warden affected` reports both and `warden e2e` needs `--platform`. */
|
|
519
|
+
platform?: WardenPlatform | undefined;
|
|
520
|
+
/**
|
|
521
|
+
* Git ref changes are measured against (merge-base with HEAD).
|
|
522
|
+
*
|
|
523
|
+
* @default "main"
|
|
524
|
+
*/
|
|
525
|
+
base?: string | undefined;
|
|
526
|
+
/**
|
|
527
|
+
* Import hops from an entry that still select a spec.
|
|
528
|
+
*
|
|
529
|
+
* @default unlimited
|
|
530
|
+
*/
|
|
531
|
+
maxDepth?: number | undefined;
|
|
532
|
+
/**
|
|
533
|
+
* Globs: a matching change selects every spec (lockfile, native dirs, bundler config…).
|
|
534
|
+
*
|
|
535
|
+
* @default []
|
|
536
|
+
* @example ["bun.lock", "apps/mobile/ios/**"]
|
|
537
|
+
*/
|
|
538
|
+
runAll?: string[] | undefined;
|
|
539
|
+
/**
|
|
540
|
+
* Globs: matching changes are ignored (docs, unit tests…).
|
|
541
|
+
*
|
|
542
|
+
* @default []
|
|
543
|
+
*/
|
|
544
|
+
ignore?: string[] | undefined;
|
|
545
|
+
/**
|
|
546
|
+
* Spec ids / globs that always run.
|
|
547
|
+
*
|
|
548
|
+
* @default []
|
|
549
|
+
*/
|
|
550
|
+
always?: string[] | undefined;
|
|
551
|
+
/**
|
|
552
|
+
* Spec-id globs: only matching specs are ever selected. Lets one `specsDir` feed several suites.
|
|
553
|
+
*
|
|
554
|
+
* @default [] (all specs)
|
|
555
|
+
*/
|
|
556
|
+
include?: string[] | undefined;
|
|
557
|
+
/**
|
|
558
|
+
* Spec-id globs that are never selected (e.g. specs another suite owns).
|
|
559
|
+
*
|
|
560
|
+
* @default []
|
|
561
|
+
*/
|
|
562
|
+
exclude?: string[] | undefined;
|
|
563
|
+
/**
|
|
564
|
+
* Changed-file globs: only matching changes count at all.
|
|
565
|
+
*
|
|
566
|
+
* @default [] (every change)
|
|
567
|
+
*/
|
|
568
|
+
scope?: string[] | undefined;
|
|
569
|
+
/**
|
|
570
|
+
* A counted change that reaches no spec (no `paths` / `entries` / `runAll` match, not `ignore`d):
|
|
571
|
+
* `"run-all"` selects every spec, so a new `src/` area never silently runs nothing; `"skip"` selects nothing.
|
|
572
|
+
*
|
|
573
|
+
* @default "skip"
|
|
574
|
+
*/
|
|
575
|
+
unmatched?: "run-all" | "skip" | undefined;
|
|
576
|
+
/**
|
|
577
|
+
* Specs with no `entries` / `paths`: run every time, or only via `runAll` / `always`.
|
|
578
|
+
*
|
|
579
|
+
* @default "run"
|
|
580
|
+
*/
|
|
581
|
+
unmapped?: "run" | "skip" | undefined;
|
|
582
|
+
/**
|
|
583
|
+
* Consecutive green runs a spec needs on the same device.
|
|
584
|
+
*
|
|
585
|
+
* @default 1
|
|
586
|
+
*/
|
|
587
|
+
passes?: number | undefined;
|
|
588
|
+
/**
|
|
589
|
+
* Re-run a failed spec up to N times.
|
|
590
|
+
*
|
|
591
|
+
* @default 0
|
|
592
|
+
*/
|
|
593
|
+
retry?: number | undefined;
|
|
594
|
+
/** Devices to claim (not with `devices`). */
|
|
595
|
+
count?: number | undefined;
|
|
596
|
+
/** Device profile (not with `devices`). */
|
|
597
|
+
profile?: string | undefined;
|
|
598
|
+
/** Ensure the app on every device first. */
|
|
599
|
+
app?: WardenAppOption | undefined;
|
|
600
|
+
/** Ports leased for the run (`<from>[:<span>]`), exported as `WARDEN_PORT_<i>` / `WARDEN_PORTS` to serve, setup and every spec process. */
|
|
601
|
+
ports?: string[] | undefined;
|
|
602
|
+
/** Added to the serve, setup and spec env. */
|
|
603
|
+
env?: Record<string, string> | undefined;
|
|
604
|
+
/** `sh -c` once per run before the specs, in its own process group; killed at the end. `{port}` / `{metroUrl}` are substituted. `--serve` overrides. */
|
|
605
|
+
serve?: string | undefined;
|
|
606
|
+
/** Wait for `http://…`, `tcp:PORT` or `file:PATH` (relative to the project root) before starting; `{port}` is the first leased port (`"tcp:{port}"`). Needs `serve`. */
|
|
607
|
+
serveReady?: string | undefined;
|
|
608
|
+
/**
|
|
609
|
+
* Give up waiting for `serveReady` after this long (`30s`, `10m`). Needs `serve`.
|
|
610
|
+
*
|
|
611
|
+
* @default "10m"
|
|
612
|
+
*/
|
|
613
|
+
serveTimeout?: string | undefined;
|
|
614
|
+
/**
|
|
615
|
+
* `sh -c` once per leased device, after the app install and before that device's first spec
|
|
616
|
+
* (`{udid}` is substituted). A non-zero exit drops that device; its specs go to the others.
|
|
617
|
+
*/
|
|
618
|
+
setup?: string | undefined;
|
|
619
|
+
/**
|
|
620
|
+
* iOS only: switch off the simulator daemons specs never need on each leased device, before `setup`.
|
|
621
|
+
* Skipped with a note on an Android run; refused on a suite fixed to `platform: "android"`.
|
|
622
|
+
* An object keeps parts running: `except` lists category ids (see `warden sim profiles`), `keep`
|
|
623
|
+
* lists single `com.apple.*` launchd labels. `false` turns it off.
|
|
624
|
+
*/
|
|
625
|
+
slim?: boolean | {
|
|
626
|
+
except?: string[] | undefined;
|
|
627
|
+
keep?: string[] | undefined;
|
|
628
|
+
} | undefined;
|
|
629
|
+
/** Removed: renamed `warmDrivers`. */
|
|
630
|
+
warmRunners?: undefined;
|
|
631
|
+
/**
|
|
632
|
+
* iOS: before the specs, start every leased device's automation helper at once (and take one
|
|
633
|
+
* snapshot), so the first spec on each device does not pay the cold start. A failed warm-up only warns.
|
|
634
|
+
*
|
|
635
|
+
* @default true
|
|
636
|
+
*/
|
|
637
|
+
warmDrivers?: boolean | undefined;
|
|
638
|
+
/**
|
|
639
|
+
* iOS: close the Simulator.app windows of this run's devices, so it shows nothing even with Simulator.app
|
|
640
|
+
* open. Overrides the top-level `headless`; `--headless` / `--no-headless` override both. Android is always headless.
|
|
641
|
+
*/
|
|
642
|
+
headless?: boolean | undefined;
|
|
643
|
+
/**
|
|
644
|
+
* Default `required` for specs that don't set it.
|
|
645
|
+
*
|
|
646
|
+
* @default true
|
|
647
|
+
*/
|
|
648
|
+
required?: boolean | undefined;
|
|
649
|
+
/**
|
|
650
|
+
* Per spec id or glob: what each spec covers and whether it gates. Not to be confused with
|
|
651
|
+
* {@link WardenE2eSuite.spec}, which sets how every spec runs.
|
|
652
|
+
*
|
|
653
|
+
* @default {}
|
|
654
|
+
* @example { "qa-login": { entries: ["apps/mobile/src/app/(auth)/login.tsx"] }, "store-*": { required: false } }
|
|
655
|
+
*/
|
|
656
|
+
specs?: Record<string, WardenE2eSpecEntry> | undefined;
|
|
657
|
+
}
|
|
658
|
+
/**
|
|
659
|
+
* `warden.config.ts`: set at least one of `projects`, `batches` and `e2e`. Without `projects`, app builds
|
|
660
|
+
* fall back to detecting `app.json` / `app.config.*`.
|
|
661
|
+
*/
|
|
662
|
+
export interface WardenConfig {
|
|
663
|
+
/** Apps warden builds, caches and installs. With several, the one containing the cwd is picked. */
|
|
664
|
+
projects?: WardenProject[] | undefined;
|
|
665
|
+
/** Named `warden batch` presets. `ios` and `android` can't be used as names. */
|
|
666
|
+
batches?: Record<string, WardenBatchPreset> | undefined;
|
|
667
|
+
/** Named `warden affected` / `warden e2e` suites. */
|
|
668
|
+
e2e?: Record<string, WardenE2eSuite> | undefined;
|
|
669
|
+
/**
|
|
670
|
+
* iOS: close the Simulator.app windows of every device an `e2e` suite or `batches` preset leases (default for
|
|
671
|
+
* those that don't set `headless`). `warden dev` ignores it. `WARDEN_HEADLESS=1` is the fallback below it.
|
|
672
|
+
*/
|
|
673
|
+
headless?: boolean | undefined;
|
|
674
|
+
/**
|
|
675
|
+
* iOS: switch off the simulator daemons specs never need on every simulator warden creates (claim, run, dev,
|
|
676
|
+
* e2e, batch). On by default. `false` turns it off; an object keeps parts running (`except`: category ids, see
|
|
677
|
+
* `warden sim profiles`; `keep`: single `com.apple.*` launchd labels). An `e2e` suite's `slim` overrides it,
|
|
678
|
+
* `--slim` / `--no-slim` override both, `WARDEN_SLIM=0` is the fallback below it.
|
|
679
|
+
*
|
|
680
|
+
* @default true
|
|
681
|
+
*/
|
|
682
|
+
slim?: boolean | {
|
|
683
|
+
except?: string[] | undefined;
|
|
684
|
+
keep?: string[] | undefined;
|
|
685
|
+
} | undefined;
|
|
686
|
+
/** Perf budgets every `e2e` suite starts from (a suite's own `perf.budgets` replace them per key). */
|
|
687
|
+
perf?: WardenPerfConfig | undefined;
|
|
688
|
+
/** Android emulator settings. */
|
|
689
|
+
android?: WardenAndroidSettings | undefined;
|
|
690
|
+
}
|
|
691
|
+
/** Android emulator settings (`android`). */
|
|
692
|
+
export interface WardenAndroidSettings {
|
|
693
|
+
/**
|
|
694
|
+
* Launch claimed emulators from warden's own quick-boot snapshot of the AVD (built once per AVD from a
|
|
695
|
+
* cold boot) instead of the AVD's `default_boot` or a cold boot. Default true; `WARDEN_ANDROID_SNAPSHOT=0`
|
|
696
|
+
* / `=1` override it.
|
|
697
|
+
*/
|
|
698
|
+
snapshot?: boolean | undefined;
|
|
699
|
+
}
|
|
700
|
+
/** What a config function receives. */
|
|
701
|
+
export interface WardenConfigContext {
|
|
702
|
+
/** Absolute directory of the config file; relative paths in the config resolve against it. */
|
|
703
|
+
configDir: string;
|
|
704
|
+
/** The environment warden runs with. */
|
|
705
|
+
env: Record<string, string | undefined>;
|
|
706
|
+
}
|
|
707
|
+
/** What `warden.config.ts` may `export default`: a config, or a sync function returning one. */
|
|
708
|
+
export type WardenConfigExport = WardenConfig | ((context: WardenConfigContext) => WardenConfig);
|
|
709
|
+
/**
|
|
710
|
+
* Type a `warden.config.ts` export with autocompletion and docs. Returns its argument unchanged.
|
|
711
|
+
*
|
|
712
|
+
* @example
|
|
713
|
+
* ```ts
|
|
714
|
+
* export default defineConfig(({ env }) => ({
|
|
715
|
+
* projects: [{ name: "app", bundleId: { ios: env.IOS_BUNDLE_ID ?? "com.example.app" } }],
|
|
716
|
+
* }));
|
|
717
|
+
* ```
|
|
718
|
+
*/
|
|
719
|
+
export declare function defineConfig(config: WardenConfigExport): WardenConfigExport;
|