@delacour/warden-e2e 0.7.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-empqr9n0.mjs → chunk-vs5npy59.mjs} +2010 -169
- package/index.mjs +214 -13
- package/package.json +2 -2
- package/preload.mjs +3002 -798
- package/types/core/src/a11y/a11y.config.d.ts +47 -0
- package/types/core/src/affected/affected.schema.d.ts +49 -1
- package/types/core/src/android-keyguard.d.ts +2 -0
- package/types/core/src/batch/preset.schema.d.ts +12 -1
- package/types/core/src/builds/app-option.schema.d.ts +1 -1
- package/types/core/src/builds/config.schema.d.ts +93 -2
- package/types/core/src/builds/define-config.d.ts +55 -0
- 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 +24 -0
- package/types/core/src/conditions/conditions.types.d.ts +30 -0
- package/types/core/src/conditions/inject-state.d.ts +1 -1
- 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/pack.d.ts +17 -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/ownership.d.ts +44 -0
- package/types/core/src/providers/android.d.ts +10 -1
- 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 +10 -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/app.d.ts +0 -1
- package/types/e2e/src/conditions/condition-events.d.ts +6 -1
- package/types/e2e/src/conditions/device-conditions.d.ts +30 -2
- package/types/e2e/src/device-a11y.d.ts +97 -0
- package/types/e2e/src/device.d.ts +11 -2
- 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-argv.d.ts +2 -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-driver.d.ts +5 -0
- package/types/e2e/src/driver/android/android-screen-reader.d.ts +23 -0
- package/types/e2e/src/driver/android/android-settings.d.ts +9 -2
- package/types/e2e/src/driver/android/android-spoken.d.ts +18 -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/driver.types.d.ts +71 -5
- package/types/e2e/src/driver/element-actions.d.ts +16 -0
- package/types/e2e/src/driver/ios/inject.d.ts +16 -2
- 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 +21 -1
- package/types/e2e/src/driver/ios/ios-network.d.ts +7 -2
- package/types/e2e/src/driver/ios/sim-runner.d.ts +41 -0
- package/types/e2e/src/driver/ios/simd-transport.d.ts +11 -2
- package/types/e2e/src/driver/native-audit.d.ts +26 -0
- package/types/e2e/src/driver/physical-conditions.d.ts +1 -1
- package/types/e2e/src/driver/screen-reader.d.ts +26 -0
- package/types/e2e/src/env.d.ts +16 -1
- 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/index.d.ts +5 -1
- package/types/e2e/src/runner/callsite.d.ts +3 -1
- package/types/e2e/src/screen/render.d.ts +8 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { WaitReason } from "./queue";
|
|
1
2
|
export type Platform = "ios" | "android";
|
|
2
3
|
/** Facts about a real phone/tablet, from its provider. Only physical devices carry one. */
|
|
3
4
|
export type PhysicalInfo = {
|
|
@@ -84,6 +85,11 @@ export type Lease = {
|
|
|
84
85
|
* attaches to it instead of refusing with `device_busy`, and never releases it. Other owners still see it as foreign.
|
|
85
86
|
*/
|
|
86
87
|
shared?: boolean;
|
|
88
|
+
/**
|
|
89
|
+
* The `claimDevices` call(s) that took this lease: a wrapper's legs share one id, so the per-owner limit counts
|
|
90
|
+
* claims, not devices. Absent on leases an older binary wrote (each counts as its own claim).
|
|
91
|
+
*/
|
|
92
|
+
claimId?: string;
|
|
87
93
|
};
|
|
88
94
|
export type DeviceState = "booted" | "shutdown" | "booting";
|
|
89
95
|
/** A device as seen by a provider, enriched with warden's own bookkeeping. */
|
|
@@ -160,6 +166,10 @@ export type QueueEntry = {
|
|
|
160
166
|
owner: Owner;
|
|
161
167
|
label?: string;
|
|
162
168
|
pid?: number;
|
|
169
|
+
/** why the claim waits, rewritten every round (absent on rows an older binary wrote) */
|
|
170
|
+
reason?: WaitReason;
|
|
171
|
+
/** the claim this row belongs to (absent on rows an older binary wrote) */
|
|
172
|
+
claimId?: string;
|
|
163
173
|
};
|
|
164
174
|
/** The one constructor of a device lease resource: carries `physical` for real devices. */
|
|
165
175
|
export declare function deviceResourceOf(d: InventoryDevice): DeviceResource;
|
|
@@ -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,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,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;
|
package/types/e2e/src/app.d.ts
CHANGED
|
@@ -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
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { A11yCondition, BatteryCondition, ClockCondition, HostFault, LocaleCondition, LocationCondition, NetworkCondition, PermissionCondition } from "../../../core/src/conditions/conditions.types";
|
|
1
|
+
import type { A11yCondition, BatteryCondition, CameraCondition, ClockCondition, HostFault, LocaleCondition, LocationCondition, NetworkCondition, PermissionCondition } from "../../../core/src/conditions/conditions.types";
|
|
2
2
|
/**
|
|
3
3
|
* One applied condition transition, the evidence line that lets a failure show which network/location was
|
|
4
4
|
* active. Written to `results.jsonl` beside the test results (`readResults` skips lines that are not a
|
|
@@ -39,6 +39,11 @@ export type ConditionEvent = {
|
|
|
39
39
|
area: "battery";
|
|
40
40
|
value: BatteryCondition;
|
|
41
41
|
at: number;
|
|
42
|
+
} | {
|
|
43
|
+
type: "condition";
|
|
44
|
+
area: "camera";
|
|
45
|
+
value: CameraCondition;
|
|
46
|
+
at: number;
|
|
42
47
|
} | {
|
|
43
48
|
type: "condition";
|
|
44
49
|
area: "faults";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type NetworkInput } from "../../../core/src/conditions/conditions.config";
|
|
2
|
-
import type { ClockCondition, HostFault, LatLon, LocationCondition, LocationScenario, NetworkCondition } from "../../../core/src/conditions/conditions.types";
|
|
3
|
-
import type { A11yOp, A11yState, BatteryOp, BatteryState, Driver, LocaleState, NetworkState, PermissionGrants, PermissionsState, PushPayload, SettingOptions, TimezoneState } from "../driver/driver.types";
|
|
2
|
+
import type { CameraFacing, ClockCondition, HostFault, LatLon, LocationCondition, LocationScenario, NetworkCondition } from "../../../core/src/conditions/conditions.types";
|
|
3
|
+
import type { A11yOp, A11yState, BatteryOp, BatteryState, CameraState, Driver, LocaleState, NetworkState, PermissionGrants, PermissionsState, PushPayload, SettingOptions, TimezoneState } from "../driver/driver.types";
|
|
4
4
|
import type { E2eEnv } from "../env";
|
|
5
5
|
import { type ConditionSink } from "./condition-events";
|
|
6
6
|
import type { ConditionRecorder } from "./lease-recorder";
|
|
@@ -123,7 +123,31 @@ export type ConditionsInternals = {
|
|
|
123
123
|
/** the suite declares something to apply */
|
|
124
124
|
readonly declared: boolean;
|
|
125
125
|
};
|
|
126
|
+
/** `facing` of `device.camera.set` / `clear`: one camera, or `"both"`. */
|
|
127
|
+
export type CameraFacingChoice = CameraFacing | "both";
|
|
128
|
+
export type CameraSetOptions = {
|
|
129
|
+
/** which camera plays the asset; default `"back"` */
|
|
130
|
+
facing?: CameraFacingChoice;
|
|
131
|
+
/** video only: play again from the start at the end; default true */
|
|
132
|
+
loop?: boolean;
|
|
133
|
+
};
|
|
134
|
+
export type DeviceCamera = {
|
|
135
|
+
/**
|
|
136
|
+
* Feeds the png, jpg, heic, mp4 or mov at `path` (relative to the spec file) into the app's camera, replacing what
|
|
137
|
+
* that facing played; the other facing keeps its feed. The result says when it shows: `appliesOnLaunch` (iOS: an
|
|
138
|
+
* app launched without the camera sees it from the next launch), `appliesOnCameraOpen` (Android: the app's next
|
|
139
|
+
* camera open) or `rebooted` (Android: a video feed restarted the emulator). Android needs a `--camera` device
|
|
140
|
+
* (`E_CAMERA_DEVICE` otherwise); a missing or unsupported file fails with `E_CAMERA_NOT_FOUND` / `E_CAMERA_MEDIA`.
|
|
141
|
+
* Taken off when the test ends (back to the suite's `camera`).
|
|
142
|
+
*/
|
|
143
|
+
set(path: string, options?: CameraSetOptions): Promise<CameraState>;
|
|
144
|
+
/** Takes the feed off one camera, or both (the default). */
|
|
145
|
+
clear(facing?: CameraFacingChoice): Promise<CameraState>;
|
|
146
|
+
/** The feeds this spec has in place. */
|
|
147
|
+
status(): Promise<CameraState>;
|
|
148
|
+
};
|
|
126
149
|
export type DeviceConditions = {
|
|
150
|
+
camera: DeviceCamera;
|
|
127
151
|
network: DeviceNetwork;
|
|
128
152
|
location: DeviceLocation;
|
|
129
153
|
clock: DeviceClock;
|
|
@@ -156,6 +180,10 @@ export type DeviceConditionsDeps = {
|
|
|
156
180
|
log?: (line: string) => void;
|
|
157
181
|
/** where changes are written on the device's lease so release can undo them after a crash; default none */
|
|
158
182
|
recorder?: ConditionRecorder;
|
|
183
|
+
/** the warden home camera assets are staged under; default `$WARDEN_HOME` / `~/.warden` */
|
|
184
|
+
wardenHome?: string;
|
|
185
|
+
/** the directory a relative camera path is resolved against; default the calling spec file's, else `specsDir`, else cwd */
|
|
186
|
+
specDir?: () => string | undefined;
|
|
159
187
|
};
|
|
160
188
|
/** Ties a device (or a wrapper of it) to its conditions' runner hooks; `Device` itself does not expose them. */
|
|
161
189
|
export declare function registerConditions(device: object, internals: ConditionsInternals): void;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import type { TextSize } from "../../core/src/conditions/conditions.types";
|
|
2
|
+
import type { Result } from "../../types/src/result";
|
|
3
|
+
import type { A11yAuditOptions, A11yAuditResult, A11yEventSource, Announcement, ScreenReaderState, SpokenLine } from "./a11y/a11y.types";
|
|
4
|
+
import type { A11yTarget } from "./a11y/a11y-target";
|
|
5
|
+
import type { Driver, DriverError, Snapshot } from "./driver/driver.types";
|
|
6
|
+
import type { E2eEnv } from "./env";
|
|
7
|
+
/** `device.a11yAudit(opts)`: the audit's own options, plus what only a device knows. */
|
|
8
|
+
export type DeviceA11yAuditOptions = A11yAuditOptions & {
|
|
9
|
+
/** the text size the app runs at; `text-clipped` only runs from `accessibility-medium` up */
|
|
10
|
+
textSize?: TextSize;
|
|
11
|
+
/** also run the platform's own audit (XCUITest on iOS, ATF on Android) when the driver can. Default true. */
|
|
12
|
+
native?: boolean;
|
|
13
|
+
};
|
|
14
|
+
/** An announcement or live-region change the app made, in the order it happened. */
|
|
15
|
+
export type A11yAnnouncement = {
|
|
16
|
+
kind: "announcement" | "live-region";
|
|
17
|
+
text: string;
|
|
18
|
+
/** epoch ms on the device */
|
|
19
|
+
at: number;
|
|
20
|
+
source: A11yEventSource;
|
|
21
|
+
};
|
|
22
|
+
export type A11yAnnouncementsOptions = {
|
|
23
|
+
/** cursor from an earlier read: only what came after it */
|
|
24
|
+
since?: number;
|
|
25
|
+
};
|
|
26
|
+
export type A11yAnnouncements = {
|
|
27
|
+
announcements: A11yAnnouncement[];
|
|
28
|
+
/** pass back as `since` to read only what is new */
|
|
29
|
+
cursor: number;
|
|
30
|
+
/** the Android event buffer overwrote events this caller never read */
|
|
31
|
+
lost?: boolean;
|
|
32
|
+
};
|
|
33
|
+
export type ScreenReaderSpokenOptions = {
|
|
34
|
+
since?: number;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* What a screen reader does with the screen. `order()` is warden's model of the reading order and works on both
|
|
38
|
+
* platforms (every `Announcement` is `source: "simulated"`: an approximation, never what VoiceOver or TalkBack
|
|
39
|
+
* say). The other calls drive a real TalkBack on an Android emulator (`device.a11y({ screenReader: true })`) and
|
|
40
|
+
* throw `ACTION_FAILED` (`E_UNSUPPORTED`) anywhere else.
|
|
41
|
+
*/
|
|
42
|
+
export type DeviceScreenReader = {
|
|
43
|
+
/** reading order of the current screen */
|
|
44
|
+
order(): Promise<Announcement[]>;
|
|
45
|
+
state(): Promise<ScreenReaderState>;
|
|
46
|
+
/** moves screen-reader focus one step on; resolves what it now holds (`undefined` past the end) */
|
|
47
|
+
next(): Promise<Announcement | undefined>;
|
|
48
|
+
prev(): Promise<Announcement | undefined>;
|
|
49
|
+
/** double-tap on the focused node */
|
|
50
|
+
activate(): Promise<void>;
|
|
51
|
+
/** the node holding screen-reader focus, if any */
|
|
52
|
+
focused(): Promise<Announcement | undefined>;
|
|
53
|
+
/** what the reader said since `since`; `cursor` goes back in as the next `since` */
|
|
54
|
+
spoken(options?: ScreenReaderSpokenOptions): Promise<{
|
|
55
|
+
lines: SpokenLine[];
|
|
56
|
+
cursor: number;
|
|
57
|
+
}>;
|
|
58
|
+
};
|
|
59
|
+
export type DeviceA11y = {
|
|
60
|
+
/**
|
|
61
|
+
* Audits the current screen: warden's rules over the snapshot (with the iOS injected read merged in when the app
|
|
62
|
+
* runs with it) plus the platform's own audit when the driver offers one. The suite's `a11y` config
|
|
63
|
+
* (`warden.config.ts`) supplies the defaults; options passed here win key by key.
|
|
64
|
+
*/
|
|
65
|
+
a11yAudit(options?: DeviceA11yAuditOptions): Promise<A11yAuditResult>;
|
|
66
|
+
readonly screenReader: DeviceScreenReader;
|
|
67
|
+
/** announcements and live-region changes since `since` (needs the injected library on iOS) */
|
|
68
|
+
announcements(options?: A11yAnnouncementsOptions): Promise<A11yAnnouncements>;
|
|
69
|
+
};
|
|
70
|
+
export type DeviceA11yDeps = {
|
|
71
|
+
driver: Driver;
|
|
72
|
+
env: Pick<E2eEnv, "a11yAudit" | "assertionTimeoutMs"> & {
|
|
73
|
+
bundleId?: string;
|
|
74
|
+
};
|
|
75
|
+
/** read per call: the runner swaps it for every attempt */
|
|
76
|
+
signal: () => AbortSignal | undefined;
|
|
77
|
+
step?: <T>(label: string, fn: () => Promise<T>) => Promise<T>;
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* The snapshot with the screen-reader facts a plain snapshot lacks merged in. A driver without the capability, or an
|
|
81
|
+
* app launched without the injected library (`E_UNSUPPORTED`), leaves it as it is. Any other failure is returned.
|
|
82
|
+
*/
|
|
83
|
+
export declare function withA11yRead(driver: Driver, snapshot: Snapshot): Promise<Result<Snapshot, DriverError>>;
|
|
84
|
+
/** The screen as text with the `--a11y` columns: what an a11y failure attaches as `screen-at-failure.txt`. */
|
|
85
|
+
export declare function renderA11yScreen(snapshot: Snapshot, maxLines?: number): string;
|
|
86
|
+
export type AuditSnapshotOptions = DeviceA11yAuditOptions & {
|
|
87
|
+
/** the app the platform's own audit checks (iOS XCUITest) */
|
|
88
|
+
bundleId?: string;
|
|
89
|
+
};
|
|
90
|
+
/**
|
|
91
|
+
* Audits one snapshot: warden's rules, plus the platform's own audit (XCUITest / ATF) unless `native: false`, `rules`
|
|
92
|
+
* leaves out `native`, or the driver has none. `device.a11yAudit` and `warden ui a11y audit` share this.
|
|
93
|
+
*/
|
|
94
|
+
export declare function auditSnapshot(driver: Driver, snapshot: Snapshot, options?: AuditSnapshotOptions): Promise<A11yAuditResult>;
|
|
95
|
+
export declare function createDeviceA11y(deps: DeviceA11yDeps): DeviceA11y & {
|
|
96
|
+
target: A11yTarget;
|
|
97
|
+
};
|