Skip to content

Commit 1a3ca4e

Browse files
committed
Handle keyboard tip in CLI integration checks
1 parent 03ec76e commit 1a3ca4e

1 file changed

Lines changed: 115 additions & 0 deletions

File tree

scripts/integration/cli.mjs

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1056,6 +1056,31 @@ async function resolveKnownSystemPromptsWithQueries(label) {
10561056
);
10571057
await sleep(500);
10581058
}
1059+
1060+
const snapshot = await queryUi(label, {
1061+
limit: 128,
1062+
maxDepth: 8,
1063+
maxElapsedMs: httpActionBudgetMs,
1064+
});
1065+
if (!looksLikeKeyboardTipQuery(snapshot)) {
1066+
return;
1067+
}
1068+
1069+
logStep(`handling system keyboard-tip prompt after ${label}`);
1070+
await retryHttpJson(
1071+
"POST",
1072+
`/api/simulators/${simulatorUDID}/tap`,
1073+
{
1074+
source: "native-ax",
1075+
maxDepth: 8,
1076+
waitTimeoutMs: 2_000,
1077+
durationMs: 80,
1078+
...keyboardTipContinueTapTarget(snapshot),
1079+
},
1080+
`${label} tap keyboard-tip prompt`,
1081+
{ attempts: 1, maxElapsedMs: httpActionBudgetMs },
1082+
);
1083+
await sleep(500);
10591084
}
10601085

10611086
function summarizeUiCheck(options = {}) {
@@ -1387,6 +1412,96 @@ function looksLikeKeyboardTipPrompt(snapshot) {
13871412
);
13881413
}
13891414

1415+
function looksLikeKeyboardTipQuery(snapshot) {
1416+
const text = JSON.stringify(snapshot?.matches ?? snapshot ?? []);
1417+
return /Speed up your typing/i.test(text) && /\bContinue\b/.test(text);
1418+
}
1419+
1420+
function keyboardTipContinueTapTarget(snapshot) {
1421+
const nodes = compactQueryNodes(snapshot);
1422+
const rootFrame = nodes
1423+
.map((node) => node.frame)
1424+
.filter(validFrame)
1425+
.sort((a, b) => b.width * b.height - a.width * a.height)[0];
1426+
const continueButton =
1427+
nodes.find(
1428+
(node) =>
1429+
node.label === "Continue" &&
1430+
node.id !== "fixture.continue" &&
1431+
String(node.role ?? "")
1432+
.toLowerCase()
1433+
.includes("button") &&
1434+
validFrame(node.frame),
1435+
) ??
1436+
nodes.find(
1437+
(node) =>
1438+
node.label === "Continue" &&
1439+
String(node.role ?? "")
1440+
.toLowerCase()
1441+
.includes("button") &&
1442+
validFrame(node.frame),
1443+
);
1444+
1445+
if (rootFrame && continueButton?.frame) {
1446+
return {
1447+
normalized: true,
1448+
x:
1449+
(continueButton.frame.x +
1450+
continueButton.frame.width / 2 -
1451+
rootFrame.x) /
1452+
rootFrame.width,
1453+
y:
1454+
(continueButton.frame.y +
1455+
continueButton.frame.height / 2 -
1456+
rootFrame.y) /
1457+
rootFrame.height,
1458+
};
1459+
}
1460+
1461+
return {
1462+
selector: { label: "Continue", elementType: "Button" },
1463+
};
1464+
}
1465+
1466+
function compactQueryNodes(snapshot) {
1467+
const nodes = [];
1468+
const visit = (node) => {
1469+
if (!node || typeof node !== "object") {
1470+
return;
1471+
}
1472+
if (
1473+
"role" in node ||
1474+
"id" in node ||
1475+
"label" in node ||
1476+
"value" in node ||
1477+
"frame" in node
1478+
) {
1479+
nodes.push(node);
1480+
}
1481+
for (const child of Array.isArray(node.children) ? node.children : []) {
1482+
visit(child);
1483+
}
1484+
};
1485+
for (const match of Array.isArray(snapshot?.matches)
1486+
? snapshot.matches
1487+
: []) {
1488+
visit(match);
1489+
}
1490+
return nodes;
1491+
}
1492+
1493+
function validFrame(frame) {
1494+
return (
1495+
frame &&
1496+
Number.isFinite(frame.x) &&
1497+
Number.isFinite(frame.y) &&
1498+
Number.isFinite(frame.width) &&
1499+
Number.isFinite(frame.height) &&
1500+
frame.width > 0 &&
1501+
frame.height > 0
1502+
);
1503+
}
1504+
13901505
function fixtureReady(snapshot) {
13911506
return (
13921507
snapshot.includes("SimDeck Fixture") &&

0 commit comments

Comments
 (0)