@delacour/warden-e2e 0.7.0 → 0.9.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.
Files changed (94) hide show
  1. package/{chunk-empqr9n0.mjs → chunk-y0y7frfz.mjs} +2010 -169
  2. package/index.mjs +214 -13
  3. package/package.json +2 -2
  4. package/preload.mjs +3007 -800
  5. package/types/core/src/a11y/a11y.config.d.ts +47 -0
  6. package/types/core/src/affected/affected.schema.d.ts +49 -1
  7. package/types/core/src/android-keyguard.d.ts +2 -0
  8. package/types/core/src/batch/preset.schema.d.ts +12 -1
  9. package/types/core/src/builds/app-option.schema.d.ts +1 -1
  10. package/types/core/src/builds/config.schema.d.ts +93 -2
  11. package/types/core/src/builds/define-config.d.ts +55 -0
  12. package/types/core/src/builds/kinds.d.ts +6 -0
  13. package/types/core/src/camera-mode.d.ts +17 -0
  14. package/types/core/src/camera-placeholder.d.ts +9 -0
  15. package/types/core/src/camera-profile.d.ts +12 -0
  16. package/types/core/src/claim-errors.d.ts +34 -0
  17. package/types/core/src/conditions/a11y-keys.d.ts +45 -0
  18. package/types/core/src/conditions/camera-media.d.ts +30 -0
  19. package/types/core/src/conditions/conditions.config.d.ts +24 -0
  20. package/types/core/src/conditions/conditions.types.d.ts +30 -0
  21. package/types/core/src/conditions/inject-state.d.ts +1 -1
  22. package/types/core/src/ios-runner/build.d.ts +28 -0
  23. package/types/core/src/ios-runner/cache.d.ts +44 -0
  24. package/types/core/src/ios-runner/pack.d.ts +17 -0
  25. package/types/core/src/ios-runner/signing.d.ts +40 -0
  26. package/types/core/src/ios-runner/sim-build.d.ts +36 -0
  27. package/types/core/src/ios-runner/stage.d.ts +26 -0
  28. package/types/core/src/lease-events.d.ts +126 -0
  29. package/types/core/src/ownership.d.ts +44 -0
  30. package/types/core/src/providers/android.d.ts +10 -1
  31. package/types/core/src/providers/provider.types.d.ts +9 -0
  32. package/types/core/src/queue.d.ts +43 -0
  33. package/types/core/src/store.d.ts +61 -5
  34. package/types/core/src/types.d.ts +10 -0
  35. package/types/core/src/xcode.d.ts +22 -0
  36. package/types/e2e/src/a11y/a11y-target.d.ts +23 -0
  37. package/types/e2e/src/a11y/a11y.types.d.ts +81 -0
  38. package/types/e2e/src/a11y/announcements.d.ts +24 -0
  39. package/types/e2e/src/a11y/audit.d.ts +22 -0
  40. package/types/e2e/src/a11y/native/atf.d.ts +51 -0
  41. package/types/e2e/src/a11y/native/xcuitest.d.ts +60 -0
  42. package/types/e2e/src/a11y/order.d.ts +5 -0
  43. package/types/e2e/src/a11y/rules/duplicate-name.d.ts +6 -0
  44. package/types/e2e/src/a11y/rules/hidden-focusable.d.ts +3 -0
  45. package/types/e2e/src/a11y/rules/image-no-name.d.ts +6 -0
  46. package/types/e2e/src/a11y/rules/label-in-name.d.ts +7 -0
  47. package/types/e2e/src/a11y/rules/missing-name.d.ts +3 -0
  48. package/types/e2e/src/a11y/rules/role-in-name.d.ts +3 -0
  49. package/types/e2e/src/a11y/rules/rule.context.d.ts +22 -0
  50. package/types/e2e/src/a11y/rules/target-size.d.ts +21 -0
  51. package/types/e2e/src/a11y/rules/text-clipped.d.ts +9 -0
  52. package/types/e2e/src/a11y/speech.d.ts +26 -0
  53. package/types/e2e/src/a11y/talkback-log.d.ts +13 -0
  54. package/types/e2e/src/app.d.ts +0 -1
  55. package/types/e2e/src/conditions/condition-events.d.ts +6 -1
  56. package/types/e2e/src/conditions/device-conditions.d.ts +30 -2
  57. package/types/e2e/src/device-a11y.d.ts +97 -0
  58. package/types/e2e/src/device.d.ts +11 -2
  59. package/types/e2e/src/driver/a11y-events.d.ts +30 -0
  60. package/types/e2e/src/driver/a11y-read.d.ts +12 -0
  61. package/types/e2e/src/driver/android/android-animations.d.ts +7 -0
  62. package/types/e2e/src/driver/android/android-argv.d.ts +2 -0
  63. package/types/e2e/src/driver/android/android-camera-host.d.ts +23 -0
  64. package/types/e2e/src/driver/android/android-camera.d.ts +28 -0
  65. package/types/e2e/src/driver/android/android-driver.d.ts +5 -0
  66. package/types/e2e/src/driver/android/android-screen-reader.d.ts +23 -0
  67. package/types/e2e/src/driver/android/android-settings.d.ts +9 -2
  68. package/types/e2e/src/driver/android/android-spoken.d.ts +18 -0
  69. package/types/e2e/src/driver/android/android-talkback.d.ts +57 -0
  70. package/types/e2e/src/driver/android/atf-device.d.ts +23 -0
  71. package/types/e2e/src/driver/android/atf-dex.generated.d.ts +7 -0
  72. package/types/e2e/src/driver/android/uiad-dex.generated.d.ts +5 -5
  73. package/types/e2e/src/driver/android/uiad-ops.d.ts +27 -1
  74. package/types/e2e/src/driver/android/uiad-protocol.d.ts +8 -0
  75. package/types/e2e/src/driver/android/uiad-screen-reader.d.ts +33 -0
  76. package/types/e2e/src/driver/driver.types.d.ts +71 -5
  77. package/types/e2e/src/driver/element-actions.d.ts +16 -0
  78. package/types/e2e/src/driver/ios/inject.d.ts +16 -2
  79. package/types/e2e/src/driver/ios/ios-a11y.d.ts +73 -0
  80. package/types/e2e/src/driver/ios/ios-audit.d.ts +36 -0
  81. package/types/e2e/src/driver/ios/ios-driver.d.ts +21 -1
  82. package/types/e2e/src/driver/ios/ios-network.d.ts +7 -2
  83. package/types/e2e/src/driver/ios/sim-runner.d.ts +41 -0
  84. package/types/e2e/src/driver/ios/simd-transport.d.ts +11 -2
  85. package/types/e2e/src/driver/native-audit.d.ts +26 -0
  86. package/types/e2e/src/driver/physical-conditions.d.ts +1 -1
  87. package/types/e2e/src/driver/screen-reader.d.ts +26 -0
  88. package/types/e2e/src/env.d.ts +16 -1
  89. package/types/e2e/src/expect/a11y-matchers.d.ts +33 -0
  90. package/types/e2e/src/expect/expect.d.ts +7 -2
  91. package/types/e2e/src/expect/matchers.d.ts +14 -0
  92. package/types/e2e/src/index.d.ts +5 -1
  93. package/types/e2e/src/runner/callsite.d.ts +3 -1
  94. package/types/e2e/src/screen/render.d.ts +8 -0
@@ -1,5 +1,6 @@
1
- import { type ConditionRun, type DeviceClock, type DeviceConditions, type DeviceLocation, type DeviceNetwork, type DevicePermissions, type TimelineStepInput } from "./conditions/device-conditions";
1
+ import { type ConditionRun, type DeviceCamera, type DeviceClock, type DeviceConditions, type DeviceLocation, type DeviceNetwork, type DevicePermissions, type TimelineStepInput } from "./conditions/device-conditions";
2
2
  import { type ConditionRecorder } from "./conditions/lease-recorder";
3
+ import { type DeviceA11y } from "./device-a11y";
3
4
  import { type DeviceDevtools } from "./device-devtools";
4
5
  import type { BiometricState, Driver, Orientation } from "./driver/driver.types";
5
6
  import { type SettleDetail } from "./driver/settle";
@@ -24,6 +25,8 @@ export type OpenLinkOptions = {
24
25
  };
25
26
  export type SnapshotTextOptions = {
26
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;
27
30
  };
28
31
  export type SettleOptions = {
29
32
  /** how long the UI must stay unchanged. Default 250 ms. */
@@ -50,7 +53,7 @@ export type DeviceBiometrics = {
50
53
  /** Presents a non-matching face / finger to the pending prompt. */
51
54
  fail(): Promise<BiometricState>;
52
55
  };
53
- export type Device = {
56
+ export type Device = DeviceA11y & {
54
57
  /**
55
58
  * Answers the system alert / sheet on top. `accept` presses the first present of OK, Allow, Open,
56
59
  * Continue, Yes, Done, Allow While Using App, Allow Once (else the last button); `dismiss` the first
@@ -122,6 +125,12 @@ export type Device = {
122
125
  a11y: DeviceConditions["a11y"];
123
126
  /** Battery level and charging: real on Android emulators, status bar only on iOS (`cosmetic: true`). */
124
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;
125
134
  /** Android: a critical trim-memory signal to the app. iOS: `E_UNSUPPORTED`. */
126
135
  memoryWarning: DeviceConditions["memoryWarning"];
127
136
  /** iOS: delivers an APNs payload to the app. Android: `E_UNSUPPORTED`. */
@@ -0,0 +1,30 @@
1
+ import type { Result } from "../../../types/src/result";
2
+ import type { A11yEvent, A11yEventSource } from "../a11y/a11y.types";
3
+ import type { Driver, DriverResult } from "./driver.types";
4
+ /**
5
+ * Optional capability next to the frozen `Driver` interface (pattern: `ReadsA11y`): the announcements, live-region
6
+ * changes and screen/layout notifications an app emitted since `since`. `cursor` is opaque to callers: pass the last
7
+ * one back to get only what is new (Android: the server's sequence number; iOS: a byte offset into the inject events
8
+ * file). `since` omitted reads from the start. `lost` is set when the Android ring overwrote events the caller never saw.
9
+ * iOS needs the app launched with the injected library (`WARDEN_A11Y=1` / the `a11y` option) and answers
10
+ * `E_UNSUPPORTED` otherwise.
11
+ */
12
+ export type A11yEventsRead = {
13
+ events: A11yEvent[];
14
+ cursor: number;
15
+ lost?: boolean;
16
+ };
17
+ export type ReadsA11yEvents = {
18
+ a11yEvents(since?: number): DriverResult<A11yEventsRead>;
19
+ };
20
+ export declare function canReadA11yEvents(driver: Driver): driver is Driver & ReadsA11yEvents;
21
+ export declare function parseA11yEvent(value: unknown, source: A11yEventSource): A11yEvent | undefined;
22
+ /** The `a11yEvents` reply of `warden-uiad`: `{events:[{kind,text,at}], cursor, lost?}`. */
23
+ export declare function parseUiadA11yEvents(data: unknown): Result<A11yEventsRead, string>;
24
+ /**
25
+ * The injected library's `<state>.events` file (JSON lines, append-only) from byte offset `since`. Only lines that end
26
+ * in a newline count: a half-written tail stays unread and the returned cursor stops before it, so the next read
27
+ * starts there. A malformed complete line is skipped (the cursor moves past it). A `since` beyond the file (the file
28
+ * was reset by a relaunch) restarts from 0.
29
+ */
30
+ export declare function parseInjectEvents(text: string, since?: number): A11yEventsRead;
@@ -0,0 +1,12 @@
1
+ import type { Driver, DriverResult, Snapshot } from "./driver.types";
2
+ /**
3
+ * Optional capability next to the frozen `Driver` interface (pattern: `RepeatingKeys` in `typing.ts`): read the
4
+ * screen-reader facts a plain snapshot lacks and return the snapshot with `Element.a11y` filled in. iOS: the injected
5
+ * dylib's UIKit walk merged by identifier / frame (`ios/ios-a11y.ts`); needs the app launched with the dylib
6
+ * (`WARDEN_A11Y=1` or the `a11y` driver option) and answers `E_UNSUPPORTED` otherwise. Android has no such driver
7
+ * method: `warden-uiad` snapshots carry `a11y` already.
8
+ */
9
+ export type ReadsA11y = {
10
+ readA11y(snapshot: Snapshot): DriverResult<Snapshot>;
11
+ };
12
+ export declare function canReadA11y(driver: Driver): driver is Driver & ReadsA11y;
@@ -12,6 +12,8 @@ import { type AndroidStateStore } from "./android-state";
12
12
  export type AnimationGuard = {
13
13
  /** put the scales back (once); a restore that fails leaves the saved originals for the next driver */
14
14
  restore(): Promise<void>;
15
+ /** the transition scale the device had before it was zeroed (`"null"` = never set); undefined when it was off already */
16
+ transitionOriginal(): string | undefined;
15
17
  };
16
18
  export type AnimationDeps = {
17
19
  session: AdbSession;
@@ -25,5 +27,10 @@ export type AnimationDeps = {
25
27
  /**
26
28
  * One shell round trip that reads the three scales and sets them to 0, then (when something has to be put
27
29
  * back) saves the originals. Never fails the open: a device that refuses just keeps its animations.
30
+ *
31
+ * The transition scale is the exception: RN (0.86) reads it as "reduce motion", and zero would make every app
32
+ * under test report reduce-motion on. It goes straight back to the device's own value, so only the window and
33
+ * animator scales stay at 0 (those are what makes the run fast and stable); `a11y({ reduceMotion: true })` zeroes it
34
+ * on request.
28
35
  */
29
36
  export declare function disableAnimations(deps: AnimationDeps): Promise<AnimationGuard>;
@@ -93,6 +93,8 @@ export declare function animationScaleArgvs(): string[][];
93
93
  export declare function animationReadArgvs(): string[][];
94
94
  /** Put saved scales back: a value is `put` again, a setting that was never set (`null`) is deleted. */
95
95
  export declare function animationRestoreArgvs(values: AnimationValues): string[][];
96
+ /** Put just the transition scale back (a setting that was never set, `null`, is deleted). */
97
+ export declare function transitionRestoreArgv(value: string): string[];
96
98
  /** Host argv: `adb -s <serial> exec-out screencap -p` (binary PNG on stdout). */
97
99
  export declare function screencapArgv(adb: string, serial: string): string[];
98
100
  /** `getprop <key>` (boot flag, sdk level, model). */
@@ -0,0 +1,23 @@
1
+ import { type Exec } from "../../../../core/src/exec";
2
+ import type { DeviceProvider } from "../../../../core/src/providers/provider.types";
3
+ import type { Store } from "../../../../core/src/store";
4
+ import { type Result } from "../../../../types/src/result";
5
+ import type { AndroidCameraDeps } from "./android-camera";
6
+ export type AndroidCameraHostOptions = {
7
+ serial: string;
8
+ env?: Record<string, string | undefined>;
9
+ exec?: Exec;
10
+ now?: () => number;
11
+ /** the lease store (default: the process's shared connection) */
12
+ store?: () => Result<Store>;
13
+ /** the Android provider (default: a real one on `adb` / `emulator`) */
14
+ provider?: (store: Store) => DeviceProvider;
15
+ };
16
+ /** What `createAndroidCamera` needs from warden's state: the device's recorded profile, a restart, the lease record. */
17
+ export type AndroidCameraHost = Pick<AndroidCameraDeps, "profile" | "relaunch"> & Required<Pick<AndroidCameraDeps, "record">>;
18
+ /**
19
+ * The camera driver's view of warden's store and provider, opened lazily: a run that never touches the camera never
20
+ * opens either. The profile (`<avd>+cam`) is what the claim recorded for the serial; a restart goes through the same
21
+ * provider that launched the emulator, so the serial, console port, device record and lease stay.
22
+ */
23
+ export declare function createAndroidCameraHost(options: AndroidCameraHostOptions): AndroidCameraHost;
@@ -0,0 +1,28 @@
1
+ import { type CameraCondition } from "../../../../core/src/conditions/conditions.types";
2
+ import type { Exec } from "../../../../core/src/exec";
3
+ import type { Driver } from "../driver.types";
4
+ export type AndroidCameraDeps = {
5
+ serial: string;
6
+ wardenHome: string;
7
+ /** host process runner: `sips` re-encodes images to PNG */
8
+ exec: Exec;
9
+ /** the profile warden recorded for this device (`pixel-10+cam`); undefined = not a warden device */
10
+ profile: () => string | undefined;
11
+ /** restart the emulator on its own serial through the provider and wait until it is ready */
12
+ relaunch: () => Promise<{
13
+ success: true;
14
+ data: undefined;
15
+ } | {
16
+ success: false;
17
+ error: string;
18
+ }>;
19
+ /** told the feeds now playing, so the lease carries the `camera` area and release undoes it */
20
+ record?: (feeds: CameraCondition) => void;
21
+ };
22
+ /**
23
+ * The Android emulator camera. `-camera-back` / `-camera-front` name a file the emulator re-reads whenever an app
24
+ * opens the camera, so an image swap is an atomic file replace (`appliesOnCameraOpen`). Only PNG renders, so every
25
+ * other image is re-encoded with `sips`. The source TYPE (image or video) is fixed at launch: a change of it is
26
+ * recorded in `mode.json` and the emulator is restarted on the same serial (`rebooted`).
27
+ */
28
+ export declare function createAndroidCamera(deps: AndroidCameraDeps): Driver["camera"];
@@ -5,6 +5,7 @@ import type { ClockRecorder } from "../clock-recorder";
5
5
  import type { Driver, DriverResult, Element, Logger, Point, ScreenInfo } from "../driver.types";
6
6
  import type { NetworkProxyPort } from "../network-proxy";
7
7
  import { type SpawnAdb } from "./adb-session";
8
+ import { type AndroidCameraHost } from "./android-camera-host";
8
9
  import { type Clock } from "./android-device";
9
10
  import { type DumpRoute } from "./android-dump";
10
11
  import { type AndroidStateStore } from "./android-state";
@@ -65,6 +66,10 @@ export type AndroidDriverOptions = {
65
66
  tickerClock?: TickerClock;
66
67
  /** told about every network flap transition (the `condition` events of `results.jsonl`) */
67
68
  conditionSink?: ConditionSink;
69
+ /** warden's home, where a camera emulator's feeds live (default `$WARDEN_HOME`, else `~/.warden`) */
70
+ wardenHome?: string;
71
+ /** the camera op's view of warden's store and provider (default: the real ones, opened on first use) */
72
+ cameraHost?: AndroidCameraHost;
68
73
  };
69
74
  /** Deepest hittable element containing `point` (later in preorder wins ties: drawn on top); else deepest any. */
70
75
  export declare function elementAt(flat: readonly Element[], point: Point): Element | null;
@@ -0,0 +1,23 @@
1
+ import type { Driver } from "../driver.types";
2
+ import type { ActsOnElements } from "../element-actions";
3
+ import type { DrivesScreenReader } from "../screen-reader";
4
+ import type { AdbSession } from "./adb-session";
5
+ import type { Clock } from "./android-device";
6
+ import type { TalkBackControl } from "./android-talkback";
7
+ import type { UiadOps } from "./uiad-ops";
8
+ /**
9
+ * The Android driver while TalkBack is on (WDN-A11Y, A11Y-7): the `DrivesScreenReader` calls, and the element actions
10
+ * that replace touches. With TalkBack on a tap only moves its focus and a one-finger swipe navigates, so a locator tap
11
+ * is `ACTION_CLICK` on the node and a locator scroll is `ACTION_SCROLL_*` on its scrollable (server ops, no touch).
12
+ * Raw coordinate taps never come through here and stay touches: with TalkBack on they move its focus, and whether that
13
+ * also activates depends on its "single-tap to activate" setting.
14
+ */
15
+ export type ScreenReaderDeps = {
16
+ uiad: UiadOps | undefined;
17
+ talkback: TalkBackControl;
18
+ session: AdbSession;
19
+ clock: Clock;
20
+ /** a touch, for the focus-then-double-tap fallback */
21
+ tap: Driver["tap"];
22
+ };
23
+ export declare function createScreenReaderOps(deps: ScreenReaderDeps): DrivesScreenReader & ActsOnElements;
@@ -1,6 +1,7 @@
1
1
  import type { PermissionName, PermissionState } from "../../../../core/src/conditions/conditions.types";
2
2
  import type { Driver } from "../driver.types";
3
3
  import type { AdbSession } from "./adb-session";
4
+ import { type TalkBackControl } from "./android-talkback";
4
5
  /** Permission name to the manifest permissions it covers; a name absent here is skipped with the reason in `ANDROID_NO_PERMISSION`. */
5
6
  export declare const ANDROID_PERMISSIONS: Partial<Record<PermissionName, readonly string[]>>;
6
7
  export declare const ANDROID_NO_PERMISSION: Partial<Record<PermissionName, string>>;
@@ -11,7 +12,7 @@ export declare const setTimezoneArgv: (tz: string) => string[];
11
12
  export declare const TIMEZONE_PROP_ARGV: readonly string[];
12
13
  export declare const setAppLocaleArgv: (pkg: string, tag: string | null) => string[];
13
14
  export declare const getAppLocaleArgv: (pkg: string) => string[];
14
- export declare const settingGetArgv: (table: "system" | "secure", key: string) => string[];
15
+ export declare const settingGetArgv: (table: "system" | "secure" | "global", key: string) => string[];
15
16
  export declare const BOLD_TEXT_ADJUSTMENT = 300;
16
17
  export declare const trimMemoryArgv: (pkg: string) => string[];
17
18
  export declare const BATTERY_RESET_ARGV: readonly string[];
@@ -29,6 +30,12 @@ export type AndroidSettingsDeps = {
29
30
  sleep?: (ms: number) => Promise<void>;
30
31
  /** the host's timezone, restored on `timezone(null)` when no previous one is given (default: `Intl`) */
31
32
  hostTimezone?: () => string;
33
+ /** the transition animation scale the device had before the driver zeroed it (what `reduceMotion: false` restores); `"null"` / absent = none known */
34
+ transitionOriginal?: () => string | undefined;
35
+ /** is `warden-uiad` how this driver reads the screen (TalkBack mode needs it: the dump fallback unbinds TalkBack); default false */
36
+ uiadAvailable?: () => boolean;
32
37
  };
33
38
  export type AndroidSettings = Pick<Driver, "permissions" | "timezone" | "locale" | "a11y" | "battery" | "memoryWarning" | "push">;
34
- export declare function createAndroidSettings(deps: AndroidSettingsDeps): AndroidSettings;
39
+ export declare function createAndroidSettings(deps: AndroidSettingsDeps): AndroidSettings & {
40
+ talkback: TalkBackControl;
41
+ };
@@ -0,0 +1,18 @@
1
+ import type { DriverResult } from "../driver.types";
2
+ import type { SpokenRead } from "../screen-reader";
3
+ import type { SrEventsRead } from "./uiad-screen-reader";
4
+ /**
5
+ * What TalkBack said since a cursor (`DrivesScreenReader.screenReaderSpoken`). The cursor is the device epoch ms of the
6
+ * last line returned, whichever source it came from. Real speech needs TalkBack's verbose log (a developer setting
7
+ * only its UI can change); without it the lines are composed by the speech model from the focus events the
8
+ * screen-reader stream saw, labelled `simulated`. Whichever the log allows is used for the whole read, never a mix.
9
+ */
10
+ export type SpokenDeps = {
11
+ /** TalkBack's log, epoch timestamps (`TALKBACK_LOGCAT_ARGV`); undefined when it cannot be read */
12
+ readLog(): Promise<string | undefined>;
13
+ /** focus moves since 0 (the ring is small) */
14
+ focusEvents(): DriverResult<SrEventsRead>;
15
+ now(): number;
16
+ sleep(ms: number): Promise<void>;
17
+ };
18
+ export declare function readSpoken(deps: SpokenDeps, since?: number): DriverResult<SpokenRead>;
@@ -0,0 +1,57 @@
1
+ import type { ScreenReaderState } from "../../a11y/a11y.types";
2
+ import type { DriverResult } from "../driver.types";
3
+ /**
4
+ * TalkBack as the screen reader of an Android emulator (WDN-A11Y, A11Y-7). Everything is shell: the two secure settings
5
+ * that switch an accessibility service on, `pm` for presence and the notification grant, `dumpsys accessibility` to
6
+ * see that the service is bound (enabled in settings is not running: a UiAutomation client connected without
7
+ * `FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES` unbinds it). The previous values of both settings are kept so turning
8
+ * TalkBack off, or the lease's undo, puts back exactly what was there.
9
+ */
10
+ export declare const TALKBACK_PACKAGE = "com.google.android.marvin.talkback";
11
+ export declare const TALKBACK_SERVICE = "com.google.android.marvin.talkback/com.google.android.marvin.talkback.TalkBackService";
12
+ export declare const POST_NOTIFICATIONS = "android.permission.POST_NOTIFICATIONS";
13
+ export declare const talkbackPathArgv: readonly string[];
14
+ export declare const talkbackGrantArgv: readonly string[];
15
+ export declare const accessibilityDumpArgv: readonly string[];
16
+ export declare const secureGetArgv: (key: string) => string[];
17
+ /** Set a secure key, or delete it when `value` is null (it was unset). The driver's shell quotes each argument. */
18
+ export declare const secureWriteArgv: (key: string, value: string | null) => string[];
19
+ /** `settings get` answers the text `null` for an unset key. */
20
+ export declare function settingValue(stdout: string): string | null;
21
+ /** `services` with the TalkBack component added after whatever is there (`:` separated, once). */
22
+ export declare function withTalkBack(services: string | null): string;
23
+ /** Is a TalkBack service among the bound ones in `dumpsys accessibility`? */
24
+ export declare function talkbackBound(dump: string): boolean;
25
+ export type Previous = {
26
+ services: string | null;
27
+ enabled: string | null;
28
+ };
29
+ type Shelled = {
30
+ ok: boolean;
31
+ stdout: string;
32
+ failure: string | undefined;
33
+ };
34
+ export type TalkBackDeps = {
35
+ serial: string;
36
+ shell(argv: readonly string[]): Promise<Shelled>;
37
+ must(argv: readonly string[]): DriverResult<string>;
38
+ /** is `warden-uiad` the way this driver reads the screen (the dump unbinds TalkBack) */
39
+ uiadAvailable(): boolean;
40
+ sleep(ms: number): Promise<void>;
41
+ };
42
+ export type TalkBackControl = {
43
+ /** warden turned TalkBack on and has not turned it off */
44
+ active(): boolean;
45
+ /** TalkBack is the screen reader now: warden's doing, or found already on and bound by `detect` */
46
+ on(): boolean;
47
+ /** read the device once (`fresh`: again, for a TalkBack another process turned on since) for a TalkBack that was on before warden touched anything (a crashed run, a person) */
48
+ detect(fresh?: boolean): Promise<boolean>;
49
+ state(): ScreenReaderState;
50
+ previous(): Previous | undefined;
51
+ enable(): DriverResult<ScreenReaderState>;
52
+ disable(): DriverResult<ScreenReaderState>;
53
+ /** is the service bound right now */
54
+ bound(): Promise<boolean>;
55
+ };
56
+ export declare function createTalkBack(deps: TalkBackDeps): TalkBackControl;
57
+ export {};
@@ -0,0 +1,23 @@
1
+ import type { DriverResult, Logger } from "../driver.types";
2
+ import type { AdbSession } from "./adb-session";
3
+ import { type PushDex } from "./uiad-device";
4
+ /**
5
+ * `warden-atf.jar` (the Accessibility Test Framework, ~180 KB) goes to the device on the first audit only, named by its
6
+ * hash like the uiad dex, so a new build is a new file and the always-pushed uiad dex stays small.
7
+ */
8
+ export type AtfJar = {
9
+ bytes: number;
10
+ jarSha256: string;
11
+ base64: string;
12
+ };
13
+ export declare function atfDevicePath(jar?: Pick<AtfJar, "jarSha256">): string;
14
+ export type EnsureAtfDeps = {
15
+ session: AdbSession;
16
+ adb: string;
17
+ serial: string;
18
+ push?: PushDex;
19
+ jar?: AtfJar;
20
+ log?: Logger;
21
+ };
22
+ /** The jar is on the device (written now if it was not); the result is its path. */
23
+ export declare function ensureAtfJar(deps: EnsureAtfDeps): DriverResult<string>;