mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-30 13:13:25 +00:00
Backport the Unistyles 3.5.3 Activity configuration fix while keeping 3.2.4 and its existing fixes. Application resources could leave the responsive shell on the previous fold size after resume. Cover the real device-state sequence with a native UI regression.
105 lines
3.8 KiB
JavaScript
105 lines
3.8 KiB
JavaScript
// Run a release build with a connected workspace open and the keyboard hidden.
|
|
// ANDROID_SERIAL=emulator-5574 node packages/app/e2e/mobile/fold/android.mjs
|
|
import assert from "node:assert/strict";
|
|
import { execFileSync } from "node:child_process";
|
|
import { mkdirSync, writeFileSync } from "node:fs";
|
|
import { resolve } from "node:path";
|
|
import { setTimeout as delay } from "node:timers/promises";
|
|
|
|
const serial = process.env.ANDROID_SERIAL;
|
|
assert(serial, "Set ANDROID_SERIAL to a booted Android foldable emulator");
|
|
const appId = process.env.PASEO_MOBILE_APP_ID ?? "sh.paseo.debug";
|
|
const session = "android-fold";
|
|
const artifacts = resolve(".dev/agent-device-artifacts/android-fold");
|
|
const env = {
|
|
...process.env,
|
|
AGENT_DEVICE_STATE_DIR: resolve(".dev/agent-device-android-fold"),
|
|
};
|
|
mkdirSync(artifacts, { recursive: true });
|
|
|
|
function run(binary, args) {
|
|
return execFileSync(binary, args, { env, encoding: "utf8", timeout: 60_000 });
|
|
}
|
|
function adb(...args) {
|
|
return run("adb", ["-s", serial, ...args]);
|
|
}
|
|
function ad(...args) {
|
|
return run("agent-device", [...args, "--session", session]);
|
|
}
|
|
function pid() {
|
|
return adb("shell", "pidof", appId).trim();
|
|
}
|
|
function open() {
|
|
ad("open", appId, "--platform", "android", "--serial", serial, "--no-test-ime");
|
|
}
|
|
function stateId(name) {
|
|
const states = adb("shell", "cmd", "device_state", "print-states");
|
|
const match = states.match(new RegExp(`identifier=(\\d+), name='${name}'`));
|
|
assert(match, `Emulator must expose the ${name} posture: ${states}`);
|
|
return match[1];
|
|
}
|
|
|
|
async function expectLayout(posture) {
|
|
const compact = posture === "CLOSED";
|
|
const expectedId = compact ? "workspace-tab-switcher-trigger" : "workspace-tabs-scroll";
|
|
const unexpectedId = compact ? "workspace-tabs-scroll" : "workspace-tab-switcher-trigger";
|
|
const deadline = Date.now() + 15_000;
|
|
let nodes = [];
|
|
do {
|
|
let data;
|
|
try {
|
|
({ data } = JSON.parse(ad("snapshot", "-i", "--json")));
|
|
} catch (error) {
|
|
// During display handoff Android briefly has no capturable app window.
|
|
if (!error.stdout?.includes("insufficient foreground app content")) throw error;
|
|
await delay(200);
|
|
continue;
|
|
}
|
|
assert.equal(data.appBundleId, appId, "Snapshot must belong to Paseo");
|
|
nodes = data.nodes;
|
|
if (
|
|
nodes.some((node) => node.identifier === expectedId) &&
|
|
!nodes.some((node) => node.identifier === unexpectedId)
|
|
) {
|
|
writeFileSync(resolve(artifacts, `${posture}.json`), JSON.stringify(data, null, 2));
|
|
console.log(`PASS: ${posture} uses ${compact ? "compact" : "wide"} workspace controls`);
|
|
return;
|
|
}
|
|
await delay(200);
|
|
} while (Date.now() < deadline);
|
|
writeFileSync(resolve(artifacts, `${posture}-failure.json`), JSON.stringify(nodes, null, 2));
|
|
writeFileSync(
|
|
resolve(artifacts, "native-failure.txt"),
|
|
adb("shell", "dumpsys", "activity", "top"),
|
|
);
|
|
assert.fail(`${posture}: expected ${expectedId}, without ${unexpectedId}`);
|
|
}
|
|
|
|
const closed = stateId("CLOSED");
|
|
const opened = stateId("OPENED");
|
|
const initialState = adb("shell", "cmd", "device_state", "state").match(
|
|
/Committed state: DeviceState\{identifier=(\d+)/,
|
|
)?.[1];
|
|
assert(initialState, "Could not read the initial fold state");
|
|
|
|
try {
|
|
adb("shell", "cmd", "device_state", "state", opened);
|
|
open();
|
|
await expectLayout("OPENED");
|
|
const initialPid = pid();
|
|
for (const [posture, id] of [
|
|
["CLOSED", closed],
|
|
["OPENED", opened],
|
|
]) {
|
|
adb("shell", "input", "keyevent", "KEYCODE_HOME");
|
|
adb("shell", "cmd", "device_state", "state", id);
|
|
// Resume immediately: waiting for configuration delivery hides the regression.
|
|
open();
|
|
await expectLayout(posture);
|
|
assert.equal(pid(), initialPid, "Fold/resume must preserve the app process");
|
|
}
|
|
} finally {
|
|
adb("shell", "cmd", "device_state", "state", initialState);
|
|
ad("close");
|
|
}
|