// Render smoke test for the Linear app — the same regression class the // GitHub app shipped (github-render.test.mjs): an auto-load firing mid-render // must neither throw nor wedge the module `busy` flag. Drives render twice // over a stub DOM and proves the fetch path both RUNS and RELEASES. // node test/linear-render.test.mjs import assert from "node:assert/strict"; // A just-enough DOM: every element accepts children, classes, listeners. function fakeEl(tag = "div") { return { tagName: tag, children: [], textContent: "", className: "", hidden: false, disabled: false, value: "", placeholder: "", type: "", onclick: null, ondblclick: null, appendChild(c) { this.children.push(c); return c; }, append(...cs) { this.children.push(...cs); }, addEventListener() {}, querySelector() { return fakeEl(); }, classList: { add() {}, remove() {}, toggle() {} }, focus() {}, scrollIntoView() {}, }; } globalThis.document = { createElement: (tag) => fakeEl(tag), getElementById: () => null, // injectStyles' dedupe check head: fakeEl("head"), activeElement: null, body: null, }; globalThis.window = globalThis; const { app } = await import("../web/static/apps/linear.js"); let failures = 0; async function test(name, fn) { try { await fn(); console.log(`PASS ${name}`); } catch (e) { failures++; console.log(`FAIL ${name} — ${e.message.split("\n")[0]}`); } } const tick = () => new Promise((r) => setTimeout(r, 0)); function ctxWith(requestOp) { const state = { teams: [], fetchedAt: 0, issues: {} }; return { threadId: "t1", host: { connected: { requestOp } }, getState: () => state, emit: () => {}, onCleanup: () => {}, id: () => "id", }; } await test("render survives the auto-load and RELEASES busy for the next fetch", async () => { let calls = 0; const ctx = ctxWith(async () => { calls++; return { ok: false, error: "stub" }; }); app.render(undefined, fakeEl(), ctx); await tick(); await tick(); assert.equal(calls, 1, "auto-load ran"); app.render(undefined, fakeEl(), ctx); await tick(); await tick(); assert.equal(calls, 2, "busy was released — the app can fetch again"); }); await test("a requestOp that THROWS still lands in the error path and releases", async () => { let calls = 0; const ctx = ctxWith(async () => { calls++; throw new Error("boom"); }); app.render(undefined, fakeEl(), ctx); await tick(); await tick(); app.render(undefined, fakeEl(), ctx); await tick(); await tick(); assert.equal(calls, 2, "a thrown fetch must not wedge the app"); }); console.log(failures === 0 ? "\nALL PASSED" : `\n${failures} FAILED`); process.exit(failures === 0 ? 0 : 1);