@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.
Files changed (151) hide show
  1. package/{chunk-yb1hcjqm.mjs → chunk-vs5npy59.mjs} +3370 -218
  2. package/index.mjs +247 -16
  3. package/package.json +2 -2
  4. package/preload.mjs +8264 -2387
  5. package/types/core/src/a11y/a11y.config.d.ts +47 -0
  6. package/types/core/src/affected/affected.schema.d.ts +142 -1
  7. package/types/core/src/allocate.d.ts +2 -1
  8. package/types/core/src/android-keyguard.d.ts +36 -0
  9. package/types/core/src/batch/preset.schema.d.ts +13 -1
  10. package/types/core/src/biometrics.d.ts +15 -0
  11. package/types/core/src/builds/app-option.schema.d.ts +1 -1
  12. package/types/core/src/builds/builds.defaults.d.ts +15 -0
  13. package/types/core/src/builds/config.d.ts +5 -1
  14. package/types/core/src/builds/config.schema.d.ts +275 -2
  15. package/types/core/src/builds/define-config.d.ts +159 -0
  16. package/types/core/src/builds/install-device.d.ts +37 -0
  17. package/types/core/src/builds/install.d.ts +17 -3
  18. package/types/core/src/builds/kinds.d.ts +21 -3
  19. package/types/core/src/builds/lock.d.ts +38 -0
  20. package/types/core/src/builds/resolve.d.ts +12 -2
  21. package/types/core/src/camera-mode.d.ts +17 -0
  22. package/types/core/src/camera-placeholder.d.ts +9 -0
  23. package/types/core/src/camera-profile.d.ts +12 -0
  24. package/types/core/src/claim-errors.d.ts +34 -0
  25. package/types/core/src/conditions/a11y-keys.d.ts +45 -0
  26. package/types/core/src/conditions/camera-media.d.ts +30 -0
  27. package/types/core/src/conditions/conditions.config.d.ts +141 -0
  28. package/types/core/src/conditions/conditions.d.ts +22 -0
  29. package/types/core/src/conditions/conditions.types.d.ts +171 -0
  30. package/types/core/src/conditions/inject-state.d.ts +13 -0
  31. package/types/core/src/conditions/launch-settings.d.ts +14 -0
  32. package/types/core/src/conditions/network.presets.d.ts +42 -0
  33. package/types/core/src/conditions/text-size.d.ts +19 -0
  34. package/types/core/src/golden/android-biometrics.d.ts +35 -0
  35. package/types/core/src/ios-runner/build.d.ts +28 -0
  36. package/types/core/src/ios-runner/cache.d.ts +44 -0
  37. package/types/core/src/ios-runner/classify.d.ts +7 -0
  38. package/types/core/src/ios-runner/pack.d.ts +17 -0
  39. package/types/core/src/ios-runner/runner-build.types.d.ts +76 -0
  40. package/types/core/src/ios-runner/signing.d.ts +40 -0
  41. package/types/core/src/ios-runner/sim-build.d.ts +36 -0
  42. package/types/core/src/ios-runner/stage.d.ts +26 -0
  43. package/types/core/src/lease-events.d.ts +126 -0
  44. package/types/core/src/netproxy/control.d.ts +75 -0
  45. package/types/core/src/netproxy/decide.d.ts +22 -0
  46. package/types/core/src/netproxy/handle-file.d.ts +12 -0
  47. package/types/core/src/netproxy/lease-proxy.d.ts +53 -0
  48. package/types/core/src/netproxy/netproxy.main.d.ts +40 -0
  49. package/types/core/src/netproxy/pipe.d.ts +37 -0
  50. package/types/core/src/netproxy/proxy.d.ts +48 -0
  51. package/types/core/src/netproxy/shaping.d.ts +43 -0
  52. package/types/core/src/ownership.d.ts +44 -0
  53. package/types/core/src/providers/android.d.ts +46 -2
  54. package/types/core/src/providers/provider.types.d.ts +9 -0
  55. package/types/core/src/queue.d.ts +43 -0
  56. package/types/core/src/store.d.ts +61 -5
  57. package/types/core/src/types.d.ts +57 -0
  58. package/types/core/src/xcode.d.ts +22 -0
  59. package/types/e2e/src/a11y/a11y-target.d.ts +23 -0
  60. package/types/e2e/src/a11y/a11y.types.d.ts +81 -0
  61. package/types/e2e/src/a11y/announcements.d.ts +24 -0
  62. package/types/e2e/src/a11y/audit.d.ts +22 -0
  63. package/types/e2e/src/a11y/native/atf.d.ts +51 -0
  64. package/types/e2e/src/a11y/native/xcuitest.d.ts +60 -0
  65. package/types/e2e/src/a11y/order.d.ts +5 -0
  66. package/types/e2e/src/a11y/rules/duplicate-name.d.ts +6 -0
  67. package/types/e2e/src/a11y/rules/hidden-focusable.d.ts +3 -0
  68. package/types/e2e/src/a11y/rules/image-no-name.d.ts +6 -0
  69. package/types/e2e/src/a11y/rules/label-in-name.d.ts +7 -0
  70. package/types/e2e/src/a11y/rules/missing-name.d.ts +3 -0
  71. package/types/e2e/src/a11y/rules/role-in-name.d.ts +3 -0
  72. package/types/e2e/src/a11y/rules/rule.context.d.ts +22 -0
  73. package/types/e2e/src/a11y/rules/target-size.d.ts +21 -0
  74. package/types/e2e/src/a11y/rules/text-clipped.d.ts +9 -0
  75. package/types/e2e/src/a11y/speech.d.ts +26 -0
  76. package/types/e2e/src/a11y/talkback-log.d.ts +13 -0
  77. package/types/e2e/src/alert-buttons.d.ts +9 -0
  78. package/types/e2e/src/app.d.ts +3 -2
  79. package/types/e2e/src/conditions/condition-events.d.ts +68 -0
  80. package/types/e2e/src/conditions/device-conditions.d.ts +198 -0
  81. package/types/e2e/src/conditions/geo.d.ts +15 -0
  82. package/types/e2e/src/conditions/gpx.d.ts +8 -0
  83. package/types/e2e/src/conditions/lease-recorder.d.ts +27 -0
  84. package/types/e2e/src/conditions/lease-store.d.ts +9 -0
  85. package/types/e2e/src/conditions/location-plan.d.ts +17 -0
  86. package/types/e2e/src/conditions/location-scenarios.data.d.ts +17 -0
  87. package/types/e2e/src/conditions/route-ticker.d.ts +53 -0
  88. package/types/e2e/src/conditions/scheduler.d.ts +83 -0
  89. package/types/e2e/src/device-a11y.d.ts +97 -0
  90. package/types/e2e/src/device.d.ts +67 -4
  91. package/types/e2e/src/driver/a11y-events.d.ts +30 -0
  92. package/types/e2e/src/driver/a11y-read.d.ts +12 -0
  93. package/types/e2e/src/driver/android/android-animations.d.ts +7 -0
  94. package/types/e2e/src/driver/android/android-app.d.ts +4 -0
  95. package/types/e2e/src/driver/android/android-argv.d.ts +21 -0
  96. package/types/e2e/src/driver/android/android-camera-host.d.ts +23 -0
  97. package/types/e2e/src/driver/android/android-camera.d.ts +28 -0
  98. package/types/e2e/src/driver/android/android-clock.d.ts +28 -0
  99. package/types/e2e/src/driver/android/android-device.d.ts +8 -0
  100. package/types/e2e/src/driver/android/android-driver.d.ts +22 -0
  101. package/types/e2e/src/driver/android/android-network.d.ts +66 -0
  102. package/types/e2e/src/driver/android/android-phone.d.ts +47 -0
  103. package/types/e2e/src/driver/android/android-screen-reader.d.ts +23 -0
  104. package/types/e2e/src/driver/android/android-settings.d.ts +41 -0
  105. package/types/e2e/src/driver/android/android-spoken.d.ts +18 -0
  106. package/types/e2e/src/driver/android/android-state.d.ts +10 -0
  107. package/types/e2e/src/driver/android/android-talkback.d.ts +57 -0
  108. package/types/e2e/src/driver/android/atf-device.d.ts +23 -0
  109. package/types/e2e/src/driver/android/atf-dex.generated.d.ts +7 -0
  110. package/types/e2e/src/driver/android/uiad-dex.generated.d.ts +5 -5
  111. package/types/e2e/src/driver/android/uiad-ops.d.ts +27 -1
  112. package/types/e2e/src/driver/android/uiad-protocol.d.ts +8 -0
  113. package/types/e2e/src/driver/android/uiad-screen-reader.d.ts +33 -0
  114. package/types/e2e/src/driver/clock-recorder.d.ts +9 -0
  115. package/types/e2e/src/driver/device-settings.d.ts +10 -0
  116. package/types/e2e/src/driver/driver.types.d.ts +242 -1
  117. package/types/e2e/src/driver/element-actions.d.ts +16 -0
  118. package/types/e2e/src/driver/ios/device/capabilities.d.ts +3 -0
  119. package/types/e2e/src/driver/ios/device/device-driver.d.ts +49 -0
  120. package/types/e2e/src/driver/ios/device/devicectl.d.ts +38 -0
  121. package/types/e2e/src/driver/ios/device/runner-protocol.d.ts +21 -0
  122. package/types/e2e/src/driver/ios/hid-recovery.d.ts +15 -0
  123. package/types/e2e/src/driver/ios/inject.d.ts +124 -0
  124. package/types/e2e/src/driver/ios/ios-a11y.d.ts +73 -0
  125. package/types/e2e/src/driver/ios/ios-audit.d.ts +36 -0
  126. package/types/e2e/src/driver/ios/ios-driver.d.ts +50 -1
  127. package/types/e2e/src/driver/ios/ios-network.d.ts +51 -0
  128. package/types/e2e/src/driver/ios/ios-settings.d.ts +44 -0
  129. package/types/e2e/src/driver/ios/sim-runner.d.ts +41 -0
  130. package/types/e2e/src/driver/ios/simctl.d.ts +41 -1
  131. package/types/e2e/src/driver/ios/simd-transport.d.ts +11 -2
  132. package/types/e2e/src/driver/ios/type-budget.d.ts +6 -1
  133. package/types/e2e/src/driver/native-audit.d.ts +26 -0
  134. package/types/e2e/src/driver/network-proxy.d.ts +52 -0
  135. package/types/e2e/src/driver/open.d.ts +20 -0
  136. package/types/e2e/src/driver/physical-conditions.d.ts +11 -0
  137. package/types/e2e/src/driver/requirements.d.ts +14 -0
  138. package/types/e2e/src/driver/screen-reader.d.ts +26 -0
  139. package/types/e2e/src/driver/typing.d.ts +1 -1
  140. package/types/e2e/src/env.d.ts +47 -0
  141. package/types/e2e/src/expect/a11y-matchers.d.ts +33 -0
  142. package/types/e2e/src/expect/expect.d.ts +7 -2
  143. package/types/e2e/src/expect/matchers.d.ts +14 -0
  144. package/types/e2e/src/fixture-session.d.ts +4 -0
  145. package/types/e2e/src/fixtures.d.ts +6 -1
  146. package/types/e2e/src/index.d.ts +6 -0
  147. package/types/e2e/src/runner/callsite.d.ts +3 -1
  148. package/types/e2e/src/runner/scope.d.ts +1 -0
  149. package/types/e2e/src/runner/test.d.ts +6 -0
  150. package/types/e2e/src/screen/render.d.ts +8 -0
  151. package/types/e2e/src/traced-device.d.ts +2 -1
@@ -0,0 +1,22 @@
1
+ import { type AsyncResult } from "../../types/src/result";
2
+ import { type Exec } from "./exec";
3
+ /** The Xcode warden would build with: `DEVELOPER_DIR` when set (Apple's per-agent setup), else `xcode-select`. */
4
+ export type XcodeInfo = {
5
+ /** the Developer directory (`…/Xcode.app/Contents/Developer`) */
6
+ path: string;
7
+ /** `27.1` */
8
+ version: string;
9
+ /** `27A9275` */
10
+ build: string;
11
+ major: number;
12
+ source: "DEVELOPER_DIR" | "xcode-select";
13
+ };
14
+ type XcodeVersion = Pick<XcodeInfo, "version" | "build" | "major">;
15
+ /** `Xcode 27.1\nBuild version 27A9275` */
16
+ export declare function parseXcodeVersion(stdout: string): XcodeVersion | undefined;
17
+ /**
18
+ * The selected Xcode. `version.plist` beside `Developer/` names version + build in two ~10 ms spawns;
19
+ * `xcodebuild -version` (0.4–1 s) is the fallback and sees the same `DEVELOPER_DIR`.
20
+ */
21
+ export declare function readXcode(exec: Exec, env: Record<string, string | undefined>): AsyncResult<XcodeInfo>;
22
+ export {};
@@ -0,0 +1,23 @@
1
+ import type { Result } from "../../../types/src/result";
2
+ import type { DriverError } from "../driver/driver.types";
3
+ import type { A11yAuditOptions, A11yAuditResult } from "./a11y.types";
4
+ import type { AnnouncementMatch } from "./announcements";
5
+ /** What `toPassA11yAudit` / `toHaveAnnounced` need of a `device` (or the `screen` beside it), reached through {@link a11yTargetOf}. */
6
+ export type A11yTarget = {
7
+ /** the audit `device.a11yAudit` runs, suite defaults applied */
8
+ audit(options: A11yAuditOptions): Promise<A11yAuditResult>;
9
+ /** polls announcements and live-region changes until one matches `match` or `timeoutMs` passes (`E_TIMEOUT`) */
10
+ waitForAnnouncement(match: string | RegExp, options: {
11
+ timeoutMs: number;
12
+ since?: number;
13
+ }): Promise<Result<AnnouncementMatch, DriverError>>;
14
+ /** the current screen as text with the `--a11y` columns (failure evidence); undefined when it cannot be read */
15
+ a11yScreen(): Promise<string | undefined>;
16
+ /** the assertion timeout in ms */
17
+ assertionTimeout(): number;
18
+ /** one reported step */
19
+ step<T>(label: string, fn: () => Promise<T>): Promise<T>;
20
+ };
21
+ /** Ties a device (and the screen made beside it, or a wrapper of either) to its a11y hooks; `Device` itself does not expose them. */
22
+ export declare function registerA11yTarget(target: object, hooks: A11yTarget): void;
23
+ export declare function a11yTargetOf(target: unknown): A11yTarget | undefined;
@@ -0,0 +1,81 @@
1
+ import type { Platform, Role } from "../driver/driver.types";
2
+ /** Where a spoken line or issue came from: warden's own model, or a real platform engine. */
3
+ export type A11ySource = "simulated" | "talkback" | "xcuitest" | "atf" | "warden";
4
+ /** What a screen reader says for one element, split so matchers can check each part. */
5
+ export type Announcement = {
6
+ /** element id in the snapshot it came from */
7
+ id: string;
8
+ identifier?: string;
9
+ /** the full phrase, parts joined in the platform's order */
10
+ text: string;
11
+ role: Role;
12
+ platform: Platform;
13
+ source: A11ySource;
14
+ parts: {
15
+ label?: string;
16
+ value?: string;
17
+ /** spoken role word: "button", "heading", "switch button" */
18
+ role?: string;
19
+ /** spoken states: "selected", "dimmed", "on", "not checked" */
20
+ state: readonly string[];
21
+ hint?: string;
22
+ };
23
+ };
24
+ export type A11yRuleId = "missing-name" | "image-no-name" | "target-size" | "target-size-platform" | "duplicate-name" | "role-in-name" | "label-in-name" | "hidden-focusable" | "text-clipped" | "native";
25
+ export type A11ySeverity = "error" | "warning";
26
+ export type A11yIssue = {
27
+ rule: A11yRuleId;
28
+ severity: A11ySeverity;
29
+ /** WCAG 2.2 success criterion, e.g. "2.5.8" */
30
+ wcag?: string;
31
+ source: Exclude<A11ySource, "simulated" | "talkback">;
32
+ elementId?: string;
33
+ identifier?: string;
34
+ message: string;
35
+ };
36
+ export type A11yIgnore = {
37
+ rule: A11yRuleId;
38
+ identifier?: string;
39
+ };
40
+ export type A11yAuditOptions = {
41
+ rules?: readonly A11yRuleId[];
42
+ ignore?: readonly A11yIgnore[];
43
+ /** lowest severity that fails `toPassA11yAudit` / `warden ui a11y audit`; default "error" */
44
+ minSeverity?: A11ySeverity;
45
+ };
46
+ export type A11yAuditResult = {
47
+ issues: readonly A11yIssue[];
48
+ checked: number;
49
+ sources: readonly A11yIssue["source"][];
50
+ skipped: readonly {
51
+ source: A11yIssue["source"];
52
+ reason: string;
53
+ }[];
54
+ };
55
+ export type ScreenReaderState = {
56
+ mode: "real";
57
+ engine: "talkback";
58
+ } | {
59
+ mode: "simulated";
60
+ engine: "voiceover";
61
+ } | {
62
+ mode: "off";
63
+ };
64
+ export type SpokenLine = {
65
+ text: string;
66
+ at: number;
67
+ source: A11ySource;
68
+ };
69
+ /** Where an {@link A11yEvent} was observed: the Android `warden-uiad` event stream, or the iOS injected library. */
70
+ export type A11yEventSource = "uiad" | "inject";
71
+ export type A11yEventKind = "announcement" | "live-region" | "screen-changed" | "layout-changed";
72
+ /**
73
+ * Something the app told assistive technology, in the order it happened (WDN-A11Y, A11Y-8). `text` is the announced
74
+ * string (announcements and live regions); screen/layout changes carry none. `at` is epoch ms on the device.
75
+ */
76
+ export type A11yEvent = {
77
+ kind: A11yEventKind;
78
+ text?: string;
79
+ at: number;
80
+ source: A11yEventSource;
81
+ };
@@ -0,0 +1,24 @@
1
+ import type { Result } from "../../../types/src/result";
2
+ import type { Driver, DriverError } from "../driver/driver.types";
3
+ import type { A11yEvent } from "./a11y.types";
4
+ export type WaitForAnnouncementOptions = {
5
+ timeoutMs?: number;
6
+ /** cursor from an earlier read: only events after it count (default: from the start) */
7
+ since?: number;
8
+ pollMs?: number;
9
+ /** injected clock and sleep (tests) */
10
+ now?: () => number;
11
+ sleep?: (ms: number) => Promise<void>;
12
+ };
13
+ export declare const ANNOUNCEMENT_TIMEOUT_MS = 5000;
14
+ export declare const ANNOUNCEMENT_POLL_MS = 100;
15
+ export type AnnouncementMatch = {
16
+ event: A11yEvent;
17
+ cursor: number;
18
+ };
19
+ /**
20
+ * Poll the driver's a11y events until an announcement or live-region change whose text contains `match` (string) or
21
+ * matches it (RegExp) arrives. `E_UNSUPPORTED` when the driver cannot read events, `E_TIMEOUT` (with the texts seen)
22
+ * when nothing matched in time. The returned `cursor` is where the next wait should start.
23
+ */
24
+ export declare function waitForAnnouncement(driver: Driver, match: string | RegExp, opts?: WaitForAnnouncementOptions): Promise<Result<AnnouncementMatch, DriverError>>;
@@ -0,0 +1,22 @@
1
+ import type { TextSize } from "../../../core/src/conditions/conditions.types";
2
+ import type { Platform, Snapshot } from "../driver/driver.types";
3
+ import type { A11yAuditOptions, A11yAuditResult, A11yIssue, A11ySeverity } from "./a11y.types";
4
+ import type { RuleEntry } from "./rules/rule.context";
5
+ /** The rules warden runs itself, in report order. `native` findings come from XCUITest / ATF (A11Y-4). */
6
+ export declare const WARDEN_RULES: readonly RuleEntry[];
7
+ export type RunAuditOptions = A11yAuditOptions & {
8
+ /** active text size; `text-clipped` only runs from `accessibility-medium` (scale 1.6) up */
9
+ textSize?: TextSize;
10
+ };
11
+ /** Runs warden's own rules on one snapshot. Native (XCUITest / ATF) findings are added with `mergeIssues`. */
12
+ export declare function runAudit(snapshot: Snapshot, platform: Platform, opts?: RunAuditOptions): A11yAuditResult;
13
+ /** Issues at or above `minSeverity` (default `error`): what fails `toPassA11yAudit` / `warden ui a11y audit`. */
14
+ export declare function failing(result: A11yAuditResult, minSeverity?: A11ySeverity): A11yIssue[];
15
+ /**
16
+ * Adds another source's findings to a result. A finding on the same element and WCAG criterion as one already there
17
+ * is dropped, and the kept issue notes the other source; findings without a WCAG ref only dedupe on an identical
18
+ * element + message. `skipped` records engines that could not run (never fails the audit).
19
+ */
20
+ export declare function mergeIssues(base: A11yAuditResult, extra: readonly A11yIssue[], skipped?: A11yAuditResult["skipped"]): A11yAuditResult;
21
+ /** Compact table for test failure output and the CLI: severity, rule, WCAG, element, message. */
22
+ export declare function formatIssues(result: A11yAuditResult, minSeverity?: A11ySeverity): string;
@@ -0,0 +1,51 @@
1
+ import { z } from "zod";
2
+ import type { NativeAuditResult } from "../../driver/native-audit";
3
+ import type { A11yIssue } from "../a11y.types";
4
+ export declare const atfReplySchema: z.ZodObject<{
5
+ checks: z.ZodNumber;
6
+ ignored: z.ZodOptional<z.ZodNumber>;
7
+ contrast: z.ZodOptional<z.ZodBoolean>;
8
+ hierarchyMs: z.ZodOptional<z.ZodNumber>;
9
+ checksMs: z.ZodOptional<z.ZodNumber>;
10
+ failedChecks: z.ZodOptional<z.ZodArray<z.ZodString>>;
11
+ results: z.ZodArray<z.ZodObject<{
12
+ check: z.ZodString;
13
+ type: z.ZodEnum<{
14
+ ERROR: "ERROR";
15
+ WARNING: "WARNING";
16
+ INFO: "INFO";
17
+ }>;
18
+ resultId: z.ZodOptional<z.ZodNumber>;
19
+ resourceName: z.ZodOptional<z.ZodString>;
20
+ className: z.ZodOptional<z.ZodString>;
21
+ text: z.ZodOptional<z.ZodString>;
22
+ contentDescription: z.ZodOptional<z.ZodString>;
23
+ bounds: z.ZodOptional<z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber, z.ZodNumber], null>>;
24
+ message: z.ZodOptional<z.ZodString>;
25
+ }, z.core.$strip>>;
26
+ }, z.core.$strip>;
27
+ export type AtfReply = z.infer<typeof atfReplySchema>;
28
+ export type AtfResult = AtfReply["results"][number];
29
+ /** WCAG 2.2 success criterion per ATF check (class name without the `Check` suffix). */
30
+ export declare const ATF_WCAG: Readonly<Record<string, string>>;
31
+ export declare function atfCheckName(check: string): string;
32
+ export declare function mapAtfResult(result: AtfResult): A11yIssue;
33
+ /** The raw `audit` reply -> issues; a reply that does not parse is a `skipped` with the reason, never a throw. */
34
+ export declare function mapAtfReply(raw: unknown): NativeAuditResult;
35
+ /** What the driver can do for ATF: run the op (`undefined` = no `warden-uiad` to run it). */
36
+ export type AtfPort = {
37
+ run(options: {
38
+ contrast: boolean;
39
+ }): Promise<{
40
+ success: true;
41
+ data: unknown;
42
+ } | {
43
+ success: false;
44
+ error: {
45
+ message: string;
46
+ };
47
+ } | undefined>;
48
+ };
49
+ export declare function atfAudit(port: AtfPort, options?: {
50
+ contrast?: boolean;
51
+ }): Promise<NativeAuditResult>;
@@ -0,0 +1,60 @@
1
+ import { z } from "zod";
2
+ import type { NativeAuditResult } from "../../driver/native-audit";
3
+ import type { A11yIssue } from "../a11y.types";
4
+ /**
5
+ * The XCUITest accessibility audit (`performAccessibilityAudit`, iOS 17+) as `A11yIssue`s. The runner's `audit` op
6
+ * answers `{issues:[{auditType, compactDescription, detailedDescription, element?}], count, types, ms}`
7
+ * (`native/ios-runner/RunnerCore/.../AuditEncoder.swift`); this file maps it. Pure: a port supplies the reply.
8
+ */
9
+ export declare const AUDIT_TYPES: readonly ["contrast", "elementDetection", "hitRegion", "sufficientElementDescription", "dynamicType", "textClipped", "trait"];
10
+ export type XcuitestAuditType = (typeof AUDIT_TYPES)[number];
11
+ export declare const auditReplySchema: z.ZodObject<{
12
+ issues: z.ZodArray<z.ZodObject<{
13
+ auditType: z.ZodString;
14
+ auditTypes: z.ZodOptional<z.ZodArray<z.ZodString>>;
15
+ compactDescription: z.ZodString;
16
+ detailedDescription: z.ZodString;
17
+ element: z.ZodOptional<z.ZodObject<{
18
+ type: z.ZodString;
19
+ identifier: z.ZodString;
20
+ label: z.ZodString;
21
+ frame: z.ZodObject<{
22
+ x: z.ZodNumber;
23
+ y: z.ZodNumber;
24
+ width: z.ZodNumber;
25
+ height: z.ZodNumber;
26
+ }, z.core.$strip>;
27
+ }, z.core.$strip>>;
28
+ }, z.core.$strip>>;
29
+ count: z.ZodNumber;
30
+ types: z.ZodOptional<z.ZodArray<z.ZodString>>;
31
+ ms: z.ZodOptional<z.ZodNumber>;
32
+ }, z.core.$strip>;
33
+ export type AuditReply = z.infer<typeof auditReplySchema>;
34
+ export type AuditWireIssue = AuditReply["issues"][number];
35
+ /** WCAG 2.2 success criterion per audit type. */
36
+ export declare const XCUITEST_WCAG: Readonly<Record<string, string>>;
37
+ export declare function mapAuditIssue(issue: AuditWireIssue): A11yIssue;
38
+ export declare function mapAuditReply(raw: unknown): NativeAuditResult;
39
+ /** What the iOS driver gives the audit: the runner `audit` op, or the reason it cannot run. */
40
+ export type XcuitestPort = {
41
+ audit(params: {
42
+ bundleId: string;
43
+ types?: readonly XcuitestAuditType[];
44
+ }): Promise<{
45
+ success: true;
46
+ data: unknown;
47
+ } | {
48
+ success: false;
49
+ error: {
50
+ code: string;
51
+ message: string;
52
+ };
53
+ }>;
54
+ };
55
+ export declare function xcuitestAudit(port: XcuitestPort | {
56
+ unavailable: string;
57
+ }, options?: {
58
+ bundleId?: string;
59
+ types?: readonly XcuitestAuditType[];
60
+ }): Promise<NativeAuditResult>;
@@ -0,0 +1,5 @@
1
+ import type { Platform, Snapshot } from "../driver/driver.types";
2
+ import type { Announcement } from "./a11y.types";
3
+ import { type AnnounceOptions } from "./speech";
4
+ /** The announcements a swipe-right sweep would produce, in order. Every entry has `source: "simulated"`. */
5
+ export declare function readingOrder(snapshot: Snapshot, platform: Platform, opts?: AnnounceOptions): Announcement[];
@@ -0,0 +1,6 @@
1
+ import { type Rule } from "./rule.context";
2
+ /**
3
+ * 2.4.6: several controls announced identically ("Delete", "Delete") cannot be told apart by ear. One issue per
4
+ * extra control; same identifier or same frame means the same control seen twice, not a duplicate.
5
+ */
6
+ export declare const duplicateName: Rule;
@@ -0,0 +1,3 @@
1
+ import { type Rule } from "./rule.context";
2
+ /** A node the platform still marks focusable and important while it is hidden, empty or off screen: a screen reader lands on nothing. */
3
+ export declare const hiddenFocusable: Rule;
@@ -0,0 +1,6 @@
1
+ import { type Rule } from "./rule.context";
2
+ /**
3
+ * 1.1.1: an image a screen reader can reach needs a name. Decorative images (`important: false`, hidden) are not
4
+ * in `ctx.reachable`, so marking one decorative is the fix.
5
+ */
6
+ export declare const imageNoName: Rule;
@@ -0,0 +1,7 @@
1
+ import { type Rule } from "./rule.context";
2
+ /**
3
+ * 2.5.3: voice-control users say what they see. When the accessible name is a separate label (content-desc /
4
+ * accessibilityLabel) and the control shows other text, the visible text must be inside the name. Names built from
5
+ * the visible text itself can't fail.
6
+ */
7
+ export declare const labelInName: Rule;
@@ -0,0 +1,3 @@
1
+ import { type Rule } from "./rule.context";
2
+ /** 4.1.2: a control a screen reader stops on must have a name, or it is announced as just its role. */
3
+ export declare const missingName: Rule;
@@ -0,0 +1,3 @@
1
+ import { type Rule } from "./rule.context";
2
+ /** The screen reader already says the role ("Save, button"); a name that says it too is announced twice. */
3
+ export declare const roleInName: Rule;
@@ -0,0 +1,22 @@
1
+ import type { Element, Platform, Snapshot } from "../../driver/driver.types";
2
+ import type { A11yIssue, A11yRuleId } from "../a11y.types";
3
+ /** What every rule reads: the snapshot, its platform and (for text-clipped) the active text size. */
4
+ export type RuleContext = {
5
+ platform: Platform;
6
+ /** font scale in force (iOS content-size multiplier / Android font_scale); undefined = not set by the run */
7
+ textScale?: number;
8
+ /** nodes a screen reader can reach: not hidden (nor under a hidden node), `important !== false` */
9
+ reachable: readonly Element[];
10
+ parents: ReadonlyMap<Element, Element>;
11
+ };
12
+ export type Rule = (snapshot: Snapshot, ctx: RuleContext) => A11yIssue[];
13
+ export type RuleEntry = {
14
+ id: A11yRuleId;
15
+ run: Rule;
16
+ };
17
+ /** Points (iOS) / dp (Android) per frame unit: Android frames are px. */
18
+ export declare function unitsPerPoint(snapshot: Snapshot): number;
19
+ export declare function isAncestor(a: Element, b: Element, parents: ReadonlyMap<Element, Element>): boolean;
20
+ export declare function related(a: Element, b: Element, parents: ReadonlyMap<Element, Element>): boolean;
21
+ export declare function issue(rule: A11yRuleId, severity: A11yIssue["severity"], el: Element, message: string, wcag?: string): A11yIssue;
22
+ export declare function describe(el: Element): string;
@@ -0,0 +1,21 @@
1
+ import type { Element, Snapshot } from "../../driver/driver.types";
2
+ import { type Rule, type RuleContext } from "./rule.context";
3
+ export declare const WCAG_MIN = 24;
4
+ export declare const IOS_MIN = 44;
5
+ export declare const ANDROID_MIN = 48;
6
+ type Box = {
7
+ el: Element;
8
+ x: number;
9
+ y: number;
10
+ w: number;
11
+ h: number;
12
+ };
13
+ /**
14
+ * WCAG 2.5.8 spacing exception, simplified: a 24 pt circle centred on each undersized target must not touch another
15
+ * target (circle-to-circle for two undersized targets, circle-to-rectangle otherwise). Ancestors and descendants
16
+ * of a target do not count as "another target". Sub-24 targets that pass the test are not reported.
17
+ */
18
+ export declare function wcagFailures(snapshot: Snapshot, ctx: RuleContext): Box[];
19
+ export declare const targetSize: Rule;
20
+ export declare const targetSizePlatform: Rule;
21
+ export {};
@@ -0,0 +1,9 @@
1
+ import { type Rule } from "./rule.context";
2
+ /** iOS `accessibility-medium`; Android font_scale 1.6 is the same step. */
3
+ export declare const CLIPPING_MIN_SCALE = 1.6;
4
+ /**
5
+ * 1.4.4: at large text sizes text must still be readable. Runs only when the run's text scale is at least
6
+ * accessibility-medium (`ctx.textScale`). Flags text ending in an ellipsis, and text whose frame spills out of a
7
+ * non-scrolling parent. Scroll containers and the screen-sized root are exempt.
8
+ */
9
+ export declare const textClipped: Rule;
@@ -0,0 +1,26 @@
1
+ import type { Element, Platform } from "../driver/driver.types";
2
+ import type { Announcement } from "./a11y.types";
3
+ /**
4
+ * Speech model. APPROXIMATES, never equals, what VoiceOver (iOS) or TalkBack (Android) says:
5
+ * word order and role/state vocabulary follow the documented behaviour of each reader, but
6
+ * locale, verbosity settings, OS version differences and app-specific custom roles are not modelled.
7
+ * Every result carries `source: "simulated"`.
8
+ */
9
+ export type AnnounceOptions = {
10
+ /** include the hint as the last part (default true) */
11
+ hints?: boolean;
12
+ };
13
+ /** Can the user activate / edit this node? (what makes a screen reader stop on it and swallow its children) */
14
+ export declare function isActionable(el: Element, platform: Platform): boolean;
15
+ /** Screen-reader focus target: actionable, or the platform says it is focusable. */
16
+ export declare function isFocusTarget(el: Element, platform: Platform): boolean;
17
+ /**
18
+ * The accessible name. label || text || (Android) value/placeholder of inputs || merged descendant text
19
+ * when the node is a focus target whose children are not themselves actionable (both readers merge them).
20
+ */
21
+ export declare function accessibleName(el: Element, platform: Platform): string | undefined;
22
+ /**
23
+ * What the platform's screen reader would say when focus lands on `el`. SIMULATED: an approximation of
24
+ * VoiceOver / TalkBack from the snapshot's facts, not their real output. `source` is always `"simulated"`.
25
+ */
26
+ export declare function announce(el: Element, platform: Platform, opts?: AnnounceOptions): Announcement;
@@ -0,0 +1,13 @@
1
+ import type { SpokenLine } from "./a11y.types";
2
+ /** One-shot dump of TalkBack's log with epoch timestamps. */
3
+ export declare const TALKBACK_LOGCAT_ARGV: readonly string[];
4
+ export type TalkBackLogOptions = {
5
+ /** `at` for a line with no epoch prefix */
6
+ now: number;
7
+ /** keep only lines after this epoch ms */
8
+ since?: number;
9
+ };
10
+ /** The spoken fragments in `log`, in log order. */
11
+ export declare function parseTalkBackLog(log: string, opts: TalkBackLogOptions): SpokenLine[];
12
+ /** Is TalkBack's log level verbose, as far as this log can tell: it holds at least one spoken fragment. */
13
+ export declare function hasSpeechLines(log: string): boolean;
@@ -39,3 +39,12 @@ export declare const ANDROID_ALLOW_IDS: readonly string[];
39
39
  * `AlertDialog`'s negative `button2` (after the names).
40
40
  */
41
41
  export declare const ANDROID_DENY_IDS: readonly string[];
42
+ /**
43
+ * The ids of the Android autofill "Save password?" sheet's buttons (`autofill_save_no` "Never", `autofill_save_yes`
44
+ * "Continue"). Either one marks the sheet, in any locale. Its two answers write to the user's account ("Never" a
45
+ * permanent opt-out, "Continue" a stored password), so `device.alert` never presses them: both `accept` and `dismiss`
46
+ * close the sheet with the Back key.
47
+ */
48
+ export declare const ANDROID_AUTOFILL_SAVE_IDS: readonly string[];
49
+ /** Whether `buttons` belong to the Android autofill save sheet. */
50
+ export declare function isAutofillSaveSheet(buttons: readonly Element[]): boolean;
@@ -30,7 +30,6 @@ export type OpenOptions = {
30
30
  */
31
31
  wait?: "quiet" | "none" | OpenWaitUntil;
32
32
  };
33
- /** The app under test (`WARDEN_BUNDLE_ID`). */
34
33
  export type App = {
35
34
  /**
36
35
  * Launches the app (terminating a running instance first) with `WARDEN_LAUNCH_ARGS` + `args`, then
@@ -58,5 +57,7 @@ export type AppDeps = {
58
57
  log?: (line: string) => void;
59
58
  /** time for the `wait: "quiet"` content poll; default the system clock */
60
59
  clock?: Clock;
60
+ /** whether an iOS launch loads the warden network dylib (the suite declares a network or clock condition, or one is active) */
61
+ injectWanted?: () => boolean;
61
62
  };
62
- export declare function createApp({ session, driver, env, log, clock }: AppDeps): App;
63
+ export declare function createApp({ session, driver, env, log, clock, injectWanted }: AppDeps): App;
@@ -0,0 +1,68 @@
1
+ import type { A11yCondition, BatteryCondition, CameraCondition, ClockCondition, HostFault, LocaleCondition, LocationCondition, NetworkCondition, PermissionCondition } from "../../../core/src/conditions/conditions.types";
2
+ /**
3
+ * One applied condition transition, the evidence line that lets a failure show which network/location was
4
+ * active. Written to `results.jsonl` beside the test results (`readResults` skips lines that are not a
5
+ * `TestResult`, so the two kinds share the file).
6
+ */
7
+ export type ConditionEvent = {
8
+ type: "condition";
9
+ area: "network";
10
+ value: NetworkCondition;
11
+ at: number;
12
+ } | {
13
+ type: "condition";
14
+ area: "location";
15
+ value: LocationCondition;
16
+ at: number;
17
+ } | {
18
+ type: "condition";
19
+ area: "clock";
20
+ value: ClockCondition;
21
+ at: number;
22
+ } | {
23
+ type: "condition";
24
+ area: "permission";
25
+ value: PermissionCondition;
26
+ at: number;
27
+ } | {
28
+ type: "condition";
29
+ area: "locale";
30
+ value: LocaleCondition;
31
+ at: number;
32
+ } | {
33
+ type: "condition";
34
+ area: "a11y";
35
+ value: A11yCondition;
36
+ at: number;
37
+ } | {
38
+ type: "condition";
39
+ area: "battery";
40
+ value: BatteryCondition;
41
+ at: number;
42
+ } | {
43
+ type: "condition";
44
+ area: "camera";
45
+ value: CameraCondition;
46
+ at: number;
47
+ } | {
48
+ type: "condition";
49
+ area: "faults";
50
+ value: {
51
+ kind: "faults";
52
+ rules: readonly HostFault[];
53
+ };
54
+ at: number;
55
+ };
56
+ export type ConditionSink = (event: ConditionEvent) => void;
57
+ /** A sink that appends each event as one JSON line (`appendFileSync`: a crash never loses an earlier line). */
58
+ export declare function openConditionSink(path: string): ConditionSink;
59
+ /** Condition events in file order; every other line (test results, blank, truncated) is skipped. */
60
+ export declare function readConditionEvents(text: string): ConditionEvent[];
61
+ /** What was active per area at epoch ms `at`: the latest event at or before it. */
62
+ export declare function activeConditionsAt(events: readonly ConditionEvent[], at: number): {
63
+ network?: NetworkCondition;
64
+ location?: LocationCondition;
65
+ clock?: ClockCondition;
66
+ };
67
+ /** The `kind` of an event's value (`offline`, `point`, ...); the area name for a setting that has none (`permission`, `a11y`). */
68
+ export declare function eventKind(event: ConditionEvent): string;