import { expect } from "bun:test"; import { forEachDb } from "./helpers"; import { FIXTURES, makeApp } from "./app"; import { extraCourses } from "./courses"; forEachDb("lesson payloads for every sample lesson type", async (db) => { const t = await makeApp(db); const get = async (id: string, cookie?: string) => (await t.call("GET", `/api/courses/sample/lessons/${id}`, { cookie })).json(); const intro = await get("m1-intro"); expect(intro.markdown).toContain("Welcome to the sample course"); expect(intro.prev).toBeNull(); expect(intro.next).toEqual({ id: "m1-video", title: "Watch the clip" }); expect(intro.code).toEqual({ moduleId: "m1", dir: "code/S01 - First steps", command: "sh hello.sh" }); expect(intro.lessonFiles["lessons/01-intro.md"]).toBe("m1-intro"); const video = await get("m1-video"); expect(video.video).toEqual({ url: "/api/media/sample/media/clip.mp4", file: "media/clip.mp4", duration: 2 }); expect(video.transcript).toContain("test pattern"); expect(video.decks).toEqual([{ id: "d1", title: "deck", url: "/api/media/sample/media/deck.pdf", attachTo: "m1-video" }]); const ex = await get("m2-ex-add"); expect(ex.markdown?.startsWith("### Exercise 2.1")).toBe(true); expect(ex.markdown).not.toContain("Hints"); expect(ex.hints.map((h: { label: string }) => h.label)).toEqual(["nudge", "near-solution"]); expect(ex.browser.language).toBe("js"); expect(ex.browser.starter).toContain("a - b"); expect(ex.browser.tests).toContain("export const tests"); expect(ex.browser.solution).toBeNull(); // not an author expect(ex.localCommand).toBe("node --test"); const owner = await t.signUp("owner@example.com"); expect((await get("m2-ex-add", owner)).browser.solution).toContain("a + b"); expect((await t.call("GET", "/api/courses/sample/lessons/nope")).status).toBe(404); }, 30_000); forEachDb("module decks and attachments with non-ASCII paths", async (db) => { const t = await makeApp(db, [FIXTURES, await extraCourses()]); const owner = await t.signUp("owner@example.com"); const l = await (await t.call("GET", "/api/courses/gated/lessons/g2a", { cookie: owner })).json(); expect(l.locked).toBe(false); // owners bypass gating expect(l.decks[0].url).toBe("/api/media/gated/%C3%86ble%20slides/deck%20one.pdf"); const first = await (await t.call("GET", "/api/courses/gated/lessons/g1a", { cookie: owner })).json(); expect(first.attachments).toEqual([{ title: "Cheat sheet", file: "Æble slides/cheat sheet.pdf", url: "/api/media/gated/%C3%86ble%20slides/cheat%20sheet.pdf", size: 14 }]); const media = await t.call("GET", first.attachments[0].url, { cookie: owner }); expect(media.status).toBe(200); }, 30_000);