feat(learner): library, search, course outline with gating, lesson payload API
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
a8a8e32bcb
commit
5f85ce1d4b
10 files changed
+699
-1
No files matched your search
@@ -0,0 +1,21 @@
|
||||
import { expect, test } from "bun:test";
|
||||
import { parseManifest } from "@stc/course-format";
|
||||
import { moduleLocks } from "../src/server/gating";
|
||||
|
||||
const m = (gating: string) => {
|
||||
const r = parseManifest(`schema: 1\nid: g\ntitle: G\ngating: ${gating}\nmodules:\n - { id: a, title: A, lessons: [{ id: a1, title: A1, type: exercise }, { id: a2, title: A2, type: exercise }] }\n - { id: b, title: B, lessons: [{ id: b1, title: B1, type: exercise }] }\n - { id: c, title: C, lessons: [{ id: c1, title: C1, type: exercise }] }\n`);
|
||||
if (!r.ok) throw new Error(r.errors.join());
|
||||
return r.manifest;
|
||||
};
|
||||
|
||||
test("no gating unlocks everything", () => {
|
||||
expect(moduleLocks(m("none"), {})).toEqual([false, false, false]);
|
||||
});
|
||||
test("sequential gating locks until the previous module is done", () => {
|
||||
expect(moduleLocks(m("sequential"), {})).toEqual([false, true, true]);
|
||||
expect(moduleLocks(m("sequential"), { a1: { state: "done" } })).toEqual([false, true, true]);
|
||||
expect(moduleLocks(m("sequential"), { a1: { state: "done" }, a2: { state: "done" } })).toEqual([false, false, true]);
|
||||
// done later modules do not unlock past an unfinished one
|
||||
expect(moduleLocks(m("sequential"), { a1: { state: "done" }, a2: { state: "done" }, c1: { state: "done" } })).toEqual([false, false, true]);
|
||||
expect(moduleLocks(m("sequential"), {}, true)).toEqual([false, false, false]);
|
||||
});
|
||||
Reference in new issue
Block a user