Files
wehub-resource-sync 426e9eeabd
Voice Workbench / headless workbench (mocked backends) (push) Has been cancelled
Voice Workbench / real acoustic lane (nightly, provisioned only) (push) Has been cancelled
ci / test (push) Has been cancelled
ci / lint-and-format (push) Has been cancelled
ci / build (push) Has been cancelled
ci / dev-startup (push) Has been cancelled
gitleaks / gitleaks (push) Has been cancelled
Markdown Links / Relative Markdown Links (push) Has been cancelled
Quality (Extended) / Homepage Build (PR smoke) (push) Has been cancelled
Quality (Extended) / Comment-only diff guard (push) Has been cancelled
Quality (Extended) / Format + Type Safety Ratchet (push) Has been cancelled
Quality (Extended) / Develop Gate (secret scan + UI determinism) (push) Has been cancelled
Quality (Extended) / Develop Gate (lint) (push) Has been cancelled
Chat shell gestures / Chat shell gesture + parity e2e (push) Has been cancelled
Cloud Gateway Discord / Test (push) Has been cancelled
Benchmark Bridge Tests / benchmark (bunx @biomejs/biome check packages/lifeops-bench/src, benchmark-lint) (push) Has been cancelled
Benchmark Bridge Tests / benchmark (bunx vitest run --config packages/lifeops-bench/vitest.config.ts --root packages/lifeops-bench --passWithNoTests, benchmark-tests) (push) Has been cancelled
Build Agent Image / build-and-push (push) Has been cancelled
Dev Smoke / bun run dev onboarding chat (push) Has been cancelled
Dev Smoke / Vite HMR dependency-level smoke (push) Has been cancelled
Electrobun Submodule Guard / electrobun gitlink is fetchable (push) Has been cancelled
Publish @elizaos/example-code / check_npm (push) Has been cancelled
Publish @elizaos/example-code / publish_npm (push) Has been cancelled
Publish @elizaos/plugin-elizacloud / verify_version (push) Has been cancelled
Publish @elizaos/plugin-elizacloud / publish_npm (push) Has been cancelled
Sandbox Live Smoke / Sandbox live smoke (push) Has been cancelled
Snap Build & Test / Build Snap (amd64) (push) Has been cancelled
Snap Build & Test / Build Snap (arm64) (push) Has been cancelled
Test Packaging / elizaos CLI global-install smoke (node + bun) (push) Has been cancelled
Cloud Gateway Webhook / Test (push) Has been cancelled
Cloud Tests / lint-and-types (push) Has been cancelled
Cloud Tests / unit-tests (push) Has been cancelled
Cloud Tests / integration-tests (push) Has been cancelled
Cloud Tests / e2e-tests (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
Deploy Apps Worker (Product 2) / Determine environment (push) Has been cancelled
Deploy Apps Worker (Product 2) / Deploy apps worker to apps-control host (${{ needs.determine-env.outputs.environment }}) (push) Has been cancelled
Deploy Eliza Provisioning Worker / Determine environment (push) Has been cancelled
Deploy Eliza Provisioning Worker / Deploy worker to Hetzner host (${{ needs.determine-env.outputs.environment }} @ ${{ needs.determine-env.outputs.deployment_sha }}) (push) Has been cancelled
Dev Smoke / Classify changed paths (push) Has been cancelled
supply-chain / sbom (push) Has been cancelled
supply-chain / vulnerability-scan (push) Has been cancelled
Build, Push & Deploy to Phala Cloud / build-and-push (push) Has been cancelled
Test Packaging / Validate Packaging Configs (push) Has been cancelled
Test Packaging / Build & Test PyPI Package (push) Has been cancelled
Test Packaging / PyPI on Python ${{ matrix.python }} (push) Has been cancelled
Test Packaging / Pack & Test JS Tarballs (push) Has been cancelled
UI Fixture E2E / ui-fixture-e2e (push) Has been cancelled
UI Fixture E2E / fixture-e2e (push) Has been cancelled
UI Story Gate / story-gate (push) Has been cancelled
vault-ci / test (macos-latest) (push) Has been cancelled
vault-ci / test (ubuntu-latest) (push) Has been cancelled
vault-ci / test (windows-latest) (push) Has been cancelled
vault-ci / app-core wiring tests (push) Has been cancelled
verify-patches / verify patches/CHECKSUMS.sha256 (push) Has been cancelled
Voice Benchmark Smoke / voice-emotion fixture smoke (push) Has been cancelled
Voice Benchmark Smoke / voiceagentbench fixture smoke (push) Has been cancelled
Voice Benchmark Smoke / voicebench-quality unit smoke (push) Has been cancelled
Voice Benchmark Smoke / voicebench TypeScript unit (no audio) (push) Has been cancelled
Voice Benchmark Smoke / voice bench smoke summary (push) Has been cancelled
Windows CI / windows ([bun run --cwd packages/app-core test bun run --cwd packages/elizaos test bun run --cwd packages/cloud/shared test], app-and-cli) (push) Has been cancelled
Windows CI / windows ([bun run --cwd packages/scenario-runner test bun run --cwd packages/vault test bun run --cwd packages/security test bun run --cwd plugins/plugin-coding-tools test], framework-packages) (push) Has been cancelled
Windows CI / windows ([bun run --cwd plugins/plugin-elizacloud test bun run --cwd plugins/plugin-discord test bun run --cwd plugins/plugin-anthropic test bun run --cwd plugins/plugin-openai test bun run --cwd plugins/plugin-app-control test bun run --cwd plugins/pl… (push) Has been cancelled
Windows CI / windows ([node packages/scripts/run-turbo.mjs run build --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/agent --concurrency=4 node packages/scripts/run-bash-linux-only.mjs scripts/verify-riscv64-buildpaths.sh node packages/scripts/run… (push) Has been cancelled
Windows CI / windows ([node packages/scripts/run-turbo.mjs run typecheck --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/cloud-shared --concurrency=4 bun run --cwd packages/core test bun run --cwd packages/shared test], core-runtime, 75) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:43:05 +08:00

198 lines
6.8 KiB
JavaScript

/**
* Pure decision logic behind the one-command iOS Simulator e2e orchestrator
* (`ios-e2e.mjs`, wired as `test:e2e:ios`). The orchestrator itself is all
* side effects — boot a simulator, build, `simctl install`, drive the auth and
* full-Bun chat legs — so the parts worth testing are pulled out here as
* deterministic functions with no I/O: CLI parsing, the ordered step plan a
* given flag set produces, the "which simulator is booted" selection over
* `simctl list … --json`, the app-id extraction, and the exact argv each leg
* is invoked with.
*
* The load-bearing invariant these encode is the orchestrator's contract:
* it must fail LOUDLY and must never report success vacuously. That is why
* `planIosE2eSteps` distinguishes setup/optional cloud work from the two
* simulator-app verification legs (auth / local-chat), and
* `assertNonVacuousPlan` refuses a run that would print "ALL iOS E2E PASSED"
* without exercising a single real app path. Runs under the packages/app vitest suite
* (`bun run --cwd packages/app test`), i.e. the root test:client lane.
*/
export const DEFAULT_IOS_SIMULATOR = "iPhone 16 Pro";
// Ordered once, consumed everywhere. Build is setup; the rest are the real
// device-path assertions. Order is the run order and is load-bearing: install
// must precede any launch, auth registers the deep-link before chat drives it.
export const IOS_E2E_STEP_IDS = ["build", "auth", "local-chat", "cloud"];
export const IOS_E2E_VERIFICATION_STEP_IDS = ["auth", "local-chat"];
/**
* Parse the orchestrator argv into an explicit flag record. Kept total (every
* field always present) so callers branch on booleans, never on `argv.includes`
* scattered through the flow.
*/
export function parseIosE2eArgs(argv) {
const has = (flag) => argv.includes(flag);
const val = (flag) => {
const i = argv.indexOf(flag);
return i >= 0 && i + 1 < argv.length ? argv[i + 1] : undefined;
};
return {
device: val("--device"),
appPath: val("--app-path"),
output: val("--output"),
skipBuild: has("--skip-build"),
skipAuth: has("--skip-auth"),
skipLocalChat: has("--skip-local-chat"),
cloud: has("--cloud"),
noWait: has("--no-wait"),
};
}
/**
* The ordered list of steps a flag set produces. Each descriptor carries a
* stable `id`, a human `label`, and `verification: true` for the legs that
* actually assert a real device path (so a plan can be checked for vacuity).
*/
export function planIosE2eSteps(flags) {
const steps = [];
if (!flags.skipBuild) {
steps.push({
id: "build",
label: "build + install the iOS Simulator app",
verification: false,
});
}
if (!flags.skipAuth) {
steps.push({
id: "auth",
label: "auth route: deep-link / callback registration + drive",
verification: true,
});
}
if (!flags.skipLocalChat) {
steps.push({
id: "local-chat",
label: "local route: on-device agent + smallest model + real chat",
verification: true,
});
}
if (flags.cloud) {
steps.push({
id: "cloud",
label: "cloud route: real provisioning probe",
verification: false,
});
}
return steps;
}
/**
* Guard against a vacuous green: a run that skips every simulator-app
* verification leg would otherwise sail to "ALL iOS E2E PASSED" having proven
* no app/auth/chat path. Cloud is useful optional coverage, but it is not a
* substitute for exercising the installed simulator app, so refuse those
* combinations up front with an actionable message instead of exiting 0.
*/
export function assertNonVacuousPlan(steps) {
const verifying = steps.filter((s) => s.verification);
if (verifying.length === 0) {
throw new Error(
"refusing to run: every simulator-app verification leg (auth / local-chat) is skipped, " +
"so the orchestrator would report success without proving the installed app path. " +
"Drop --skip-auth or --skip-local-chat; --cloud alone is not enough.",
);
}
return verifying;
}
/**
* Select a booted simulator udid from the parsed output of
* `xcrun simctl list devices booted --json`. Returns the first Booted device's
* udid, or null when none is booted. Tolerant of the shape (missing `devices`,
* non-array runtimes) because simctl JSON varies across Xcode versions.
*/
export function selectBootedUdid(listJson) {
const devices = listJson?.devices;
if (!devices || typeof devices !== "object") return null;
for (const runtime of Object.values(devices)) {
if (!Array.isArray(runtime)) continue;
const booted = runtime.find((d) => d?.state === "Booted");
if (booted?.udid) return booted.udid;
}
return null;
}
/** Resolve the target simulator name, defaulting to the pinned dev device. */
export function resolveTargetDevice(deviceArg) {
return deviceArg ?? DEFAULT_IOS_SIMULATOR;
}
/**
* Extract the Capacitor app id from `app.config.ts` source. The bundle id is
* the `simctl` handle for install/terminate/uninstall, so a missing match falls
* back to the known default rather than throwing mid-orchestration.
*/
export function extractAppId(configSrc) {
return configSrc.match(/appId:\s*["']([^"']+)["']/)?.[1] ?? "ai.elizaos.app";
}
// Leg command builders. Each returns { cmd, args } exactly as spawned. Kept
// pure so the tests pin the flags that make each leg *real* — e.g. the chat leg
// must carry --require-installed (no host fallback) and --ios-full-bun-smoke
// (the real on-device engine), and the auth leg must target the booted udid.
export function buildIosSimBuildCommand() {
return { cmd: "bun", args: ["run", "build:ios:local:sim"] };
}
export function buildAuthSmokeCommand(udid) {
if (!udid) throw new Error("buildAuthSmokeCommand requires a simulator udid");
return {
cmd: "node",
args: [
"../../packages/app-core/scripts/mobile-auth-simulator-smoke.mjs",
"--platform",
"ios",
"--device",
udid,
],
};
}
export function buildLocalChatSmokeCommand() {
return {
cmd: "node",
args: [
"scripts/mobile-local-chat-smoke.mjs",
"--platform",
"ios",
"--require-installed",
"--ios-select-local",
"--ios-full-bun-smoke",
],
};
}
export function buildCloudProvisioningCommand() {
return { cmd: "node", args: ["scripts/cloud-provisioning-e2e.mjs"] };
}
/**
* Classify a spawned leg's exit status into the loud-or-pass decision. A
* non-zero (or null, i.e. killed by signal) status is a hard failure that must
* abort the whole orchestration — never a warn-and-continue.
*/
export function classifyStepExit(status) {
if (status === 0) return { ok: true };
return {
ok: false,
reason:
status === null ? "terminated by signal" : `exited with code ${status}`,
};
}
/** Whether a `simctl get_app_container` result proves the app is installed. */
export function isAppInstalled(container) {
return typeof container === "string" && container.trim().length > 0;
}