Files

66 lines
4.7 KiB
TypeScript

import { expect } from "bun:test";
import { forEachDb } from "./helpers";
import { FIXTURES, makeApp } from "./app";
import { extraCourses } from "./courses";
forEachDb("progress: mark done, positions never undo done, gating blocks writes", async (db) => {
const t = await makeApp(db, [FIXTURES, await extraCourses()]);
await t.signUp("[email protected]");
const me = await t.signUp("[email protected]");
const put = (course: string, lesson: string, body: unknown, cookie = me) => t.call("PUT", `/api/courses/${course}/progress/${lesson}`, { cookie, body });
expect((await put("sample", "m1-intro", { state: "done" }, "")).status).toBe(401);
expect((await put("sample", "m1-intro", { state: "finished" })).status).toBe(400);
expect((await (await put("sample", "m1-video", { state: "in-progress", positionSeconds: 1.5 })).json()).positionSeconds).toBe(1.5);
await put("sample", "m1-video", { state: "done" });
const after = await (await put("sample", "m1-video", { state: "in-progress", positionSeconds: 0.5 })).json();
expect(after).toMatchObject({ state: "done", positionSeconds: 0.5 });
expect((await put("sample", "nope", { state: "done" })).status).toBe(404);
expect((await put("gated", "g2a", { state: "done" })).status).toBe(403);
await put("gated", "g1a", { state: "done" });
await put("gated", "g1b", { state: "done" });
expect((await put("gated", "g2a", { state: "done" })).status).toBe(200);
const all = await (await t.call("GET", "/api/courses/gated/progress", { cookie: me })).json();
expect(Object.keys(all).sort()).toEqual(["g1a", "g1b", "g2a"]);
}, 30_000);
forEachDb("notes persist per lesson with slide and video anchors", async (db) => {
const t = await makeApp(db);
const me = await t.signUp("[email protected]");
const other = await t.signUp("[email protected]");
const post = (b: unknown, cookie = me) => t.call("POST", "/api/courses/sample/notes", { cookie, body: b });
expect((await post({ lessonId: "m1-video", anchorKind: "slide", anchorValue: 2, body: "x" })).status).toBe(400); // no deck id
expect((await post({ lessonId: "m1-video", anchorKind: "video", body: "x" })).status).toBe(400);
const s10 = await (await post({ lessonId: "m1-video", anchorKind: "slide", anchorValue: 10, deckId: "d1", body: "slide ten" })).json();
await post({ lessonId: "m1-video", anchorKind: "slide", anchorValue: 2, deckId: "d1", body: "slide two" });
await post({ lessonId: "m1-video", anchorKind: "video", anchorValue: 1.25, body: "at 1.25s" });
await post({ lessonId: "m1-intro", body: "general" });
const list = await (await t.call("GET", "/api/courses/sample/notes?lesson=m1-video", { cookie: me })).json();
expect(list.map((n: { body: string }) => n.body)).toEqual(["slide two", "slide ten", "at 1.25s"]);
expect(list[0]).toMatchObject({ anchorKind: "slide", anchorValue: "2", deckId: "d1" });
expect(list[0].userId).toBeUndefined();
expect((await (await t.call("GET", "/api/courses/sample/notes", { cookie: me })).json()).length).toBe(4);
expect(await (await t.call("GET", "/api/courses/sample/notes", { cookie: other })).json()).toEqual([]);
expect((await t.call("PATCH", `/api/notes/${s10.id}`, { cookie: other, body: { body: "hijack" } })).status).toBe(404);
expect((await (await t.call("PATCH", `/api/notes/${s10.id}`, { cookie: me, body: { body: "edited" } })).json()).body).toBe("edited");
expect((await t.call("DELETE", `/api/notes/${s10.id}`, { cookie: other })).status).toBe(404);
expect((await t.call("DELETE", `/api/notes/${s10.id}`, { cookie: me })).status).toBe(200);
expect((await (await t.call("GET", "/api/courses/sample/notes?lesson=m1-video", { cookie: me })).json()).length).toBe(2);
}, 30_000);
forEachDb("exercise attempts save code and a pass marks the lesson done", async (db) => {
const t = await makeApp(db);
const me = await t.signUp("[email protected]");
const url = "/api/courses/sample/lessons/m2-ex-add/attempt";
expect(await (await t.call("GET", `${url}?track=js`, { cookie: me })).json()).toBeNull();
const r1 = await (await t.call("PUT", url, { cookie: me, body: { track: "js", code: "draft", passed: false, result: [{ name: "a", passed: false }] } })).json();
expect(r1).toEqual({ ok: true, passed: false, state: "in-progress" });
const r2 = await (await t.call("PUT", url, { cookie: me, body: { track: "js", code: "export const add=(a,b)=>a+b", passed: true, result: [] } })).json();
expect(r2.state).toBe("done");
const got = await (await t.call("GET", `${url}?track=js`, { cookie: me })).json();
expect(got).toMatchObject({ code: "export const add=(a,b)=>a+b", passed: true });
expect((await t.call("PUT", "/api/courses/sample/lessons/m1-intro/attempt", { cookie: me, body: { code: "x" } })).status).toBe(400);
}, 30_000);