aboutsummaryrefslogtreecommitdiff
path: root/test/profile/launch-test.ts
diff options
context:
space:
mode:
authoramankumarsingh77 <amankumarsingh7702@gmail.com>2026-07-27 00:27:08 +0530
committerAman Kumar <145339995+amankumarsingh77@users.noreply.github.com>2026-08-02 19:12:46 +0530
commit15a537a99efba614e6e108722462bcb78ec6c3c4 (patch)
tree2ff20907fee8ace7a071470d491fe7808dce6a63 /test/profile/launch-test.ts
parentce5d808cb4cc3581d85b829656d1d478dc43e3a6 (diff)
test: port remaining suites to node:test
Mechanical port of the hand-rolled check() harness. Import-after-env ordering preserved so suites still read their sandbox dirs. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'test/profile/launch-test.ts')
-rw-r--r--test/profile/launch-test.ts167
1 files changed, 103 insertions, 64 deletions
diff --git a/test/profile/launch-test.ts b/test/profile/launch-test.ts
index 0061273..70ca8af 100644
--- a/test/profile/launch-test.ts
+++ b/test/profile/launch-test.ts
@@ -7,89 +7,128 @@
* "couldn't open a window in <profile> automatically". These tests pin the
* failure to its real cause instead.
*
- * Run: npx tsx test/profile/launch-test.ts
+ * Run: npm test
*/
+import { test, before, after, describe } from "node:test";
+import assert from "node:assert/strict";
import { existsSync, mkdtempSync, readdirSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { fileURLToPath } from "node:url";
import { openProfileWindow } from "../../src/profile/launch";
-let passed = 0;
-let failed = 0;
-const check = (cond: boolean, label: string): void => {
- if (cond) {
- passed++;
- console.log(` ✓ ${label}`);
- } else {
- failed++;
- console.error(` ✗ ${label}`);
- }
-};
-
-async function main(): Promise<void> {
- const root = mkdtempSync(join(tmpdir(), "pi-launch-"));
- try {
- // L1: a nonexistent binary — the shape of an in-place browser upgrade,
- // where /proc/<pid>/exe still names the unlinked file.
- {
- const missing = join(root, "chrome (deleted)");
- const r = await openProfileWindow({ exePath: missing, profileDir: "Default" });
- check(!r.success, "L1: a missing executable fails the launch");
- if (!r.success) {
- check(r.error.includes(missing), "L1: the error names the path that could not be launched");
- check(r.error.includes("ENOENT"), "L1: the error carries spawn's own cause");
- }
- }
+describe("openProfileWindow's failure reporting", () => {
+ let root: string;
+
+ before(() => {
+ root = mkdtempSync(join(tmpdir(), "pi-launch-"));
+ });
+
+ after(() => {
+ rmSync(root, { recursive: true, force: true });
+ });
+
+ // L1: a nonexistent binary — the shape of an in-place browser upgrade,
+ // where /proc/<pid>/exe still names the unlinked file.
+ describe("L1", () => {
+ const missing = (): string => join(root, "chrome (deleted)");
+
+ test("L1: a missing executable fails the launch", async () => {
+ const r = await openProfileWindow({ exePath: missing(), profileDir: "Default" });
+ assert.ok(!r.success);
+ });
+
+ test("L1: the error names the path that could not be launched", async () => {
+ const r = await openProfileWindow({ exePath: missing(), profileDir: "Default" });
+ assert.ok(!r.success);
+ assert.ok(r.error.includes(missing()));
+ });
- // L2: a file that exists but is not executable — same async 'error' path,
- // different errno (EACCES). Skipped where the check is meaningless.
- if (process.platform !== "win32" && process.getuid?.() !== 0) {
+ test("L1: the error carries spawn's own cause", async () => {
+ const r = await openProfileWindow({ exePath: missing(), profileDir: "Default" });
+ assert.ok(!r.success);
+ assert.ok(r.error.includes("ENOENT"));
+ });
+ });
+
+ // L2: a file that exists but is not executable — same async 'error' path,
+ // different errno (EACCES). Skipped where the check is meaningless.
+ test(
+ "L2: a non-executable file fails the launch",
+ { skip: process.platform === "win32" || process.getuid?.() === 0 },
+ async () => {
const notExec = join(root, "not-executable");
writeFileSync(notExec, "#!/bin/sh\n", { mode: 0o644 });
const r = await openProfileWindow({ exePath: notExec, profileDir: "Default" });
- check(!r.success, "L2: a non-executable file fails the launch");
- }
+ assert.ok(!r.success);
+ },
+ );
+
+ // L3: a failed launch leaves no sentinel file behind. The caller only calls
+ // cleanup() on the success path, so the failure path owns its own tidying.
+ describe("L3", () => {
+ let before_: string | undefined;
- // L3: a failed launch leaves no sentinel file behind. The caller only calls
- // cleanup() on the success path, so the failure path owns its own tidying.
- {
- const before = process.env["TMPDIR"];
+ before(() => {
+ before_ = process.env["TMPDIR"];
process.env["TMPDIR"] = root;
- try {
- const r = await openProfileWindow({ exePath: join(root, "absent"), profileDir: "Default" });
- check(!r.success, "L3: launch failed as set up");
- const leftovers = readdirSync(root).filter((n) => n.startsWith("pi-harness-"));
- check(leftovers.length === 0, "L3: no handshake page is left on disk after a failed launch");
- } finally {
- if (before === undefined) delete process.env["TMPDIR"];
- else process.env["TMPDIR"] = before;
- }
- }
+ });
+
+ after(() => {
+ if (before_ === undefined) delete process.env["TMPDIR"];
+ else process.env["TMPDIR"] = before_;
+ });
+
+ test("L3: launch failed as set up", async () => {
+ const r = await openProfileWindow({ exePath: join(root, "absent"), profileDir: "Default" });
+ assert.ok(!r.success);
+ });
+
+ test("L3: no handshake page is left on disk after a failed launch", async () => {
+ await openProfileWindow({ exePath: join(root, "absent"), profileDir: "Default" });
+ const leftovers = readdirSync(root).filter((n) => n.startsWith("pi-harness-"));
+ assert.equal(leftovers.length, 0);
+ });
+ });
- // L4: a launcher that starts succeeds, and the sentinel it advertises is a
- // real, readable file — the page the browser is asked to open.
- {
+ // L4: a launcher that starts succeeds, and the sentinel it advertises is a
+ // real, readable file — the page the browser is asked to open.
+ describe("L4", () => {
+ let r: Awaited<ReturnType<typeof openProfileWindow>>;
+
+ before(async () => {
const stub = join(root, "stub-browser");
writeFileSync(stub, "#!/bin/sh\nexit 0\n", { mode: 0o755 });
- const r = await openProfileWindow({ exePath: stub, profileDir: "Default" });
- check(r.success, "L4: a startable binary yields a handle");
+ r = await openProfileWindow({ exePath: stub, profileDir: "Default" });
+ });
+
+ test("L4: a startable binary yields a handle", () => {
+ assert.ok(r.success);
+ });
+
+ test("L4: the sentinel page exists before the browser is polled", () => {
+ assert.ok(r.success);
+ if (r.success) {
+ const sentinel = fileURLToPath(r.data.sentinelUrl);
+ assert.ok(existsSync(sentinel));
+ }
+ });
+
+ test("L4: cleanup removes the sentinel page", async () => {
+ assert.ok(r.success);
if (r.success) {
const sentinel = fileURLToPath(r.data.sentinelUrl);
- check(existsSync(sentinel), "L4: the sentinel page exists before the browser is polled");
await r.data.cleanup();
- check(!existsSync(sentinel), "L4: cleanup removes the sentinel page");
+ assert.ok(!existsSync(sentinel));
+ }
+ });
+
+ test("L4: kill is safe to call twice", () => {
+ assert.ok(r.success);
+ if (r.success) {
r.data.kill();
r.data.kill();
- check(true, "L4: kill is safe to call twice");
}
- }
- } finally {
- rmSync(root, { recursive: true, force: true });
- }
-
- console.log(`\n${passed} passed, ${failed} failed`);
- process.exit(failed === 0 ? 0 : 1);
-}
-
-void main();
+ });
+ });
+});