import { beforeAll, describe, expect, test } from "bun:test"; import { join } from "node:path"; import { BucketWriteError, openBucket } from "../src/bucket/reader.ts"; import { listRepos } from "../src/bucket/repo.ts"; import { Browser, type Instance, instance } from "./browser.ts"; import { makeRepos } from "./fixture.ts"; const PNG = new Uint8Array([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 1, 2, 3]); const MP4 = new Uint8Array([0, 0, 0, 0x18, ...new TextEncoder().encode("ftypisom"), 1, 2, 3, 4]); describe("editing a repo's description and details while signed in", () => { let inst: Instance; let browser: Browser; let bucketDir: string; const page = async (path: string) => (await inst.app.request(path)).text(); const save = (field: string, value: string) => browser.post("/admin/repos/app/text", { field, value }); const upload = (bytes: Uint8Array, name: string) => { const data = new FormData(); data.append("file", new File([bytes], name)); return browser.post("/admin/repos/app/media", data); }; beforeAll(async () => { const repos = await makeRepos(["app", "shy"]); bucketDir = repos.bucketDir; inst = await instance(bucketDir); inst.kepr.run("UPDATE repos SET public = 1 WHERE name = 'app'"); browser = new Browser(inst.app); }); test("signing in leaves a script-readable mark, the form token, beside the session", async () => { const response = await browser.signIn(); const cookies = response.headers.getSetCookie(); expect(cookies.find((line) => line.startsWith("kepr_editor="))).toMatch( /^kepr_editor=[0-9a-f]{64};/, ); expect(cookies.find((line) => line.startsWith("kepr_editor="))).not.toMatch(/HttpOnly/i); expect(cookies.find((line) => line.startsWith("kepr_session="))).toMatch(/HttpOnly/i); // The public page is the same for everyone, signed in or not. expect(await (await browser.request("/app")).text()).toBe(await page("/app")); }); test("starts from the README's words", async () => { const response = await browser.request("/admin/repos/app/text"); const texts = (await response.json()) as Record<"form" | "description" | "details", string>; expect(texts.description).toBe("The app project."); expect(texts.details).toContain("# app"); // The same token the console's forms carry, so the editor's posts pass the guard. expect(texts.form).toBe(browser.form); }); test("the operator's description replaces the README's on the page, in cards and previews", async () => { expect((await save("description", "My **own** words.")).status).toBe(204); const repo = await page("/app"); expect(repo).toContain('
My own words.
My own words.
'); }); test("details size and place pictures and videos from their options, and keep uploads' URLs", async () => { const details = [ '', '', "", '', ].join("\n\n"); expect((await save("details", details)).status).toBe(204); const repo = await page("/app"); expect(repo).toContain( '
',
);
expect(repo).toContain(
'',
);
expect(repo).toContain(
'',
);
// A title that is not options stays a title, and a root path is still the repo's own file.
expect(repo).toContain('
');
expect(repo).not.toContain("# app");
});
test("the preview renders as the page will", async () => {
const response = await browser.post("/admin/repos/app/preview", {
field: "details",
value: "**hi** ",
});
expect(await response.text()).toBe(
"hi <script>x</script>
\n", ); }); test("clearing a text brings the README's back", async () => { await save("description", ""); await save("details", ""); const repo = await page("/app"); expect(repo).toContain("The app project.
"); expect(repo).toContain("