@delacour/warden-e2e 0.14.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-zk3wxa70.mjs → chunk-e3cry5br.mjs} +287 -37
- package/index.mjs +32 -3
- package/package.json +2 -2
- package/preload.mjs +304 -30
- 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 +4 -0
- package/types/e2e/src/driver/driver.types.d.ts +30 -1
- package/types/e2e/src/driver/ios/inject.d.ts +17 -2
- package/types/e2e/src/driver/ios/ios-driver.d.ts +7 -2
- package/types/e2e/src/driver/ios/ios-keyboard.d.ts +39 -0
- package/types/e2e/src/driver/ios/simd-protocol.d.ts +99 -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 +3 -1
- package/types/e2e/src/expect/a11y-matchers.d.ts +7 -0
- package/types/e2e/src/index.d.ts +2 -1
- package/types/e2e/src/screen/actions.d.ts +7 -0
- package/types/e2e/src/screen/context.d.ts +3 -1
- package/types/e2e/src/screen/keyboard-cover.d.ts +26 -0
- package/types/e2e/src/screen/locator.d.ts +16 -7
- package/types/e2e/src/session.d.ts +6 -0
|
@@ -358,6 +358,7 @@ export declare const wardenConfigSchema: z.ZodObject<{
|
|
|
358
358
|
except: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
359
359
|
keep: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
360
360
|
}, z.core.$strict>]>>;
|
|
361
|
+
golden: z.ZodOptional<z.ZodBoolean>;
|
|
361
362
|
biometrics: z.ZodOptional<z.ZodBoolean>;
|
|
362
363
|
reclaim: z.ZodOptional<z.ZodEnum<{
|
|
363
364
|
none: "none";
|
|
@@ -453,6 +454,10 @@ export declare const wardenConfigSchema: z.ZodObject<{
|
|
|
453
454
|
}>>;
|
|
454
455
|
reduceMotion: z.ZodOptional<z.ZodBoolean>;
|
|
455
456
|
}, z.core.$strict>>;
|
|
457
|
+
keyboard: z.ZodOptional<z.ZodEnum<{
|
|
458
|
+
software: "software";
|
|
459
|
+
hardware: "hardware";
|
|
460
|
+
}>>;
|
|
456
461
|
}, z.core.$strict>>;
|
|
457
462
|
a11y: z.ZodOptional<z.ZodObject<{
|
|
458
463
|
ignore: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
@@ -606,6 +611,10 @@ export declare const wardenConfigSchema: z.ZodObject<{
|
|
|
606
611
|
}>>;
|
|
607
612
|
reduceMotion: z.ZodOptional<z.ZodBoolean>;
|
|
608
613
|
}, z.core.$strict>>;
|
|
614
|
+
keyboard: z.ZodOptional<z.ZodEnum<{
|
|
615
|
+
software: "software";
|
|
616
|
+
hardware: "hardware";
|
|
617
|
+
}>>;
|
|
609
618
|
}, z.core.$strict>>;
|
|
610
619
|
a11y: z.ZodOptional<z.ZodObject<{
|
|
611
620
|
ignore: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
@@ -579,7 +579,7 @@ export interface WardenE2eSuite {
|
|
|
579
579
|
* `"idle"`: leave them booted, so the next run skips the 10-30 s boot, and let `warden gc` shut
|
|
580
580
|
* them down once they have sat idle. A failed run always leaves them up. `--no-shutdown` wins.
|
|
581
581
|
*
|
|
582
|
-
* @default "always"
|
|
582
|
+
* @default "always" locally, "idle" under `CI` / `GITHUB_ACTIONS` (the runner is thrown away)
|
|
583
583
|
*/
|
|
584
584
|
shutdown?: "always" | "idle" | undefined;
|
|
585
585
|
/**
|
|
@@ -734,6 +734,14 @@ export interface WardenE2eSuite {
|
|
|
734
734
|
* `biometrics`. Default false.
|
|
735
735
|
*/
|
|
736
736
|
biometrics?: boolean | undefined;
|
|
737
|
+
/**
|
|
738
|
+
* iOS: simulators the run creates are cloned from a golden image (a first-booted, settled simulator built once
|
|
739
|
+
* per profile and runtime). Off: each is a fresh `simctl create` and first boot, and `warden ci cache --suite`
|
|
740
|
+
* prints no golden group. Existing pool devices are reused either way; `WARDEN_GOLDEN=0` turns it off everywhere.
|
|
741
|
+
*
|
|
742
|
+
* @default false
|
|
743
|
+
*/
|
|
744
|
+
golden?: boolean | undefined;
|
|
737
745
|
/**
|
|
738
746
|
* What happens to the state of the devices of this run when they are released: `"erase"` (iOS: shutdown +
|
|
739
747
|
* `simctl erase`; Android: uninstalls the third-party apps), `"uninstall"` (removes the user-installed apps) or
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type Result } from "../../../types/src/result";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
-
import { type A11yCondition, type ClockCondition, type LocationCondition, type NetworkCondition, type NetworkPreset, type PermissionName, type PermissionState } from "./conditions.types";
|
|
3
|
+
import { type A11yCondition, type ClockCondition, type KeyboardCondition, type LocationCondition, type NetworkCondition, type NetworkPreset, type PermissionName, type PermissionState } from "./conditions.types";
|
|
4
4
|
/** `network: { latencyMs, ... }`: the same fields as a `custom` network condition, without its `kind`. */
|
|
5
5
|
declare const customNetworkSchema: z.ZodObject<{
|
|
6
6
|
latencyMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -91,6 +91,10 @@ export declare const conditionsConfigSchema: z.ZodObject<{
|
|
|
91
91
|
}>>;
|
|
92
92
|
reduceMotion: z.ZodOptional<z.ZodBoolean>;
|
|
93
93
|
}, z.core.$strict>>;
|
|
94
|
+
keyboard: z.ZodOptional<z.ZodEnum<{
|
|
95
|
+
software: "software";
|
|
96
|
+
hardware: "hardware";
|
|
97
|
+
}>>;
|
|
94
98
|
}, z.core.$strict>;
|
|
95
99
|
export type ConditionsConfig = z.input<typeof conditionsConfigSchema>;
|
|
96
100
|
/**
|
|
@@ -129,6 +133,7 @@ export type SuiteConditions = {
|
|
|
129
133
|
locale?: string;
|
|
130
134
|
timezone?: string;
|
|
131
135
|
a11y?: A11yCondition;
|
|
136
|
+
keyboard?: KeyboardCondition;
|
|
132
137
|
};
|
|
133
138
|
/** A config as conditions: shaped values, with a text size resolved. */
|
|
134
139
|
export declare function normalizeConditions(config: ConditionsConfig): Result<{
|
|
@@ -127,6 +127,26 @@ export type BatteryCondition = {
|
|
|
127
127
|
charging?: boolean;
|
|
128
128
|
cosmetic?: boolean;
|
|
129
129
|
};
|
|
130
|
+
export type KeyboardMode = "software" | "hardware";
|
|
131
|
+
export declare const KEYBOARD_MODES: readonly KeyboardMode[];
|
|
132
|
+
/** What every warden-leased device starts on and what release restores: the on-screen keyboard shows. */
|
|
133
|
+
export declare const DEFAULT_KEYBOARD_MODE: KeyboardMode;
|
|
134
|
+
/** `software`: the on-screen keyboard shows; `hardware`: it is hidden, input arrives as from a connected keyboard. */
|
|
135
|
+
export type KeyboardCondition = {
|
|
136
|
+
mode: KeyboardMode;
|
|
137
|
+
previousIme?: ImeState;
|
|
138
|
+
};
|
|
139
|
+
/** Global settings holding the IME state `hardware` mode switched off (survives the driver process). */
|
|
140
|
+
export declare const IME_STASH_ENABLED = "warden_ime_enabled";
|
|
141
|
+
export declare const IME_STASH_DEFAULT = "warden_ime_default";
|
|
142
|
+
/**
|
|
143
|
+
* Android only: the input methods `hardware` mode switched off, recorded so release puts them back exactly.
|
|
144
|
+
* `enabled` is `settings secure enabled_input_methods` and `default` is `default_input_method` (empty = none).
|
|
145
|
+
*/
|
|
146
|
+
export type ImeState = {
|
|
147
|
+
enabled: string;
|
|
148
|
+
default: string;
|
|
149
|
+
};
|
|
130
150
|
export type CameraFacing = "back" | "front";
|
|
131
151
|
export declare const CAMERA_FACINGS: readonly CameraFacing[];
|
|
132
152
|
/** A staged asset the simulated camera plays; `path` is the staged copy under the device's camera dir. */
|
|
@@ -150,6 +170,7 @@ export type ConditionValues = {
|
|
|
150
170
|
a11y: A11yCondition;
|
|
151
171
|
battery: BatteryCondition;
|
|
152
172
|
camera: CameraCondition;
|
|
173
|
+
keyboard: KeyboardCondition;
|
|
153
174
|
};
|
|
154
175
|
export type ConditionArea = keyof ConditionValues;
|
|
155
176
|
/** Display/iteration order; also the order undo resets areas in. */
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type ConditionArea } from "./conditions.types";
|
|
1
2
|
/**
|
|
2
3
|
* The state file an iOS app launched with `libwarden-inject.dylib` watches (offline flag, wall-clock offset /
|
|
3
4
|
* freeze). The e2e driver owns the full format (`packages/e2e/src/driver/ios/inject.ts`); core only knows where it
|
|
@@ -7,7 +8,7 @@
|
|
|
7
8
|
export declare function injectStatePath(wardenHome: string, udid: string): string;
|
|
8
9
|
/**
|
|
9
10
|
* Write `{ seq: previous + 1, offline: false, clockOffsetMs: 0, camera: {} }` (no freeze, no camera feeds) when a state file exists; the dylib
|
|
10
|
-
* applies any higher `seq`. No file = no injected app was ever launched on the device: nothing to do.
|
|
11
|
+
* applies any higher `seq`. `keyboard` stays what the file had, except when `area` is `keyboard`: then it goes back to the default. No file = no injected app was ever launched on the device: nothing to do.
|
|
11
12
|
* Returns an error message, or undefined.
|
|
12
13
|
*/
|
|
13
|
-
export declare function neutralizeInjectState(path: string): string | undefined;
|
|
14
|
+
export declare function neutralizeInjectState(path: string, area?: ConditionArea): string | undefined;
|
|
@@ -13,6 +13,8 @@ export type A11yTarget = {
|
|
|
13
13
|
}): Promise<Result<AnnouncementMatch, DriverError>>;
|
|
14
14
|
/** the current screen as text with the `--a11y` columns (failure evidence); undefined when it cannot be read */
|
|
15
15
|
a11yScreen(): Promise<string | undefined>;
|
|
16
|
+
/** whether the on-screen keyboard is up right now (one fresh read) */
|
|
17
|
+
keyboardVisible(): Promise<boolean>;
|
|
16
18
|
/** the assertion timeout in ms */
|
|
17
19
|
assertionTimeout(): number;
|
|
18
20
|
/** one reported step */
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { A11yCondition, BatteryCondition, CameraCondition, ClockCondition, HostFault, LocaleCondition, LocationCondition, NetworkCondition, PermissionCondition } from "../../../core/src/conditions/conditions.types";
|
|
1
|
+
import type { A11yCondition, BatteryCondition, CameraCondition, ClockCondition, HostFault, KeyboardCondition, 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: "keyboard";
|
|
45
|
+
value: KeyboardCondition;
|
|
46
|
+
at: number;
|
|
42
47
|
} | {
|
|
43
48
|
type: "condition";
|
|
44
49
|
area: "camera";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type NetworkInput } from "../../../core/src/conditions/conditions.config";
|
|
2
|
-
import type
|
|
3
|
-
import type { A11yOp, A11yState, BatteryOp, BatteryState, CameraState, Driver, LocaleState, NetworkState, PermissionGrants, PermissionsState, PushPayload, SettingOptions, TimezoneState } from "../driver/driver.types";
|
|
2
|
+
import { type CameraFacing, type ClockCondition, type HostFault, type KeyboardMode, type LatLon, type LocationCondition, type LocationScenario, type NetworkCondition } from "../../../core/src/conditions/conditions.types";
|
|
3
|
+
import type { A11yOp, A11yState, BatteryOp, BatteryState, CameraState, Driver, KeyboardResult, 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";
|
|
@@ -160,6 +160,12 @@ export type DeviceConditions = {
|
|
|
160
160
|
a11y(op: A11yOp | null): Promise<A11yState>;
|
|
161
161
|
/** Battery level and charging. Android emulators: real. iOS: the status bar only (`cosmetic: true`). `null` resets. */
|
|
162
162
|
battery(op: BatteryOp | null): Promise<BatteryState>;
|
|
163
|
+
/**
|
|
164
|
+
* The on-screen keyboard: `software` (the default) keeps it up while the spec types, `hardware` hides it (input as from
|
|
165
|
+
* a connected keyboard), `toggle` flips the mode this spec has in place. Live, no relaunch. Put back to the suite's
|
|
166
|
+
* `conditions.keyboard` (else `software`) when the test ends.
|
|
167
|
+
*/
|
|
168
|
+
keyboard(mode: KeyboardMode | "toggle"): Promise<KeyboardResult>;
|
|
163
169
|
/** Android: a critical trim-memory signal to the running app. iOS: `E_UNSUPPORTED` (no delivery mechanism works). */
|
|
164
170
|
memoryWarning(options?: SettingOptions): Promise<void>;
|
|
165
171
|
/** iOS: delivers an APNs payload (`{ aps: { alert: ... } }`) to the app. Android: `E_UNSUPPORTED`. */
|
|
@@ -81,8 +81,8 @@ export type Device = DeviceA11y & {
|
|
|
81
81
|
clipboard(): Promise<string>;
|
|
82
82
|
setClipboard(text: string): Promise<void>;
|
|
83
83
|
/**
|
|
84
|
-
* Dismisses the soft keyboard when the snapshot shows one, else does nothing. iOS
|
|
85
|
-
* (which may submit the
|
|
84
|
+
* Dismisses the soft keyboard when the snapshot shows one, else does nothing. iOS resigns the first responder
|
|
85
|
+
* (an app launched without the injected library gets Enter, which may submit the field); Android presses back.
|
|
86
86
|
*/
|
|
87
87
|
hideKeyboard(): Promise<void>;
|
|
88
88
|
/** Saves `<attemptDir>/screenshots/NNN-<name>.png` and returns the path. */
|
|
@@ -125,6 +125,14 @@ export type Device = DeviceA11y & {
|
|
|
125
125
|
a11y: DeviceConditions["a11y"];
|
|
126
126
|
/** Battery level and charging: real on Android emulators, status bar only on iOS (`cosmetic: true`). */
|
|
127
127
|
battery: DeviceConditions["battery"];
|
|
128
|
+
/**
|
|
129
|
+
* The on-screen keyboard mode: `"software"` (the default: it stays up while the spec types), `"hardware"` (hidden,
|
|
130
|
+
* as with a connected keyboard) or `"toggle"`. Live. Put back to the suite's `conditions.keyboard` when the test
|
|
131
|
+
* ends. Physical iPhones: `E_UNSUPPORTED`.
|
|
132
|
+
*/
|
|
133
|
+
keyboard: DeviceConditions["keyboard"];
|
|
134
|
+
/** Whether the on-screen keyboard is up now, read from the accessibility tree. `expect(device).toHaveKeyboard()` polls it. */
|
|
135
|
+
keyboardVisible(): Promise<boolean>;
|
|
128
136
|
/**
|
|
129
137
|
* Simulated camera: `set("fixtures/qr.png", { facing })`, `clear`, `status`. The asset (png, jpg, heic, mp4, mov; path relative
|
|
130
138
|
* to the spec file) plays in the app's real camera. iOS: live in an app launched with the camera, else from the next launch.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type KeyboardMode, type PermissionName, type PermissionState } from "../../../../core/src/conditions/conditions.types";
|
|
2
2
|
import type { Driver } from "../driver.types";
|
|
3
3
|
import type { AdbSession } from "./adb-session";
|
|
4
4
|
import { type TalkBackControl } from "./android-talkback";
|
|
@@ -35,7 +35,14 @@ export type AndroidSettingsDeps = {
|
|
|
35
35
|
/** is `warden-uiad` how this driver reads the screen (TalkBack mode needs it: the dump fallback unbinds TalkBack); default false */
|
|
36
36
|
uiadAvailable?: () => boolean;
|
|
37
37
|
};
|
|
38
|
-
export type AndroidSettings = Pick<Driver, "permissions" | "timezone" | "locale" | "a11y" | "battery" | "memoryWarning" | "push">;
|
|
38
|
+
export type AndroidSettings = Pick<Driver, "permissions" | "timezone" | "locale" | "a11y" | "battery" | "memoryWarning" | "push" | "keyboard" | "keyboardState">;
|
|
39
|
+
/**
|
|
40
|
+
* Whether the IME window is up: `dumpsys window InputMethod` says `isVisible=true` while it is (the uiautomator dump
|
|
41
|
+
* carries the app's window only, so the keyboard is never in the snapshot on Android).
|
|
42
|
+
*/
|
|
43
|
+
export declare function imeVisibleIn(dump: string): boolean;
|
|
39
44
|
export declare function createAndroidSettings(deps: AndroidSettingsDeps): AndroidSettings & {
|
|
40
45
|
talkback: TalkBackControl;
|
|
46
|
+
keyboardMode(): Promise<KeyboardMode>;
|
|
47
|
+
keyboardVisible(): Promise<boolean>;
|
|
41
48
|
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { Driver, DriverResult } from "./driver.types";
|
|
2
|
+
/**
|
|
3
|
+
* The iOS helper's check for a wedged accessibility service (`warden-simd` `AXWedgeRecovery`): when both AX readers
|
|
4
|
+
* stay silent and the sim log names the wedge, it restarts backboardd (which quits the foreground app), ends its stale
|
|
5
|
+
* guests and reads on. `last` is the latest check; `agoMs` counts from its end.
|
|
6
|
+
*/
|
|
7
|
+
export type AxWedgeState = {
|
|
8
|
+
restarts: number;
|
|
9
|
+
maxRestarts: number;
|
|
10
|
+
recovering: boolean;
|
|
11
|
+
/** `recover: true` only: what the check that ask ran (or joined) found; absent when none ran (budget spent) */
|
|
12
|
+
checked?: "restarted" | "not-wedged" | "failed";
|
|
13
|
+
last?: {
|
|
14
|
+
outcome: "restarted";
|
|
15
|
+
agoMs: number;
|
|
16
|
+
ms?: number;
|
|
17
|
+
} | {
|
|
18
|
+
outcome: "not-wedged";
|
|
19
|
+
agoMs: number;
|
|
20
|
+
} | {
|
|
21
|
+
outcome: "failed";
|
|
22
|
+
agoMs: number;
|
|
23
|
+
error: string;
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Optional capability next to the frozen `Driver` (iOS simulators): read the helper's wedge state, or with
|
|
28
|
+
* `recover: true` run the check now and answer once it is over. A helper built before it answers `E_UNSUPPORTED`
|
|
29
|
+
* `detail.reason: "helper_outdated"`.
|
|
30
|
+
*/
|
|
31
|
+
export type RecoversAxWedge = {
|
|
32
|
+
axWedge(opts?: {
|
|
33
|
+
recover?: boolean;
|
|
34
|
+
}): DriverResult<AxWedgeState>;
|
|
35
|
+
};
|
|
36
|
+
export declare function canRecoverAxWedge(driver: Driver): driver is Driver & RecoversAxWedge;
|
|
37
|
+
/** Whether the helper restarted backboardd within the last `windowMs` (e.g. during the test that just failed). */
|
|
38
|
+
export declare function restartedWithin(state: AxWedgeState, windowMs: number): boolean;
|
|
@@ -15,6 +15,8 @@ export declare const bridgeStallSchema: z.ZodObject<{
|
|
|
15
15
|
probes: z.ZodNumber;
|
|
16
16
|
recycles: z.ZodOptional<z.ZodNumber>;
|
|
17
17
|
recyclesExhausted: z.ZodOptional<z.ZodBoolean>;
|
|
18
|
+
axWedge: z.ZodOptional<z.ZodString>;
|
|
19
|
+
axWedgeRestarts: z.ZodOptional<z.ZodNumber>;
|
|
18
20
|
}, z.core.$loose>;
|
|
19
21
|
export type BridgeStall = {
|
|
20
22
|
stage: string;
|
|
@@ -26,6 +28,8 @@ export type BridgeStall = {
|
|
|
26
28
|
probes: number;
|
|
27
29
|
recycles?: number;
|
|
28
30
|
recyclesExhausted?: boolean;
|
|
31
|
+
axWedge?: string;
|
|
32
|
+
axWedgeRestarts?: number;
|
|
29
33
|
};
|
|
30
34
|
/** Only the fields of the schema, so a newer helper's extra keys stay off the span and out of the results. */
|
|
31
35
|
export declare function toBridgeStall(wire: z.infer<typeof bridgeStallSchema>): BridgeStall;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CameraCondition, ClockCondition, HostFault, LocationCondition, NetworkCondition, PermissionName, PermissionState, TextSize } from "../../../core/src/conditions/conditions.types";
|
|
1
|
+
import type { CameraCondition, ClockCondition, HostFault, ImeState, KeyboardMode, LocationCondition, NetworkCondition, PermissionName, PermissionState, TextSize } from "../../../core/src/conditions/conditions.types";
|
|
2
2
|
import type { AsyncResult } from "../../../types/src/result";
|
|
3
3
|
import type { ScreenReaderState } from "../a11y/a11y.types";
|
|
4
4
|
import type { BridgeStall } from "./bridge-stall";
|
|
@@ -344,6 +344,21 @@ export interface Driver {
|
|
|
344
344
|
* OS and the app. iOS: status bar override only (`cosmetic: true`): the app still reads the real battery.
|
|
345
345
|
*/
|
|
346
346
|
battery(op: BatteryOp | null): DriverResult<BatteryState>;
|
|
347
|
+
/**
|
|
348
|
+
* The on-screen keyboard mode (KBD-2): `software` keeps it up while warden types, `hardware` hides it (input as from
|
|
349
|
+
* a connected keyboard). Live, no relaunch. iOS simulator: the injected dylib (`appliesOnLaunch` when the running
|
|
350
|
+
* app was launched without it). Android: disables the soft IMEs (stashed on the device, restored by `software` and on release). Physical iOS:
|
|
351
|
+
* `E_UNSUPPORTED`.
|
|
352
|
+
*/
|
|
353
|
+
keyboard(mode: KeyboardMode): DriverResult<KeyboardResult>;
|
|
354
|
+
/** The mode in force and whether a keyboard is on screen now (a `keyboard` element in the accessibility tree). */
|
|
355
|
+
keyboardState(): DriverResult<KeyboardState>;
|
|
356
|
+
/**
|
|
357
|
+
* Takes the on-screen keyboard down without a keystroke that could submit a form. iOS simulator: the injected
|
|
358
|
+
* dylib resigns the first responder (`via: "resign"`); an app launched without it falls back to Enter (`via:
|
|
359
|
+
* "key"`, with a warning in the log). Android: back. Physical iPhone: Enter.
|
|
360
|
+
*/
|
|
361
|
+
dismissKeyboard(): DriverResult<DismissKeyboardResult>;
|
|
347
362
|
/** Android: `am send-trim-memory RUNNING_CRITICAL` to the running app. iOS: `E_UNSUPPORTED` (no way verified to reach an app). */
|
|
348
363
|
memoryWarning(opts?: SettingOptions): DriverResult<void>;
|
|
349
364
|
/** iOS: `simctl push` of an APNs payload (needs `aps`) to the app. Android: `E_UNSUPPORTED`. */
|
|
@@ -443,6 +458,20 @@ export type A11yState = {
|
|
|
443
458
|
enabled: string | null;
|
|
444
459
|
};
|
|
445
460
|
};
|
|
461
|
+
/** `previousIme` (Android `hardware`): the input methods switched off, for the lease record. */
|
|
462
|
+
export type KeyboardResult = {
|
|
463
|
+
mode: KeyboardMode;
|
|
464
|
+
appliesOnLaunch?: boolean;
|
|
465
|
+
previousIme?: ImeState;
|
|
466
|
+
};
|
|
467
|
+
/** `via`: how the keyboard was dismissed; `key` (Enter) can submit the focused field. */
|
|
468
|
+
export type DismissKeyboardResult = {
|
|
469
|
+
via: "resign" | "key" | "back";
|
|
470
|
+
};
|
|
471
|
+
export type KeyboardState = {
|
|
472
|
+
mode: KeyboardMode;
|
|
473
|
+
visible: boolean;
|
|
474
|
+
};
|
|
446
475
|
export type BatteryOp = {
|
|
447
476
|
level?: number;
|
|
448
477
|
charging?: boolean;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type CameraCondition, type KeyboardMode } from "../../../../core/src/conditions/conditions.types";
|
|
2
2
|
import type { Result } from "../../../../types/src/result";
|
|
3
3
|
import type { DriverError } from "../driver.types";
|
|
4
4
|
import type { ProxyAddress } from "../network-proxy";
|
|
@@ -25,6 +25,17 @@ export type InjectState = {
|
|
|
25
25
|
/** one-shot request (A11Y-1): walk the accessibility tree and write `<state>.a11y` before acknowledging this `seq` */
|
|
26
26
|
a11yRead?: boolean;
|
|
27
27
|
camera?: CameraCondition;
|
|
28
|
+
/**
|
|
29
|
+
* The on-screen keyboard (KBD-2): `software` keeps it up while warden types, `hardware` lets iOS minimise it. Unlike
|
|
30
|
+
* the other fields a message that leaves it out keeps what the file had (see `createInjectChannel`), so a network or
|
|
31
|
+
* a11y write never undoes it.
|
|
32
|
+
*/
|
|
33
|
+
keyboard?: KeyboardMode;
|
|
34
|
+
/**
|
|
35
|
+
* one-shot request: resign the first responder (the keyboard goes away without a Return that could submit the form)
|
|
36
|
+
* before acknowledging this `seq`. Written, never read back, so a later write cannot repeat it.
|
|
37
|
+
*/
|
|
38
|
+
dismissKeyboard?: boolean;
|
|
28
39
|
};
|
|
29
40
|
/** What the dylib answers in `<state>.ack` once it applied `seq` (`bundleId`: the app it loaded into; `camera`: launched with `WARDEN_CAMERA=1`). */
|
|
30
41
|
export type InjectAck = {
|
|
@@ -56,6 +67,8 @@ export declare function injectLaunchEnv(input: {
|
|
|
56
67
|
clockFrozenMs?: number;
|
|
57
68
|
/** fake the capture stack from the state file's `camera` feeds (`WARDEN_CAMERA=1`) */
|
|
58
69
|
camera?: boolean;
|
|
70
|
+
/** the keyboard mode the app starts in (`WARDEN_KEYBOARD`); the state file's `keyboard` overrides it live */
|
|
71
|
+
keyboard?: KeyboardMode;
|
|
59
72
|
debug?: boolean;
|
|
60
73
|
}): Record<string, string>;
|
|
61
74
|
/**
|
|
@@ -69,6 +82,8 @@ export declare function shouldInject(input: {
|
|
|
69
82
|
clockActive: boolean;
|
|
70
83
|
/** the lease has a camera feed: the dylib must be loaded at launch to fake the capture stack */
|
|
71
84
|
cameraActive?: boolean;
|
|
85
|
+
/** the lease's keyboard mode is `software` (the default): the dylib keeps the on-screen keyboard up while warden types */
|
|
86
|
+
keyboardActive?: boolean;
|
|
72
87
|
/** the caller asked for injection (`LaunchOptions.inject`, a suite that declares `conditions.network`) */
|
|
73
88
|
requested: boolean;
|
|
74
89
|
}): boolean;
|
|
@@ -111,7 +126,7 @@ export type InjectChannel = {
|
|
|
111
126
|
attached(): InjectAck | undefined;
|
|
112
127
|
/** Wait until the app acknowledged `seq`; `false` on timeout. */
|
|
113
128
|
acked(seq: number, timeoutMs: number): Promise<boolean>;
|
|
114
|
-
/** Forget the state and ack of a previous app (before a launch). */
|
|
129
|
+
/** Forget the state and ack of a previous app (before a launch); the keyboard mode is the lease's, so it stays. */
|
|
115
130
|
reset(): void;
|
|
116
131
|
};
|
|
117
132
|
export declare function createInjectChannel(input: {
|
|
@@ -3,6 +3,7 @@ import type { E2eEnv } from "../../env";
|
|
|
3
3
|
import type { ReadsA11yEvents } from "../a11y-events";
|
|
4
4
|
import type { ReadsA11y } from "../a11y-read";
|
|
5
5
|
import type { ProbesFrontmost, ReadsAlertOn } from "../alerts";
|
|
6
|
+
import type { RecoversAxWedge } from "../ax-wedge";
|
|
6
7
|
import type { ClockRecorder } from "../clock-recorder";
|
|
7
8
|
import type { Driver, DriverResult, KeyName, Logger } from "../driver.types";
|
|
8
9
|
import type { AuditsNatively } from "../native-audit";
|
|
@@ -11,6 +12,7 @@ import type { PastesText, SelectsAll } from "../typing";
|
|
|
11
12
|
import { type InjectFs } from "./inject";
|
|
12
13
|
import { type IosA11y } from "./ios-a11y";
|
|
13
14
|
import { type IosNativeAudit, type IosNativeAuditOptions } from "./ios-audit";
|
|
15
|
+
import { type IosKeyboard } from "./ios-keyboard";
|
|
14
16
|
import { type IosNetwork } from "./ios-network";
|
|
15
17
|
import { type SwappableClient } from "./ios-session";
|
|
16
18
|
import { type IosSettings, type StagedSettings } from "./ios-settings";
|
|
@@ -27,7 +29,7 @@ import type { SimdSpawn } from "./simd-transport";
|
|
|
27
29
|
*/
|
|
28
30
|
export type IosDriverEnv = Pick<E2eEnv, "simd" | "simdSocket" | "helperDir"> & Partial<Pick<E2eEnv, "wardenVersion">>;
|
|
29
31
|
/** The iOS driver is a `Driver` plus the optional capabilities of `driver/alerts.ts`. */
|
|
30
|
-
export type IosDriver = Driver & SelectsAll & PastesText & ReadsAlertOn & ProbesFrontmost & ReadsA11y & ReadsA11yEvents & AuditsNatively;
|
|
32
|
+
export type IosDriver = Driver & SelectsAll & PastesText & ReadsAlertOn & ProbesFrontmost & RecoversAxWedge & ReadsA11y & ReadsA11yEvents & AuditsNatively;
|
|
31
33
|
export type OpenIosDriverOptions = {
|
|
32
34
|
udid: string;
|
|
33
35
|
env?: IosDriverEnv;
|
|
@@ -88,7 +90,8 @@ export type OpenIosDriverOptions = {
|
|
|
88
90
|
* How long the first snapshot of a session waits for an in-sim bridge that is up but silent. A freshly cloned
|
|
89
91
|
* simulator on a loaded host can take longer than one read's budget (axbridge 4 s + AX fallback 5 s) to answer the
|
|
90
92
|
* first time, and every read after it is fast, so the first one retries while the failure says "not ready yet".
|
|
91
|
-
* Once a read
|
|
93
|
+
* Once a read has succeeded the normal per-call budgets apply. Inside a screen-layer poll (`withReadDeadline`) the
|
|
94
|
+
* wait also ends at that poll's deadline, so a 5 s assertion is not held for the whole budget.
|
|
92
95
|
*/
|
|
93
96
|
export declare const COLD_BRIDGE_BUDGET_MS = 60000;
|
|
94
97
|
/**
|
|
@@ -122,6 +125,8 @@ export declare function createIosDriver(swap: SwappableClient, simctl: Simctl, o
|
|
|
122
125
|
}) => IosSettings;
|
|
123
126
|
/** screen-reader facts from the injected dylib (`ios-a11y.ts`); absent: `readA11y` answers `E_UNSUPPORTED` */
|
|
124
127
|
a11y?: () => IosA11y;
|
|
128
|
+
/** the on-screen keyboard mode (`ios-keyboard.ts`); absent: `keyboard` answers `E_UNSUPPORTED` */
|
|
129
|
+
keyboard?: () => IosKeyboard;
|
|
125
130
|
/** per-app network conditions (`ios-network.ts`); absent: `network` answers `E_UNSUPPORTED` */
|
|
126
131
|
/** the XCUITest audit (`ios-audit.ts`); absent: `auditNatively` skips with a reason */
|
|
127
132
|
nativeAudit?: (ctx: {
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { type KeyboardMode } from "../../../../core/src/conditions/conditions.types";
|
|
2
|
+
import type { Result } from "../../../../types/src/result";
|
|
3
|
+
import type { Driver, DriverError, DriverResult } from "../driver.types";
|
|
4
|
+
import { type InjectChannel } from "./inject";
|
|
5
|
+
/**
|
|
6
|
+
* The on-screen keyboard of an iOS simulator app (KBD-2). HID typing makes iOS treat a hardware keyboard as attached
|
|
7
|
+
* and minimise the software keyboard; the injected dylib answers `-[UIKeyboardImpl isMinimized]` with NO while the mode
|
|
8
|
+
* is `software`, and re-lays the input views on every change. The mode lives in the state file's `keyboard` field (the
|
|
9
|
+
* lease's, so it survives an app relaunch) and in `WARDEN_KEYBOARD` at launch.
|
|
10
|
+
*/
|
|
11
|
+
export type IosKeyboardDeps = {
|
|
12
|
+
channel: InjectChannel;
|
|
13
|
+
/** where `libwarden-inject.dylib` is */
|
|
14
|
+
dylib: () => Result<string, DriverError>;
|
|
15
|
+
debug?: boolean;
|
|
16
|
+
};
|
|
17
|
+
export type IosKeyboard = {
|
|
18
|
+
/** the mode in force: the state file's, else the default */
|
|
19
|
+
mode(): KeyboardMode;
|
|
20
|
+
/**
|
|
21
|
+
* Env that loads the dylib with the keyboard mode, for a launch no other layer already injects (`undefined`: those
|
|
22
|
+
* layers carry the dylib; `WARDEN_KEYBOARD` goes through `launchMode`). A dylib that cannot be found is no launch
|
|
23
|
+
* failure: the app runs plain, with the keyboard as iOS decides.
|
|
24
|
+
*/
|
|
25
|
+
launchEnv(): Record<string, string> | undefined;
|
|
26
|
+
/** `WARDEN_KEYBOARD` to merge into the env of a launch that carries the dylib already */
|
|
27
|
+
launchMode(): Record<string, string>;
|
|
28
|
+
/** after a launch that carried the dylib on its own: wait until it loaded */
|
|
29
|
+
confirmLoaded(): DriverResult<void>;
|
|
30
|
+
keyboard: Driver["keyboard"];
|
|
31
|
+
/**
|
|
32
|
+
* Resign the first responder in the app (the dylib, on the main thread), acknowledged through the channel.
|
|
33
|
+
* `attached: false`: no app with the dylib is running, nothing was written and the caller falls back.
|
|
34
|
+
*/
|
|
35
|
+
dismiss(): DriverResult<{
|
|
36
|
+
attached: boolean;
|
|
37
|
+
}>;
|
|
38
|
+
};
|
|
39
|
+
export declare function createIosKeyboard(deps: IosKeyboardDeps): IosKeyboard;
|
|
@@ -196,6 +196,28 @@ export declare const healthDataSchema: z.ZodObject<{
|
|
|
196
196
|
count: z.ZodNumber;
|
|
197
197
|
exhausted: z.ZodBoolean;
|
|
198
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>>>;
|
|
199
221
|
state: z.ZodOptional<z.ZodString>;
|
|
200
222
|
uptimeMs: z.ZodOptional<z.ZodNumber>;
|
|
201
223
|
}, z.core.$loose>;
|
|
@@ -228,6 +250,8 @@ export declare const snapshotDataSchema: z.ZodObject<{
|
|
|
228
250
|
probes: z.ZodNumber;
|
|
229
251
|
recycles: z.ZodOptional<z.ZodNumber>;
|
|
230
252
|
recyclesExhausted: z.ZodOptional<z.ZodBoolean>;
|
|
253
|
+
axWedge: z.ZodOptional<z.ZodString>;
|
|
254
|
+
axWedgeRestarts: z.ZodOptional<z.ZodNumber>;
|
|
231
255
|
}, z.core.$loose>>;
|
|
232
256
|
}, z.core.$strip>>;
|
|
233
257
|
target: z.ZodOptional<z.ZodObject<{
|
|
@@ -339,6 +363,34 @@ export declare const orientationDataSchema: z.ZodObject<{
|
|
|
339
363
|
}>>;
|
|
340
364
|
}, z.core.$strip>;
|
|
341
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>;
|
|
342
394
|
/** Ops whose `data` the client validates and types; every other op's data stays `unknown`. */
|
|
343
395
|
export declare const opSchemas: {
|
|
344
396
|
readonly health: z.ZodObject<{
|
|
@@ -381,6 +433,28 @@ export declare const opSchemas: {
|
|
|
381
433
|
count: z.ZodNumber;
|
|
382
434
|
exhausted: z.ZodBoolean;
|
|
383
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>>>;
|
|
384
458
|
state: z.ZodOptional<z.ZodString>;
|
|
385
459
|
uptimeMs: z.ZodOptional<z.ZodNumber>;
|
|
386
460
|
}, z.core.$loose>;
|
|
@@ -412,6 +486,8 @@ export declare const opSchemas: {
|
|
|
412
486
|
probes: z.ZodNumber;
|
|
413
487
|
recycles: z.ZodOptional<z.ZodNumber>;
|
|
414
488
|
recyclesExhausted: z.ZodOptional<z.ZodBoolean>;
|
|
489
|
+
axWedge: z.ZodOptional<z.ZodString>;
|
|
490
|
+
axWedgeRestarts: z.ZodOptional<z.ZodNumber>;
|
|
415
491
|
}, z.core.$loose>>;
|
|
416
492
|
}, z.core.$strip>>;
|
|
417
493
|
target: z.ZodOptional<z.ZodObject<{
|
|
@@ -478,6 +554,28 @@ export declare const opSchemas: {
|
|
|
478
554
|
readonly terminate: z.ZodObject<{
|
|
479
555
|
terminated: z.ZodBoolean;
|
|
480
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>;
|
|
481
579
|
};
|
|
482
580
|
export type OpDataMap = {
|
|
483
581
|
health: HealthData;
|
|
@@ -491,6 +589,7 @@ export type OpDataMap = {
|
|
|
491
589
|
orientation: OrientationData;
|
|
492
590
|
launch: LaunchData;
|
|
493
591
|
terminate: TerminateData;
|
|
592
|
+
axWedge: AxWedgeData;
|
|
494
593
|
};
|
|
495
594
|
export type OpData<Op extends string> = Op extends keyof OpDataMap ? OpDataMap[Op] : unknown;
|
|
496
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;
|