mirror of
https://github.com/software-mansion/argent.git
synced 2026-09-14 19:27:14 +08:00
6d907cc86b
A `launch:` on Chromium now starts its own Electron instance, so nested e2e flows and mid-flow relaunches work as they do on iOS/Android. Previously the runner booted one app per run and rejected any second chromium launch. A chromium device id _is_ its CDP port, so a launch that starts an app from scratch has to boot one and move the run onto it: `state.device` became reassignable (steps already read it per call), and `state.owned` tracks what the runner booted, torn down in reverse at run end. An instance the runner did not boot — pinned via `--device`, or auto-detected — is attached to, never killed. Relaunching the same app kills its instance and waits for the process to actually exit, or the replacement loses a race against the app's single-instance lock. The runner needs a device before step 1, so `resolveRunDevice` follows a leading `run:` chain to find the launch the run _begins_ with: a fragment composing a chromium e2e flow now boots instead of failing with "No booted chromium device found". A launch declaring no app id for the run's platform stays an error — the run's platform is fixed start to finish. Also fixes a pre-existing teardown leak: the spawned handle is the npm `electron` node wrapper, not Electron, so signalling it left the app and its helpers running (reparented to launchd, still in the dock). Teardown now sweeps the child's process group. **Behavior change:** a fragment leading with `run:` into a chromium e2e flow now boots a fresh instance where it previously attached to a running one; pass `--device` to keep attaching. ## Tests Cover the boot/attach/settle three-way, teardown ordering and the group sweep, device rebinding after a nested launch, same-app retire ordering, cancellation mid-boot, both "no chromium app declared" guards, and the leading-`run:` chain (multi-hop, cyclic, no-launch). The guard, exit-wait and sweep tests are mutation-checked.
64 lines
2.2 KiB
TypeScript
64 lines
2.2 KiB
TypeScript
import { describe, it, expect } from "vitest";
|
|
import { renderReport, renderSummary, type FlowReport } from "../src/flow.js";
|
|
|
|
function report(overrides: Partial<FlowReport> = {}): FlowReport {
|
|
return {
|
|
flow: "echo-only",
|
|
device: "",
|
|
ok: true,
|
|
passed: 0,
|
|
failed: 0,
|
|
skipped: 0,
|
|
errored: 0,
|
|
steps: [],
|
|
...overrides,
|
|
} as FlowReport;
|
|
}
|
|
|
|
describe("rendering a run that resolved no device", () => {
|
|
it("does not claim the run happened on a device", () => {
|
|
const line = renderSummary(report(), { withDevice: true });
|
|
|
|
expect(line).not.toContain(" on ");
|
|
expect(line).toBe("PASS — 0 passed, 0 failed, 0 errored, 0 skipped (no test steps)");
|
|
});
|
|
|
|
it("omits the device from the report header too", () => {
|
|
const lines = renderReport(report()).split("\n");
|
|
|
|
expect(lines[0]).toBe('Flow "echo-only"');
|
|
});
|
|
|
|
it("still names a device when the run had one", () => {
|
|
const line = renderSummary(report({ device: "UDID-1", passed: 2 }), { withDevice: true });
|
|
|
|
expect(line).toBe("PASS (started on UDID-1) — 2 passed, 0 failed, 0 errored, 0 skipped");
|
|
});
|
|
});
|
|
|
|
describe("the no-test-steps note", () => {
|
|
it("explains a passing run whose counters are all zero", () => {
|
|
expect(renderSummary(report())).toContain("(no test steps)");
|
|
});
|
|
|
|
it("is absent whenever anything was counted", () => {
|
|
expect(renderSummary(report({ passed: 1 }))).not.toContain("(no test steps)");
|
|
expect(renderSummary(report({ skipped: 1 }))).not.toContain("(no test steps)");
|
|
});
|
|
|
|
it("is absent on a failure, where the counts are not what needs explaining", () => {
|
|
// A cancelled run can be a FAIL with every counter still zero; calling that
|
|
// "no test steps" would read as though the failure had no cause.
|
|
const line = renderSummary(report({ ok: false }));
|
|
|
|
expect(line).toBe("FAIL — 0 passed, 0 failed, 0 errored, 0 skipped");
|
|
expect(line).not.toContain("(no test steps)");
|
|
});
|
|
|
|
it("leaves an ordinary summary byte-for-byte unchanged", () => {
|
|
const line = renderSummary(report({ ok: false, passed: 2, failed: 1, skipped: 1 }));
|
|
|
|
expect(line).toBe("FAIL — 2 passed, 1 failed, 0 errored, 1 skipped");
|
|
});
|
|
});
|