62 lines
3.6 KiB
TypeScript
62 lines
3.6 KiB
TypeScript
import { expect } from "bun:test";
|
|
import { forEachDb } from "./helpers";
|
|
import { FIXTURES, makeApp } from "./app";
|
|
import { extraCourses } from "./courses";
|
|
|
|
forEachDb("library shows only visible courses, with formats and lengths", async (db) => {
|
|
const t = await makeApp(db, [FIXTURES, await extraCourses()]);
|
|
const anon = await (await t.call("GET", "/api/library")).json();
|
|
expect(anon.signedIn).toBe(false);
|
|
expect(anon.courses.map((c: { id: string }) => c.id)).toEqual(["sample"]);
|
|
const sample = anon.courses[0];
|
|
expect(sample.formats).toEqual(["text", "video", "slides", "exercises"]);
|
|
expect(sample.lessonCount).toBe(5);
|
|
expect(sample.minutes).toBeGreaterThan(14);
|
|
expect(anon.featured.id).toBe("sample");
|
|
|
|
const owner = await t.signUp("[email protected]");
|
|
const learner = await t.signUp("[email protected]");
|
|
const asOwner = await (await t.call("GET", "/api/library", { cookie: owner })).json();
|
|
expect(asOwner.courses.map((c: { id: string }) => c.id).sort()).toEqual(["gated", "sample", "secret"]);
|
|
const asLearner = await (await t.call("GET", "/api/library", { cookie: learner })).json();
|
|
expect(asLearner.courses.map((c: { id: string }) => c.id).sort()).toEqual(["gated", "sample"]);
|
|
// manifest collections become rows; restricted courses do not leak into them
|
|
expect(asLearner.collections).toEqual([
|
|
{ id: "devops", title: "DevOps", courseIds: ["gated"] },
|
|
{ id: "functional-programming", title: "Functional programming", courseIds: ["gated"] },
|
|
]);
|
|
expect((await t.call("GET", "/api/courses/secret", { cookie: learner })).status).toBe(404);
|
|
expect((await t.call("GET", "/api/courses/secret/lessons/s1", { cookie: learner })).status).toBe(404);
|
|
|
|
// continue row: after touching a lesson
|
|
await db.sql`INSERT INTO progress (user_id, course_id, lesson_id, state, position_seconds, updated_at)
|
|
SELECT id, 'gated', 'g1a', 'done', NULL, '2026-10-30T00:00:00Z' FROM auth_user WHERE email = '[email protected]'`;
|
|
const again = await (await t.call("GET", "/api/library", { cookie: learner })).json();
|
|
expect(again.continue).toEqual([{ courseId: "gated", lessonId: "g1b", lessonTitle: "Then this", moduleTitle: "First", moduleIndex: 0, moduleDone: 1, moduleTotal: 2, progress: 1 / 3 }]);
|
|
expect(again.featured.id).toBe("gated");
|
|
}, 30_000);
|
|
|
|
forEachDb("search matches titles, pitch and lesson titles", async (db) => {
|
|
const t = await makeApp(db, [FIXTURES, await extraCourses()]);
|
|
const hits = await (await t.call("GET", "/api/library/search?q=clip")).json();
|
|
expect(hits).toEqual([{ courseId: "sample", courseTitle: "Sample course", course: false, lessons: [{ id: "m1-video", title: "Watch the clip", moduleTitle: "First steps" }] }]);
|
|
const tiny = await (await t.call("GET", "/api/library/search?q=TINY")).json();
|
|
expect(tiny[0].course).toBe(true);
|
|
expect(await (await t.call("GET", "/api/library/search?q=")).json()).toEqual([]);
|
|
}, 30_000);
|
|
|
|
forEachDb("course outline carries gating locks and progress", async (db) => {
|
|
const t = await makeApp(db, [FIXTURES, await extraCourses()]);
|
|
await t.signUp("[email protected]");
|
|
const learner = await t.signUp("[email protected]");
|
|
const c = await (await t.call("GET", "/api/courses/gated", { cookie: learner })).json();
|
|
expect(c.modules.map((m: { locked: boolean }) => m.locked)).toEqual([false, true]);
|
|
expect(c.modules[1].lessons[0].hasDeck).toBe(true);
|
|
expect(c.canEdit).toBe(false);
|
|
expect(c.diagnostics).toEqual([]);
|
|
expect((await t.call("GET", "/api/courses/gated")).status).toBe(404); // signed-in only
|
|
const locked = await (await t.call("GET", "/api/courses/gated/lessons/g2a", { cookie: learner })).json();
|
|
expect(locked.locked).toBe(true);
|
|
expect(locked.markdown).toBeNull();
|
|
}, 30_000);
|