@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
|
@@ -264,7 +264,7 @@ async function settleByHash(poll, deps) {
|
|
|
264
264
|
return { quiet: false, waitedMs: t - start };
|
|
265
265
|
}
|
|
266
266
|
}
|
|
267
|
-
function
|
|
267
|
+
function elementKey(element) {
|
|
268
268
|
const { x, y, width, height } = element.frame;
|
|
269
269
|
return JSON.stringify([
|
|
270
270
|
element.role,
|
|
@@ -280,7 +280,7 @@ function tuple(element) {
|
|
|
280
280
|
function snapshotHash(snapshot) {
|
|
281
281
|
let text = "";
|
|
282
282
|
for (const element of snapshot.flat)
|
|
283
|
-
text += `${
|
|
283
|
+
text += `${elementKey(element)}
|
|
284
284
|
`;
|
|
285
285
|
return Bun.hash(text).toString(36);
|
|
286
286
|
}
|
|
@@ -434,6 +434,11 @@ function fieldContent(entry) {
|
|
|
434
434
|
const text = fieldValue(entry);
|
|
435
435
|
return text === "" ? { kind: "empty" } : { kind: "text", text };
|
|
436
436
|
}
|
|
437
|
+
function maskedReading(entry, text) {
|
|
438
|
+
const content = fieldContent(entry);
|
|
439
|
+
const count = content.kind === "dots" ? content.count : 0;
|
|
440
|
+
return count === graphemes(text) ? text : "\u2022".repeat(count);
|
|
441
|
+
}
|
|
437
442
|
function clearKeyPresses(entry) {
|
|
438
443
|
const content = fieldContent(entry);
|
|
439
444
|
switch (content.kind) {
|
|
@@ -757,7 +762,7 @@ function nonEmpty(value) {
|
|
|
757
762
|
}
|
|
758
763
|
var MASKED_VALUE = /^[\u2022\u25CF]+$/;
|
|
759
764
|
function isSecure(element, traits) {
|
|
760
|
-
return element.role === "textfield" && element.value !== undefined && MASKED_VALUE.test(element.value) || element.role === "securefield" || traits.has("password") || traits.has("secure") || element.rawType.toLowerCase().includes("secure");
|
|
765
|
+
return element.role === "textfield" && element.value !== undefined && MASKED_VALUE.test(element.value) || element.role === "securefield" || traits.has("password") || traits.has("secure") || traits.has("secure-text-field") || element.rawType.toLowerCase().includes("secure");
|
|
761
766
|
}
|
|
762
767
|
function deriveChecked(element, role, spelled, traits, value) {
|
|
763
768
|
if (spelled?.checked !== undefined)
|
|
@@ -1016,6 +1021,60 @@ async function typeOver(driver, sleep, prepared, text, via) {
|
|
|
1016
1021
|
return typeText(driver, sleep, text);
|
|
1017
1022
|
}
|
|
1018
1023
|
|
|
1024
|
+
// ../../packages/e2e/src/driver/keyboard-state.ts
|
|
1025
|
+
function keyboardElement(snapshot) {
|
|
1026
|
+
const { height } = snapshot.screen;
|
|
1027
|
+
const onScreen = snapshot.flat.filter((e) => e.role === "keyboard" && e.hidden !== true && e.frame.width > 0 && e.frame.height > 0 && (height <= 0 || e.frame.y < height && e.frame.y + e.frame.height > 0));
|
|
1028
|
+
return onScreen.sort((a, b) => b.frame.width * b.frame.height - a.frame.width * a.frame.height)[0];
|
|
1029
|
+
}
|
|
1030
|
+
function keyboardVisible(snapshot) {
|
|
1031
|
+
return keyboardElement(snapshot) !== undefined;
|
|
1032
|
+
}
|
|
1033
|
+
function keyboardFrame(snapshot) {
|
|
1034
|
+
return keyboardElement(snapshot)?.frame;
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
// ../../packages/e2e/src/screen/keyboard-cover.ts
|
|
1038
|
+
var COVER_MARGIN = 40;
|
|
1039
|
+
var SWIPE_INSET = 12;
|
|
1040
|
+
var SCROLL_SWIPE = { durationMs: 350, momentum: false };
|
|
1041
|
+
var SCROLL_ROLES = new Set(["scrollview", "list"]);
|
|
1042
|
+
function scrollContainer(entry) {
|
|
1043
|
+
for (let node = entry.parent;node !== undefined; node = node.parent) {
|
|
1044
|
+
if (SCROLL_ROLES.has(node.element.role))
|
|
1045
|
+
return node;
|
|
1046
|
+
}
|
|
1047
|
+
return;
|
|
1048
|
+
}
|
|
1049
|
+
function coverOf(entry, point, snapshot) {
|
|
1050
|
+
const keyboard = keyboardFrame(snapshot);
|
|
1051
|
+
if (keyboard === undefined || entry.element.role === "keyboard")
|
|
1052
|
+
return;
|
|
1053
|
+
const inside = point.x >= keyboard.x && point.x <= keyboard.x + keyboard.width && point.y >= keyboard.y && point.y <= keyboard.y + keyboard.height;
|
|
1054
|
+
if (!inside)
|
|
1055
|
+
return;
|
|
1056
|
+
const container = scrollContainer(entry);
|
|
1057
|
+
const shift = point.y - (keyboard.y - COVER_MARGIN);
|
|
1058
|
+
if (container === undefined)
|
|
1059
|
+
return { top: keyboard.y, shift };
|
|
1060
|
+
const { frame } = container.element;
|
|
1061
|
+
const bottom = Math.min(frame.y + frame.height, keyboard.y);
|
|
1062
|
+
const free = { x: frame.x, y: frame.y, width: frame.width, height: Math.max(0, bottom - frame.y) };
|
|
1063
|
+
return free.height > 2 * SWIPE_INSET ? { top: keyboard.y, scroller: free, shift } : { top: keyboard.y, shift };
|
|
1064
|
+
}
|
|
1065
|
+
function scrollPath(scroller, shift) {
|
|
1066
|
+
const x = scroller.x + scroller.width / 2;
|
|
1067
|
+
const room = scroller.height - 2 * SWIPE_INSET;
|
|
1068
|
+
const travel = Math.max(1, Math.min(shift, room));
|
|
1069
|
+
const from = scroller.y + scroller.height - SWIPE_INSET;
|
|
1070
|
+
return { from: { x, y: from }, to: { x, y: from - travel } };
|
|
1071
|
+
}
|
|
1072
|
+
var COVER_SCROLL_SWIPE = SCROLL_SWIPE;
|
|
1073
|
+
async function dismissKeyboard(driver) {
|
|
1074
|
+
const done = await driver.dismissKeyboard();
|
|
1075
|
+
return done.success ? { success: true, data: undefined } : done;
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1019
1078
|
// ../../packages/e2e/src/screen/selector.ts
|
|
1020
1079
|
var VALUE_KEYS = ["id", "label", "text", "role", "value"];
|
|
1021
1080
|
var KEY_ORDER = [...VALUE_KEYS, "editable", "nth"];
|
|
@@ -1581,7 +1640,8 @@ async function lookUpTestIdViaReact(facet, testId, view, options = {}) {
|
|
|
1581
1640
|
if (!result.success)
|
|
1582
1641
|
return { answered: false };
|
|
1583
1642
|
const matches = [];
|
|
1584
|
-
|
|
1643
|
+
const { components } = result.data;
|
|
1644
|
+
for (const component of components) {
|
|
1585
1645
|
const { testID, rect } = component;
|
|
1586
1646
|
if (testID === undefined || rect === undefined || !matchesText(testID, testId))
|
|
1587
1647
|
continue;
|
|
@@ -1591,11 +1651,25 @@ async function lookUpTestIdViaReact(facet, testId, view, options = {}) {
|
|
|
1591
1651
|
source: "react",
|
|
1592
1652
|
testId: testID,
|
|
1593
1653
|
name: component.name,
|
|
1594
|
-
...component
|
|
1654
|
+
...textOf2(component, components)
|
|
1595
1655
|
});
|
|
1596
1656
|
}
|
|
1597
1657
|
return { answered: true, matches };
|
|
1598
1658
|
}
|
|
1659
|
+
function textOf2(component, components) {
|
|
1660
|
+
if (component.text !== undefined)
|
|
1661
|
+
return { text: component.text };
|
|
1662
|
+
const inside = new Set([component.id]);
|
|
1663
|
+
const texts = [];
|
|
1664
|
+
for (const other of components) {
|
|
1665
|
+
if (other.parent === null || !inside.has(other.parent))
|
|
1666
|
+
continue;
|
|
1667
|
+
inside.add(other.id);
|
|
1668
|
+
if (other.text !== undefined)
|
|
1669
|
+
texts.push(other.text);
|
|
1670
|
+
}
|
|
1671
|
+
return texts.length === 0 ? {} : { text: texts.join(" ") };
|
|
1672
|
+
}
|
|
1599
1673
|
function reactTapPoint(match, position) {
|
|
1600
1674
|
if (position === undefined)
|
|
1601
1675
|
return match.tapPoint;
|
|
@@ -1643,6 +1717,8 @@ var DEFAULT_MAX_SWIPES = 10;
|
|
|
1643
1717
|
var VERIFY_MS = 1000;
|
|
1644
1718
|
var CONFIRM_FLOOR_MS = 500;
|
|
1645
1719
|
var SCROLL_SETTLE = { quietMs: 150, timeoutMs: 1000 };
|
|
1720
|
+
var MAX_COVER_SCROLLS = 3;
|
|
1721
|
+
var QUICK_CHECK_WINDOW_MS = 2000;
|
|
1646
1722
|
function assertOptions(verb, options, allowed) {
|
|
1647
1723
|
const unknown = Object.keys(options).filter((key) => !allowed.includes(key));
|
|
1648
1724
|
if (unknown.length > 0) {
|
|
@@ -1762,11 +1838,12 @@ class Locator {
|
|
|
1762
1838
|
return degradedFailure(this.toString(), read, waitedMs, this.reactNote(), screen);
|
|
1763
1839
|
}
|
|
1764
1840
|
async conclusive(options = {}) {
|
|
1765
|
-
const first = await this.look(options);
|
|
1841
|
+
const first = await this.look(options.reuse === undefined ? {} : { reuse: options.reuse });
|
|
1766
1842
|
const settled = await this.settled(first.observed);
|
|
1767
1843
|
if (settled !== undefined)
|
|
1768
1844
|
return settled;
|
|
1769
|
-
const
|
|
1845
|
+
const actionMs = this.rt.actionTimeout();
|
|
1846
|
+
const timeoutMs = options.quick === true ? Math.min(actionMs, QUICK_CHECK_WINDOW_MS) : actionMs;
|
|
1770
1847
|
let last;
|
|
1771
1848
|
const result = await this.rt.poll(async () => {
|
|
1772
1849
|
const observation = await this.rt.tryObserve({ reuse: false });
|
|
@@ -1828,7 +1905,7 @@ class Locator {
|
|
|
1828
1905
|
}
|
|
1829
1906
|
count() {
|
|
1830
1907
|
return this.traced("count", async () => {
|
|
1831
|
-
const { entries, react } = await this.conclusive();
|
|
1908
|
+
const { entries, react } = await this.conclusive({ quick: true });
|
|
1832
1909
|
return react?.length ?? entries.length;
|
|
1833
1910
|
});
|
|
1834
1911
|
}
|
|
@@ -1865,7 +1942,7 @@ class Locator {
|
|
|
1865
1942
|
}
|
|
1866
1943
|
isVisible() {
|
|
1867
1944
|
return this.traced("isVisible", async () => {
|
|
1868
|
-
const { entries, react, observed } = await this.conclusive();
|
|
1945
|
+
const { entries, react, observed } = await this.conclusive({ quick: true });
|
|
1869
1946
|
if (react !== undefined)
|
|
1870
1947
|
return reactVisible(this.reactOnly(react, observed), observed);
|
|
1871
1948
|
return entries.length > 0 && this.exactlyOne(entries, observed).visible;
|
|
@@ -2038,6 +2115,26 @@ class Locator {
|
|
|
2038
2115
|
return;
|
|
2039
2116
|
return { entry, observed };
|
|
2040
2117
|
}
|
|
2118
|
+
async clearOfKeyboard(found, spec, position, timeoutMs) {
|
|
2119
|
+
const coverAt = (at) => coverOf(at.entry, tapPointOf(at.entry, position), at.observed.snapshot);
|
|
2120
|
+
let current = found;
|
|
2121
|
+
let cover = coverAt(current);
|
|
2122
|
+
if (cover === undefined)
|
|
2123
|
+
return found;
|
|
2124
|
+
const { driver } = found.observed;
|
|
2125
|
+
for (let scrolls = 0;cover?.scroller !== undefined && scrolls < MAX_COVER_SCROLLS; scrolls += 1) {
|
|
2126
|
+
const path = scrollPath(cover.scroller, cover.shift);
|
|
2127
|
+
this.rt.drive("scrollIntoView", await driver.swipe(path.from, path.to, COVER_SCROLL_SWIPE));
|
|
2128
|
+
await driver.settle(SCROLL_SETTLE);
|
|
2129
|
+
current = await this.actionable(spec, timeoutMs);
|
|
2130
|
+
cover = coverAt(current);
|
|
2131
|
+
}
|
|
2132
|
+
if (cover === undefined)
|
|
2133
|
+
return current;
|
|
2134
|
+
this.rt.drive("dismissKeyboard", await dismissKeyboard(driver));
|
|
2135
|
+
await driver.settle(SCROLL_SETTLE);
|
|
2136
|
+
return this.actionable(spec, timeoutMs);
|
|
2137
|
+
}
|
|
2041
2138
|
async tap(options = {}) {
|
|
2042
2139
|
assertOptions("tap", options, ["position", "timeout", "until", "retries", "windowMs", "pollMs"]);
|
|
2043
2140
|
const retry = retryPlan(options);
|
|
@@ -2047,7 +2144,7 @@ class Locator {
|
|
|
2047
2144
|
if (retry === undefined)
|
|
2048
2145
|
return;
|
|
2049
2146
|
for (let resent = 0;resent < retry.retries; resent += 1) {
|
|
2050
|
-
const outcome = await this.arrivedWithin(retry,
|
|
2147
|
+
const outcome = await this.arrivedWithin(retry, before);
|
|
2051
2148
|
if (outcome !== "press-again")
|
|
2052
2149
|
return;
|
|
2053
2150
|
before = await this.tapOnce(options);
|
|
@@ -2057,19 +2154,31 @@ class Locator {
|
|
|
2057
2154
|
async tapOnce(options) {
|
|
2058
2155
|
const withPosition = options.position !== undefined;
|
|
2059
2156
|
const spec = { enabled: !withPosition, tapPointOnScreen: !withPosition };
|
|
2060
|
-
const
|
|
2157
|
+
const timeoutMs = options.timeout ?? this.rt.actionTimeout();
|
|
2158
|
+
const look = (options.until === undefined ? await this.aimFromKept(spec, options.position) : undefined) ?? await this.actionable(spec, timeoutMs, true);
|
|
2159
|
+
const found = "entry" in look ? await this.clearOfKeyboard(look, spec, options.position, timeoutMs) : look;
|
|
2061
2160
|
const point = "react" in found ? reactTapPoint(found.react, options.position) : tapPointOf(found.entry, options.position);
|
|
2062
2161
|
const { driver } = found.observed;
|
|
2162
|
+
const aimed = {
|
|
2163
|
+
snapshot: found.observed.snapshot,
|
|
2164
|
+
target: "entry" in found ? found.entry.element : undefined
|
|
2165
|
+
};
|
|
2063
2166
|
if (!withPosition && "entry" in found && canActOnElements(driver)) {
|
|
2064
2167
|
if (this.rt.drive("tap", await driver.pressElement(found.entry.element)))
|
|
2065
|
-
return
|
|
2168
|
+
return aimed;
|
|
2066
2169
|
}
|
|
2067
2170
|
this.rt.drive("tap", await driver.tap(point));
|
|
2068
|
-
return
|
|
2171
|
+
return aimed;
|
|
2069
2172
|
}
|
|
2070
|
-
async arrivedWithin(plan,
|
|
2173
|
+
async arrivedWithin(plan, aimed) {
|
|
2071
2174
|
const { until } = plan;
|
|
2072
|
-
const watch = {
|
|
2175
|
+
const watch = {
|
|
2176
|
+
before: new Set(aimed.snapshot.flat.map(elementKey)),
|
|
2177
|
+
target: aimed.target === undefined ? undefined : elementKey(aimed.target),
|
|
2178
|
+
started: this.rt.clock.now(),
|
|
2179
|
+
changed: false,
|
|
2180
|
+
unchanged: 0
|
|
2181
|
+
};
|
|
2073
2182
|
const result = await this.rt.poll(async (attempt) => {
|
|
2074
2183
|
if (typeof until !== "function")
|
|
2075
2184
|
return this.checkLocatorUntil(until, watch, attempt);
|
|
@@ -2092,7 +2201,7 @@ class Locator {
|
|
|
2092
2201
|
}
|
|
2093
2202
|
if (await until.shownIn(observation.observed))
|
|
2094
2203
|
return { done: true, value: "arrived" };
|
|
2095
|
-
if (
|
|
2204
|
+
if (!reacted(observation.observed.snapshot, watch))
|
|
2096
2205
|
watch.unchanged += 1;
|
|
2097
2206
|
else {
|
|
2098
2207
|
watch.changed = true;
|
|
@@ -2104,7 +2213,9 @@ class Locator {
|
|
|
2104
2213
|
async longPress(options = {}) {
|
|
2105
2214
|
assertOptions("longPress", options, ["durationMs", "timeout"]);
|
|
2106
2215
|
return this.act("longPress", async () => {
|
|
2107
|
-
const
|
|
2216
|
+
const spec = { enabled: true, tapPointOnScreen: true };
|
|
2217
|
+
const timeoutMs = options.timeout ?? this.rt.actionTimeout();
|
|
2218
|
+
const { entry, observed } = await this.clearOfKeyboard(await this.actionable(spec, timeoutMs), spec, undefined, timeoutMs);
|
|
2108
2219
|
const opts = options.durationMs === undefined ? undefined : { durationMs: options.durationMs };
|
|
2109
2220
|
this.rt.drive("longPress", await observed.driver.longPress(tapPointOf(entry), opts));
|
|
2110
2221
|
});
|
|
@@ -2115,18 +2226,18 @@ class Locator {
|
|
|
2115
2226
|
return this.act("fill", async () => {
|
|
2116
2227
|
const budget = this.rt.budget(options.timeout);
|
|
2117
2228
|
let target = await this.actionable({ enabled: true, tapPointOnScreen: false }, budget.remaining());
|
|
2229
|
+
const verify = !target.entry.secure;
|
|
2118
2230
|
let firstHeld;
|
|
2119
2231
|
for (let attempt = 1;; attempt += 1) {
|
|
2120
2232
|
const point = focusPoint(target.entry.element.frame);
|
|
2121
2233
|
this.rt.drive("fill", await focusClearType(target.observed.driver, this.pause, point, target.entry, text, clearMethodFor(attempt), process.env, viaFor(attempt, via)));
|
|
2122
|
-
if (
|
|
2234
|
+
if (!verify)
|
|
2123
2235
|
return;
|
|
2124
2236
|
const outcome = await this.confirm(target, text, budget);
|
|
2125
2237
|
if (outcome.ok)
|
|
2126
2238
|
return;
|
|
2127
2239
|
if (attempt === 2) {
|
|
2128
|
-
|
|
2129
|
-
throw new E2eError2("ACTION_FAILED", `${this}: fill did not take: expected ${JSON.stringify(text)} but the field holds ${JSON.stringify(outcome.held)}${earlier}`, outcome.screen);
|
|
2240
|
+
throw new E2eError2("ACTION_FAILED", `${this}: ${fillMismatch(text, target.entry.secure, outcome.held, firstHeld)}`, outcome.screen);
|
|
2130
2241
|
}
|
|
2131
2242
|
firstHeld = outcome.held;
|
|
2132
2243
|
target = await this.actionable({ enabled: true, tapPointOnScreen: false }, budget.remaining());
|
|
@@ -2249,7 +2360,11 @@ class Locator {
|
|
|
2249
2360
|
async confirm({ entry, observed }, text, budget) {
|
|
2250
2361
|
const span = Math.max(budget.remaining(), CONFIRM_FLOOR_MS);
|
|
2251
2362
|
let lastSeen;
|
|
2252
|
-
const readValue = (found) =>
|
|
2363
|
+
const readValue = (found) => {
|
|
2364
|
+
if (text === "")
|
|
2365
|
+
return fieldValue(found);
|
|
2366
|
+
return found.secure ? maskedReading(found, text) : rawValue(found);
|
|
2367
|
+
};
|
|
2253
2368
|
const outcome = await confirmFieldValue({
|
|
2254
2369
|
text,
|
|
2255
2370
|
now: this.rt.clock.now,
|
|
@@ -2307,6 +2422,16 @@ var TAP_WINDOW_MS = 4000;
|
|
|
2307
2422
|
var TAP_CHECK_MS = 250;
|
|
2308
2423
|
var UNCHANGED_CHECKS = 2;
|
|
2309
2424
|
var UNCHANGED_AFTER_MS = 750;
|
|
2425
|
+
function reacted(snapshot, watch) {
|
|
2426
|
+
const now = new Set;
|
|
2427
|
+
for (const element of snapshot.flat) {
|
|
2428
|
+
const key = elementKey(element);
|
|
2429
|
+
if (!watch.before.has(key))
|
|
2430
|
+
return true;
|
|
2431
|
+
now.add(key);
|
|
2432
|
+
}
|
|
2433
|
+
return watch.target !== undefined && !now.has(watch.target);
|
|
2434
|
+
}
|
|
2310
2435
|
function wholeNumber(name, value, atLeast) {
|
|
2311
2436
|
if (!Number.isInteger(value) || value < atLeast) {
|
|
2312
2437
|
throw new E2eError2("INVALID_ARGUMENT", `tap ${name} must be an integer of at least ${atLeast}, got ${String(value)}`);
|
|
@@ -2349,6 +2474,11 @@ function viaFor(attempt, via) {
|
|
|
2349
2474
|
function clearMethodFor(attempt) {
|
|
2350
2475
|
return attempt === 1 ? "auto" : "counted";
|
|
2351
2476
|
}
|
|
2477
|
+
function fillMismatch(text, secure, held, firstHeld) {
|
|
2478
|
+
const expected = secure ? `${graphemes(text)} characters (secure)` : JSON.stringify(text);
|
|
2479
|
+
const earlier = firstHeld === undefined ? "" : ` (first attempt held ${JSON.stringify(firstHeld)})`;
|
|
2480
|
+
return `fill did not take: expected ${expected} but the field holds ${JSON.stringify(held)}${earlier}`;
|
|
2481
|
+
}
|
|
2352
2482
|
function rawValue(entry) {
|
|
2353
2483
|
return entry.value ?? (entry.role === "textbox" || entry.role === "searchbox" ? entry.element.text ?? "" : "");
|
|
2354
2484
|
}
|
|
@@ -2493,7 +2623,7 @@ function describeException(details) {
|
|
|
2493
2623
|
return String(thrown.value);
|
|
2494
2624
|
return details.text ?? "JS exception";
|
|
2495
2625
|
}
|
|
2496
|
-
function
|
|
2626
|
+
function textOf3(data) {
|
|
2497
2627
|
if (typeof data === "string")
|
|
2498
2628
|
return data;
|
|
2499
2629
|
if (data instanceof ArrayBuffer)
|
|
@@ -2516,7 +2646,7 @@ function taggedBindingPayload(params) {
|
|
|
2516
2646
|
return tagged.success ? { requestId: tagged.data.requestId, payload } : undefined;
|
|
2517
2647
|
}
|
|
2518
2648
|
function parseEnvelope(data) {
|
|
2519
|
-
const text =
|
|
2649
|
+
const text = textOf3(data);
|
|
2520
2650
|
if (text === undefined)
|
|
2521
2651
|
return;
|
|
2522
2652
|
let json;
|
|
@@ -4551,6 +4681,14 @@ async function captureNativeLog(target, deps, win) {
|
|
|
4551
4681
|
}
|
|
4552
4682
|
}
|
|
4553
4683
|
|
|
4684
|
+
// ../../packages/e2e/src/driver/ax-wedge.ts
|
|
4685
|
+
function canRecoverAxWedge(driver) {
|
|
4686
|
+
return typeof driver.axWedge === "function";
|
|
4687
|
+
}
|
|
4688
|
+
function restartedWithin(state, windowMs) {
|
|
4689
|
+
return state.last?.outcome === "restarted" && state.last.agoMs <= windowMs;
|
|
4690
|
+
}
|
|
4691
|
+
|
|
4554
4692
|
// ../../packages/e2e/src/driver/ios/hid-recovery.ts
|
|
4555
4693
|
import { existsSync as existsSync2 } from "fs";
|
|
4556
4694
|
|
|
@@ -4639,7 +4777,9 @@ var bridgeStallSchema = z6.looseObject({
|
|
|
4639
4777
|
cooldownRemainingMs: z6.number().optional(),
|
|
4640
4778
|
probes: z6.number(),
|
|
4641
4779
|
recycles: z6.number().optional(),
|
|
4642
|
-
recyclesExhausted: z6.boolean().optional()
|
|
4780
|
+
recyclesExhausted: z6.boolean().optional(),
|
|
4781
|
+
axWedge: z6.string().optional(),
|
|
4782
|
+
axWedgeRestarts: z6.number().optional()
|
|
4643
4783
|
});
|
|
4644
4784
|
var SPAN_FIELDS = [
|
|
4645
4785
|
["stallStage", "stage"],
|
|
@@ -4650,7 +4790,9 @@ var SPAN_FIELDS = [
|
|
|
4650
4790
|
["stallCooldownMs", "cooldownRemainingMs"],
|
|
4651
4791
|
["stallProbes", "probes"],
|
|
4652
4792
|
["stallRecycles", "recycles"],
|
|
4653
|
-
["stallRecyclesExhausted", "recyclesExhausted"]
|
|
4793
|
+
["stallRecyclesExhausted", "recyclesExhausted"],
|
|
4794
|
+
["stallAxWedge", "axWedge"],
|
|
4795
|
+
["stallAxWedgeRestarts", "axWedgeRestarts"]
|
|
4654
4796
|
];
|
|
4655
4797
|
function toBridgeStall(wire) {
|
|
4656
4798
|
const stall = { stage: wire.stage, cold: wire.cold, probes: wire.probes };
|
|
@@ -4666,6 +4808,10 @@ function toBridgeStall(wire) {
|
|
|
4666
4808
|
stall.recycles = wire.recycles;
|
|
4667
4809
|
if (wire.recyclesExhausted !== undefined)
|
|
4668
4810
|
stall.recyclesExhausted = wire.recyclesExhausted;
|
|
4811
|
+
if (wire.axWedge !== undefined)
|
|
4812
|
+
stall.axWedge = wire.axWedge;
|
|
4813
|
+
if (wire.axWedgeRestarts !== undefined)
|
|
4814
|
+
stall.axWedgeRestarts = wire.axWedgeRestarts;
|
|
4669
4815
|
return stall;
|
|
4670
4816
|
}
|
|
4671
4817
|
function noteBridgeStall(stall) {
|
|
@@ -4689,7 +4835,8 @@ function bridgeStallFromSpan(detail) {
|
|
|
4689
4835
|
["stallDeadlineMs", "deadlineMs"],
|
|
4690
4836
|
["stallGuestAgeMs", "guestAgeMs"],
|
|
4691
4837
|
["stallCooldownMs", "cooldownRemainingMs"],
|
|
4692
|
-
["stallRecycles", "recycles"]
|
|
4838
|
+
["stallRecycles", "recycles"],
|
|
4839
|
+
["stallAxWedgeRestarts", "axWedgeRestarts"]
|
|
4693
4840
|
];
|
|
4694
4841
|
for (const [key, field] of numbers) {
|
|
4695
4842
|
const value = detail[key];
|
|
@@ -4698,9 +4845,20 @@ function bridgeStallFromSpan(detail) {
|
|
|
4698
4845
|
}
|
|
4699
4846
|
if (typeof detail.stallRecyclesExhausted === "boolean")
|
|
4700
4847
|
stall.recyclesExhausted = detail.stallRecyclesExhausted;
|
|
4848
|
+
if (typeof detail.stallAxWedge === "string")
|
|
4849
|
+
stall.axWedge = detail.stallAxWedge;
|
|
4701
4850
|
return stall;
|
|
4702
4851
|
}
|
|
4703
4852
|
var seconds = (ms) => `${(ms / 1000).toFixed(1)} s`;
|
|
4853
|
+
function axWedgeParts(stall) {
|
|
4854
|
+
const parts = [];
|
|
4855
|
+
if (stall.axWedge === "checking")
|
|
4856
|
+
parts.push("both readers silent: checking for a wedged accessibility service");
|
|
4857
|
+
if (stall.axWedgeRestarts !== undefined) {
|
|
4858
|
+
parts.push(`backboardd restarted ${stall.axWedgeRestarts} time${stall.axWedgeRestarts === 1 ? "" : "s"}`);
|
|
4859
|
+
}
|
|
4860
|
+
return parts;
|
|
4861
|
+
}
|
|
4704
4862
|
function formatBridgeStall(stall) {
|
|
4705
4863
|
const parts = [`stage ${stall.stage}`];
|
|
4706
4864
|
if (stall.readMs !== undefined) {
|
|
@@ -4717,7 +4875,7 @@ function formatBridgeStall(stall) {
|
|
|
4717
4875
|
const guests = stall.recycles === 1 ? "guest" : "guests";
|
|
4718
4876
|
parts.push(`${stall.recycles} stalled ${guests} recycled${stall.recyclesExhausted === true ? ", none left" : ""}`);
|
|
4719
4877
|
}
|
|
4720
|
-
return parts.join(" \xB7 ");
|
|
4878
|
+
return [...parts, ...axWedgeParts(stall)].join(" \xB7 ");
|
|
4721
4879
|
}
|
|
4722
4880
|
|
|
4723
4881
|
// ../../packages/e2e/src/driver/ios/simd-protocol.ts
|
|
@@ -4879,6 +5037,7 @@ var healthDataSchema = z7.looseObject({
|
|
|
4879
5037
|
coreSimulatorVersion: z7.string().nullish(),
|
|
4880
5038
|
frontmost: z7.object({ unresolvedAgoMs: z7.number(), method: z7.string() }).optional(),
|
|
4881
5039
|
bridgeRecycles: z7.object({ count: z7.number(), exhausted: z7.boolean() }).optional(),
|
|
5040
|
+
axWedge: z7.lazy(() => axWedgeDataSchema).optional(),
|
|
4882
5041
|
state: z7.string().optional(),
|
|
4883
5042
|
uptimeMs: z7.number().optional()
|
|
4884
5043
|
});
|
|
@@ -4945,6 +5104,17 @@ var orientationDataSchema = z7.object({
|
|
|
4945
5104
|
]),
|
|
4946
5105
|
source: z7.enum(["device", "written", "assumed"]).optional()
|
|
4947
5106
|
});
|
|
5107
|
+
var axWedgeDataSchema = z7.looseObject({
|
|
5108
|
+
restarts: z7.number(),
|
|
5109
|
+
maxRestarts: z7.number(),
|
|
5110
|
+
recovering: z7.boolean(),
|
|
5111
|
+
checked: z7.enum(["restarted", "not-wedged", "failed"]).optional(),
|
|
5112
|
+
last: z7.discriminatedUnion("outcome", [
|
|
5113
|
+
z7.looseObject({ outcome: z7.literal("restarted"), agoMs: z7.number(), ms: z7.number().optional() }),
|
|
5114
|
+
z7.looseObject({ outcome: z7.literal("not-wedged"), agoMs: z7.number() }),
|
|
5115
|
+
z7.looseObject({ outcome: z7.literal("failed"), agoMs: z7.number(), error: z7.string() })
|
|
5116
|
+
]).optional()
|
|
5117
|
+
});
|
|
4948
5118
|
var opSchemas = {
|
|
4949
5119
|
health: healthDataSchema,
|
|
4950
5120
|
snapshot: snapshotDataSchema,
|
|
@@ -4956,7 +5126,8 @@ var opSchemas = {
|
|
|
4956
5126
|
frontmost: frontmostDataSchema,
|
|
4957
5127
|
orientation: orientationDataSchema,
|
|
4958
5128
|
launch: launchDataSchema,
|
|
4959
|
-
terminate: terminateDataSchema
|
|
5129
|
+
terminate: terminateDataSchema,
|
|
5130
|
+
axWedge: axWedgeDataSchema
|
|
4960
5131
|
};
|
|
4961
5132
|
function hasOpSchema(op) {
|
|
4962
5133
|
return Object.hasOwn(opSchemas, op);
|
|
@@ -5722,6 +5893,10 @@ var TEXT_SIZE_NAMES = [
|
|
|
5722
5893
|
"accessibility-extra-extra-large",
|
|
5723
5894
|
"accessibility-extra-extra-extra-large"
|
|
5724
5895
|
];
|
|
5896
|
+
var KEYBOARD_MODES = ["software", "hardware"];
|
|
5897
|
+
var DEFAULT_KEYBOARD_MODE = "software";
|
|
5898
|
+
var IME_STASH_ENABLED = "warden_ime_enabled";
|
|
5899
|
+
var IME_STASH_DEFAULT = "warden_ime_default";
|
|
5725
5900
|
var CAMERA_FACINGS = ["back", "front"];
|
|
5726
5901
|
var CONDITION_AREAS = [
|
|
5727
5902
|
"network",
|
|
@@ -5731,7 +5906,8 @@ var CONDITION_AREAS = [
|
|
|
5731
5906
|
"locale",
|
|
5732
5907
|
"a11y",
|
|
5733
5908
|
"battery",
|
|
5734
|
-
"camera"
|
|
5909
|
+
"camera",
|
|
5910
|
+
"keyboard"
|
|
5735
5911
|
];
|
|
5736
5912
|
|
|
5737
5913
|
// ../../packages/core/src/conditions/network.presets.ts
|
|
@@ -5876,7 +6052,8 @@ var conditionsConfigSchema = z9.object({
|
|
|
5876
6052
|
permissions: z9.partialRecord(z9.enum(PERMISSION_NAMES), z9.enum(["granted", "denied", "unset"])).optional(),
|
|
5877
6053
|
locale: z9.string().min(1).optional(),
|
|
5878
6054
|
timezone: z9.string().min(1).optional(),
|
|
5879
|
-
a11y: a11yConfigSchema.optional()
|
|
6055
|
+
a11y: a11yConfigSchema.optional(),
|
|
6056
|
+
keyboard: z9.enum(KEYBOARD_MODES).optional()
|
|
5880
6057
|
}).strict();
|
|
5881
6058
|
var cameraFeedInput = z9.union([
|
|
5882
6059
|
z9.string().min(1),
|
|
@@ -5943,6 +6120,8 @@ function normalizeConditions(config) {
|
|
|
5943
6120
|
conditions.locale = config.locale;
|
|
5944
6121
|
if (config.timezone !== undefined)
|
|
5945
6122
|
conditions.timezone = config.timezone;
|
|
6123
|
+
if (config.keyboard !== undefined)
|
|
6124
|
+
conditions.keyboard = { mode: config.keyboard };
|
|
5946
6125
|
if (config.a11y !== undefined) {
|
|
5947
6126
|
const a11y = { ...config.a11y };
|
|
5948
6127
|
if (config.a11y.textSize !== undefined) {
|
|
@@ -6318,6 +6497,8 @@ ${read.error}`);
|
|
|
6318
6497
|
throw new E2eError2("SESSION", "not running under warden: WARDEN_UDID is not set");
|
|
6319
6498
|
return read.data;
|
|
6320
6499
|
}
|
|
6500
|
+
var AX_WEDGE_WAIT_MS = 60000;
|
|
6501
|
+
var AX_WEDGE_POLL_MS = 1000;
|
|
6321
6502
|
var WARM_TRIES = 15;
|
|
6322
6503
|
var WARM_INTERVAL_MS = 2000;
|
|
6323
6504
|
function createSession(opts = {}) {
|
|
@@ -6362,6 +6543,27 @@ function createSession(opts = {}) {
|
|
|
6362
6543
|
await sleep(WARM_INTERVAL_MS);
|
|
6363
6544
|
}
|
|
6364
6545
|
}
|
|
6546
|
+
let axWedgeRerunFor = 0;
|
|
6547
|
+
async function recoverAxWedge(attemptMs) {
|
|
6548
|
+
const driver = current;
|
|
6549
|
+
if (closing !== undefined || driver === undefined || !canRecoverAxWedge(driver))
|
|
6550
|
+
return false;
|
|
6551
|
+
const sleep = hid?.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
6552
|
+
let state = await driver.axWedge();
|
|
6553
|
+
let waited = 0;
|
|
6554
|
+
while (state.success && state.data.recovering && waited < AX_WEDGE_WAIT_MS) {
|
|
6555
|
+
await sleep(AX_WEDGE_POLL_MS);
|
|
6556
|
+
waited += AX_WEDGE_POLL_MS;
|
|
6557
|
+
state = await driver.axWedge();
|
|
6558
|
+
}
|
|
6559
|
+
if (!state.success || state.data.restarts <= axWedgeRerunFor)
|
|
6560
|
+
return false;
|
|
6561
|
+
if (!restartedWithin(state.data, attemptMs + waited))
|
|
6562
|
+
return false;
|
|
6563
|
+
axWedgeRerunFor = state.data.restarts;
|
|
6564
|
+
note(`sim ${resolved.deviceName ?? resolved.udid}: accessibility service wedged, warden-simd restarted backboardd (the app was quit); running the test again`);
|
|
6565
|
+
return true;
|
|
6566
|
+
}
|
|
6365
6567
|
async function recoverRunner(deps, name) {
|
|
6366
6568
|
if (hidRecovered) {
|
|
6367
6569
|
note(`iPhone ${name}: the runner connection was lost again after a restart; check the phone and retry`);
|
|
@@ -6439,6 +6641,7 @@ function createSession(opts = {}) {
|
|
|
6439
6641
|
fixtures: opts.fixtures ?? defaultFixtures,
|
|
6440
6642
|
nativeLog: opts.nativeLog ?? defaultNativeLog,
|
|
6441
6643
|
recoverHid: hid === undefined || resolved.platform !== "ios" ? undefined : recoverHid,
|
|
6644
|
+
recoverAxWedge: resolved.platform !== "ios" || resolved.deviceKind === "physical" ? undefined : recoverAxWedge,
|
|
6442
6645
|
resetDevice,
|
|
6443
6646
|
log: opts.log ?? writeLine,
|
|
6444
6647
|
get attempt() {
|
|
@@ -6577,6 +6780,9 @@ function attemptSession(session, binding) {
|
|
|
6577
6780
|
get recoverHid() {
|
|
6578
6781
|
return session.recoverHid;
|
|
6579
6782
|
},
|
|
6783
|
+
get recoverAxWedge() {
|
|
6784
|
+
return session.recoverAxWedge;
|
|
6785
|
+
},
|
|
6580
6786
|
resetDevice: () => session.resetDevice(),
|
|
6581
6787
|
get fixtures() {
|
|
6582
6788
|
return session.fixtures;
|
|
@@ -7207,15 +7413,15 @@ function runAudit(snapshot, platform, opts = {}) {
|
|
|
7207
7413
|
function failing(result, minSeverity = "error") {
|
|
7208
7414
|
return result.issues.filter((i) => SEVERITY_RANK[i.severity] >= SEVERITY_RANK[minSeverity]);
|
|
7209
7415
|
}
|
|
7210
|
-
function
|
|
7416
|
+
function elementKey2(i) {
|
|
7211
7417
|
return i.identifier ?? i.elementId;
|
|
7212
7418
|
}
|
|
7213
7419
|
function mergeIssues(base, extra, skipped = []) {
|
|
7214
7420
|
const issues = [...base.issues];
|
|
7215
7421
|
const added = [];
|
|
7216
7422
|
for (const e of extra) {
|
|
7217
|
-
const key =
|
|
7218
|
-
const idx = issues.findIndex((i) => key !== undefined &&
|
|
7423
|
+
const key = elementKey2(e);
|
|
7424
|
+
const idx = issues.findIndex((i) => key !== undefined && elementKey2(i) === key && (e.wcag !== undefined ? i.wcag === e.wcag : i.rule === e.rule && i.message === e.message));
|
|
7219
7425
|
const hit = idx === -1 ? undefined : issues[idx];
|
|
7220
7426
|
if (hit === undefined) {
|
|
7221
7427
|
added.push(e);
|
|
@@ -7558,6 +7764,12 @@ function createDeviceA11y(deps) {
|
|
|
7558
7764
|
const merged = await withA11yRead(driver, taken.data);
|
|
7559
7765
|
return renderA11yScreen(merged.success ? merged.data : taken.data);
|
|
7560
7766
|
},
|
|
7767
|
+
async keyboardVisible() {
|
|
7768
|
+
const state = await settled(() => driver.keyboardState(), "keyboardState", signal());
|
|
7769
|
+
if (!state.success)
|
|
7770
|
+
throw toE2eError(state.error, "read the keyboard");
|
|
7771
|
+
return state.data.visible;
|
|
7772
|
+
},
|
|
7561
7773
|
assertionTimeout: () => env.assertionTimeoutMs,
|
|
7562
7774
|
step: (label, fn) => deps.step === undefined ? fn() : deps.step(label, fn)
|
|
7563
7775
|
};
|
|
@@ -8353,6 +8565,7 @@ var KNOWN_AREAS = new Set([
|
|
|
8353
8565
|
"locale",
|
|
8354
8566
|
"a11y",
|
|
8355
8567
|
"battery",
|
|
8568
|
+
"keyboard",
|
|
8356
8569
|
"camera"
|
|
8357
8570
|
]);
|
|
8358
8571
|
|
|
@@ -8726,6 +8939,8 @@ function createDeviceConditions(deps) {
|
|
|
8726
8939
|
let localeDirty = false;
|
|
8727
8940
|
let a11yDirty = false;
|
|
8728
8941
|
let batteryDirty = false;
|
|
8942
|
+
let keyboardDirty = false;
|
|
8943
|
+
let currentKeyboard = DEFAULT_KEYBOARD_MODE;
|
|
8729
8944
|
let cameraDirty = false;
|
|
8730
8945
|
let currentCamera = {};
|
|
8731
8946
|
const permissionStates = new Map;
|
|
@@ -9102,6 +9317,28 @@ the app runs without the warden network dylib: declare \`conditions.network\` fo
|
|
|
9102
9317
|
emit({ area: "battery", value });
|
|
9103
9318
|
return state;
|
|
9104
9319
|
}
|
|
9320
|
+
const ownsDevice = recorder !== undefined && env.deviceKind !== "physical";
|
|
9321
|
+
const keyboardGoal = baseline.keyboard?.mode ?? DEFAULT_KEYBOARD_MODE;
|
|
9322
|
+
async function applyKeyboard(input) {
|
|
9323
|
+
const mode = input === "toggle" ? currentKeyboard === "software" ? "hardware" : "software" : input;
|
|
9324
|
+
if (!KEYBOARD_MODES.includes(mode)) {
|
|
9325
|
+
throw invalid2(`unknown keyboard mode "${String(input)}" (use software, hardware or toggle)`);
|
|
9326
|
+
}
|
|
9327
|
+
keyboardDirty = true;
|
|
9328
|
+
const run = settingOp("keyboard");
|
|
9329
|
+
const state = await call("keyboard", () => run(mode));
|
|
9330
|
+
currentKeyboard = mode;
|
|
9331
|
+
const value = {
|
|
9332
|
+
mode,
|
|
9333
|
+
...state.previousIme === undefined ? {} : { previousIme: state.previousIme }
|
|
9334
|
+
};
|
|
9335
|
+
if (mode === DEFAULT_KEYBOARD_MODE)
|
|
9336
|
+
recorder?.settle("keyboard", value, true);
|
|
9337
|
+
else
|
|
9338
|
+
recorder?.record("keyboard", value);
|
|
9339
|
+
emit({ area: "keyboard", value });
|
|
9340
|
+
return state;
|
|
9341
|
+
}
|
|
9105
9342
|
const home = deps.wardenHome ?? wardenHome(process.env);
|
|
9106
9343
|
async function applyCamera(next) {
|
|
9107
9344
|
cameraDirty = true;
|
|
@@ -9204,7 +9441,7 @@ the app runs without the warden network dylib: declare \`conditions.network\` fo
|
|
|
9204
9441
|
throw new E2eError2("ACTION_FAILED", `a network flap or timeline stopped on its own after failing: ${failures[0]}`);
|
|
9205
9442
|
}
|
|
9206
9443
|
}
|
|
9207
|
-
const declared = baseline.network !== undefined || baseline.location !== undefined || baseline.clock !== undefined || baseline.permissions !== undefined || baseline.locale !== undefined || baseline.timezone !== undefined || baseline.a11y !== undefined || env.cameraFeeds !== undefined;
|
|
9444
|
+
const declared = baseline.network !== undefined || baseline.location !== undefined || baseline.clock !== undefined || baseline.permissions !== undefined || baseline.locale !== undefined || baseline.timezone !== undefined || baseline.a11y !== undefined || baseline.keyboard !== undefined || ownsDevice || env.cameraFeeds !== undefined;
|
|
9208
9445
|
async function applyBaselinePermissions() {
|
|
9209
9446
|
if (baseline.permissions === undefined)
|
|
9210
9447
|
return;
|
|
@@ -9241,6 +9478,10 @@ the app runs without the warden network dylib: declare \`conditions.network\` fo
|
|
|
9241
9478
|
async () => {
|
|
9242
9479
|
if (env.cameraFeeds !== undefined)
|
|
9243
9480
|
await applyCamera(baselineCamera());
|
|
9481
|
+
},
|
|
9482
|
+
async () => {
|
|
9483
|
+
if (baseline.keyboard !== undefined || ownsDevice)
|
|
9484
|
+
await applyKeyboard(keyboardGoal);
|
|
9244
9485
|
}
|
|
9245
9486
|
];
|
|
9246
9487
|
const teardownSteps = [
|
|
@@ -9280,6 +9521,13 @@ the app runs without the warden network dylib: declare \`conditions.network\` fo
|
|
|
9280
9521
|
batteryDirty = false;
|
|
9281
9522
|
}
|
|
9282
9523
|
},
|
|
9524
|
+
{
|
|
9525
|
+
dirty: () => keyboardDirty,
|
|
9526
|
+
run: async () => {
|
|
9527
|
+
await applyKeyboard(keyboardGoal);
|
|
9528
|
+
keyboardDirty = false;
|
|
9529
|
+
}
|
|
9530
|
+
},
|
|
9283
9531
|
{
|
|
9284
9532
|
dirty: () => cameraDirty,
|
|
9285
9533
|
run: async () => {
|
|
@@ -9290,7 +9538,7 @@ the app runs without the warden network dylib: declare \`conditions.network\` fo
|
|
|
9290
9538
|
];
|
|
9291
9539
|
const internals = {
|
|
9292
9540
|
declared,
|
|
9293
|
-
dirty: () => networkDirty || locationDirty || clockDirty || permissionsDirty || localeDirty || a11yDirty || batteryDirty || cameraDirty || runs.size > 0,
|
|
9541
|
+
dirty: () => networkDirty || locationDirty || clockDirty || permissionsDirty || localeDirty || a11yDirty || batteryDirty || keyboardDirty || cameraDirty || runs.size > 0,
|
|
9294
9542
|
wantsInject: () => baseline.network !== undefined || baseline.clock !== undefined || currentNetwork.kind !== "online" || currentClock.kind !== "real",
|
|
9295
9543
|
async applyBaseline() {
|
|
9296
9544
|
for (const step of baselineSteps)
|
|
@@ -9303,6 +9551,7 @@ the app runs without the warden network dylib: declare \`conditions.network\` fo
|
|
|
9303
9551
|
localeDirty = false;
|
|
9304
9552
|
a11yDirty = false;
|
|
9305
9553
|
batteryDirty = false;
|
|
9554
|
+
keyboardDirty = false;
|
|
9306
9555
|
},
|
|
9307
9556
|
async teardown() {
|
|
9308
9557
|
const errors = [];
|
|
@@ -9331,6 +9580,7 @@ the app runs without the warden network dylib: declare \`conditions.network\` fo
|
|
|
9331
9580
|
locale: applyLocale,
|
|
9332
9581
|
a11y: applyA11y,
|
|
9333
9582
|
battery: applyBattery,
|
|
9583
|
+
keyboard: applyKeyboard,
|
|
9334
9584
|
camera: cameraApi,
|
|
9335
9585
|
memoryWarning: async (options) => {
|
|
9336
9586
|
const op = settingOp("memoryWarning");
|
|
@@ -9650,6 +9900,6 @@ function onlyWasSeen() {
|
|
|
9650
9900
|
|
|
9651
9901
|
// ../../packages/e2e/src/version.ts
|
|
9652
9902
|
var WORKSPACE_VERSION = "0.0.0-workspace";
|
|
9653
|
-
var WARDEN_E2E_VERSION2 = "0.
|
|
9903
|
+
var WARDEN_E2E_VERSION2 = "0.15.0";
|
|
9654
9904
|
|
|
9655
|
-
export { E2eError2, registerA11yTarget, a11yTargetOf, canActOnElements, noteSpanDetail, addSpanDetail, canObserve, canDetectAlerts, settleDetailOf, DEFAULT_SETTLE_QUIET_MS, DEFAULT_SETTLE_TIMEOUT_MS, hashFallbackQuietMs, settleByHash, snapshotHash, ok, err, swipeVector, screenRect, isOnScreen, fieldValue, PINCH_MS, ROTATE_MS, PATH_MS, centreOf, pinchRadius, rotateRadius, requireGesture, assertScale, assertDegrees, isInteractiveRole, resolveRole, normalizeText, matchesText, indexSnapshot, testIdQuery, textQuery, roleQuery, selectorExpression, renderSnapshot, renderHeader, lookUpTestIdViaReact, isLocator, Locator, pickOne, textOf, scrollUntilVisible, formatNativeTail, realNativeLogDeps, captureNativeLog, bunExec, execError, driverError, driverErr, bridgeStallSchema, toBridgeStall, noteBridgeStall, bridgeStallFromSpan, formatBridgeStall, logEventSchema, fatalEventSchema, responseSchema, mapWireError, createLineSplitter, encodeRequest, bunSpawn, stdioTransport, socketTransport, createSimdClient, spawnSimd, connectSimd, isRecoverableLoss, parseDuration, CAMERA_FACINGS, CONDITION_AREAS, resolveNetwork, resolveTextSize, androidFontScale, ENV_NAMES, DEFAULT_ASSERTION_TIMEOUT_MS, readEnv, runStep, setDefaultHidRecovery, setDefaultNativeLog, setDefaultDriverFactory, hasDefaultDriverFactory, setDefaultFixtureFactory, hasDefaultFixtureFactory, attemptSession, getSession, peekSession, accessibleName, announce, failing, formatIssues, toE2eError, settled, unwrap, parseUiadA11yEvents, parseInjectEvents, withA11yRead, renderA11yScreen, createDeviceA11y, IOS_DEVICE_CAPABILITIES, sniffCameraMedia, cameraFeedDir, clearCameraFeeds, isLeaseAlive, processAlive, describeOwner, isPhysicalIosId, wardenHome, defaultDbPath, ownerKey, openStore, callerFile, formatCoord, DEFAULT_SPEED_MPS, DEFAULT_INTERVAL_S, realTickerClock, startRouteTicker, startFlap, registerConditions, conditionsOf, conditionSinkFor, createDeviceConditions, parseBudgets, createDevicePerf, perfStartFor, spawnWarden, hash6, attemptDir, screenshotPath, specIdOf, BUN_TIMEOUT_MARGIN_MS2, ownHook, currentScope, createScope, runInScope, hooksFor, resolveUse, registrationDefaults, markOnly, onlyWasSeen, WORKSPACE_VERSION, WARDEN_E2E_VERSION2 };
|
|
9905
|
+
export { E2eError2, registerA11yTarget, a11yTargetOf, canActOnElements, noteSpanDetail, addSpanDetail, canObserve, canDetectAlerts, settleDetailOf, DEFAULT_SETTLE_QUIET_MS, DEFAULT_SETTLE_TIMEOUT_MS, hashFallbackQuietMs, settleByHash, snapshotHash, ok, err, swipeVector, screenRect, isOnScreen, fieldValue, PINCH_MS, ROTATE_MS, PATH_MS, centreOf, pinchRadius, rotateRadius, requireGesture, assertScale, assertDegrees, isInteractiveRole, resolveRole, normalizeText, matchesText, indexSnapshot, keyboardVisible, testIdQuery, textQuery, roleQuery, selectorExpression, renderSnapshot, renderHeader, lookUpTestIdViaReact, isLocator, Locator, pickOne, textOf, scrollUntilVisible, formatNativeTail, realNativeLogDeps, captureNativeLog, bunExec, execError, driverError, driverErr, bridgeStallSchema, toBridgeStall, noteBridgeStall, bridgeStallFromSpan, formatBridgeStall, logEventSchema, fatalEventSchema, responseSchema, mapWireError, createLineSplitter, encodeRequest, bunSpawn, stdioTransport, socketTransport, createSimdClient, spawnSimd, connectSimd, isRecoverableLoss, parseDuration, KEYBOARD_MODES, DEFAULT_KEYBOARD_MODE, IME_STASH_ENABLED, IME_STASH_DEFAULT, CAMERA_FACINGS, CONDITION_AREAS, resolveNetwork, resolveTextSize, androidFontScale, ENV_NAMES, DEFAULT_ASSERTION_TIMEOUT_MS, readEnv, runStep, setDefaultHidRecovery, setDefaultNativeLog, setDefaultDriverFactory, hasDefaultDriverFactory, setDefaultFixtureFactory, hasDefaultFixtureFactory, attemptSession, getSession, peekSession, accessibleName, announce, failing, formatIssues, toE2eError, settled, unwrap, parseUiadA11yEvents, parseInjectEvents, withA11yRead, renderA11yScreen, createDeviceA11y, IOS_DEVICE_CAPABILITIES, sniffCameraMedia, cameraFeedDir, clearCameraFeeds, isLeaseAlive, processAlive, describeOwner, isPhysicalIosId, wardenHome, defaultDbPath, ownerKey, openStore, callerFile, formatCoord, DEFAULT_SPEED_MPS, DEFAULT_INTERVAL_S, realTickerClock, startRouteTicker, startFlap, registerConditions, conditionsOf, conditionSinkFor, createDeviceConditions, parseBudgets, createDevicePerf, perfStartFor, spawnWarden, hash6, attemptDir, screenshotPath, specIdOf, BUN_TIMEOUT_MARGIN_MS2, ownHook, currentScope, createScope, runInScope, hooksFor, resolveUse, registrationDefaults, markOnly, onlyWasSeen, WORKSPACE_VERSION, WARDEN_E2E_VERSION2 };
|