@delacour/warden-e2e 0.6.0 → 0.8.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-yb1hcjqm.mjs → chunk-vs5npy59.mjs} +3370 -218
- package/index.mjs +247 -16
- package/package.json +2 -2
- package/preload.mjs +8264 -2387
- package/types/core/src/a11y/a11y.config.d.ts +47 -0
- package/types/core/src/affected/affected.schema.d.ts +142 -1
- package/types/core/src/allocate.d.ts +2 -1
- package/types/core/src/android-keyguard.d.ts +36 -0
- package/types/core/src/batch/preset.schema.d.ts +13 -1
- package/types/core/src/biometrics.d.ts +15 -0
- package/types/core/src/builds/app-option.schema.d.ts +1 -1
- package/types/core/src/builds/builds.defaults.d.ts +15 -0
- package/types/core/src/builds/config.d.ts +5 -1
- package/types/core/src/builds/config.schema.d.ts +275 -2
- package/types/core/src/builds/define-config.d.ts +159 -0
- package/types/core/src/builds/install-device.d.ts +37 -0
- package/types/core/src/builds/install.d.ts +17 -3
- package/types/core/src/builds/kinds.d.ts +21 -3
- package/types/core/src/builds/lock.d.ts +38 -0
- package/types/core/src/builds/resolve.d.ts +12 -2
- package/types/core/src/camera-mode.d.ts +17 -0
- package/types/core/src/camera-placeholder.d.ts +9 -0
- package/types/core/src/camera-profile.d.ts +12 -0
- package/types/core/src/claim-errors.d.ts +34 -0
- package/types/core/src/conditions/a11y-keys.d.ts +45 -0
- package/types/core/src/conditions/camera-media.d.ts +30 -0
- package/types/core/src/conditions/conditions.config.d.ts +141 -0
- package/types/core/src/conditions/conditions.d.ts +22 -0
- package/types/core/src/conditions/conditions.types.d.ts +171 -0
- package/types/core/src/conditions/inject-state.d.ts +13 -0
- package/types/core/src/conditions/launch-settings.d.ts +14 -0
- package/types/core/src/conditions/network.presets.d.ts +42 -0
- package/types/core/src/conditions/text-size.d.ts +19 -0
- package/types/core/src/golden/android-biometrics.d.ts +35 -0
- package/types/core/src/ios-runner/build.d.ts +28 -0
- package/types/core/src/ios-runner/cache.d.ts +44 -0
- package/types/core/src/ios-runner/classify.d.ts +7 -0
- package/types/core/src/ios-runner/pack.d.ts +17 -0
- package/types/core/src/ios-runner/runner-build.types.d.ts +76 -0
- package/types/core/src/ios-runner/signing.d.ts +40 -0
- package/types/core/src/ios-runner/sim-build.d.ts +36 -0
- package/types/core/src/ios-runner/stage.d.ts +26 -0
- package/types/core/src/lease-events.d.ts +126 -0
- package/types/core/src/netproxy/control.d.ts +75 -0
- package/types/core/src/netproxy/decide.d.ts +22 -0
- package/types/core/src/netproxy/handle-file.d.ts +12 -0
- package/types/core/src/netproxy/lease-proxy.d.ts +53 -0
- package/types/core/src/netproxy/netproxy.main.d.ts +40 -0
- package/types/core/src/netproxy/pipe.d.ts +37 -0
- package/types/core/src/netproxy/proxy.d.ts +48 -0
- package/types/core/src/netproxy/shaping.d.ts +43 -0
- package/types/core/src/ownership.d.ts +44 -0
- package/types/core/src/providers/android.d.ts +46 -2
- package/types/core/src/providers/provider.types.d.ts +9 -0
- package/types/core/src/queue.d.ts +43 -0
- package/types/core/src/store.d.ts +61 -5
- package/types/core/src/types.d.ts +57 -0
- package/types/core/src/xcode.d.ts +22 -0
- package/types/e2e/src/a11y/a11y-target.d.ts +23 -0
- package/types/e2e/src/a11y/a11y.types.d.ts +81 -0
- package/types/e2e/src/a11y/announcements.d.ts +24 -0
- package/types/e2e/src/a11y/audit.d.ts +22 -0
- package/types/e2e/src/a11y/native/atf.d.ts +51 -0
- package/types/e2e/src/a11y/native/xcuitest.d.ts +60 -0
- package/types/e2e/src/a11y/order.d.ts +5 -0
- package/types/e2e/src/a11y/rules/duplicate-name.d.ts +6 -0
- package/types/e2e/src/a11y/rules/hidden-focusable.d.ts +3 -0
- package/types/e2e/src/a11y/rules/image-no-name.d.ts +6 -0
- package/types/e2e/src/a11y/rules/label-in-name.d.ts +7 -0
- package/types/e2e/src/a11y/rules/missing-name.d.ts +3 -0
- package/types/e2e/src/a11y/rules/role-in-name.d.ts +3 -0
- package/types/e2e/src/a11y/rules/rule.context.d.ts +22 -0
- package/types/e2e/src/a11y/rules/target-size.d.ts +21 -0
- package/types/e2e/src/a11y/rules/text-clipped.d.ts +9 -0
- package/types/e2e/src/a11y/speech.d.ts +26 -0
- package/types/e2e/src/a11y/talkback-log.d.ts +13 -0
- package/types/e2e/src/alert-buttons.d.ts +9 -0
- package/types/e2e/src/app.d.ts +3 -2
- package/types/e2e/src/conditions/condition-events.d.ts +68 -0
- package/types/e2e/src/conditions/device-conditions.d.ts +198 -0
- package/types/e2e/src/conditions/geo.d.ts +15 -0
- package/types/e2e/src/conditions/gpx.d.ts +8 -0
- package/types/e2e/src/conditions/lease-recorder.d.ts +27 -0
- package/types/e2e/src/conditions/lease-store.d.ts +9 -0
- package/types/e2e/src/conditions/location-plan.d.ts +17 -0
- package/types/e2e/src/conditions/location-scenarios.data.d.ts +17 -0
- package/types/e2e/src/conditions/route-ticker.d.ts +53 -0
- package/types/e2e/src/conditions/scheduler.d.ts +83 -0
- package/types/e2e/src/device-a11y.d.ts +97 -0
- package/types/e2e/src/device.d.ts +67 -4
- package/types/e2e/src/driver/a11y-events.d.ts +30 -0
- package/types/e2e/src/driver/a11y-read.d.ts +12 -0
- package/types/e2e/src/driver/android/android-animations.d.ts +7 -0
- package/types/e2e/src/driver/android/android-app.d.ts +4 -0
- package/types/e2e/src/driver/android/android-argv.d.ts +21 -0
- package/types/e2e/src/driver/android/android-camera-host.d.ts +23 -0
- package/types/e2e/src/driver/android/android-camera.d.ts +28 -0
- package/types/e2e/src/driver/android/android-clock.d.ts +28 -0
- package/types/e2e/src/driver/android/android-device.d.ts +8 -0
- package/types/e2e/src/driver/android/android-driver.d.ts +22 -0
- package/types/e2e/src/driver/android/android-network.d.ts +66 -0
- package/types/e2e/src/driver/android/android-phone.d.ts +47 -0
- package/types/e2e/src/driver/android/android-screen-reader.d.ts +23 -0
- package/types/e2e/src/driver/android/android-settings.d.ts +41 -0
- package/types/e2e/src/driver/android/android-spoken.d.ts +18 -0
- package/types/e2e/src/driver/android/android-state.d.ts +10 -0
- package/types/e2e/src/driver/android/android-talkback.d.ts +57 -0
- package/types/e2e/src/driver/android/atf-device.d.ts +23 -0
- package/types/e2e/src/driver/android/atf-dex.generated.d.ts +7 -0
- package/types/e2e/src/driver/android/uiad-dex.generated.d.ts +5 -5
- package/types/e2e/src/driver/android/uiad-ops.d.ts +27 -1
- package/types/e2e/src/driver/android/uiad-protocol.d.ts +8 -0
- package/types/e2e/src/driver/android/uiad-screen-reader.d.ts +33 -0
- package/types/e2e/src/driver/clock-recorder.d.ts +9 -0
- package/types/e2e/src/driver/device-settings.d.ts +10 -0
- package/types/e2e/src/driver/driver.types.d.ts +242 -1
- package/types/e2e/src/driver/element-actions.d.ts +16 -0
- package/types/e2e/src/driver/ios/device/capabilities.d.ts +3 -0
- package/types/e2e/src/driver/ios/device/device-driver.d.ts +49 -0
- package/types/e2e/src/driver/ios/device/devicectl.d.ts +38 -0
- package/types/e2e/src/driver/ios/device/runner-protocol.d.ts +21 -0
- package/types/e2e/src/driver/ios/hid-recovery.d.ts +15 -0
- package/types/e2e/src/driver/ios/inject.d.ts +124 -0
- package/types/e2e/src/driver/ios/ios-a11y.d.ts +73 -0
- package/types/e2e/src/driver/ios/ios-audit.d.ts +36 -0
- package/types/e2e/src/driver/ios/ios-driver.d.ts +50 -1
- package/types/e2e/src/driver/ios/ios-network.d.ts +51 -0
- package/types/e2e/src/driver/ios/ios-settings.d.ts +44 -0
- package/types/e2e/src/driver/ios/sim-runner.d.ts +41 -0
- package/types/e2e/src/driver/ios/simctl.d.ts +41 -1
- package/types/e2e/src/driver/ios/simd-transport.d.ts +11 -2
- package/types/e2e/src/driver/ios/type-budget.d.ts +6 -1
- package/types/e2e/src/driver/native-audit.d.ts +26 -0
- package/types/e2e/src/driver/network-proxy.d.ts +52 -0
- package/types/e2e/src/driver/open.d.ts +20 -0
- package/types/e2e/src/driver/physical-conditions.d.ts +11 -0
- package/types/e2e/src/driver/requirements.d.ts +14 -0
- package/types/e2e/src/driver/screen-reader.d.ts +26 -0
- package/types/e2e/src/driver/typing.d.ts +1 -1
- package/types/e2e/src/env.d.ts +47 -0
- package/types/e2e/src/expect/a11y-matchers.d.ts +33 -0
- package/types/e2e/src/expect/expect.d.ts +7 -2
- package/types/e2e/src/expect/matchers.d.ts +14 -0
- package/types/e2e/src/fixture-session.d.ts +4 -0
- package/types/e2e/src/fixtures.d.ts +6 -1
- package/types/e2e/src/index.d.ts +6 -0
- package/types/e2e/src/runner/callsite.d.ts +3 -1
- package/types/e2e/src/runner/scope.d.ts +1 -0
- package/types/e2e/src/runner/test.d.ts +6 -0
- package/types/e2e/src/screen/render.d.ts +8 -0
- package/types/e2e/src/traced-device.d.ts +2 -1
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { type NetworkInput } from "../../../core/src/conditions/conditions.config";
|
|
2
|
+
import type { CameraFacing, ClockCondition, HostFault, LatLon, LocationCondition, LocationScenario, NetworkCondition } from "../../../core/src/conditions/conditions.types";
|
|
3
|
+
import type { A11yOp, A11yState, BatteryOp, BatteryState, CameraState, Driver, LocaleState, NetworkState, PermissionGrants, PermissionsState, PushPayload, SettingOptions, TimezoneState } from "../driver/driver.types";
|
|
4
|
+
import type { E2eEnv } from "../env";
|
|
5
|
+
import { type ConditionSink } from "./condition-events";
|
|
6
|
+
import type { ConditionRecorder } from "./lease-recorder";
|
|
7
|
+
import { type TickerClock } from "./route-ticker";
|
|
8
|
+
import { type SchedulerOutcome, type SchedulerStatus } from "./scheduler";
|
|
9
|
+
/** A duration in ms, or text like `"10s"`, `"2m"`, `"2d"`. */
|
|
10
|
+
export type Duration = string | number;
|
|
11
|
+
export type NetworkSetOptions = {
|
|
12
|
+
/** iOS: relaunch the app when it runs without the warden network dylib (the default is to fail and say so) */
|
|
13
|
+
relaunch?: boolean;
|
|
14
|
+
};
|
|
15
|
+
export type FlapOptions = {
|
|
16
|
+
/** how long the network stays up per cycle */
|
|
17
|
+
up: Duration;
|
|
18
|
+
/** how long it stays down (offline) per cycle */
|
|
19
|
+
down: Duration;
|
|
20
|
+
/** each phase lasts its length +/- this percent, drawn from `seed` */
|
|
21
|
+
jitterPct?: number;
|
|
22
|
+
/** same seed, same sequence; default 1 */
|
|
23
|
+
seed?: number;
|
|
24
|
+
/** the "up" condition and what `stop()` puts back; default `"online"` */
|
|
25
|
+
base?: NetworkInput;
|
|
26
|
+
};
|
|
27
|
+
/** A running flap or timeline. It is stopped by `stop()` and, at the latest, when the test ends. */
|
|
28
|
+
export type ConditionRun = {
|
|
29
|
+
/** stops it (a flap puts its base back first) and resolves with how it ended */
|
|
30
|
+
stop(): Promise<SchedulerOutcome>;
|
|
31
|
+
/** resolves when it ended; never rejects */
|
|
32
|
+
readonly done: Promise<SchedulerOutcome>;
|
|
33
|
+
status(): SchedulerStatus;
|
|
34
|
+
};
|
|
35
|
+
export type NetworkStatus = {
|
|
36
|
+
/** what is in effect (`flap` while one runs) */
|
|
37
|
+
condition: NetworkCondition;
|
|
38
|
+
faults: readonly HostFault[];
|
|
39
|
+
flapping: boolean;
|
|
40
|
+
};
|
|
41
|
+
export type DeviceNetwork = {
|
|
42
|
+
/**
|
|
43
|
+
* Puts the network into `condition`: `"online"`, `"offline"`, a preset (`"edge"`, `"3g"`, `"4g"`, `"lte"`,
|
|
44
|
+
* `"wifi"`, `"slow"`, `"lossy"`) or a full `NetworkCondition`. A running flap stops first. On iOS the app must
|
|
45
|
+
* run with the warden network dylib: a suite that declares `conditions.network` launches it that way, else the
|
|
46
|
+
* call fails until you pass `{ relaunch: true }`.
|
|
47
|
+
*/
|
|
48
|
+
set(condition: NetworkInput, options?: NetworkSetOptions): Promise<NetworkState>;
|
|
49
|
+
/** Runs `fn` under `condition` and puts the previous condition (and flap) back after, also when `fn` throws. */
|
|
50
|
+
during<T>(condition: NetworkInput, fn: () => Promise<T> | T, options?: NetworkSetOptions): Promise<T>;
|
|
51
|
+
/** Alternates up and down until `stop()` or the end of the test; resolves once the first phase is in place. */
|
|
52
|
+
flap(options: FlapOptions): Promise<ConditionRun>;
|
|
53
|
+
/** Replaces the per-host faults (status, reset, delay, blackhole by host glob); `[]` clears. */
|
|
54
|
+
fault(rules: HostFault | readonly HostFault[]): Promise<{
|
|
55
|
+
notes: readonly string[];
|
|
56
|
+
}>;
|
|
57
|
+
status(): Promise<NetworkStatus>;
|
|
58
|
+
/** Stops a flap, clears the faults and returns to the suite's `conditions.network` (online when it has none). */
|
|
59
|
+
reset(): Promise<void>;
|
|
60
|
+
};
|
|
61
|
+
export type LocationPoint = {
|
|
62
|
+
lat: number;
|
|
63
|
+
lon: number;
|
|
64
|
+
altM?: number;
|
|
65
|
+
};
|
|
66
|
+
export type RouteOptions = {
|
|
67
|
+
speedMps?: number;
|
|
68
|
+
intervalS?: number;
|
|
69
|
+
loop?: boolean;
|
|
70
|
+
};
|
|
71
|
+
export type DeviceLocation = {
|
|
72
|
+
set(point: LocationPoint): Promise<void>;
|
|
73
|
+
/** Plays waypoints (or a `{ gpx }` file, relative to the working directory) at `speedMps`. */
|
|
74
|
+
route(points: readonly LatLon[] | {
|
|
75
|
+
gpx: string;
|
|
76
|
+
}, options?: RouteOptions): Promise<void>;
|
|
77
|
+
scenario(name: LocationScenario): Promise<void>;
|
|
78
|
+
clear(): Promise<void>;
|
|
79
|
+
};
|
|
80
|
+
/** An ISO string, a `Date` or epoch milliseconds. */
|
|
81
|
+
export type ClockInput = string | number | Date;
|
|
82
|
+
export type DeviceClock = {
|
|
83
|
+
/** Sets the device's wall clock; it runs on from there unless `frozen`. */
|
|
84
|
+
set(at: ClockInput, options?: {
|
|
85
|
+
frozen?: boolean;
|
|
86
|
+
}): Promise<void>;
|
|
87
|
+
/** Moves the clock on by `by` (`"2d"`, or ms). */
|
|
88
|
+
advance(by: Duration): Promise<void>;
|
|
89
|
+
/** Runs `fn` with the clock at `at`, then puts the previous clock back, also when `fn` throws. */
|
|
90
|
+
during<T>(at: ClockInput, fn: () => Promise<T> | T, options?: {
|
|
91
|
+
frozen?: boolean;
|
|
92
|
+
}): Promise<T>;
|
|
93
|
+
/** Back to the suite's `conditions.clock`, else real time. */
|
|
94
|
+
reset(): Promise<void>;
|
|
95
|
+
status(): ClockCondition;
|
|
96
|
+
};
|
|
97
|
+
export type DevicePermissions = {
|
|
98
|
+
/**
|
|
99
|
+
* Sets permissions of the app, per name (`location`, `camera`, `photos`, `contacts`, `notifications`, `microphone`,
|
|
100
|
+
* ...): `"granted"`, `"denied"` or `"unset"` (asks again). The result lists the names this platform skipped. Set
|
|
101
|
+
* them before `app.open()`: iOS terminates a running app when its permission changes.
|
|
102
|
+
*/
|
|
103
|
+
set(grants: PermissionGrants, options?: SettingOptions): Promise<PermissionsState>;
|
|
104
|
+
/** Puts every permission this test set back to the suite's `conditions.permissions`, else `"unset"`. */
|
|
105
|
+
reset(options?: SettingOptions): Promise<void>;
|
|
106
|
+
};
|
|
107
|
+
export type TimelineStepInput = {
|
|
108
|
+
/** when, from the start of the timeline */
|
|
109
|
+
at: Duration;
|
|
110
|
+
network?: NetworkInput;
|
|
111
|
+
location?: LocationCondition | LatLon;
|
|
112
|
+
};
|
|
113
|
+
/** What the runner needs of a device's conditions; reached through {@link conditionsOf}, not on `Device`. */
|
|
114
|
+
export type ConditionsInternals = {
|
|
115
|
+
/** the suite declares a network or clock condition, or one is active: iOS must launch with the dylib */
|
|
116
|
+
wantsInject(): boolean;
|
|
117
|
+
/** puts the suite's declared network, location and clock in place; warns once about areas not applied yet */
|
|
118
|
+
applyBaseline(): Promise<void>;
|
|
119
|
+
/** something changed or still runs: `teardown` has work to do */
|
|
120
|
+
dirty(): boolean;
|
|
121
|
+
/** stops flaps and timelines, and puts every area this device changed back to the baseline */
|
|
122
|
+
teardown(): Promise<void>;
|
|
123
|
+
/** the suite declares something to apply */
|
|
124
|
+
readonly declared: boolean;
|
|
125
|
+
};
|
|
126
|
+
/** `facing` of `device.camera.set` / `clear`: one camera, or `"both"`. */
|
|
127
|
+
export type CameraFacingChoice = CameraFacing | "both";
|
|
128
|
+
export type CameraSetOptions = {
|
|
129
|
+
/** which camera plays the asset; default `"back"` */
|
|
130
|
+
facing?: CameraFacingChoice;
|
|
131
|
+
/** video only: play again from the start at the end; default true */
|
|
132
|
+
loop?: boolean;
|
|
133
|
+
};
|
|
134
|
+
export type DeviceCamera = {
|
|
135
|
+
/**
|
|
136
|
+
* Feeds the png, jpg, heic, mp4 or mov at `path` (relative to the spec file) into the app's camera, replacing what
|
|
137
|
+
* that facing played; the other facing keeps its feed. The result says when it shows: `appliesOnLaunch` (iOS: an
|
|
138
|
+
* app launched without the camera sees it from the next launch), `appliesOnCameraOpen` (Android: the app's next
|
|
139
|
+
* camera open) or `rebooted` (Android: a video feed restarted the emulator). Android needs a `--camera` device
|
|
140
|
+
* (`E_CAMERA_DEVICE` otherwise); a missing or unsupported file fails with `E_CAMERA_NOT_FOUND` / `E_CAMERA_MEDIA`.
|
|
141
|
+
* Taken off when the test ends (back to the suite's `camera`).
|
|
142
|
+
*/
|
|
143
|
+
set(path: string, options?: CameraSetOptions): Promise<CameraState>;
|
|
144
|
+
/** Takes the feed off one camera, or both (the default). */
|
|
145
|
+
clear(facing?: CameraFacingChoice): Promise<CameraState>;
|
|
146
|
+
/** The feeds this spec has in place. */
|
|
147
|
+
status(): Promise<CameraState>;
|
|
148
|
+
};
|
|
149
|
+
export type DeviceConditions = {
|
|
150
|
+
camera: DeviceCamera;
|
|
151
|
+
network: DeviceNetwork;
|
|
152
|
+
location: DeviceLocation;
|
|
153
|
+
clock: DeviceClock;
|
|
154
|
+
permissions: DevicePermissions;
|
|
155
|
+
/** Device timezone (`"Asia/Tokyo"`); `null` puts it back. iOS: applies to the app launched next. */
|
|
156
|
+
timezone(tz: string | null): Promise<TimezoneState>;
|
|
157
|
+
/** The app's locale (`"fr-FR"`); `null` puts it back. iOS: applies to the app launched next. Android 13+. */
|
|
158
|
+
locale(tag: string | null, options?: SettingOptions): Promise<LocaleState>;
|
|
159
|
+
/** Text size (a name like `"accessibility-large"` or a scale like `1.5`), bold text, contrast, reduce motion; `null` resets. */
|
|
160
|
+
a11y(op: A11yOp | null): Promise<A11yState>;
|
|
161
|
+
/** Battery level and charging. Android emulators: real. iOS: the status bar only (`cosmetic: true`). `null` resets. */
|
|
162
|
+
battery(op: BatteryOp | null): Promise<BatteryState>;
|
|
163
|
+
/** Android: a critical trim-memory signal to the running app. iOS: `E_UNSUPPORTED` (no delivery mechanism works). */
|
|
164
|
+
memoryWarning(options?: SettingOptions): Promise<void>;
|
|
165
|
+
/** iOS: delivers an APNs payload (`{ aps: { alert: ... } }`) to the app. Android: `E_UNSUPPORTED`. */
|
|
166
|
+
push(payload: PushPayload, options?: SettingOptions): Promise<void>;
|
|
167
|
+
timeline(steps: readonly TimelineStepInput[]): Promise<ConditionRun>;
|
|
168
|
+
internals: ConditionsInternals;
|
|
169
|
+
};
|
|
170
|
+
export type DeviceConditionsDeps = {
|
|
171
|
+
driver: Driver;
|
|
172
|
+
env: E2eEnv;
|
|
173
|
+
/** the running attempt's signal, read per call */
|
|
174
|
+
signal: () => AbortSignal | undefined;
|
|
175
|
+
/** where condition events go (`results.jsonl`); default none */
|
|
176
|
+
sink?: ConditionSink;
|
|
177
|
+
/** time for flaps, timelines and event stamps */
|
|
178
|
+
tickerClock?: TickerClock;
|
|
179
|
+
/** notes (a restore that failed, areas not applied yet); default stderr */
|
|
180
|
+
log?: (line: string) => void;
|
|
181
|
+
/** where changes are written on the device's lease so release can undo them after a crash; default none */
|
|
182
|
+
recorder?: ConditionRecorder;
|
|
183
|
+
/** the warden home camera assets are staged under; default `$WARDEN_HOME` / `~/.warden` */
|
|
184
|
+
wardenHome?: string;
|
|
185
|
+
/** the directory a relative camera path is resolved against; default the calling spec file's, else `specsDir`, else cwd */
|
|
186
|
+
specDir?: () => string | undefined;
|
|
187
|
+
};
|
|
188
|
+
/** Ties a device (or a wrapper of it) to its conditions' runner hooks; `Device` itself does not expose them. */
|
|
189
|
+
export declare function registerConditions(device: object, internals: ConditionsInternals): void;
|
|
190
|
+
/** The runner hooks of a device made by `createDevice` (or a `traceDevice` wrapper of one). */
|
|
191
|
+
export declare function conditionsOf(device: object): ConditionsInternals | undefined;
|
|
192
|
+
/** The condition sink for a session: events append to its `results.jsonl`; none without one. */
|
|
193
|
+
export declare function conditionSinkFor(session: {
|
|
194
|
+
results?: {
|
|
195
|
+
path: string;
|
|
196
|
+
};
|
|
197
|
+
}): ConditionSink | undefined;
|
|
198
|
+
export declare function createDeviceConditions(deps: DeviceConditionsDeps): DeviceConditions;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { LatLon } from "../../../core/src/conditions/conditions.types";
|
|
2
|
+
/** Great-circle distance in metres. */
|
|
3
|
+
export declare function haversineM(a: LatLon, b: LatLon): number;
|
|
4
|
+
/** Initial bearing from `a` to `b`, degrees clockwise from north in [0, 360). */
|
|
5
|
+
export declare function bearingDeg(a: LatLon, b: LatLon): number;
|
|
6
|
+
/** The point a fraction `f` (0..1) of the way along the great circle from `a` to `b`. */
|
|
7
|
+
export declare function interpolate(a: LatLon, b: LatLon, f: number): LatLon;
|
|
8
|
+
export declare function pathLengthM(points: readonly LatLon[]): number;
|
|
9
|
+
/**
|
|
10
|
+
* One fix per interval along `points`: a fix every `speedMps * intervalS` metres of path, the first
|
|
11
|
+
* waypoint first and the last waypoint last (no duplicate when the length is an exact multiple).
|
|
12
|
+
*/
|
|
13
|
+
export declare function routeSteps(points: readonly LatLon[], speedMps: number, intervalS: number): LatLon[];
|
|
14
|
+
/** 7 decimals (about 1 cm), no trailing zeros, never an exponent. */
|
|
15
|
+
export declare function formatCoord(n: number): string;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { LatLon } from "../../../core/src/conditions/conditions.types";
|
|
2
|
+
import { type Result } from "../../../types/src/result";
|
|
3
|
+
/**
|
|
4
|
+
* Tolerant, dependency-free GPX reader: every `<trkpt>`, `<rtept>` and `<wpt>` with a valid `lat` and
|
|
5
|
+
* `lon` attribute, in document order. Comments are dropped, a point with a bad or missing coordinate is
|
|
6
|
+
* skipped, and truncated XML yields the points before the break. No points at all is an error.
|
|
7
|
+
*/
|
|
8
|
+
export declare function parseGpx(xml: string): Result<LatLon[]>;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { ConditionArea, ConditionValues } from "../../../core/src/conditions/conditions.types";
|
|
2
|
+
import { type PidAlive } from "../../../core/src/liveness";
|
|
3
|
+
import type { E2eEnv } from "../env";
|
|
4
|
+
import { type StoreOpener } from "./lease-store";
|
|
5
|
+
/**
|
|
6
|
+
* Writes what a spec changed on its device to the lease (`lease_conditions`), so `warden release` / gc can undo it
|
|
7
|
+
* when the spec process dies before its own teardown. Best effort: a failure is logged and never reaches the test.
|
|
8
|
+
*/
|
|
9
|
+
export type ConditionRecorder = {
|
|
10
|
+
/** the device now has `value` in `area` */
|
|
11
|
+
record<A extends ConditionArea>(area: A, value: ConditionValues[A]): void;
|
|
12
|
+
/**
|
|
13
|
+
* the device is back at its baseline `value`: when that is `neutral` (nothing to undo) the area is forgotten,
|
|
14
|
+
* unless a process is still held for it (a live shaping proxy), which release must still stop
|
|
15
|
+
*/
|
|
16
|
+
settle<A extends ConditionArea>(area: A, value: ConditionValues[A], neutral: boolean): void;
|
|
17
|
+
};
|
|
18
|
+
export type LeaseRecorderOptions = {
|
|
19
|
+
leaseId: string;
|
|
20
|
+
openStore?: StoreOpener;
|
|
21
|
+
now?: () => number;
|
|
22
|
+
log?: (line: string) => void;
|
|
23
|
+
pidAlive?: PidAlive;
|
|
24
|
+
};
|
|
25
|
+
export declare function createLeaseRecorder(o: LeaseRecorderOptions): ConditionRecorder;
|
|
26
|
+
/** The recorder for a spec under a lease; none without `WARDEN_LEASE_ID` (a bare `bun test` has no lease to record on). */
|
|
27
|
+
export declare function leaseRecorderFor(env: Pick<E2eEnv, "leaseId">): ConditionRecorder | undefined;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type Store } from "../../../core/src/store";
|
|
2
|
+
import { type Result } from "../../../types/src/result";
|
|
3
|
+
/** Opens the lease store; a failure is a `Result`, never a throw. */
|
|
4
|
+
export type StoreOpener = () => Result<Store>;
|
|
5
|
+
/**
|
|
6
|
+
* The spec process's one connection to the lease store, opened on first use: a run that sets no condition and
|
|
7
|
+
* shapes no network never opens it. Connections are kept per db path for the life of the process.
|
|
8
|
+
*/
|
|
9
|
+
export declare function sharedStoreOpener(env?: Readonly<Record<string, string | undefined>>): StoreOpener;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { LatLon, LocationCondition } from "../../../core/src/conditions/conditions.types";
|
|
2
|
+
import { type Result } from "../../../types/src/result";
|
|
3
|
+
export type ReadText = (path: string) => Promise<string>;
|
|
4
|
+
export type RoutePlan = {
|
|
5
|
+
points: readonly LatLon[];
|
|
6
|
+
speedMps: number;
|
|
7
|
+
intervalS: number;
|
|
8
|
+
loop: boolean;
|
|
9
|
+
};
|
|
10
|
+
export declare function loadGpxPoints(path: string, readText: ReadText): Promise<Result<LatLon[]>>;
|
|
11
|
+
/**
|
|
12
|
+
* Everything a ticker needs for the moving location ops (`route`, `gpx`, `scenario`): waypoints, pace,
|
|
13
|
+
* loop. Used where the platform has no player of its own (Android); iOS hands the waypoints to simctl.
|
|
14
|
+
*/
|
|
15
|
+
export declare function planRoute(op: Extract<LocationCondition, {
|
|
16
|
+
kind: "route" | "gpx" | "scenario";
|
|
17
|
+
}>, readText: ReadText): Promise<Result<RoutePlan>>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { LatLon, LocationScenario } from "../../../core/src/conditions/conditions.types";
|
|
2
|
+
/**
|
|
3
|
+
* Apple's names for the three simulated-location scenarios `simctl location <udid> run` knows (verify
|
|
4
|
+
* with `xcrun simctl location <udid> list`).
|
|
5
|
+
*/
|
|
6
|
+
export declare const IOS_SCENARIO_NAMES: Record<LocationScenario, string>;
|
|
7
|
+
export type ScenarioRoute = {
|
|
8
|
+
points: readonly LatLon[];
|
|
9
|
+
speedMps: number;
|
|
10
|
+
loop: boolean;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Android parity: a similar path and pace for each scenario, played by the route ticker. These are
|
|
14
|
+
* warden's own waypoints around Apple Park, Cupertino, not Apple's data: same character (walking
|
|
15
|
+
* pace loop, cycling loop, highway stretch), not identical coordinates.
|
|
16
|
+
*/
|
|
17
|
+
export declare const ANDROID_SCENARIOS: Record<LocationScenario, ScenarioRoute>;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { LatLon } from "../../../core/src/conditions/conditions.types";
|
|
2
|
+
import { type Result } from "../../../types/src/result";
|
|
3
|
+
/** simctl's own default pace, so a route moves the same on both platforms. */
|
|
4
|
+
export declare const DEFAULT_SPEED_MPS = 20;
|
|
5
|
+
export declare const DEFAULT_INTERVAL_S = 1;
|
|
6
|
+
export type TickerClock = {
|
|
7
|
+
now(): number;
|
|
8
|
+
/** resolves after `ms`, or as soon as `signal` aborts */
|
|
9
|
+
sleep(ms: number, signal: AbortSignal): Promise<void>;
|
|
10
|
+
};
|
|
11
|
+
export declare const realTickerClock: TickerClock;
|
|
12
|
+
export type TickerRoute = {
|
|
13
|
+
points: readonly LatLon[];
|
|
14
|
+
speedMps?: number;
|
|
15
|
+
intervalS?: number;
|
|
16
|
+
loop?: boolean;
|
|
17
|
+
};
|
|
18
|
+
export type TickerDeps = TickerClock & {
|
|
19
|
+
/** send one fix; return an error string (or throw) to count a failure, nothing when it landed */
|
|
20
|
+
fix(point: LatLon): Promise<string | undefined>;
|
|
21
|
+
};
|
|
22
|
+
export type TickerOutcome = {
|
|
23
|
+
ticks: number;
|
|
24
|
+
stopped: "finished" | "aborted";
|
|
25
|
+
} | {
|
|
26
|
+
ticks: number;
|
|
27
|
+
stopped: "failed";
|
|
28
|
+
error: string;
|
|
29
|
+
};
|
|
30
|
+
export type RouteTicker = {
|
|
31
|
+
/** idempotent; the loop exits before sending another fix */
|
|
32
|
+
stop(): void;
|
|
33
|
+
done: Promise<TickerOutcome>;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Plays `route` as one fix per `intervalS`, paced against the clock (a slow fix shortens the next
|
|
37
|
+
* sleep). Runs in this process: specs hold the handle and `stop()` it, a detached child runs the same
|
|
38
|
+
* loop from `route-ticker.main.ts`. Ends when the route is done (never, with `loop`), when stopped, or
|
|
39
|
+
* after {@link MAX_CONSECUTIVE_FAILURES} failed fixes in a row.
|
|
40
|
+
*/
|
|
41
|
+
export declare function startRouteTicker(route: TickerRoute, deps: TickerDeps): RouteTicker;
|
|
42
|
+
/** What the detached child needs: the adb to call and the route to play. */
|
|
43
|
+
export type TickerConfig = TickerRoute & {
|
|
44
|
+
adb: string;
|
|
45
|
+
serial: string;
|
|
46
|
+
};
|
|
47
|
+
export declare function parseTickerConfig(raw: string): Result<TickerConfig>;
|
|
48
|
+
/**
|
|
49
|
+
* argv that runs the ticker as its own process: `[runtime, route-ticker.main.ts, <config JSON>]`. The
|
|
50
|
+
* caller spawns it detached and stores the child's pid as an `OwnedProcess` with `recordCondition`, so
|
|
51
|
+
* COND-1 undo (`warden release`, gc) kills it.
|
|
52
|
+
*/
|
|
53
|
+
export declare function detachedTickerArgv(runtime: string, mainPath: string, config: TickerConfig): string[];
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { LocationCondition, NetworkCondition } from "../../../core/src/conditions/conditions.types";
|
|
2
|
+
import type { ConditionSink } from "./condition-events";
|
|
3
|
+
import type { TickerClock } from "./route-ticker";
|
|
4
|
+
export type StepResult = {
|
|
5
|
+
success: true;
|
|
6
|
+
} | {
|
|
7
|
+
success: false;
|
|
8
|
+
error: {
|
|
9
|
+
message: string;
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
/** The slice of a `Driver` the scheduler drives. A driver satisfies it; the detached child builds a smaller one. */
|
|
13
|
+
export type SchedulerTarget = {
|
|
14
|
+
network?(condition: NetworkCondition): Promise<StepResult>;
|
|
15
|
+
location?(op: LocationCondition): Promise<StepResult>;
|
|
16
|
+
};
|
|
17
|
+
export type SchedulerDeps = TickerClock & {
|
|
18
|
+
/** uniform [0, 1) for jitter; default is a generator seeded from `seed` */
|
|
19
|
+
rng?: () => number;
|
|
20
|
+
/** told about every transition that landed */
|
|
21
|
+
sink?: ConditionSink;
|
|
22
|
+
};
|
|
23
|
+
export type FlapOptions = {
|
|
24
|
+
upMs: number;
|
|
25
|
+
downMs: number;
|
|
26
|
+
/** each phase lasts its length +/- this percent, drawn from the seeded rng */
|
|
27
|
+
jitterPct?: number;
|
|
28
|
+
/** the "up" condition, default online; also what stop restores */
|
|
29
|
+
base?: NetworkCondition;
|
|
30
|
+
seed?: number;
|
|
31
|
+
/** false when the caller already put the device in `base`; the first phase then just waits */
|
|
32
|
+
applyInitial?: boolean;
|
|
33
|
+
};
|
|
34
|
+
export type TimelineStep = {
|
|
35
|
+
atMs: number;
|
|
36
|
+
network?: NetworkCondition;
|
|
37
|
+
location?: LocationCondition;
|
|
38
|
+
};
|
|
39
|
+
export type SchedulerOutcome = {
|
|
40
|
+
transitions: number;
|
|
41
|
+
stopped: "finished" | "aborted";
|
|
42
|
+
} | {
|
|
43
|
+
transitions: number;
|
|
44
|
+
stopped: "failed";
|
|
45
|
+
error: string;
|
|
46
|
+
};
|
|
47
|
+
export type SchedulerStatus = {
|
|
48
|
+
state: "running";
|
|
49
|
+
transitions: number;
|
|
50
|
+
phase?: "up" | "down";
|
|
51
|
+
step?: number;
|
|
52
|
+
} | {
|
|
53
|
+
state: "finished" | "stopped";
|
|
54
|
+
transitions: number;
|
|
55
|
+
} | {
|
|
56
|
+
state: "failed";
|
|
57
|
+
transitions: number;
|
|
58
|
+
error: string;
|
|
59
|
+
};
|
|
60
|
+
export type Scheduler = {
|
|
61
|
+
/** idempotent; flap puts `base` back before `done` resolves, a timeline just stops */
|
|
62
|
+
stop(): void;
|
|
63
|
+
/** resolves with the outcome, never rejects */
|
|
64
|
+
done: Promise<SchedulerOutcome>;
|
|
65
|
+
current(): SchedulerStatus;
|
|
66
|
+
};
|
|
67
|
+
/** mulberry32: small, fast, and the same sequence for the same seed. */
|
|
68
|
+
export declare function seededRng(seed: number): () => number;
|
|
69
|
+
/**
|
|
70
|
+
* Alternates the network between `base` (up, `upMs`) and offline (down, `downMs`), each phase stretched or
|
|
71
|
+
* shrunk by up to `jitterPct` using a seeded rng, paced against the clock. Starts in the up phase. In this
|
|
72
|
+
* process: the driver holds the handle and stops it on the next network op or `close()`; a detached child runs
|
|
73
|
+
* the same loop (`scheduler.main.ts`). 3 failed transitions in a row end it as `failed`. However it ends, `base`
|
|
74
|
+
* is put back before `done` resolves (best effort when it ended on a failure).
|
|
75
|
+
*/
|
|
76
|
+
export declare function startFlap(target: SchedulerTarget, options: FlapOptions, deps: SchedulerDeps): Scheduler;
|
|
77
|
+
/**
|
|
78
|
+
* Runs `steps` in `atMs` order (ties keep the given order), each at `atMs` after the start, paced against the
|
|
79
|
+
* clock. A step with both `network` and `location` applies the network first, then the location. A step counts
|
|
80
|
+
* as failed when either part fails; 3 failed steps in a row end it as `failed`. Stopping leaves the device as
|
|
81
|
+
* the last step left it (restoring is the spec API's job: it knows the previous conditions).
|
|
82
|
+
*/
|
|
83
|
+
export declare function startTimeline(target: SchedulerTarget, steps: readonly TimelineStep[], deps: SchedulerDeps): Scheduler;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import type { TextSize } from "../../core/src/conditions/conditions.types";
|
|
2
|
+
import type { Result } from "../../types/src/result";
|
|
3
|
+
import type { A11yAuditOptions, A11yAuditResult, A11yEventSource, Announcement, ScreenReaderState, SpokenLine } from "./a11y/a11y.types";
|
|
4
|
+
import type { A11yTarget } from "./a11y/a11y-target";
|
|
5
|
+
import type { Driver, DriverError, Snapshot } from "./driver/driver.types";
|
|
6
|
+
import type { E2eEnv } from "./env";
|
|
7
|
+
/** `device.a11yAudit(opts)`: the audit's own options, plus what only a device knows. */
|
|
8
|
+
export type DeviceA11yAuditOptions = A11yAuditOptions & {
|
|
9
|
+
/** the text size the app runs at; `text-clipped` only runs from `accessibility-medium` up */
|
|
10
|
+
textSize?: TextSize;
|
|
11
|
+
/** also run the platform's own audit (XCUITest on iOS, ATF on Android) when the driver can. Default true. */
|
|
12
|
+
native?: boolean;
|
|
13
|
+
};
|
|
14
|
+
/** An announcement or live-region change the app made, in the order it happened. */
|
|
15
|
+
export type A11yAnnouncement = {
|
|
16
|
+
kind: "announcement" | "live-region";
|
|
17
|
+
text: string;
|
|
18
|
+
/** epoch ms on the device */
|
|
19
|
+
at: number;
|
|
20
|
+
source: A11yEventSource;
|
|
21
|
+
};
|
|
22
|
+
export type A11yAnnouncementsOptions = {
|
|
23
|
+
/** cursor from an earlier read: only what came after it */
|
|
24
|
+
since?: number;
|
|
25
|
+
};
|
|
26
|
+
export type A11yAnnouncements = {
|
|
27
|
+
announcements: A11yAnnouncement[];
|
|
28
|
+
/** pass back as `since` to read only what is new */
|
|
29
|
+
cursor: number;
|
|
30
|
+
/** the Android event buffer overwrote events this caller never read */
|
|
31
|
+
lost?: boolean;
|
|
32
|
+
};
|
|
33
|
+
export type ScreenReaderSpokenOptions = {
|
|
34
|
+
since?: number;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* What a screen reader does with the screen. `order()` is warden's model of the reading order and works on both
|
|
38
|
+
* platforms (every `Announcement` is `source: "simulated"`: an approximation, never what VoiceOver or TalkBack
|
|
39
|
+
* say). The other calls drive a real TalkBack on an Android emulator (`device.a11y({ screenReader: true })`) and
|
|
40
|
+
* throw `ACTION_FAILED` (`E_UNSUPPORTED`) anywhere else.
|
|
41
|
+
*/
|
|
42
|
+
export type DeviceScreenReader = {
|
|
43
|
+
/** reading order of the current screen */
|
|
44
|
+
order(): Promise<Announcement[]>;
|
|
45
|
+
state(): Promise<ScreenReaderState>;
|
|
46
|
+
/** moves screen-reader focus one step on; resolves what it now holds (`undefined` past the end) */
|
|
47
|
+
next(): Promise<Announcement | undefined>;
|
|
48
|
+
prev(): Promise<Announcement | undefined>;
|
|
49
|
+
/** double-tap on the focused node */
|
|
50
|
+
activate(): Promise<void>;
|
|
51
|
+
/** the node holding screen-reader focus, if any */
|
|
52
|
+
focused(): Promise<Announcement | undefined>;
|
|
53
|
+
/** what the reader said since `since`; `cursor` goes back in as the next `since` */
|
|
54
|
+
spoken(options?: ScreenReaderSpokenOptions): Promise<{
|
|
55
|
+
lines: SpokenLine[];
|
|
56
|
+
cursor: number;
|
|
57
|
+
}>;
|
|
58
|
+
};
|
|
59
|
+
export type DeviceA11y = {
|
|
60
|
+
/**
|
|
61
|
+
* Audits the current screen: warden's rules over the snapshot (with the iOS injected read merged in when the app
|
|
62
|
+
* runs with it) plus the platform's own audit when the driver offers one. The suite's `a11y` config
|
|
63
|
+
* (`warden.config.ts`) supplies the defaults; options passed here win key by key.
|
|
64
|
+
*/
|
|
65
|
+
a11yAudit(options?: DeviceA11yAuditOptions): Promise<A11yAuditResult>;
|
|
66
|
+
readonly screenReader: DeviceScreenReader;
|
|
67
|
+
/** announcements and live-region changes since `since` (needs the injected library on iOS) */
|
|
68
|
+
announcements(options?: A11yAnnouncementsOptions): Promise<A11yAnnouncements>;
|
|
69
|
+
};
|
|
70
|
+
export type DeviceA11yDeps = {
|
|
71
|
+
driver: Driver;
|
|
72
|
+
env: Pick<E2eEnv, "a11yAudit" | "assertionTimeoutMs"> & {
|
|
73
|
+
bundleId?: string;
|
|
74
|
+
};
|
|
75
|
+
/** read per call: the runner swaps it for every attempt */
|
|
76
|
+
signal: () => AbortSignal | undefined;
|
|
77
|
+
step?: <T>(label: string, fn: () => Promise<T>) => Promise<T>;
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* The snapshot with the screen-reader facts a plain snapshot lacks merged in. A driver without the capability, or an
|
|
81
|
+
* app launched without the injected library (`E_UNSUPPORTED`), leaves it as it is. Any other failure is returned.
|
|
82
|
+
*/
|
|
83
|
+
export declare function withA11yRead(driver: Driver, snapshot: Snapshot): Promise<Result<Snapshot, DriverError>>;
|
|
84
|
+
/** The screen as text with the `--a11y` columns: what an a11y failure attaches as `screen-at-failure.txt`. */
|
|
85
|
+
export declare function renderA11yScreen(snapshot: Snapshot, maxLines?: number): string;
|
|
86
|
+
export type AuditSnapshotOptions = DeviceA11yAuditOptions & {
|
|
87
|
+
/** the app the platform's own audit checks (iOS XCUITest) */
|
|
88
|
+
bundleId?: string;
|
|
89
|
+
};
|
|
90
|
+
/**
|
|
91
|
+
* Audits one snapshot: warden's rules, plus the platform's own audit (XCUITest / ATF) unless `native: false`, `rules`
|
|
92
|
+
* leaves out `native`, or the driver has none. `device.a11yAudit` and `warden ui a11y audit` share this.
|
|
93
|
+
*/
|
|
94
|
+
export declare function auditSnapshot(driver: Driver, snapshot: Snapshot, options?: AuditSnapshotOptions): Promise<A11yAuditResult>;
|
|
95
|
+
export declare function createDeviceA11y(deps: DeviceA11yDeps): DeviceA11y & {
|
|
96
|
+
target: A11yTarget;
|
|
97
|
+
};
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import { type ConditionRun, type DeviceCamera, type DeviceClock, type DeviceConditions, type DeviceLocation, type DeviceNetwork, type DevicePermissions, type TimelineStepInput } from "./conditions/device-conditions";
|
|
2
|
+
import { type ConditionRecorder } from "./conditions/lease-recorder";
|
|
3
|
+
import { type DeviceA11y } from "./device-a11y";
|
|
1
4
|
import { type DeviceDevtools } from "./device-devtools";
|
|
2
|
-
import type { Driver, Orientation } from "./driver/driver.types";
|
|
5
|
+
import type { BiometricState, Driver, Orientation } from "./driver/driver.types";
|
|
3
6
|
import { type SettleDetail } from "./driver/settle";
|
|
4
7
|
import type { E2eEnv } from "./env";
|
|
5
8
|
import { type Clock, type FixtureSession } from "./fixture-session";
|
|
@@ -22,6 +25,8 @@ export type OpenLinkOptions = {
|
|
|
22
25
|
};
|
|
23
26
|
export type SnapshotTextOptions = {
|
|
24
27
|
interactiveOnly?: boolean;
|
|
28
|
+
/** append hint, actions, focus and live-region facts to each line (the iOS injected read is merged in when available) */
|
|
29
|
+
a11y?: boolean;
|
|
25
30
|
};
|
|
26
31
|
export type SettleOptions = {
|
|
27
32
|
/** how long the UI must stay unchanged. Default 250 ms. */
|
|
@@ -29,12 +34,33 @@ export type SettleOptions = {
|
|
|
29
34
|
/** give up after this long. Default 3000 ms. */
|
|
30
35
|
timeout?: number;
|
|
31
36
|
};
|
|
32
|
-
export type
|
|
37
|
+
export type { BiometricState };
|
|
38
|
+
/**
|
|
39
|
+
* Simulated Face ID / Touch ID / fingerprint (simulators and emulators only). Every method answers the state after the op.
|
|
40
|
+
* iOS: the first prompt per app shows an "Allow X to use Face ID?" alert; answer it with `device.alert("accept")`.
|
|
41
|
+
* Enroll before installing or launching the app: a new enrollment invalidates biometry-bound keys.
|
|
42
|
+
* Android: `enroll` / `unenroll` throw `ACTION_FAILED` (driver `E_UNSUPPORTED`); enrollment comes from the golden snapshot, so set
|
|
43
|
+
* the profile's `biometrics: true`.
|
|
44
|
+
*/
|
|
45
|
+
export type DeviceBiometrics = {
|
|
46
|
+
status(): Promise<BiometricState>;
|
|
47
|
+
/** iOS: enrolls Face ID / Touch ID. Android: unsupported, use profile `biometrics: true`. */
|
|
48
|
+
enroll(): Promise<BiometricState>;
|
|
49
|
+
/** iOS: removes the enrollment. Android: unsupported. */
|
|
50
|
+
unenroll(): Promise<BiometricState>;
|
|
51
|
+
/** Presents a matching face / finger to the pending prompt. */
|
|
52
|
+
match(): Promise<BiometricState>;
|
|
53
|
+
/** Presents a non-matching face / finger to the pending prompt. */
|
|
54
|
+
fail(): Promise<BiometricState>;
|
|
55
|
+
};
|
|
56
|
+
export type Device = DeviceA11y & {
|
|
33
57
|
/**
|
|
34
58
|
* Answers the system alert / sheet on top. `accept` presses the first present of OK, Allow, Open,
|
|
35
59
|
* Continue, Yes, Done, Allow While Using App, Allow Once (else the last button); `dismiss` the first
|
|
36
60
|
* of Cancel, Don't Allow, Not Now, Dismiss, No, Close (else the first button); `{button}` the button
|
|
37
|
-
* with that name (`ACTION_FAILED` when absent).
|
|
61
|
+
* with that name (`ACTION_FAILED` when absent). The Android autofill "Save password?" sheet is closed with Back for
|
|
62
|
+
* `accept` and `dismiss` alike (never its Never / Continue buttons, which write to the user's account). Resolves false
|
|
63
|
+
* when no alert is showing.
|
|
38
64
|
*/
|
|
39
65
|
alert(action: AlertAction): Promise<boolean>;
|
|
40
66
|
/**
|
|
@@ -50,6 +76,8 @@ export type Device = {
|
|
|
50
76
|
back(): Promise<void>;
|
|
51
77
|
orientation(set?: Orientation): Promise<Orientation>;
|
|
52
78
|
appearance(set?: "light" | "dark"): Promise<"light" | "dark">;
|
|
79
|
+
/** Simulated biometrics: `status`, `enroll`, `unenroll`, `match`, `fail`; see {@link DeviceBiometrics}. */
|
|
80
|
+
readonly biometrics: DeviceBiometrics;
|
|
53
81
|
clipboard(): Promise<string>;
|
|
54
82
|
setClipboard(text: string): Promise<void>;
|
|
55
83
|
/**
|
|
@@ -78,6 +106,37 @@ export type Device = {
|
|
|
78
106
|
* typed report. The report is copied under the attempt's `perf/`; budgets from `warden.config.ts` fail `stop`.
|
|
79
107
|
*/
|
|
80
108
|
readonly perf: DevicePerf;
|
|
109
|
+
/**
|
|
110
|
+
* Network conditions: `set("offline" | "3g" | …)`, `during`, `flap`, `fault`, `status`, `reset`. Everything a test
|
|
111
|
+
* changes is put back when it ends, to the suite's `conditions` (`warden.config.ts`).
|
|
112
|
+
*/
|
|
113
|
+
readonly network: DeviceNetwork;
|
|
114
|
+
/** Simulated location: `set`, `route`, `scenario`, `clear`. Put back when the test ends. */
|
|
115
|
+
readonly location: DeviceLocation;
|
|
116
|
+
/** The device's wall clock: `set`, `advance("2d")`, `during`, `reset`. Needs a driver with a clock op. */
|
|
117
|
+
readonly clock: DeviceClock;
|
|
118
|
+
/** Runtime permissions of the app: `set({ location: "denied", camera: "granted" })`, `reset`. Set before `app.open()`. */
|
|
119
|
+
readonly permissions: DevicePermissions;
|
|
120
|
+
/** Device timezone (`"Asia/Tokyo"`, `null` resets). iOS: for the app launched next. */
|
|
121
|
+
timezone: DeviceConditions["timezone"];
|
|
122
|
+
/** The app's locale (`"fr-FR"`, `null` resets). iOS: for the app launched next. Android 13+. */
|
|
123
|
+
locale: DeviceConditions["locale"];
|
|
124
|
+
/** Text size, bold text, contrast, reduce motion (`null` resets); the result names what the platform skipped. */
|
|
125
|
+
a11y: DeviceConditions["a11y"];
|
|
126
|
+
/** Battery level and charging: real on Android emulators, status bar only on iOS (`cosmetic: true`). */
|
|
127
|
+
battery: DeviceConditions["battery"];
|
|
128
|
+
/**
|
|
129
|
+
* Simulated camera: `set("fixtures/qr.png", { facing })`, `clear`, `status`. The asset (png, jpg, heic, mp4, mov; path relative
|
|
130
|
+
* to the spec file) plays in the app's real camera. iOS: live in an app launched with the camera, else from the next launch.
|
|
131
|
+
* Android: a `--camera` device; an image shows on the next camera open, a video feed reboots the emulator.
|
|
132
|
+
*/
|
|
133
|
+
readonly camera: DeviceCamera;
|
|
134
|
+
/** Android: a critical trim-memory signal to the app. iOS: `E_UNSUPPORTED`. */
|
|
135
|
+
memoryWarning: DeviceConditions["memoryWarning"];
|
|
136
|
+
/** iOS: delivers an APNs payload to the app. Android: `E_UNSUPPORTED`. */
|
|
137
|
+
push: DeviceConditions["push"];
|
|
138
|
+
/** Applies network and location changes at times from now; stopped when the test ends. */
|
|
139
|
+
timeline(steps: readonly TimelineStepInput[]): Promise<ConditionRun>;
|
|
81
140
|
};
|
|
82
141
|
export type DeviceDeps = {
|
|
83
142
|
session: FixtureSession;
|
|
@@ -86,5 +145,9 @@ export type DeviceDeps = {
|
|
|
86
145
|
clock?: Clock;
|
|
87
146
|
/** runs `warden …` for `device.perf`; default spawns `WARDEN_BIN` */
|
|
88
147
|
perfExec?: PerfExec;
|
|
148
|
+
/** shared with `app` so a launch knows to load the network dylib; default: made here */
|
|
149
|
+
conditions?: DeviceConditions;
|
|
89
150
|
};
|
|
90
|
-
export declare function createDevice({ session, driver, env, clock, perfExec }: DeviceDeps): Device;
|
|
151
|
+
export declare function createDevice({ session, driver, env, clock, perfExec, conditions, }: DeviceDeps): Device;
|
|
152
|
+
/** The conditions of one attempt's device: events go to the session's `results.jsonl`, calls honour its signal. */
|
|
153
|
+
export declare function createSessionConditions(session: FixtureSession, driver: Driver, env: E2eEnv, recorder?: ConditionRecorder | undefined): DeviceConditions;
|