eleven / ocho Public

Clone
git clone https://amadan.net/eleven/ocho

Plain git — no account needed to clone.

Download

Download this file

1// Render smoke test for the Linear app — the same regression class the
2// GitHub app shipped (github-render.test.mjs): an auto-load firing mid-render
3// must neither throw nor wedge the module `busy` flag. Drives render twice
4// over a stub DOM and proves the fetch path both RUNS and RELEASES.
5// node test/linear-render.test.mjs
6import assert from "node:assert/strict";
7
8// A just-enough DOM: every element accepts children, classes, listeners.
9function fakeEl(tag = "div") {
10 return {
11 tagName: tag,
12 children: [],
13 textContent: "",
14 className: "",
15 hidden: false,
16 disabled: false,
17 value: "",
18 placeholder: "",
19 type: "",
20 onclick: null,
21 ondblclick: null,
22 appendChild(c) {
23 this.children.push(c);
24 return c;
25 },
26 append(...cs) {
27 this.children.push(...cs);
28 },
29 addEventListener() {},
30 querySelector() {
31 return fakeEl();
32 },
33 classList: { add() {}, remove() {}, toggle() {} },
34 focus() {},
35 scrollIntoView() {},
36 };
37}
38globalThis.document = {
39 createElement: (tag) => fakeEl(tag),
40 getElementById: () => null, // injectStyles' dedupe check
41 head: fakeEl("head"),
42 activeElement: null,
43 body: null,
44};
45globalThis.window = globalThis;
46
47const { app } = await import("../web/static/apps/linear.js");
48
49let failures = 0;
50async function test(name, fn) {
51 try {
52 await fn();
53 console.log(`PASS ${name}`);
54 } catch (e) {
55 failures++;
56 console.log(`FAIL ${name} — ${e.message.split("\n")[0]}`);
57 }
58}
59
60const tick = () => new Promise((r) => setTimeout(r, 0));
61
62function ctxWith(requestOp) {
63 const state = { teams: [], fetchedAt: 0, issues: {} };
64 return {
65 threadId: "t1",
66 host: { connected: { requestOp } },
67 getState: () => state,
68 emit: () => {},
69 onCleanup: () => {},
70 id: () => "id",
71 };
72}
73
74await test("render survives the auto-load and RELEASES busy for the next fetch", async () => {
75 let calls = 0;
76 const ctx = ctxWith(async () => {
77 calls++;
78 return { ok: false, error: "stub" };
79 });
80 app.render(undefined, fakeEl(), ctx);
81 await tick();
82 await tick();
83 assert.equal(calls, 1, "auto-load ran");
84 app.render(undefined, fakeEl(), ctx);
85 await tick();
86 await tick();
87 assert.equal(calls, 2, "busy was released — the app can fetch again");
88});
89
90await test("a requestOp that THROWS still lands in the error path and releases", async () => {
91 let calls = 0;
92 const ctx = ctxWith(async () => {
93 calls++;
94 throw new Error("boom");
95 });
96 app.render(undefined, fakeEl(), ctx);
97 await tick();
98 await tick();
99 app.render(undefined, fakeEl(), ctx);
100 await tick();
101 await tick();
102 assert.equal(calls, 2, "a thrown fetch must not wedge the app");
103});
104
105console.log(failures === 0 ? "\nALL PASSED" : `\n${failures} FAILED`);
106process.exit(failures === 0 ? 0 : 1);
107