@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,36 @@
|
|
|
1
|
+
import type { Exec } from "../../../../core/src/exec";
|
|
2
|
+
import type { RunnerSourceFiles } from "../../../../core/src/ios-runner/runner-build.types";
|
|
3
|
+
import { type AsyncResult } from "../../../../types/src/result";
|
|
4
|
+
import type { Logger } from "../driver.types";
|
|
5
|
+
import type { NativeAuditOptions, NativeAuditResult } from "../native-audit";
|
|
6
|
+
import { type SimRunnerDeps } from "./sim-runner";
|
|
7
|
+
/**
|
|
8
|
+
* The iOS Simulator's XCUITest accessibility audit: the runner build (cached, unsigned), the runner process and the
|
|
9
|
+
* `audit` op behind `AuditsNatively`. Without runner sources the audit is skipped with the reason.
|
|
10
|
+
*/
|
|
11
|
+
export type IosNativeAudit = {
|
|
12
|
+
audit(options: NativeAuditOptions): Promise<NativeAuditResult>;
|
|
13
|
+
close(): Promise<void>;
|
|
14
|
+
};
|
|
15
|
+
export type IosNativeAuditOptions = {
|
|
16
|
+
udid: string;
|
|
17
|
+
exec: Exec;
|
|
18
|
+
/** the app the audit checks when the caller names none */
|
|
19
|
+
bundleId: () => string | undefined;
|
|
20
|
+
env?: Record<string, string | undefined>;
|
|
21
|
+
wardenHome?: string;
|
|
22
|
+
version?: string;
|
|
23
|
+
log?: Logger;
|
|
24
|
+
/** the runner's Xcode project as text files; default: this checkout's `packages/e2e/native` when running from source */
|
|
25
|
+
runnerSources?: () => AsyncResult<RunnerSourceFiles>;
|
|
26
|
+
/** test seams */
|
|
27
|
+
ensureBuild?: SimRunnerDeps["ensureBuild"];
|
|
28
|
+
spawn?: SimRunnerDeps["spawn"];
|
|
29
|
+
connect?: SimRunnerDeps["connect"];
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* `packages/e2e/native` of the checkout this file runs from; in a bundled install (no checkout beside it) the blob
|
|
33
|
+
* embedded in the compiled `warden` binary, which a spec process (the same binary) sees too.
|
|
34
|
+
*/
|
|
35
|
+
export declare function checkoutRunnerSources(importDir?: string, embedded?: () => AsyncResult<RunnerSourceFiles>): AsyncResult<RunnerSourceFiles>;
|
|
36
|
+
export declare function createIosNativeAudit(o: IosNativeAuditOptions): IosNativeAudit;
|
|
@@ -1,9 +1,19 @@
|
|
|
1
1
|
import type { Exec } from "../../../../core/src/exec";
|
|
2
2
|
import type { E2eEnv } from "../../env";
|
|
3
|
+
import type { ReadsA11yEvents } from "../a11y-events";
|
|
4
|
+
import type { ReadsA11y } from "../a11y-read";
|
|
3
5
|
import type { ProbesFrontmost, ReadsAlertOn } from "../alerts";
|
|
6
|
+
import type { ClockRecorder } from "../clock-recorder";
|
|
4
7
|
import type { Driver, DriverResult, KeyName, Logger } from "../driver.types";
|
|
8
|
+
import type { AuditsNatively } from "../native-audit";
|
|
9
|
+
import type { NetworkProxyPort } from "../network-proxy";
|
|
5
10
|
import type { PastesText, SelectsAll } from "../typing";
|
|
11
|
+
import { type InjectFs } from "./inject";
|
|
12
|
+
import { type IosA11y } from "./ios-a11y";
|
|
13
|
+
import { type IosNativeAudit, type IosNativeAuditOptions } from "./ios-audit";
|
|
14
|
+
import { type IosNetwork } from "./ios-network";
|
|
6
15
|
import { type SwappableClient } from "./ios-session";
|
|
16
|
+
import { type IosSettings, type StagedSettings } from "./ios-settings";
|
|
7
17
|
import { type Simctl } from "./simctl";
|
|
8
18
|
import { type SimdClient } from "./simd-client";
|
|
9
19
|
import type { SimdSpawn } from "./simd-transport";
|
|
@@ -16,7 +26,7 @@ import type { SimdSpawn } from "./simd-transport";
|
|
|
16
26
|
*/
|
|
17
27
|
export type IosDriverEnv = Pick<E2eEnv, "simd" | "simdSocket" | "helperDir">;
|
|
18
28
|
/** The iOS driver is a `Driver` plus the optional capabilities of `driver/alerts.ts`. */
|
|
19
|
-
export type IosDriver = Driver & SelectsAll & PastesText & ReadsAlertOn & ProbesFrontmost;
|
|
29
|
+
export type IosDriver = Driver & SelectsAll & PastesText & ReadsAlertOn & ProbesFrontmost & ReadsA11y & ReadsA11yEvents & AuditsNatively;
|
|
20
30
|
export type OpenIosDriverOptions = {
|
|
21
31
|
udid: string;
|
|
22
32
|
env?: IosDriverEnv;
|
|
@@ -46,8 +56,30 @@ export type OpenIosDriverOptions = {
|
|
|
46
56
|
defaultTimeoutMs?: number;
|
|
47
57
|
/** log each helper op and simctl call with its duration (`WARDEN_E2E_DEBUG=1`) */
|
|
48
58
|
debug?: boolean;
|
|
59
|
+
/**
|
|
60
|
+
* The lease's shaping proxy (`leaseNetworkProxy`). Without it `network` / `networkFaults` answer `E_UNSUPPORTED`:
|
|
61
|
+
* a driver opened by a bare caller has no lease to hang a proxy on.
|
|
62
|
+
*/
|
|
63
|
+
networkProxy?: NetworkProxyPort;
|
|
64
|
+
/** told about every successful `clock` op, so the lease records the `clock` area (`leaseClockRecorder`) */
|
|
65
|
+
clockRecorder?: ClockRecorder;
|
|
66
|
+
/** the session's app (`WARDEN_BUNDLE_ID`): the default target of permissions, push and locale */
|
|
67
|
+
bundleId?: string;
|
|
68
|
+
/** where a staged timezone / locale is kept between processes (default: `<wardenHome>/launch-settings/<udid>.json`) */
|
|
69
|
+
stagedSettings?: StagedSettings;
|
|
70
|
+
/** state-file operations of the injected dylib's control channel, injected for tests */
|
|
71
|
+
injectFs?: InjectFs;
|
|
72
|
+
/**
|
|
73
|
+
* Launch apps with the injected library so `readA11y` works (hint, custom actions, order). Default: `WARDEN_A11Y=1`.
|
|
74
|
+
* Off, a launch pays nothing for it.
|
|
75
|
+
*/
|
|
76
|
+
a11y?: boolean;
|
|
49
77
|
/** CoreSimulator devices directory for the host-side `AutomationEnabled` check (default `~/Library/Developer/CoreSimulator/Devices`) */
|
|
50
78
|
devicesRoot?: string;
|
|
79
|
+
/** the runner's Xcode project as text files for the XCUITest audit (default: this checkout when run from source) */
|
|
80
|
+
runnerSources?: IosNativeAuditOptions["runnerSources"];
|
|
81
|
+
/** test seams for the XCUITest audit's runner */
|
|
82
|
+
nativeAudit?: Pick<IosNativeAuditOptions, "ensureBuild" | "spawn" | "connect">;
|
|
51
83
|
};
|
|
52
84
|
/** USB HID keyboard-page usage ids for the keys `Driver.key` offers. */
|
|
53
85
|
export declare const KEY_USAGE: Readonly<Record<KeyName, number>>;
|
|
@@ -56,6 +88,23 @@ export declare function createIosDriver(swap: SwappableClient, simctl: Simctl, o
|
|
|
56
88
|
now: () => number;
|
|
57
89
|
sleep: (ms: number) => Promise<void>;
|
|
58
90
|
log?: Logger;
|
|
91
|
+
/** told about every successful `clock` op */
|
|
92
|
+
clockRecorder?: ClockRecorder;
|
|
93
|
+
/** device settings (`ios-settings.ts`); absent: those ops answer `E_UNSUPPORTED` */
|
|
94
|
+
settings?: (ctx: {
|
|
95
|
+
lastBundle: () => string | undefined;
|
|
96
|
+
}) => IosSettings;
|
|
97
|
+
/** screen-reader facts from the injected dylib (`ios-a11y.ts`); absent: `readA11y` answers `E_UNSUPPORTED` */
|
|
98
|
+
a11y?: () => IosA11y;
|
|
99
|
+
/** per-app network conditions (`ios-network.ts`); absent: `network` answers `E_UNSUPPORTED` */
|
|
100
|
+
/** the XCUITest audit (`ios-audit.ts`); absent: `auditNatively` skips with a reason */
|
|
101
|
+
nativeAudit?: (ctx: {
|
|
102
|
+
lastBundle: () => string | undefined;
|
|
103
|
+
}) => IosNativeAudit;
|
|
104
|
+
network?: (deps: {
|
|
105
|
+
lastBundle: () => string | undefined;
|
|
106
|
+
relaunch: (bundleId: string) => DriverResult<void>;
|
|
107
|
+
}) => IosNetwork;
|
|
59
108
|
}): IosDriver;
|
|
60
109
|
/**
|
|
61
110
|
* Open the iOS driver for `udid`. Writes `AutomationEnabled` (best effort) while the helper starts.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { CameraCondition, ClockCondition, HostFault, NetworkCondition } from "../../../../core/src/conditions/conditions.types";
|
|
2
|
+
import type { Result } from "../../../../types/src/result";
|
|
3
|
+
import type { CameraState, ClockState, DriverError, DriverResult, LaunchOptions, NetworkOptions, NetworkState } from "../driver.types";
|
|
4
|
+
import { type NetworkProxyPort } from "../network-proxy";
|
|
5
|
+
import { type InjectChannel } from "./inject";
|
|
6
|
+
/**
|
|
7
|
+
* iOS simulator network conditions: a simulator shares the host network, so nothing can shape one device. Instead
|
|
8
|
+
* the app is launched with `libwarden-inject.dylib` (see `inject.ts`), which routes its NSURLSession traffic
|
|
9
|
+
* through the lease's shaping proxy; the proxy does latency / bandwidth / loss / faults, and an `offline` flag in
|
|
10
|
+
* the dylib's state file makes the app's own reachability APIs say so. Presets and offline switch live over the
|
|
11
|
+
* proxy's control channel and the state file. Only the first enable needs a launch that carries the dylib.
|
|
12
|
+
*/
|
|
13
|
+
/** How long the injected app gets to acknowledge a state write (the dylib applies it on a dispatch source). */
|
|
14
|
+
export declare const INJECT_ACK_MS = 3000;
|
|
15
|
+
/** How long a fresh launch gets to load the dylib and acknowledge its first state. */
|
|
16
|
+
export declare const INJECT_LOAD_MS = 8000;
|
|
17
|
+
export type IosNetworkDeps = {
|
|
18
|
+
proxy: NetworkProxyPort;
|
|
19
|
+
channel: InjectChannel;
|
|
20
|
+
/** the dylib path, resolved on first use so a run without conditions never looks for it */
|
|
21
|
+
dylib: () => Result<string, DriverError>;
|
|
22
|
+
/** is `bundleId` running now */
|
|
23
|
+
isRunning: (bundleId: string) => DriverResult<boolean>;
|
|
24
|
+
/** the last app this driver launched */
|
|
25
|
+
lastBundle: () => string | undefined;
|
|
26
|
+
/** launch `bundleId` again with the dylib (late-bound: the driver's own `app.launch`) */
|
|
27
|
+
relaunch: (bundleId: string) => DriverResult<void>;
|
|
28
|
+
debug?: boolean;
|
|
29
|
+
/** wall time, ms (default `Date.now`); `fixed` clocks are an offset from it */
|
|
30
|
+
now?: () => number;
|
|
31
|
+
};
|
|
32
|
+
export type IosNetwork = {
|
|
33
|
+
network(op: NetworkCondition, opts?: NetworkOptions): DriverResult<NetworkState>;
|
|
34
|
+
networkFaults(faults: readonly HostFault[]): DriverResult<{
|
|
35
|
+
notes: readonly string[];
|
|
36
|
+
}>;
|
|
37
|
+
/** Env to merge into a launch (`undefined`: launch plainly). Stages the state file the dylib reads at load. */
|
|
38
|
+
launchEnv(opts: LaunchOptions): DriverResult<Record<string, string> | undefined>;
|
|
39
|
+
/** After a launch that carried the dylib: wait until it loaded, so a missing injection fails here, not as a test that sees the wrong network. */
|
|
40
|
+
confirmLoaded(): DriverResult<void>;
|
|
41
|
+
/** Wall-clock offset for the injected app (COND-12). Live when the app is injected, else staged for the next launch. */
|
|
42
|
+
setClockOffset(ms: number): DriverResult<void>;
|
|
43
|
+
/** Mock the injected app's wall clock (`Driver.clock`). Live when the app is injected, else staged for the next launch. */
|
|
44
|
+
clock(op: ClockCondition, opts?: NetworkOptions): DriverResult<ClockState>;
|
|
45
|
+
/**
|
|
46
|
+
* The simulated camera (`Driver.camera`): feeds are staged copies. Live on an app launched with the camera on
|
|
47
|
+
* (`WARDEN_CAMERA=1`), else from the next launch, which loads the dylib for it (`appliesOnLaunch`).
|
|
48
|
+
*/
|
|
49
|
+
camera(op: CameraCondition | null): DriverResult<CameraState>;
|
|
50
|
+
};
|
|
51
|
+
export declare function createIosNetwork(deps: IosNetworkDeps): IosNetwork;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { PermissionName, PermissionState } from "../../../../core/src/conditions/conditions.types";
|
|
2
|
+
import type { LaunchSettings } from "../../../../core/src/conditions/launch-settings";
|
|
3
|
+
import { type Exec } from "../../../../core/src/exec";
|
|
4
|
+
import type { Driver, LaunchOptions } from "../driver.types";
|
|
5
|
+
/** Permission name to `simctl privacy` service; a name without one is skipped with the reason in `IOS_NO_SERVICE`. */
|
|
6
|
+
export declare const IOS_PRIVACY_SERVICE: Partial<Record<PermissionName, string>>;
|
|
7
|
+
export declare const IOS_NO_SERVICE: Partial<Record<PermissionName, string>>;
|
|
8
|
+
export declare const privacyArgv: (udid: string, state: PermissionState, service: string, bundleId: string) => string[];
|
|
9
|
+
export declare const contentSizeArgv: (udid: string, category: string) => string[];
|
|
10
|
+
export declare const contrastArgv: (udid: string, increased: boolean) => string[];
|
|
11
|
+
export declare const DEFAULT_CONTENT_SIZE = "large";
|
|
12
|
+
export declare function statusBarBatteryArgv(udid: string, op: {
|
|
13
|
+
level?: number;
|
|
14
|
+
charging?: boolean;
|
|
15
|
+
}): string[];
|
|
16
|
+
export declare const statusBarClearArgv: (udid: string) => string[];
|
|
17
|
+
/** `simctl push` reads the payload from stdin (`-`); a `sh -c` with positional args keeps it out of the command line quoting. */
|
|
18
|
+
export declare const pushArgv: (udid: string, bundleId: string, payload: unknown) => string[];
|
|
19
|
+
/** The launch env and args a staged timezone / locale becomes. */
|
|
20
|
+
export declare function launchExtras(settings: LaunchSettings): {
|
|
21
|
+
env: Record<string, string>;
|
|
22
|
+
args: string[];
|
|
23
|
+
};
|
|
24
|
+
/** `requested` with the staged timezone / locale merged in (the caller's own env and args win on a clash). */
|
|
25
|
+
export declare function withStagedSettings(requested: LaunchOptions | undefined, settings: LaunchSettings): LaunchOptions | undefined;
|
|
26
|
+
export type StagedSettings = {
|
|
27
|
+
read(): LaunchSettings;
|
|
28
|
+
write(settings: LaunchSettings): string | undefined;
|
|
29
|
+
};
|
|
30
|
+
export type IosSettingsDeps = {
|
|
31
|
+
udid: string;
|
|
32
|
+
exec: Exec;
|
|
33
|
+
/** the last app this driver launched */
|
|
34
|
+
lastBundle: () => string | undefined;
|
|
35
|
+
/** the session's app (`WARDEN_BUNDLE_ID`) when nothing was launched yet */
|
|
36
|
+
defaultBundle?: string;
|
|
37
|
+
/** where timezone / locale are staged between processes; absent: kept in this driver only */
|
|
38
|
+
staged?: StagedSettings;
|
|
39
|
+
};
|
|
40
|
+
export type IosSettings = Pick<Driver, "permissions" | "timezone" | "locale" | "a11y" | "battery" | "memoryWarning" | "push"> & {
|
|
41
|
+
/** the staged timezone / locale, for `app.launch` to merge into a launch */
|
|
42
|
+
launchSettings(): LaunchSettings;
|
|
43
|
+
};
|
|
44
|
+
export declare function createIosSettings(deps: IosSettingsDeps): IosSettings;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { Result } from "../../../../types/src/result";
|
|
2
|
+
import type { DriverError, DriverResult, Logger } from "../driver.types";
|
|
3
|
+
import { type ClientOptions, type RequestOptions, type SimdClient } from "./simd-client";
|
|
4
|
+
import { type SimdSpawn } from "./simd-transport";
|
|
5
|
+
/**
|
|
6
|
+
* `warden-runner` (the XCUITest server of `native/ios-runner`) on an iOS Simulator, for the ops XCUITest has and the
|
|
7
|
+
* simulator helper does not: today the accessibility audit (A11Y-4). Started lazily on the first request:
|
|
8
|
+
*
|
|
9
|
+
* build (cached, no signing) -> `xcodebuild test-without-building -xctestrun … -destination id=<udid>
|
|
10
|
+
* -only-testing:…/testServe` -> wait for `WARDEN_RUNNER_READY {json}` (the port) -> NDJSON over TCP on this Mac
|
|
11
|
+
*
|
|
12
|
+
* The runner outlives the request (the next audit costs the audit only, about 2 s) and is shut down by `close()`.
|
|
13
|
+
* Anything that stops it from starting is a {@link DriverError} the caller turns into a skipped audit, never a failure.
|
|
14
|
+
*/
|
|
15
|
+
export declare const READY_PREFIX = "WARDEN_RUNNER_READY";
|
|
16
|
+
export declare const TEST_FILTER = "-only-testing:WardenRunnerUITests/RunnerTests/testServe";
|
|
17
|
+
/** The port in a `WARDEN_RUNNER_READY {json}` line, or undefined for any other line. */
|
|
18
|
+
export declare function parseRunnerReady(line: string): number | undefined;
|
|
19
|
+
/** The argv that serves the runner on `udid`; `env` carries the `TEST_RUNNER_*` variables `xcodebuild` forwards. */
|
|
20
|
+
export declare function simRunnerArgv(o: {
|
|
21
|
+
xctestrun: string;
|
|
22
|
+
udid: string;
|
|
23
|
+
idleSeconds?: number;
|
|
24
|
+
}): string[];
|
|
25
|
+
export type SimRunnerDeps = {
|
|
26
|
+
udid: string;
|
|
27
|
+
/** a cached simulator runner build (`ensureSimRunnerBuild`), or why there is none */
|
|
28
|
+
ensureBuild(): Promise<Result<{
|
|
29
|
+
xctestrun: string;
|
|
30
|
+
}, string>>;
|
|
31
|
+
spawn?: SimdSpawn;
|
|
32
|
+
/** connect to the runner's port (tests inject a fake) */
|
|
33
|
+
connect?: (port: number, options: ClientOptions) => Promise<Result<SimdClient, DriverError>>;
|
|
34
|
+
readyTimeoutMs?: number;
|
|
35
|
+
log?: Logger;
|
|
36
|
+
};
|
|
37
|
+
export type SimRunner = {
|
|
38
|
+
request(op: string, params?: Record<string, unknown>, opts?: RequestOptions): DriverResult<unknown>;
|
|
39
|
+
close(): Promise<void>;
|
|
40
|
+
};
|
|
41
|
+
export declare function createSimRunner(deps: SimRunnerDeps): SimRunner;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type ResetFs } from "../../../../core/src/builds/install";
|
|
2
|
+
import type { LatLon, LocationCondition } from "../../../../core/src/conditions/conditions.types";
|
|
2
3
|
import { type Exec, type ExecResult } from "../../../../core/src/exec";
|
|
3
|
-
import type { DriverError, DriverResult, LaunchOptions } from "../driver.types";
|
|
4
|
+
import type { BiometricOp, BiometricState, DriverError, DriverResult, LaunchOptions } from "../driver.types";
|
|
4
5
|
export declare const SIMCTL_CHILD_PREFIX = "SIMCTL_CHILD_";
|
|
5
6
|
/** `xcrun simctl launch [--terminate-running-process] <udid> <bundle> <args…>` (env goes through `SIMCTL_CHILD_*`, see {@link launchEnv}). */
|
|
6
7
|
export declare function launchArgv(udid: string, bundleId: string, opts?: LaunchOptions): string[];
|
|
@@ -16,6 +17,22 @@ export declare const appContainerArgv: (udid: string, bundleId: string) => strin
|
|
|
16
17
|
export declare const launchctlListArgv: (udid: string) => string[];
|
|
17
18
|
export declare const pbpasteArgv: (udid: string) => string[];
|
|
18
19
|
export declare const appearanceArgv: (udid: string, set?: "light" | "dark") => string[];
|
|
20
|
+
/** Sets the enrollment state, then posts the change (`notifyutil -s <name> <state>` before `-p <name>`). */
|
|
21
|
+
export declare const biometricEnrollArgvs: (udid: string, enrolled: boolean) => string[][];
|
|
22
|
+
/** Prints `com.apple.BiometricKit.enrollmentChanged <0|1>`. */
|
|
23
|
+
export declare const biometricStateArgv: (udid: string) => string[];
|
|
24
|
+
/** The Face ID (`pearl`) or Touch ID (`fingerTouch`) match / nomatch notification the simulator's BiometricKit listens for. */
|
|
25
|
+
export declare const biometricEventArgv: (udid: string, modality: BiometricState["modality"], event: "match" | "nomatch") => string[];
|
|
26
|
+
/** The notifications one op posts, in order; `status` posts nothing. */
|
|
27
|
+
export declare function biometricArgvs(udid: string, modality: BiometricState["modality"], op: BiometricOp): string[][];
|
|
28
|
+
/** Model identifier (`iPhone14,7`) of a booted sim. */
|
|
29
|
+
export declare const modelIdentifierArgv: (udid: string) => string[];
|
|
30
|
+
/**
|
|
31
|
+
* Touch ID models by identifier: iPhone SE and everything up to the 8 (`iPhone10,1|2|4|5`; the X, `10,3|6`, has Face ID),
|
|
32
|
+
* and the iPads with a home or power-button sensor. Face ID iPads are the Pros from 2018 (`iPad8,*`, `iPad13,4-11`,
|
|
33
|
+
* `iPad14,3-6`, `iPad16,3-6`, `iPad17,*`). Unknown or empty falls back to Face ID.
|
|
34
|
+
*/
|
|
35
|
+
export declare function biometricModalityOf(model: string): BiometricState["modality"];
|
|
19
36
|
export declare const screenshotArgv: (udid: string, path: string) => string[];
|
|
20
37
|
/** `AutomationEnabled` lets the AX bridge attach without the runner app; written once per open. */
|
|
21
38
|
export declare const automationArgv: (udid: string) => string[];
|
|
@@ -38,6 +55,22 @@ export type StatusBarOverrides = {
|
|
|
38
55
|
/** Clean 9:41 status bar by default (stable screenshots); pass `undefined` fields to drop them. */
|
|
39
56
|
export declare function statusBarOverrideArgv(udid: string, o?: StatusBarOverrides): string[];
|
|
40
57
|
export declare const statusBarClearArgv: (udid: string) => string[];
|
|
58
|
+
export declare const locationSetArgv: (udid: string, point: LatLon) => string[];
|
|
59
|
+
export declare const locationClearArgv: (udid: string) => string[];
|
|
60
|
+
/** `name` is Apple's scenario name (`City Run`), see {@link IOS_SCENARIO_NAMES}. */
|
|
61
|
+
export declare const locationRunArgv: (udid: string, name: string) => string[];
|
|
62
|
+
/** A route with more waypoints than this goes through stdin (`-`) instead of the argument list. */
|
|
63
|
+
export declare const LOCATION_ARGV_MAX_POINTS = 100;
|
|
64
|
+
export type LocationStartOptions = {
|
|
65
|
+
speedMps?: number;
|
|
66
|
+
intervalS?: number;
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* `simctl location <udid> start [--speed=] [--interval=] <lat,lon>…`. The sim plays the path itself
|
|
70
|
+
* after the command returns. Past {@link LOCATION_ARGV_MAX_POINTS} the waypoints ride stdin of a tiny
|
|
71
|
+
* `sh -c` (one `lat,lon` per line, never interpolated into the script) with the `-` operand.
|
|
72
|
+
*/
|
|
73
|
+
export declare function locationStartArgv(udid: string, points: readonly LatLon[], o?: LocationStartOptions): string[];
|
|
41
74
|
/**
|
|
42
75
|
* `simctl pbcopy` reads stdin and `Exec` has none, so the text rides as `$1` of a tiny `sh -c`
|
|
43
76
|
* (never interpolated into the script): `printf %s "$1" | xcrun simctl pbcopy "$2"`.
|
|
@@ -91,6 +124,13 @@ export type Simctl = {
|
|
|
91
124
|
pbpaste(): DriverResult<string>;
|
|
92
125
|
appearance(set?: "light" | "dark"): DriverResult<"light" | "dark">;
|
|
93
126
|
statusBar(overrides?: StatusBarOverrides | "clear"): DriverResult<void>;
|
|
127
|
+
/** `simctl location`: point, route (also from a GPX file), Apple scenario, or clear. Loop is not supported. */
|
|
128
|
+
location(op: LocationCondition): DriverResult<void>;
|
|
129
|
+
/**
|
|
130
|
+
* Face ID / Touch ID through BiometricKit notifications inside the sim. Modality comes from the booted sim's
|
|
131
|
+
* `SIMULATOR_MODEL_IDENTIFIER` (read once; unreadable = Face ID); the answer is the state after the op.
|
|
132
|
+
*/
|
|
133
|
+
biometrics(op: BiometricOp): DriverResult<BiometricState>;
|
|
94
134
|
/** `simctl io screenshot` to `path`, or (no path) to a scratch file read back as PNG bytes */
|
|
95
135
|
screenshot(path?: string): DriverResult<{
|
|
96
136
|
path?: string;
|
|
@@ -38,6 +38,15 @@ export declare function stdioTransport(opts: {
|
|
|
38
38
|
/** how long `close()` waits for the helper to exit after closing stdin before killing it */
|
|
39
39
|
graceMs?: number;
|
|
40
40
|
}): TransportFactory;
|
|
41
|
-
|
|
41
|
+
/** Where a socket transport connects: the daemon's unix socket, or a TCP port on this Mac (the Simulator runner). */
|
|
42
|
+
export type SocketTarget = {
|
|
42
43
|
socketPath: string;
|
|
43
|
-
|
|
44
|
+
tcp?: undefined;
|
|
45
|
+
} | {
|
|
46
|
+
tcp: {
|
|
47
|
+
hostname: string;
|
|
48
|
+
port: number;
|
|
49
|
+
};
|
|
50
|
+
socketPath?: undefined;
|
|
51
|
+
};
|
|
52
|
+
export declare function socketTransport(opts: SocketTarget): TransportFactory;
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Per-request budget for the helper's `typeText`: it holds, paces and (for a repeated key) waits per character.
|
|
3
3
|
* The helper's defaults (pace 5, hold 20, repeat gap 50) cost ~38 ms per character on a quiet local simulator; the
|
|
4
|
-
* budget allows ~100 ms (the EAS VirtualMac ran ~3.7x the nominal sleeps) and 200 ms per repeated key.
|
|
4
|
+
* budget allows ~100 ms (the EAS VirtualMac ran ~3.7x the nominal sleeps) and 200 ms per repeated key. The one
|
|
5
|
+
* lead-in pause after the first key (60 ms) fits in the base.
|
|
5
6
|
*/
|
|
6
7
|
export declare const TYPE_BASE_MS = 10000;
|
|
7
8
|
export declare const TYPE_PER_CHAR_MS = 100;
|
|
8
9
|
export declare const TYPE_PER_REPEAT_MS = 200;
|
|
9
10
|
/** Base + ~100 ms per character + 200 ms per character equal to the one before it. */
|
|
10
11
|
export declare function typeTextBudgetMs(text: string): number;
|
|
12
|
+
/** The XCUITest runner on a real device types one character per `typeText` call, each waiting for the app to go idle. */
|
|
13
|
+
export declare const DEVICE_TYPE_PER_CHAR_MS = 600;
|
|
14
|
+
/** Base + ~600 ms per character: the runner's per-character typing on a real device. */
|
|
15
|
+
export declare function deviceTypeTextBudgetMs(text: string): number;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { A11yIssue } from "../a11y/a11y.types";
|
|
2
|
+
import type { Driver } from "./driver.types";
|
|
3
|
+
/** What a platform's own audit engine found, or why it did not run (never an error: a missing engine only skips it). */
|
|
4
|
+
export type NativeAuditResult = {
|
|
5
|
+
issues: A11yIssue[];
|
|
6
|
+
skipped?: {
|
|
7
|
+
source: A11yIssue["source"];
|
|
8
|
+
reason: string;
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
export type NativeAuditOptions = {
|
|
12
|
+
/** iOS: the app whose accessibility the XCUITest audit checks (required there; the audit needs it running) */
|
|
13
|
+
bundleId?: string;
|
|
14
|
+
/** Android: let ATF's contrast checks read a screenshot (default true) */
|
|
15
|
+
contrast?: boolean;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Optional capability next to the frozen `Driver` interface (pattern: `ReadsA11y` in `a11y-read.ts`): run the
|
|
19
|
+
* platform's own accessibility audit on the screen now. iOS simulator: XCUITest `performAccessibilityAudit` through
|
|
20
|
+
* the runner (`a11y/native/xcuitest.ts`). Android: the Accessibility Test Framework inside `warden-uiad`
|
|
21
|
+
* (`a11y/native/atf.ts`). Resolves with `skipped` and a reason when the engine cannot run.
|
|
22
|
+
*/
|
|
23
|
+
export type AuditsNatively = {
|
|
24
|
+
auditNatively(options?: NativeAuditOptions): Promise<NativeAuditResult>;
|
|
25
|
+
};
|
|
26
|
+
export declare function canAuditNatively(driver: Driver): driver is Driver & AuditsNatively;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { HostFault, NetworkCondition } from "../../../core/src/conditions/conditions.types";
|
|
2
|
+
import type { ConditionsUpdate } from "../../../core/src/netproxy/control";
|
|
3
|
+
import { type LeaseProxyDeps } from "../../../core/src/netproxy/lease-proxy";
|
|
4
|
+
import { type ProxyConditions } from "../../../core/src/netproxy/shaping";
|
|
5
|
+
import type { Store } from "../../../core/src/store";
|
|
6
|
+
import type { Lease } from "../../../core/src/types";
|
|
7
|
+
import { type AsyncResult, type Result } from "../../../types/src/result";
|
|
8
|
+
/** Where a lease's shaping proxy listens (always loopback on the host). */
|
|
9
|
+
export type ProxyAddress = {
|
|
10
|
+
host: string;
|
|
11
|
+
port: number;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* What a driver needs from the lease's shaping proxy (COND-4). The drivers never touch the lease store: whoever
|
|
15
|
+
* opens the driver builds this (`leaseNetworkProxy`) or passes a fake in tests.
|
|
16
|
+
*/
|
|
17
|
+
export type NetworkProxyPort = {
|
|
18
|
+
/** The live proxy's address, `undefined` when this lease has none. */
|
|
19
|
+
current(): Promise<ProxyAddress | undefined>;
|
|
20
|
+
/** Start the proxy (or reuse the live one) with `conditions` applied; records `condition` as the lease's network condition. */
|
|
21
|
+
ensure(condition: NetworkCondition, conditions: ProxyConditions): AsyncResult<ProxyAddress>;
|
|
22
|
+
/** Reconfigure the live proxy without dropping connections. Fails when there is none. */
|
|
23
|
+
update(update: ConditionsUpdate): AsyncResult<void>;
|
|
24
|
+
/** Stop the proxy and free its port. */
|
|
25
|
+
stop(): Promise<void>;
|
|
26
|
+
};
|
|
27
|
+
/** `NetworkProxyPort` for one lease over the real store and the detached `netproxy` process. */
|
|
28
|
+
export declare function leaseNetworkProxy(deps: LeaseProxyDeps, lease: Pick<Lease, "id" | "owner" | "ttlMs">): NetworkProxyPort;
|
|
29
|
+
/** The proxy parameters for `condition` plus the faults to keep. Offline is `refuse`: connections close at accept. */
|
|
30
|
+
export declare function proxyConditionsFor(condition: NetworkCondition, faults: readonly HostFault[]): ProxyConditions;
|
|
31
|
+
/**
|
|
32
|
+
* Faults the proxy cannot honour as written. An HTTPS request is a CONNECT tunnel the proxy never reads inside
|
|
33
|
+
* (no MITM), so a `status` fault on a host reaches an https:// request as a reset, not a status code.
|
|
34
|
+
*/
|
|
35
|
+
export declare function faultNotes(faults: readonly HostFault[]): string[];
|
|
36
|
+
export type LazyLeaseProxyOptions = {
|
|
37
|
+
leaseId: string;
|
|
38
|
+
env: Record<string, string | undefined>;
|
|
39
|
+
now: () => number;
|
|
40
|
+
/** opens the lease store; called only when the proxy has to be started or stopped */
|
|
41
|
+
openStore: () => Result<Store>;
|
|
42
|
+
/** argv that runs the proxy process (`WARDEN_NETPROXY_COMMAND`); default: `bun netproxy.main.ts` from source */
|
|
43
|
+
command?: readonly string[] | undefined;
|
|
44
|
+
/** test seams passed through to `ensureLeaseProxy` (spawn, pid liveness, port range…) */
|
|
45
|
+
proxyDeps?: Partial<Omit<LeaseProxyDeps, "store" | "env" | "now" | "command">>;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* `NetworkProxyPort` for a spec's lease that touches nothing until a network condition needs it: the store is not
|
|
49
|
+
* opened (nor the lease read) before `ensure`, so a spec that never shapes the network costs no store access or
|
|
50
|
+
* proxy. `current` / `update` / `stop` work from the lease's handle file and stay off the store while there is none.
|
|
51
|
+
*/
|
|
52
|
+
export declare function lazyLeaseNetworkProxy(o: LazyLeaseProxyOptions): NetworkProxyPort;
|
|
@@ -1,19 +1,37 @@
|
|
|
1
1
|
import { type Exec } from "../../../core/src/exec";
|
|
2
|
+
import { type StoreOpener } from "../conditions/lease-store";
|
|
2
3
|
import type { E2eEnv } from "../env";
|
|
3
4
|
import type { DriverFactory } from "../session";
|
|
4
5
|
import { type AndroidDriverOptions } from "./android/android-driver";
|
|
6
|
+
import { type ClockRecorder } from "./clock-recorder";
|
|
5
7
|
import type { Driver, DriverResult } from "./driver.types";
|
|
8
|
+
import { type OpenIosDeviceDriverOptions } from "./ios/device/device-driver";
|
|
6
9
|
import { type OpenIosDriverOptions } from "./ios/ios-driver";
|
|
10
|
+
import { type NetworkProxyPort } from "./network-proxy";
|
|
7
11
|
/**
|
|
8
12
|
* Test seams for {@link openDriver}: everything the platform openers inject except what the env supplies.
|
|
9
13
|
* `android` carries the Android-only seams (`adb` path, resident-shell spawn, binary runner, clock).
|
|
10
14
|
*/
|
|
11
15
|
export type OpenDriverDeps = Omit<OpenIosDriverOptions, "udid" | "env" | "exec"> & {
|
|
12
16
|
exec?: Exec;
|
|
17
|
+
/** bundle ids warden installed on a physical iPhone earlier (the `installs` table): the only others `app.uninstall` may touch */
|
|
18
|
+
ownedBundles?: readonly string[];
|
|
13
19
|
android?: Omit<AndroidDriverOptions, "serial" | "exec" | "log" | "adb"> & {
|
|
14
20
|
adb?: string;
|
|
15
21
|
};
|
|
22
|
+
/** opens the lease store for the shaping proxy; default: the spec process's shared connection */
|
|
23
|
+
openStore?: StoreOpener;
|
|
16
24
|
};
|
|
25
|
+
/**
|
|
26
|
+
* The shaping proxy of the spec's lease (`WARDEN_LEASE_ID`), built lazily: nothing opens the store or starts a
|
|
27
|
+
* process until a network condition asks for it. None without a lease id, so `network` answers `E_UNSUPPORTED`.
|
|
28
|
+
*/
|
|
29
|
+
export declare function leaseProxyFor(env: E2eEnv, deps?: OpenDriverDeps): NetworkProxyPort | undefined;
|
|
30
|
+
/**
|
|
31
|
+
* Records the clock the driver sets on the spec's lease (`leaseClockRecorder`), opening the store on the first
|
|
32
|
+
* change. Best effort: a store that fails is logged and never fails the clock call. None without a lease id.
|
|
33
|
+
*/
|
|
34
|
+
export declare function leaseClockRecorderFor(env: E2eEnv, deps?: OpenDriverDeps): ClockRecorder | undefined;
|
|
17
35
|
/** `WARDEN_E2E_DEBUG=1` logs every helper op and simctl call with its duration to stderr. */
|
|
18
36
|
export declare const DEBUG_ENV = "WARDEN_E2E_DEBUG";
|
|
19
37
|
/**
|
|
@@ -23,6 +41,8 @@ export declare const DEBUG_ENV = "WARDEN_E2E_DEBUG";
|
|
|
23
41
|
* shell `spawn`, which is a test that must never reach a real `adb`), and the injected seams.
|
|
24
42
|
*/
|
|
25
43
|
export declare function androidOptions(env: E2eEnv, deps: OpenDriverDeps, debug: boolean): AndroidDriverOptions;
|
|
44
|
+
/** The physical iPhone opener's options: the env's udid, runner socket, app and bundle id, and the injected seams. */
|
|
45
|
+
export declare function deviceOptions(env: E2eEnv, deps: OpenDriverDeps, debug: boolean): OpenIosDeviceDriverOptions;
|
|
26
46
|
/** Open the `Driver` for the device in `env`, by platform. Android deep links go to `env.bundleId` only. */
|
|
27
47
|
export declare function openDriver(env: E2eEnv, deps?: OpenDriverDeps): DriverResult<Driver>;
|
|
28
48
|
/** What the session uses when no factory is passed; `preload.ts` registers it with `setDefaultDriverFactory`. */
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Driver } from "./driver.types";
|
|
2
|
+
/** The condition and setting ops of a `Driver`: each changes the device (or an app's grants) beyond driving its UI. */
|
|
3
|
+
export declare const PHYSICAL_CONDITION_OPS: readonly ["network", "networkFaults", "location", "clock", "permissions", "timezone", "locale", "a11y", "battery", "memoryWarning", "push", "camera"];
|
|
4
|
+
type PhysicalConditionOps = Pick<Driver, (typeof PHYSICAL_CONDITION_OPS)[number]>;
|
|
5
|
+
/**
|
|
6
|
+
* The condition ops for a physical phone: all `E_UNSUPPORTED`. On a phone they would flip airplane mode (cutting
|
|
7
|
+
* wireless adb), set the system clock, timezone or font scale, or proxy the network of somebody's device; the
|
|
8
|
+
* mechanisms behind them (`simctl`, the emulator console, the injected dylib) are simulator / emulator only.
|
|
9
|
+
*/
|
|
10
|
+
export declare function physicalConditionOps(device: string): PhysicalConditionOps;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Platform } from "./driver.types";
|
|
2
|
+
/**
|
|
3
|
+
* What a spec can ask of its device with `test(title, { requires: [...] })`:
|
|
4
|
+
* - `pinch`: two-finger `pinch` / `rotate` (`capabilities.pinch`)
|
|
5
|
+
* - `multiTouch`: a gesture path with more than one finger (`capabilities.maxPointers` of 2)
|
|
6
|
+
*/
|
|
7
|
+
export type Requirement = "pinch" | "multiTouch";
|
|
8
|
+
/** The device a spec was collected for, as the env says (`WARDEN_PLATFORM`, `WARDEN_DEVICE_KIND`). */
|
|
9
|
+
export type RequirementTarget = {
|
|
10
|
+
platform: Platform | undefined;
|
|
11
|
+
deviceKind?: "physical" | undefined;
|
|
12
|
+
};
|
|
13
|
+
/** The `requires` the target's driver is known to lack: a spec with any of them is skipped when collected. */
|
|
14
|
+
export declare function missingRequirements(target: RequirementTarget, requires: readonly Requirement[] | undefined): Requirement[];
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { ScreenReaderState, SpokenLine } from "../a11y/a11y.types";
|
|
2
|
+
import type { Driver, DriverResult, Element } from "./driver.types";
|
|
3
|
+
/**
|
|
4
|
+
* Optional capability next to the frozen `Driver` interface (pattern: `ReadsA11yEvents`): drive a real screen reader
|
|
5
|
+
* the way a user's swipes and double-taps would. Only Android offers it, and only while TalkBack is on through
|
|
6
|
+
* `a11y({ screenReader: true })`; any call with TalkBack off answers `E_UNSUPPORTED`.
|
|
7
|
+
*
|
|
8
|
+
* `next` / `prev` move TalkBack's accessibility focus one step in its traversal order and answer the node that holds
|
|
9
|
+
* it afterwards (`undefined` at either end). `activate` clicks the focused node (`ACTION_CLICK` on it or its clickable
|
|
10
|
+
* ancestor). `focused` reads the node holding accessibility focus. `spoken` answers what TalkBack said since `since`
|
|
11
|
+
* (opaque cursor, as for `a11yEvents`): from its verbose log when that is on (`source: "talkback"`), else composed
|
|
12
|
+
* from the focus events by the speech model (`source: "simulated"`).
|
|
13
|
+
*/
|
|
14
|
+
export type SpokenRead = {
|
|
15
|
+
lines: SpokenLine[];
|
|
16
|
+
cursor: number;
|
|
17
|
+
};
|
|
18
|
+
export type DrivesScreenReader = {
|
|
19
|
+
screenReaderState(): DriverResult<ScreenReaderState>;
|
|
20
|
+
screenReaderNext(): DriverResult<Element | undefined>;
|
|
21
|
+
screenReaderPrev(): DriverResult<Element | undefined>;
|
|
22
|
+
screenReaderActivate(): DriverResult<void>;
|
|
23
|
+
screenReaderFocused(): DriverResult<Element | undefined>;
|
|
24
|
+
screenReaderSpoken(since?: number): DriverResult<SpokenRead>;
|
|
25
|
+
};
|
|
26
|
+
export declare function canDriveScreenReader(driver: Driver): driver is Driver & DrivesScreenReader;
|
|
@@ -4,7 +4,7 @@ import type { Driver, DriverResult, KeyName } from "./driver.types";
|
|
|
4
4
|
* fill`/`type` all go through it).
|
|
5
5
|
*
|
|
6
6
|
* iOS: `warden-simd` holds, paces and spaces repeated keys itself (`WARDEN_SIMD_PACE_MS`, `WARDEN_SIMD_HOLD_MS`,
|
|
7
|
-
* `WARDEN_SIMD_REPEAT_GAP_MS`), so the whole string goes in one request and the client adds no gap. Before that
|
|
7
|
+
* `WARDEN_SIMD_REPEAT_GAP_MS`, `WARDEN_SIMD_LEAD_MS`), so the whole string goes in one request and the client adds no gap. Before that
|
|
8
8
|
* the client sent one grapheme per request, 25 ms apart (set `KEY_PACE_MS` 25 and `SPLIT_TEXT` true to go back).
|
|
9
9
|
*/
|
|
10
10
|
/** Gap after each key on iOS. */
|