// Covers §23–§24's triage: tab counts, awaiting reply, repo/type/word filters, hiding one // follow-up, and pausing new issues per repo. import { beforeAll, describe, expect, test } from "bun:test"; import { addEntry, fileIssue, setClosed } from "../src/issues/store.ts"; import { Browser, type Instance, instance } from "./browser.ts"; import { makeRepos } from "./fixture.ts"; describe("issue triage (§23, §24)", () => { let inst: Instance; let operator: Browser; let followUp: number; let token: string; const list = async (query = "") => (await operator.get(`/admin/issues${query}`)).text(); const text = async (path: string) => (await inst.app.request(path)).text(); const file = (repo: string, title: string, body: string, type: "bug" | "idea" | "question") => fileIssue(inst.kepr, { repo, title, body, type, version: "", reporterName: "", contactEmail: "", ipHash: "x", filedAt: 1, }); const id = (repo: string, n: number) => ( inst.kepr .query<{ id: number }, [string, number]>( "SELECT id FROM issues WHERE repo = ? AND number = ?", ) .get(repo, n) as { id: number } ).id; beforeAll(async () => { const repos = await makeRepos(["app", "tools"]); inst = await instance(repos.bucketDir); operator = new Browser(inst.app); await operator.signIn(); for (const repo of ["app", "tools"]) { await operator.post(`/admin/repos/${repo}`, { public: "1" }); } file("app", "Crash on start", "A segfault in main", "bug"); file("app", "Dark mode", "Please", "idea"); token = file("app", "How to install", "Which command?", "question").token; file("tools", "Tools broken", "Nothing works", "bug"); addEntry(inst.kepr, id("app", 2), "operator", "Planned.", 2); addEntry(inst.kepr, id("app", 3), "operator", "Use the script.", 2); followUp = addEntry(inst.kepr, id("app", 3), "reporter", "Buy cheap pills here", 3); setClosed(inst.kepr, id("tools", 1), 4); }); test("every tab carries its count", async () => { const page = await list(); expect(page).toMatch(/>\s*open 3<\/span>/); expect(page).toMatch(/>\s*awaiting reply 2<\/span>/); expect(page).toMatch(/>\s*closed 1<\/span>/); expect(page).toMatch(/>\s*hidden 0<\/span>/); }); test("awaiting reply is the open issues where the reporter spoke last", async () => { const page = await list("?filter=awaiting"); expect(page).toContain('href="/admin/issues/app/1"'); expect(page).toContain('href="/admin/issues/app/3"'); expect(page).not.toContain('href="/admin/issues/app/2"'); const rows = (await list()).split('class="issue-row"'); const row = (n: number) => rows.find((r) => r.includes(`/admin/issues/app/${n}"`)) ?? ""; expect(row(1)).toContain("awaiting reply"); expect(row(2)).not.toContain("awaiting reply"); }); test("narrows by repo, type and words in the title or text, keeping the form filled", async () => { const closedTools = await list("?filter=closed&repo=tools"); expect(closedTools).toContain('href="/admin/issues/tools/1"'); expect(closedTools).toContain('