mirror of
https://github.com/getpaseo/paseo.git
synced 2026-11-02 22:52:58 +00:00
test(app): exercise toast placement through host disconnection
Observe the public reconnecting state with host connectivity interrupted externally. Keep native bounds and software keyboard assertions while removing runtime evaluation and private timeline-owner mutations.
This commit is contained in:
@@ -1,21 +1,17 @@
|
||||
// Open an existing connected compact chat in a dev client, with the keyboard closed.
|
||||
// node placement.mjs <session> <platform> <cdp-target> <server-id> <coordinate-scale> <composer-label> <artifacts>
|
||||
// Open an existing compact chat, with the keyboard closed, then interrupt its host connection.
|
||||
// Hold that interruption externally (for example, stop a disposable SSH TCP forward).
|
||||
// Leave the daemon running. This test observes the public reconnecting state.
|
||||
// node placement.mjs <session> <platform> <coordinate-scale> <composer-label> <artifacts>
|
||||
// coordinate-scale is screenshot pixels per layout point (Android density; iOS snapshots use 1).
|
||||
import assert from "node:assert/strict";
|
||||
import { execFileSync } from "node:child_process";
|
||||
import { mkdirSync, writeFileSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
const [, , session, platform, target, serverId, coordinateScale, composerLabel, artifacts] =
|
||||
process.argv;
|
||||
const [, , session, platform, coordinateScale, composerLabel, artifacts] = process.argv;
|
||||
assert(
|
||||
session &&
|
||||
["android", "ios"].includes(platform) &&
|
||||
target &&
|
||||
serverId &&
|
||||
composerLabel &&
|
||||
artifacts,
|
||||
"Pass all seven arguments",
|
||||
session && ["android", "ios"].includes(platform) && composerLabel && artifacts,
|
||||
"Pass all five arguments",
|
||||
);
|
||||
const scale = Number(coordinateScale);
|
||||
assert(scale > 0, "Pass a positive coordinate scale");
|
||||
@@ -26,61 +22,33 @@ function ad(...args) {
|
||||
timeout: 90_000,
|
||||
});
|
||||
}
|
||||
function evaluate(expression) {
|
||||
const result = ad("cdp", "runtime", "eval", "--expr", expression);
|
||||
assert(!result.includes("Runtime evaluation failed"), result);
|
||||
}
|
||||
function capture(name) {
|
||||
const snapshot = ad("snapshot", "--raw", "--json");
|
||||
writeFileSync(path.join(artifacts, `${name}.json`), snapshot);
|
||||
ad("screenshot", path.join(artifacts, `${name}.png`));
|
||||
const nodes = JSON.parse(snapshot).data.nodes;
|
||||
const header = nodes.find((node) => node.identifier === "floating-header");
|
||||
const toast = nodes.find((node) => node.identifier === "agent-updating-toast");
|
||||
assert(header?.rect && toast?.rect, "Measured header and updating toast must be present");
|
||||
const toast = nodes.find((node) => node.identifier === "agent-reconnecting-toast");
|
||||
assert(header?.rect && toast?.rect, "Measured header and reconnecting toast must be present");
|
||||
assert(toast.rect.height > 0 && toast.rect.width > 0, "Toast must have visible bounds");
|
||||
const gap = (toast.rect.y - header.rect.y - header.rect.height) / scale;
|
||||
assert(Math.abs(gap - 12) <= 1, `${name}: actual header-to-toast gap ${gap}, expected12 points`);
|
||||
return toast.rect.y / scale;
|
||||
}
|
||||
|
||||
ad("wait", `label=${JSON.stringify(composerLabel)}`, "20000");
|
||||
ad("cdp", "target", "select", target);
|
||||
try {
|
||||
// Hold completion of the real timeline response. No response data is synthesized.
|
||||
evaluate(`
|
||||
globalThis.__statusPlacement = {};
|
||||
(() => {
|
||||
const module = name => __r(Array.from(__r.getModules()).find(([,v]) => v.verboseName === name)[0]);
|
||||
const store = module("src/runtime/host-runtime.ts").getHostRuntimeStore();
|
||||
const owner = module("src/stores/session-store.ts").useSessionStore.getState().sessions[${JSON.stringify(serverId)}].viewedTimelineSync;
|
||||
const original = store.fetchAgentTimeline;
|
||||
const releases = [];
|
||||
let held = true;
|
||||
store.fetchAgentTimeline = async function(...args) {
|
||||
const response = await original.apply(this, args);
|
||||
if (held) await new Promise(resolve => releases.push(resolve));
|
||||
return response;
|
||||
};
|
||||
__statusPlacement.restore = () => { held = false; store.fetchAgentTimeline = original; releases.forEach(resolve => resolve()); };
|
||||
owner.setActive(false);
|
||||
owner.setActive(true);
|
||||
})();
|
||||
`);
|
||||
ad("wait", 'id="agent-updating-toast"', "15000");
|
||||
const closedTop = capture("keyboard-closed");
|
||||
ad("press", `label=${JSON.stringify(composerLabel)}`);
|
||||
const opened = ad("snapshot", "--raw", "--json");
|
||||
const nodes = JSON.parse(opened).data.nodes;
|
||||
assert(
|
||||
nodes.some(
|
||||
(node) => /keyboard/i.test(node.type ?? "") || /inputmethod/i.test(node.bundleId ?? ""),
|
||||
),
|
||||
"Use a real visible software keyboard; open Android sessions with --no-test-ime",
|
||||
);
|
||||
const openTop = capture("keyboard-open");
|
||||
assert(Math.abs(openTop - closedTop) <= 1, "Keyboard must not translate the toast");
|
||||
console.log("PASS: rendered toast stays12 points below measured header, keyboard closed/open");
|
||||
} finally {
|
||||
evaluate("globalThis.__statusPlacement?.restore?.(); delete globalThis.__statusPlacement;");
|
||||
}
|
||||
ad("wait", 'id="agent-reconnecting-toast"', "20000");
|
||||
const closedTop = capture("keyboard-closed");
|
||||
ad("press", `label=${JSON.stringify(composerLabel)}`);
|
||||
const opened = ad("snapshot", "--raw", "--json");
|
||||
const nodes = JSON.parse(opened).data.nodes;
|
||||
assert(
|
||||
nodes.some(
|
||||
(node) => /keyboard/i.test(node.type ?? "") || /inputmethod/i.test(node.bundleId ?? ""),
|
||||
),
|
||||
"Use a real visible software keyboard; open Android sessions with --no-test-ime",
|
||||
);
|
||||
const openTop = capture("keyboard-open");
|
||||
assert(Math.abs(openTop - closedTop) <= 1, "Keyboard must not translate the toast");
|
||||
console.log(
|
||||
"PASS: rendered reconnecting toast stays12 points below measured header, keyboard closed/open",
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user