@delacour/warden-e2e 0.13.0 → 0.15.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-tnr47n9c.mjs → chunk-e3cry5br.mjs} +828 -255
- package/index.mjs +144 -14
- package/package.json +2 -2
- package/preload.mjs +375 -63
- package/types/core/src/affected/affected.schema.d.ts +5 -0
- package/types/core/src/builds/builds.defaults.d.ts +8 -0
- package/types/core/src/builds/config.schema.d.ts +9 -0
- package/types/core/src/builds/define-config.d.ts +9 -1
- package/types/core/src/conditions/conditions.config.d.ts +6 -1
- package/types/core/src/conditions/conditions.types.d.ts +21 -0
- package/types/core/src/conditions/inject-state.d.ts +3 -2
- package/types/e2e/src/a11y/a11y-target.d.ts +2 -0
- package/types/e2e/src/conditions/condition-events.d.ts +6 -1
- package/types/e2e/src/conditions/device-conditions.d.ts +8 -2
- package/types/e2e/src/device.d.ts +10 -2
- package/types/e2e/src/driver/android/android-settings.d.ts +9 -2
- package/types/e2e/src/driver/ax-wedge.d.ts +38 -0
- package/types/e2e/src/driver/bridge-stall.d.ts +41 -0
- package/types/e2e/src/driver/driver.types.d.ts +35 -2
- package/types/e2e/src/driver/ios/inject.d.ts +17 -2
- package/types/e2e/src/driver/ios/ios-driver.d.ts +17 -2
- package/types/e2e/src/driver/ios/ios-keyboard.d.ts +39 -0
- package/types/e2e/src/driver/ios/simd-client.d.ts +2 -0
- package/types/e2e/src/driver/ios/simd-protocol.d.ts +129 -0
- package/types/e2e/src/driver/keyboard-state.d.ts +9 -0
- package/types/e2e/src/driver/physical-conditions.d.ts +1 -1
- package/types/e2e/src/driver/read-deadline.d.ts +4 -0
- package/types/e2e/src/driver/settle.d.ts +11 -1
- package/types/e2e/src/errors.d.ts +3 -1
- package/types/e2e/src/expect/a11y-matchers.d.ts +7 -0
- package/types/e2e/src/expect/matchers.d.ts +5 -0
- package/types/e2e/src/index.d.ts +2 -1
- package/types/e2e/src/runner/bridge-evidence.d.ts +12 -0
- package/types/e2e/src/runner/results.d.ts +29 -0
- package/types/e2e/src/screen/actions.d.ts +7 -0
- package/types/e2e/src/screen/context.d.ts +11 -12
- package/types/e2e/src/screen/degraded-read.d.ts +23 -0
- package/types/e2e/src/screen/keyboard-cover.d.ts +26 -0
- package/types/e2e/src/screen/locator.d.ts +61 -7
- package/types/e2e/src/screen/react-locator.d.ts +17 -2
- package/types/e2e/src/screen/react-merge.d.ts +10 -0
- package/types/e2e/src/session.d.ts +6 -0
|
@@ -192,6 +192,32 @@ export declare const healthDataSchema: z.ZodObject<{
|
|
|
192
192
|
unresolvedAgoMs: z.ZodNumber;
|
|
193
193
|
method: z.ZodString;
|
|
194
194
|
}, z.core.$strip>>;
|
|
195
|
+
bridgeRecycles: z.ZodOptional<z.ZodObject<{
|
|
196
|
+
count: z.ZodNumber;
|
|
197
|
+
exhausted: z.ZodBoolean;
|
|
198
|
+
}, z.core.$strip>>;
|
|
199
|
+
axWedge: z.ZodOptional<z.ZodLazy<z.ZodObject<{
|
|
200
|
+
restarts: z.ZodNumber;
|
|
201
|
+
maxRestarts: z.ZodNumber;
|
|
202
|
+
recovering: z.ZodBoolean;
|
|
203
|
+
checked: z.ZodOptional<z.ZodEnum<{
|
|
204
|
+
failed: "failed";
|
|
205
|
+
restarted: "restarted";
|
|
206
|
+
"not-wedged": "not-wedged";
|
|
207
|
+
}>>;
|
|
208
|
+
last: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
209
|
+
outcome: z.ZodLiteral<"restarted">;
|
|
210
|
+
agoMs: z.ZodNumber;
|
|
211
|
+
ms: z.ZodOptional<z.ZodNumber>;
|
|
212
|
+
}, z.core.$loose>, z.ZodObject<{
|
|
213
|
+
outcome: z.ZodLiteral<"not-wedged">;
|
|
214
|
+
agoMs: z.ZodNumber;
|
|
215
|
+
}, z.core.$loose>, z.ZodObject<{
|
|
216
|
+
outcome: z.ZodLiteral<"failed">;
|
|
217
|
+
agoMs: z.ZodNumber;
|
|
218
|
+
error: z.ZodString;
|
|
219
|
+
}, z.core.$loose>], "outcome">>;
|
|
220
|
+
}, z.core.$loose>>>;
|
|
195
221
|
state: z.ZodOptional<z.ZodString>;
|
|
196
222
|
uptimeMs: z.ZodOptional<z.ZodNumber>;
|
|
197
223
|
}, z.core.$loose>;
|
|
@@ -214,6 +240,19 @@ export declare const snapshotDataSchema: z.ZodObject<{
|
|
|
214
240
|
from: z.ZodString;
|
|
215
241
|
to: z.ZodString;
|
|
216
242
|
reason: z.ZodString;
|
|
243
|
+
stall: z.ZodOptional<z.ZodObject<{
|
|
244
|
+
stage: z.ZodString;
|
|
245
|
+
readMs: z.ZodOptional<z.ZodNumber>;
|
|
246
|
+
deadlineMs: z.ZodOptional<z.ZodNumber>;
|
|
247
|
+
guestAgeMs: z.ZodOptional<z.ZodNumber>;
|
|
248
|
+
cold: z.ZodBoolean;
|
|
249
|
+
cooldownRemainingMs: z.ZodOptional<z.ZodNumber>;
|
|
250
|
+
probes: z.ZodNumber;
|
|
251
|
+
recycles: z.ZodOptional<z.ZodNumber>;
|
|
252
|
+
recyclesExhausted: z.ZodOptional<z.ZodBoolean>;
|
|
253
|
+
axWedge: z.ZodOptional<z.ZodString>;
|
|
254
|
+
axWedgeRestarts: z.ZodOptional<z.ZodNumber>;
|
|
255
|
+
}, z.core.$loose>>;
|
|
217
256
|
}, z.core.$strip>>;
|
|
218
257
|
target: z.ZodOptional<z.ZodObject<{
|
|
219
258
|
pid: z.ZodNumber;
|
|
@@ -324,6 +363,34 @@ export declare const orientationDataSchema: z.ZodObject<{
|
|
|
324
363
|
}>>;
|
|
325
364
|
}, z.core.$strip>;
|
|
326
365
|
export type OrientationData = z.infer<typeof orientationDataSchema>;
|
|
366
|
+
/**
|
|
367
|
+
* `axWedge{recover?}`: the helper's check for a wedged accessibility service (both readers silent + the marker in the
|
|
368
|
+
* sim log → backboardd restarted). `recover: true` runs the check now (or joins the one in flight) and answers when it
|
|
369
|
+
* is over; nothing runs once `restarts` reached `maxRestarts` (`WARDEN_SIMD_AX_WEDGE_RESTARTS`).
|
|
370
|
+
*/
|
|
371
|
+
export declare const axWedgeDataSchema: z.ZodObject<{
|
|
372
|
+
restarts: z.ZodNumber;
|
|
373
|
+
maxRestarts: z.ZodNumber;
|
|
374
|
+
recovering: z.ZodBoolean;
|
|
375
|
+
checked: z.ZodOptional<z.ZodEnum<{
|
|
376
|
+
failed: "failed";
|
|
377
|
+
restarted: "restarted";
|
|
378
|
+
"not-wedged": "not-wedged";
|
|
379
|
+
}>>;
|
|
380
|
+
last: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
381
|
+
outcome: z.ZodLiteral<"restarted">;
|
|
382
|
+
agoMs: z.ZodNumber;
|
|
383
|
+
ms: z.ZodOptional<z.ZodNumber>;
|
|
384
|
+
}, z.core.$loose>, z.ZodObject<{
|
|
385
|
+
outcome: z.ZodLiteral<"not-wedged">;
|
|
386
|
+
agoMs: z.ZodNumber;
|
|
387
|
+
}, z.core.$loose>, z.ZodObject<{
|
|
388
|
+
outcome: z.ZodLiteral<"failed">;
|
|
389
|
+
agoMs: z.ZodNumber;
|
|
390
|
+
error: z.ZodString;
|
|
391
|
+
}, z.core.$loose>], "outcome">>;
|
|
392
|
+
}, z.core.$loose>;
|
|
393
|
+
export type AxWedgeData = z.infer<typeof axWedgeDataSchema>;
|
|
327
394
|
/** Ops whose `data` the client validates and types; every other op's data stays `unknown`. */
|
|
328
395
|
export declare const opSchemas: {
|
|
329
396
|
readonly health: z.ZodObject<{
|
|
@@ -362,6 +429,32 @@ export declare const opSchemas: {
|
|
|
362
429
|
unresolvedAgoMs: z.ZodNumber;
|
|
363
430
|
method: z.ZodString;
|
|
364
431
|
}, z.core.$strip>>;
|
|
432
|
+
bridgeRecycles: z.ZodOptional<z.ZodObject<{
|
|
433
|
+
count: z.ZodNumber;
|
|
434
|
+
exhausted: z.ZodBoolean;
|
|
435
|
+
}, z.core.$strip>>;
|
|
436
|
+
axWedge: z.ZodOptional<z.ZodLazy<z.ZodObject<{
|
|
437
|
+
restarts: z.ZodNumber;
|
|
438
|
+
maxRestarts: z.ZodNumber;
|
|
439
|
+
recovering: z.ZodBoolean;
|
|
440
|
+
checked: z.ZodOptional<z.ZodEnum<{
|
|
441
|
+
failed: "failed";
|
|
442
|
+
restarted: "restarted";
|
|
443
|
+
"not-wedged": "not-wedged";
|
|
444
|
+
}>>;
|
|
445
|
+
last: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
446
|
+
outcome: z.ZodLiteral<"restarted">;
|
|
447
|
+
agoMs: z.ZodNumber;
|
|
448
|
+
ms: z.ZodOptional<z.ZodNumber>;
|
|
449
|
+
}, z.core.$loose>, z.ZodObject<{
|
|
450
|
+
outcome: z.ZodLiteral<"not-wedged">;
|
|
451
|
+
agoMs: z.ZodNumber;
|
|
452
|
+
}, z.core.$loose>, z.ZodObject<{
|
|
453
|
+
outcome: z.ZodLiteral<"failed">;
|
|
454
|
+
agoMs: z.ZodNumber;
|
|
455
|
+
error: z.ZodString;
|
|
456
|
+
}, z.core.$loose>], "outcome">>;
|
|
457
|
+
}, z.core.$loose>>>;
|
|
365
458
|
state: z.ZodOptional<z.ZodString>;
|
|
366
459
|
uptimeMs: z.ZodOptional<z.ZodNumber>;
|
|
367
460
|
}, z.core.$loose>;
|
|
@@ -383,6 +476,19 @@ export declare const opSchemas: {
|
|
|
383
476
|
from: z.ZodString;
|
|
384
477
|
to: z.ZodString;
|
|
385
478
|
reason: z.ZodString;
|
|
479
|
+
stall: z.ZodOptional<z.ZodObject<{
|
|
480
|
+
stage: z.ZodString;
|
|
481
|
+
readMs: z.ZodOptional<z.ZodNumber>;
|
|
482
|
+
deadlineMs: z.ZodOptional<z.ZodNumber>;
|
|
483
|
+
guestAgeMs: z.ZodOptional<z.ZodNumber>;
|
|
484
|
+
cold: z.ZodBoolean;
|
|
485
|
+
cooldownRemainingMs: z.ZodOptional<z.ZodNumber>;
|
|
486
|
+
probes: z.ZodNumber;
|
|
487
|
+
recycles: z.ZodOptional<z.ZodNumber>;
|
|
488
|
+
recyclesExhausted: z.ZodOptional<z.ZodBoolean>;
|
|
489
|
+
axWedge: z.ZodOptional<z.ZodString>;
|
|
490
|
+
axWedgeRestarts: z.ZodOptional<z.ZodNumber>;
|
|
491
|
+
}, z.core.$loose>>;
|
|
386
492
|
}, z.core.$strip>>;
|
|
387
493
|
target: z.ZodOptional<z.ZodObject<{
|
|
388
494
|
pid: z.ZodNumber;
|
|
@@ -448,6 +554,28 @@ export declare const opSchemas: {
|
|
|
448
554
|
readonly terminate: z.ZodObject<{
|
|
449
555
|
terminated: z.ZodBoolean;
|
|
450
556
|
}, z.core.$loose>;
|
|
557
|
+
readonly axWedge: z.ZodObject<{
|
|
558
|
+
restarts: z.ZodNumber;
|
|
559
|
+
maxRestarts: z.ZodNumber;
|
|
560
|
+
recovering: z.ZodBoolean;
|
|
561
|
+
checked: z.ZodOptional<z.ZodEnum<{
|
|
562
|
+
failed: "failed";
|
|
563
|
+
restarted: "restarted";
|
|
564
|
+
"not-wedged": "not-wedged";
|
|
565
|
+
}>>;
|
|
566
|
+
last: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
567
|
+
outcome: z.ZodLiteral<"restarted">;
|
|
568
|
+
agoMs: z.ZodNumber;
|
|
569
|
+
ms: z.ZodOptional<z.ZodNumber>;
|
|
570
|
+
}, z.core.$loose>, z.ZodObject<{
|
|
571
|
+
outcome: z.ZodLiteral<"not-wedged">;
|
|
572
|
+
agoMs: z.ZodNumber;
|
|
573
|
+
}, z.core.$loose>, z.ZodObject<{
|
|
574
|
+
outcome: z.ZodLiteral<"failed">;
|
|
575
|
+
agoMs: z.ZodNumber;
|
|
576
|
+
error: z.ZodString;
|
|
577
|
+
}, z.core.$loose>], "outcome">>;
|
|
578
|
+
}, z.core.$loose>;
|
|
451
579
|
};
|
|
452
580
|
export type OpDataMap = {
|
|
453
581
|
health: HealthData;
|
|
@@ -461,6 +589,7 @@ export type OpDataMap = {
|
|
|
461
589
|
orientation: OrientationData;
|
|
462
590
|
launch: LaunchData;
|
|
463
591
|
terminate: TerminateData;
|
|
592
|
+
axWedge: AxWedgeData;
|
|
464
593
|
};
|
|
465
594
|
export type OpData<Op extends string> = Op extends keyof OpDataMap ? OpDataMap[Op] : unknown;
|
|
466
595
|
export declare function hasOpSchema(op: string): op is keyof typeof opSchemas;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Element, Rect, Snapshot } from "./driver.types";
|
|
2
|
+
/**
|
|
3
|
+
* The on-screen keyboard of a snapshot: the largest `keyboard` element with a frame on the screen. A keyboard slid
|
|
4
|
+
* off the bottom (iOS keeps the hidden one in the tree) is not on screen.
|
|
5
|
+
*/
|
|
6
|
+
export declare function keyboardElement(snapshot: Snapshot): Element | undefined;
|
|
7
|
+
export declare function keyboardVisible(snapshot: Snapshot): boolean;
|
|
8
|
+
/** The keyboard's frame, or `undefined` when none shows. */
|
|
9
|
+
export declare function keyboardFrame(snapshot: Snapshot): Rect | undefined;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Driver } from "./driver.types";
|
|
2
2
|
/** The condition and setting ops of a `Driver`: each changes the device (or an app's grants) beyond driving its UI. */
|
|
3
|
-
export declare const PHYSICAL_CONDITION_OPS: readonly ["network", "networkFaults", "location", "clock", "permissions", "timezone", "locale", "a11y", "battery", "memoryWarning", "push", "camera"];
|
|
3
|
+
export declare const PHYSICAL_CONDITION_OPS: readonly ["network", "networkFaults", "location", "clock", "permissions", "timezone", "locale", "a11y", "battery", "keyboard", "keyboardState", "memoryWarning", "push", "camera"];
|
|
4
4
|
type PhysicalConditionOps = Pick<Driver, (typeof PHYSICAL_CONDITION_OPS)[number]>;
|
|
5
5
|
/**
|
|
6
6
|
* The condition ops for a physical phone: all `E_UNSUPPORTED`. On a phone they would flip airplane mode (cutting
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** Runs `fn` with `remaining` as the deadline of every read made inside it, awaits included. */
|
|
2
|
+
export declare function withReadDeadline<T>(remaining: () => number, fn: () => T): T;
|
|
3
|
+
/** How long the innermost read deadline has left (never negative), or undefined outside one. */
|
|
4
|
+
export declare function readDeadlineRemaining(): number | undefined;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Snapshot } from "./driver.types";
|
|
1
|
+
import type { Element, Snapshot } from "./driver.types";
|
|
2
2
|
export type SettleDeps = {
|
|
3
3
|
quietMs: number;
|
|
4
4
|
timeoutMs: number;
|
|
@@ -33,11 +33,21 @@ export declare function settleDetailOf(outcome: SettleOutcome): SettleDetail;
|
|
|
33
33
|
export declare const DEFAULT_SETTLE_QUIET_MS = 250;
|
|
34
34
|
/** How long a settle waits for quiet before it gives up. */
|
|
35
35
|
export declare const DEFAULT_SETTLE_TIMEOUT_MS = 3000;
|
|
36
|
+
/**
|
|
37
|
+
* The shortest window a snapshot-hash settle fallback waits (iOS, and `device.settle` on a driver without one). A navigation push exposes both screens at their final
|
|
38
|
+
* frames for 300-460 ms before the old one leaves the tree, so two equal reads 250 ms apart can settle on that mixed
|
|
39
|
+
* tree. The helper's own snapshot fallback (`FrameStability.minQuietMs`) uses the same floor.
|
|
40
|
+
*/
|
|
41
|
+
export declare const HASH_FALLBACK_MIN_QUIET_MS = 500;
|
|
42
|
+
/** The quiet window a hash fallback uses for a caller's `quietMs`. */
|
|
43
|
+
export declare function hashFallbackQuietMs(quietMs: number): number;
|
|
36
44
|
/**
|
|
37
45
|
* Snapshot-hash settle: poll a UI fingerprint until it stays unchanged for `quietMs`
|
|
38
46
|
* (two consecutive equal hashes and ≥ quietMs since the last change), or give up at `timeoutMs`.
|
|
39
47
|
* Side effects are injected so tests drive a fake clock.
|
|
40
48
|
*/
|
|
41
49
|
export declare function settleByHash(poll: () => Promise<string>, deps: SettleDeps): Promise<SettleOutcome>;
|
|
50
|
+
/** One element as `(role, identifier, label, value, frame to 1 pt)`: what `snapshotHash` hashes, per node. */
|
|
51
|
+
export declare function elementKey(element: Element): string;
|
|
42
52
|
/** Stable fingerprint over `(role, identifier, label, value, frame)` of every node in preorder. */
|
|
43
53
|
export declare function snapshotHash(snapshot: Pick<Snapshot, "flat">): string;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { ErrorResult } from "../../types/src/result";
|
|
2
|
-
export type E2eErrorCode = "LOCATOR_NOT_FOUND" | "LOCATOR_AMBIGUOUS" | "INVALID_LOCATOR" | "ACTION_FAILED" | "TIMEOUT" | "NOT_ACTIONABLE" | "INVALID_ARGUMENT" | "SESSION" | "DRIVER" | "ASSERTION" | "PERF_BUDGET" | "RUN_FAILED"
|
|
2
|
+
export type E2eErrorCode = "LOCATOR_NOT_FOUND" | "LOCATOR_AMBIGUOUS" | "INVALID_LOCATOR" | "ACTION_FAILED" | "TIMEOUT" | "NOT_ACTIONABLE" | "INVALID_ARGUMENT" | "SESSION" | "DRIVER" | "ASSERTION" | "PERF_BUDGET" | "RUN_FAILED"
|
|
3
|
+
/** a read that fell back from its first reader (or came back an empty root) could not confirm the locator either way */
|
|
4
|
+
| "READ_DEGRADED";
|
|
3
5
|
/** Plain shape of a spec error: what `e2eErr()` accepts and what a `Result` carries. */
|
|
4
6
|
export type E2eErrorShape = {
|
|
5
7
|
code: E2eErrorCode;
|
|
@@ -28,6 +28,13 @@ export declare class DeviceAssertions {
|
|
|
28
28
|
toPassA11yAudit(options?: PassA11yAuditOptions): Promise<void>;
|
|
29
29
|
/** An announcement or live-region change whose text contains the string or matches the RegExp arrived (`.not`: none did within the timeout). */
|
|
30
30
|
toHaveAnnounced(expected: TextMatch, options?: AnnouncedOptions): Promise<void>;
|
|
31
|
+
/**
|
|
32
|
+
* The on-screen keyboard is up (`.not`: it is gone), read from the accessibility tree. Polls until it holds or
|
|
33
|
+
* `timeout` (default: the assertion timeout) passes. Under `hardware` mode the keyboard never shows.
|
|
34
|
+
*/
|
|
35
|
+
toHaveKeyboard(options?: {
|
|
36
|
+
timeout?: number;
|
|
37
|
+
}): Promise<void>;
|
|
31
38
|
private failure;
|
|
32
39
|
private run;
|
|
33
40
|
}
|
|
@@ -81,6 +81,11 @@ export declare class LocatorAssertions {
|
|
|
81
81
|
toHaveCount(expected: number, options?: AssertionOptions): Promise<void>;
|
|
82
82
|
private assert;
|
|
83
83
|
private poll;
|
|
84
|
+
/**
|
|
85
|
+
* What one look says about `spec`: a judgement (from the accessibility tree, or from the React tree for a degraded
|
|
86
|
+
* read it answered), or `unsettled` when the read was degraded and nothing could say whether the locator is there.
|
|
87
|
+
*/
|
|
88
|
+
private judgeLook;
|
|
84
89
|
/** The screen is rendered here, from the last snapshot read, because only a failure needs the text. */
|
|
85
90
|
private failure;
|
|
86
91
|
}
|
package/types/e2e/src/index.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
export type { KeyboardMode } from "../../core/src/conditions/conditions.types";
|
|
1
2
|
export type { A11yAuditOptions, A11yAuditResult, A11yEvent, A11yEventKind, A11yEventSource, A11yIgnore, A11yIssue, A11yRuleId, A11ySeverity, A11ySource, Announcement, ScreenReaderState, SpokenLine, } from "./a11y/a11y.types";
|
|
2
3
|
export type { OpenOptions, OpenWaitUntil } from "./app";
|
|
3
4
|
export type { CameraFacingChoice, CameraSetOptions, ClockInput, ConditionRun, DeviceCamera, DeviceClock, DeviceLocation, DeviceNetwork, DevicePermissions, Duration, FlapOptions, LocationPoint, NetworkSetOptions, NetworkStatus, RouteOptions, TimelineStepInput, } from "./conditions/device-conditions";
|
|
4
5
|
export type { BiometricState, DeviceBiometrics } from "./device";
|
|
5
6
|
export type { A11yAnnouncement, A11yAnnouncements, A11yAnnouncementsOptions, DeviceA11y, DeviceA11yAuditOptions, DeviceScreenReader, ScreenReaderSpokenOptions, } from "./device-a11y";
|
|
6
|
-
export type { A11yOp, A11yState, CameraState } from "./driver/driver.types";
|
|
7
|
+
export type { A11yOp, A11yState, CameraState, KeyboardResult, KeyboardState } from "./driver/driver.types";
|
|
7
8
|
export { E2eError } from "./errors";
|
|
8
9
|
export type { AnnouncedOptions, PassA11yAuditOptions } from "./expect/a11y-matchers";
|
|
9
10
|
export { expect } from "./expect/expect";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { TestResult } from "./results";
|
|
2
|
+
import type { TraceEvent } from "./trace";
|
|
3
|
+
export type BridgeEvidence = NonNullable<TestResult["bridge"]>;
|
|
4
|
+
/**
|
|
5
|
+
* Collects an attempt's iOS bridge fallbacks from its spans for `results.jsonl` (`bridge`): reads answered by `ax`
|
|
6
|
+
* in place of `axbridge`, and failed reads that carried a stall report. A read the bridge answered by pid (frontmost
|
|
7
|
+
* unresolved) is not one.
|
|
8
|
+
*/
|
|
9
|
+
export declare function bridgeEvidence(): {
|
|
10
|
+
observe(event: TraceEvent): void;
|
|
11
|
+
summary(): BridgeEvidence | undefined;
|
|
12
|
+
};
|
|
@@ -26,6 +26,35 @@ export declare const testResultSchema: z.ZodObject<{
|
|
|
26
26
|
dir: z.ZodString;
|
|
27
27
|
files: z.ZodArray<z.ZodString>;
|
|
28
28
|
}, z.core.$strip>>;
|
|
29
|
+
bridge: z.ZodOptional<z.ZodObject<{
|
|
30
|
+
fallbacks: z.ZodNumber;
|
|
31
|
+
first: z.ZodObject<{
|
|
32
|
+
reason: z.ZodOptional<z.ZodString>;
|
|
33
|
+
code: z.ZodOptional<z.ZodString>;
|
|
34
|
+
stage: z.ZodOptional<z.ZodString>;
|
|
35
|
+
readMs: z.ZodOptional<z.ZodNumber>;
|
|
36
|
+
deadlineMs: z.ZodOptional<z.ZodNumber>;
|
|
37
|
+
guestAgeMs: z.ZodOptional<z.ZodNumber>;
|
|
38
|
+
cold: z.ZodOptional<z.ZodBoolean>;
|
|
39
|
+
cooldownRemainingMs: z.ZodOptional<z.ZodNumber>;
|
|
40
|
+
probes: z.ZodOptional<z.ZodNumber>;
|
|
41
|
+
recycles: z.ZodOptional<z.ZodNumber>;
|
|
42
|
+
recyclesExhausted: z.ZodOptional<z.ZodBoolean>;
|
|
43
|
+
}, z.core.$strip>;
|
|
44
|
+
last: z.ZodObject<{
|
|
45
|
+
reason: z.ZodOptional<z.ZodString>;
|
|
46
|
+
code: z.ZodOptional<z.ZodString>;
|
|
47
|
+
stage: z.ZodOptional<z.ZodString>;
|
|
48
|
+
readMs: z.ZodOptional<z.ZodNumber>;
|
|
49
|
+
deadlineMs: z.ZodOptional<z.ZodNumber>;
|
|
50
|
+
guestAgeMs: z.ZodOptional<z.ZodNumber>;
|
|
51
|
+
cold: z.ZodOptional<z.ZodBoolean>;
|
|
52
|
+
cooldownRemainingMs: z.ZodOptional<z.ZodNumber>;
|
|
53
|
+
probes: z.ZodOptional<z.ZodNumber>;
|
|
54
|
+
recycles: z.ZodOptional<z.ZodNumber>;
|
|
55
|
+
recyclesExhausted: z.ZodOptional<z.ZodBoolean>;
|
|
56
|
+
}, z.core.$strip>;
|
|
57
|
+
}, z.core.$strip>>;
|
|
29
58
|
device: z.ZodObject<{
|
|
30
59
|
udid: z.ZodString;
|
|
31
60
|
platform: z.ZodEnum<{
|
|
@@ -39,6 +39,8 @@ export declare function isOnScreen(point: Point, screen: ScreenInfo): boolean;
|
|
|
39
39
|
* field; Android keeps an input's text in `text`.
|
|
40
40
|
*/
|
|
41
41
|
export declare function fieldValue(entry: NodeEntry): string;
|
|
42
|
+
/** User-perceived characters in `text` (what a secure field shows one bullet for). */
|
|
43
|
+
export declare function graphemes(text: string): number;
|
|
42
44
|
/** What one read says a field holds: nothing, text, or (secure fields) a number of hidden characters. */
|
|
43
45
|
export type FieldContent = {
|
|
44
46
|
kind: "empty";
|
|
@@ -55,6 +57,11 @@ export type FieldContent = {
|
|
|
55
57
|
* plain field showing its placeholder cannot be told from one holding that text and reads as `text`.
|
|
56
58
|
*/
|
|
57
59
|
export declare function fieldContent(entry: NodeEntry): FieldContent;
|
|
60
|
+
/**
|
|
61
|
+
* What a fill read-back of a secure field says it holds, given the typed `text`: `text` when it shows one bullet per
|
|
62
|
+
* character of it (the only check a masked field allows), else its bullets (never the typed text, in a message).
|
|
63
|
+
*/
|
|
64
|
+
export declare function maskedReading(entry: NodeEntry, text: string): string;
|
|
58
65
|
/**
|
|
59
66
|
* Backspaces that empty a field once the caret is at its end: one per character plus a spare. Secure fields are
|
|
60
67
|
* counted by their dots. The fallback for a driver without select-all, and the retry when select-all did not empty
|
|
@@ -4,7 +4,7 @@ import { E2eError } from "../errors";
|
|
|
4
4
|
import type { SpanOptions, SpanRunner } from "../runner/span";
|
|
5
5
|
import { type NodeIndex } from "./nodes";
|
|
6
6
|
import { type Clock, type PollResult, type PollStep } from "./poll";
|
|
7
|
-
import { type ReactMatch } from "./react-locator";
|
|
7
|
+
import { type ReactLookup, type ReactMatch } from "./react-locator";
|
|
8
8
|
import type { TextMatch } from "./text";
|
|
9
9
|
/**
|
|
10
10
|
* What `screen` and `Locator` need from the test session. `Session` satisfies it as is; the optional members are
|
|
@@ -78,8 +78,6 @@ export declare class Runtime {
|
|
|
78
78
|
private reactCache;
|
|
79
79
|
/** the index of each snapshot, so a snapshot that several reads share is indexed once */
|
|
80
80
|
private readonly indexed;
|
|
81
|
-
/** the time before which a degraded look is not retried on its first reader, after that reader failed to answer */
|
|
82
|
-
private firstReaderRetryAt;
|
|
83
81
|
constructor(host: ScreenHost);
|
|
84
82
|
get clock(): Clock;
|
|
85
83
|
actionTimeout(): number;
|
|
@@ -97,14 +95,6 @@ export declare class Runtime {
|
|
|
97
95
|
* `systemAlerts` on, an alert the screen shows is answered and the screen looked at again.
|
|
98
96
|
*/
|
|
99
97
|
tryObserve(options?: ObserveOptions): Promise<Observation>;
|
|
100
|
-
/**
|
|
101
|
-
* A snapshot the driver answered from a fallback reader (`Snapshot.fellBackFrom`, iOS `ax` after `axbridge` stalled)
|
|
102
|
-
* can lack what the first reader has: React Native testIDs on static text. Such a look is read once more on the
|
|
103
|
-
* first reader, and that read replaces it when it answers. When it does not (the guest is still silent) the
|
|
104
|
-
* fallback snapshot stands and the first reader is left alone for `FIRST_READER_RETRY_MS`, so a dead guest costs a
|
|
105
|
-
* poll one deadline, not one per attempt. Every locator and matcher looks through here, so none needs its own rule.
|
|
106
|
-
*/
|
|
107
|
-
private restoreDegraded;
|
|
108
98
|
/** What came before `look`, given the cache's answer from just before it was taken. */
|
|
109
99
|
private earlierThan;
|
|
110
100
|
/** Whether `snapshot` showed an alert that has now been answered. A screen without one costs no extra read. */
|
|
@@ -124,6 +114,13 @@ export declare class Runtime {
|
|
|
124
114
|
* Empty without devtools. Reads are throttled: inside the window the previous answer for the same query is reused.
|
|
125
115
|
*/
|
|
126
116
|
reactMatches(testId: TextMatch, observed: Observed): Promise<ReactMatch[]>;
|
|
117
|
+
/** Whether the host has devtools to ask the React tree through. */
|
|
118
|
+
hasReactTree(): boolean;
|
|
119
|
+
/**
|
|
120
|
+
* `reactMatches`, keeping "the React tree could not be read" (`answered: false`, also without devtools) apart from
|
|
121
|
+
* "it has no such component", for a degraded read that must not take the first for the second.
|
|
122
|
+
*/
|
|
123
|
+
reactLookup(testId: TextMatch, observed: Observed): Promise<ReactLookup>;
|
|
127
124
|
/**
|
|
128
125
|
* The interactive part of the tree as text, for failure messages. It indexes the snapshot again and renders up to
|
|
129
126
|
* `EXCERPT_LINES` lines, so call it only once an error is certain; keep the snapshot, not this text, while polling.
|
|
@@ -131,7 +128,9 @@ export declare class Runtime {
|
|
|
131
128
|
excerpt(snapshot: Snapshot): string;
|
|
132
129
|
/**
|
|
133
130
|
* `poll` on this runtime's clock and abort signal; an abort throws instead of returning. `intervalMs` is a fixed gap
|
|
134
|
-
* between attempts instead of the back-off schedule.
|
|
131
|
+
* between attempts instead of the back-off schedule. The poll's deadline is the read deadline of every driver read
|
|
132
|
+
* made inside it (`withReadDeadline`), so a read that waits for the device (the cold iOS bridge) stops with it; a
|
|
133
|
+
* poll inside another keeps the nearer deadline.
|
|
135
134
|
*/
|
|
136
135
|
poll<T, S>(step: (attempt: number) => Promise<PollStep<T, S>> | PollStep<T, S>, timeoutMs: number, options?: {
|
|
137
136
|
intervalMs?: number;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Snapshot, SnapshotBackend } from "../driver/driver.types";
|
|
2
|
+
import { E2eError } from "../errors";
|
|
3
|
+
/**
|
|
4
|
+
* Why a snapshot cannot say a node is absent: it was answered by a fallback reader (`Snapshot.fellBackFrom`, iOS `ax`
|
|
5
|
+
* after `axbridge` stalled, which drops React Native testIDs on static text), or it is one childless root with no
|
|
6
|
+
* area (the reader could not see into the app). A match found on such a snapshot is real; a missing one is unknown.
|
|
7
|
+
*/
|
|
8
|
+
export type DegradedRead = {
|
|
9
|
+
kind: "fallback";
|
|
10
|
+
from: SnapshotBackend;
|
|
11
|
+
backend: SnapshotBackend;
|
|
12
|
+
} | {
|
|
13
|
+
kind: "emptyRoot";
|
|
14
|
+
backend: SnapshotBackend;
|
|
15
|
+
};
|
|
16
|
+
/** What makes `snapshot` inconclusive about absence; undefined for a whole read. */
|
|
17
|
+
export declare function degradedRead(snapshot: Snapshot): DegradedRead | undefined;
|
|
18
|
+
/** One phrase naming what was degraded, with the fallback and read backend. */
|
|
19
|
+
export declare function describeDegraded(read: DegradedRead): string;
|
|
20
|
+
/** Why the React tree did not settle it. */
|
|
21
|
+
export type ReactNote = "unanswered" | "no-devtools" | "not-testid";
|
|
22
|
+
/** `READ_DEGRADED`: the bridge is degraded and `locator` could be confirmed neither present nor absent in `waitedMs`. */
|
|
23
|
+
export declare function degradedFailure(locator: string, read: DegradedRead, waitedMs: number, react: ReactNote, screen?: string): E2eError;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Driver, DriverResult, Point, Rect, Snapshot } from "../driver/driver.types";
|
|
2
|
+
import type { NodeEntry } from "./nodes";
|
|
3
|
+
export type Cover = {
|
|
4
|
+
/** the keyboard's top edge */
|
|
5
|
+
top: number;
|
|
6
|
+
/** the free part of the nearest scroll container (its frame down to the keyboard), when the target sits in one */
|
|
7
|
+
scroller?: Rect;
|
|
8
|
+
/** points the content has to move up to clear the keyboard with a margin */
|
|
9
|
+
shift: number;
|
|
10
|
+
};
|
|
11
|
+
/** Whether `point` is under the keyboard of `snapshot`, and how to get clear of it; undefined when it is not. */
|
|
12
|
+
export declare function coverOf(entry: NodeEntry, point: Point, snapshot: Snapshot): Cover | undefined;
|
|
13
|
+
/**
|
|
14
|
+
* The finger path that moves the content of `scroller` up by about `shift` points (a shorter one when the free part
|
|
15
|
+
* is not that tall: the caller looks again and swipes again).
|
|
16
|
+
*/
|
|
17
|
+
export declare function scrollPath(scroller: Rect, shift: number): {
|
|
18
|
+
from: Point;
|
|
19
|
+
to: Point;
|
|
20
|
+
};
|
|
21
|
+
export declare const COVER_SCROLL_SWIPE: {
|
|
22
|
+
durationMs: number;
|
|
23
|
+
momentum: boolean;
|
|
24
|
+
};
|
|
25
|
+
/** Dismisses the keyboard the way `device.hideKeyboard()` does: iOS resigns the first responder (no Return), Android presses back. */
|
|
26
|
+
export declare function dismissKeyboard(driver: Driver): DriverResult<void>;
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import type { KeyName, Modifier, Point, Rect } from "../driver/driver.types";
|
|
2
|
+
import { E2eError } from "../errors";
|
|
2
3
|
import { type Momentum, type SwipeDirection } from "./actions";
|
|
3
4
|
import type { Observed, Runtime } from "./context";
|
|
5
|
+
import { type DegradedRead, type ReactNote } from "./degraded-read";
|
|
4
6
|
import { type FillVia } from "./field";
|
|
5
7
|
import type { NodeEntry } from "./nodes";
|
|
6
8
|
import { type RoleOptions, type TestIdOptions, type TextQueryOptions } from "./queries";
|
|
7
9
|
import { type LocatorExpression } from "./query";
|
|
10
|
+
import { type ReactMatch } from "./react-locator";
|
|
8
11
|
import type { QueryRole } from "./roles";
|
|
9
12
|
import { type TextMatch } from "./text";
|
|
10
13
|
export type ActionOptions = {
|
|
@@ -69,6 +72,21 @@ export type WaitState = "attached" | "detached" | "visible" | "hidden";
|
|
|
69
72
|
export type WaitForOptions = ActionOptions & {
|
|
70
73
|
state?: WaitState;
|
|
71
74
|
};
|
|
75
|
+
/**
|
|
76
|
+
* What one look says about a locator. `nodes`: the accessibility tree's answer (none included, when the read was whole
|
|
77
|
+
* or the React tree confirmed the absence). `react`: the read was degraded and the React tree has the component(s).
|
|
78
|
+
* `unknown`: the read was degraded (`screen/degraded-read.ts`) and nothing could confirm the locator either way.
|
|
79
|
+
*/
|
|
80
|
+
export type Resolved = {
|
|
81
|
+
kind: "nodes";
|
|
82
|
+
entries: NodeEntry[];
|
|
83
|
+
} | {
|
|
84
|
+
kind: "react";
|
|
85
|
+
matches: ReactMatch[];
|
|
86
|
+
} | {
|
|
87
|
+
kind: "unknown";
|
|
88
|
+
read: DegradedRead;
|
|
89
|
+
};
|
|
72
90
|
declare const LOCATOR_BRAND: unique symbol;
|
|
73
91
|
/** Whether `value` is a `Locator` (also across duplicated copies of this module, e.g. the bundled entry and the preload). */
|
|
74
92
|
export declare function isLocator(value: unknown): value is Locator;
|
|
@@ -102,7 +120,29 @@ export declare class Locator {
|
|
|
102
120
|
entries: NodeEntry[];
|
|
103
121
|
observed: Observed;
|
|
104
122
|
}>;
|
|
123
|
+
/**
|
|
124
|
+
* What `observed` says about this locator. A whole read, or one with matches, is the accessibility tree's answer. A
|
|
125
|
+
* degraded read with no match is asked of the React tree (plain `getByTestId` with devtools): it answers presence or
|
|
126
|
+
* absence; otherwise the look is `unknown`, and no caller may take it as "absent". A component whose rect is an
|
|
127
|
+
* accessibility node's (the fallback reader kept the node, dropped the testID) resolves to that node, so text,
|
|
128
|
+
* visibility and actions read it like any match; one with no such node stays a React-only match.
|
|
129
|
+
*/
|
|
130
|
+
resolveIn(observed: Observed): Promise<Resolved>;
|
|
131
|
+
/** Why the React tree did not settle a degraded look, for the `READ_DEGRADED` message. */
|
|
132
|
+
reactNote(): ReactNote;
|
|
133
|
+
/** `READ_DEGRADED` for this locator after `waitedMs`, with the screen of the last degraded look. */
|
|
134
|
+
degradedFailure(read: DegradedRead, waitedMs: number, snapshot: Observed["snapshot"] | undefined): E2eError;
|
|
135
|
+
/**
|
|
136
|
+
* One look that settles this locator. A whole read answers at once (never waits); a degraded one that cannot say
|
|
137
|
+
* whether the locator is there is looked at again until one can, within the action timeout (`quick`: within
|
|
138
|
+
* `QUICK_CHECK_WINDOW_MS`, for checks a spec branches on), then `READ_DEGRADED`.
|
|
139
|
+
*/
|
|
140
|
+
private conclusive;
|
|
141
|
+
/** The conclusive answer of `observed`, or undefined while it is `unknown`. */
|
|
142
|
+
private settled;
|
|
105
143
|
private matches;
|
|
144
|
+
/** The one React match a read of a component the accessibility tree lacks stands for; several are ambiguous. */
|
|
145
|
+
private reactOnly;
|
|
106
146
|
private single;
|
|
107
147
|
/** Components of the React tree that this locator names, when it is a plain `getByTestId` and the app has devtools. */
|
|
108
148
|
private reactCandidates;
|
|
@@ -113,6 +153,8 @@ export declare class Locator {
|
|
|
113
153
|
all(): Promise<Locator[]>;
|
|
114
154
|
text(): Promise<string>;
|
|
115
155
|
allTexts(): Promise<string[]>;
|
|
156
|
+
/** The text the React tree reported for a component of a degraded read that no accessibility node stands for. */
|
|
157
|
+
private reactText;
|
|
116
158
|
value(): Promise<string>;
|
|
117
159
|
isVisible(): Promise<boolean>;
|
|
118
160
|
/**
|
|
@@ -124,7 +166,11 @@ export declare class Locator {
|
|
|
124
166
|
*/
|
|
125
167
|
read(): Promise<LocatorReading>;
|
|
126
168
|
/** Whether this locator's one match is visible in `observed`; no match is not visible, several matches throw. */
|
|
127
|
-
|
|
169
|
+
/**
|
|
170
|
+
* Whether `observed` shows this locator, without waiting: a degraded read is resolved like `resolveIn` (the React
|
|
171
|
+
* tree's node or rect), and one nothing could settle counts as not shown yet.
|
|
172
|
+
*/
|
|
173
|
+
shownIn(observed: Observed): Promise<boolean>;
|
|
128
174
|
isHidden(): Promise<boolean>;
|
|
129
175
|
isEnabled(): Promise<boolean>;
|
|
130
176
|
isChecked(): Promise<boolean>;
|
|
@@ -174,6 +220,13 @@ export declare class Locator {
|
|
|
174
220
|
* the kept snapshot sits outside the app, where the app-scoped hit-test may not see it, and the fresh look answers it.
|
|
175
221
|
*/
|
|
176
222
|
private aimFromKept;
|
|
223
|
+
/**
|
|
224
|
+
* A target under the on-screen keyboard (the default `software` mode keeps it up) would get the touch on a key. In a
|
|
225
|
+
* scroll container it is scrolled into the free part of the screen (at most `MAX_COVER_SCROLLS` swipes, each looked at
|
|
226
|
+
* fresh); otherwise, or when scrolling does not clear it, the keyboard is dismissed once (`dismissKeyboard`). Either way
|
|
227
|
+
* the match is looked at again, and whatever that look shows is what the action uses: no retry.
|
|
228
|
+
*/
|
|
229
|
+
private clearOfKeyboard;
|
|
177
230
|
/**
|
|
178
231
|
* Taps the one visible, enabled match. With `until` it is also the retrying tap: the press on a tab bar or a list row
|
|
179
232
|
* right after a navigation is sometimes dropped with nothing on screen to tell, so it checks that `until` shows
|
|
@@ -181,9 +234,10 @@ export declare class Locator {
|
|
|
181
234
|
*/
|
|
182
235
|
tap(options?: TapOptions): Promise<void>;
|
|
183
236
|
/**
|
|
184
|
-
* One press; resolves to the snapshot it aimed from (the screen just before the press)
|
|
185
|
-
*
|
|
186
|
-
*
|
|
237
|
+
* One press; resolves to the snapshot it aimed from (the screen just before the press) and the element pressed (none
|
|
238
|
+
* for a match only the React tree knows). A plain tap may aim from the kept snapshot (`aimFromKept`); a tap with
|
|
239
|
+
* `until` takes a fresh look, since the screen it aimed from is what the checks compare against to tell the press
|
|
240
|
+
* landed, and an older one could differ only because it was older.
|
|
187
241
|
*/
|
|
188
242
|
private tapOnce;
|
|
189
243
|
/**
|
|
@@ -191,9 +245,9 @@ export declare class Locator {
|
|
|
191
245
|
* (`driver.tap` returns once the HID press is over, ~85 ms, and an AX read queues behind the app's main thread, so
|
|
192
246
|
* that look usually already shows the screen the press made; measured: holding it back to `pollMs` saved no read
|
|
193
247
|
* and cost 250 ms a press). A locator is checked on the snapshot of
|
|
194
|
-
* the poll itself, which is also compared with
|
|
195
|
-
*
|
|
196
|
-
*
|
|
248
|
+
* the poll itself, which is also compared with the screen the press aimed from (`reacted`): once the screen reacted
|
|
249
|
+
* the press landed, so it is never sent again (`landed`, even when `until` is late); a screen that has not reacted
|
|
250
|
+
* on `UNCHANGED_CHECKS` checks in a row from `UNCHANGED_AFTER_MS` on is pressed again at once. A function
|
|
197
251
|
* is called with the kept snapshot forgotten (after the first check), so its own reads take one new snapshot
|
|
198
252
|
* between them rather than the previous check's; it gets the whole window.
|
|
199
253
|
*/
|
|
@@ -11,17 +11,32 @@ export type ReactMatch = {
|
|
|
11
11
|
testId: string;
|
|
12
12
|
/** the component's name (`View`, `Button`, ...) */
|
|
13
13
|
name: string;
|
|
14
|
+
/** the strings rendered inside it, when the React tree reported any */
|
|
15
|
+
text?: string;
|
|
14
16
|
};
|
|
15
17
|
export type ReactView = {
|
|
16
18
|
platform: Platform;
|
|
17
19
|
screen: ScreenInfo;
|
|
18
20
|
};
|
|
21
|
+
/** What the React tree said: `answered: false` when it could not be read (no devtools, a failed or timed-out read). */
|
|
22
|
+
export type ReactLookup = {
|
|
23
|
+
answered: true;
|
|
24
|
+
matches: ReactMatch[];
|
|
25
|
+
} | {
|
|
26
|
+
answered: false;
|
|
27
|
+
};
|
|
19
28
|
/**
|
|
20
29
|
* Components of the running app whose `testID` matches, with rects in the driver's pixels. This is the fallback for
|
|
21
30
|
* `getByTestId` when the platform hides the view from accessibility (React Native only exposes `testID` to
|
|
22
|
-
* accessibility on some views)
|
|
23
|
-
*
|
|
31
|
+
* accessibility on some views), and the second opinion on a degraded read. A devtools failure is `answered: false`,
|
|
32
|
+
* kept apart from an empty answer so a caller can tell "absent" from "cannot tell". Android measures in dp but the
|
|
33
|
+
* driver taps in px: `view.screen.scale` converts.
|
|
24
34
|
*/
|
|
35
|
+
export declare function lookUpTestIdViaReact(facet: Pick<DevtoolsFacet, "tree">, testId: TextMatch, view: ReactView, options?: {
|
|
36
|
+
screenName?: string;
|
|
37
|
+
timeoutMs?: number;
|
|
38
|
+
}): Promise<ReactLookup>;
|
|
39
|
+
/** `lookUpTestIdViaReact` where any failure means "nothing found", so callers keep their own not-found error. */
|
|
25
40
|
export declare function locateByTestIdViaReact(facet: Pick<DevtoolsFacet, "tree">, testId: TextMatch, view: ReactView, options?: {
|
|
26
41
|
screenName?: string;
|
|
27
42
|
timeoutMs?: number;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { NodeEntry, NodeIndex } from "./nodes";
|
|
2
|
+
import type { ReactMatch } from "./react-locator";
|
|
3
|
+
/**
|
|
4
|
+
* The accessibility node a React component stands for on a degraded read: a fallback reader (iOS host `ax`) keeps the
|
|
5
|
+
* node but drops its testID, so the component's rect is what ties the two. Never a node that carries another testID,
|
|
6
|
+
* never an area-less rect. Several nodes with the rect: the one whose own text is the component's, else the deepest.
|
|
7
|
+
*/
|
|
8
|
+
export declare function nodeOfReactMatch(match: ReactMatch, index: NodeIndex): NodeEntry | undefined;
|
|
9
|
+
/** The node of every match, each a different node; undefined when one of them has none (the React tree answers alone). */
|
|
10
|
+
export declare function mergeReactMatches(matches: readonly ReactMatch[], index: NodeIndex): NodeEntry[] | undefined;
|