/** * Core regression: library, study feed, homework entry, review card, and the * desktop shell + settings panel. */ import { BASE, DECK, launchBrowser, makeChecker, makePage, seed } from "./lib.mjs"; const { check, finish } = makeChecker(); const browser = await launchBrowser(); /* 1. desktop shell --------------------------------------------------- */ { const { context, page } = await makePage(browser, { width: 1280, height: 900, deviceScaleFactor: 1 }); await seed(page, { decks: [DECK], folders: [{ id: "fold_1", name: "Biology", createdAt: 1 }] }); await page.goto(BASE, { waitUntil: "networkidle" }); await page.waitForTimeout(900); check("library title", await page.getByRole("heading", { name: "Your icebox" }).isVisible()); const sidebar = page.getByRole("complementary", { name: "Sidebar" }); for (const row of ["New deck", "Make my homework", "All decks", "Unorganized", "Biology", "Progress", "Exam mode", "Settings"]) { check(`sidebar row: ${row}`, (await sidebar.getByText(row, { exact: true }).count()) >= 1); } check("desktop hides the tab bar", !(await page.locator("nav.tabbar").isVisible())); check("desktop keeps the import button", await page.getByRole("button", { name: "Import JSON" }).isVisible()); await page.getByRole("button", { name: "Collapse sidebar" }).click(); await page.waitForTimeout(400); check("sidebar collapses", await page.getByRole("button", { name: "Expand sidebar" }).isVisible()); await page.getByRole("button", { name: "Expand sidebar" }).click(); await page.waitForTimeout(400); check("sidebar expands again", await page.getByRole("button", { name: "Collapse sidebar" }).isVisible()); // Settings from the sidebar footer. await sidebar.getByRole("button", { name: "Settings" }).click(); const dialog = page.getByRole("dialog"); await dialog.waitFor({ timeout: 6000 }); await page.waitForTimeout(700); check("settings: connection card", await dialog.getByText("Connection").isVisible()); check("settings: key field", await dialog.locator("#ollama-api-key").count() === 1); check("settings: learning section", await dialog.getByText("Learning").isVisible()); check("settings: data section", await dialog.getByText("Data & backup").isVisible()); check("settings: sync section", await dialog.getByText("Sync").first().isVisible()); // Health panel: it can take a while while the Ollama probe answers. const visionRow = dialog.getByText("Vision model"); await visionRow.waitFor({ timeout: 25_000 }).catch(() => {}); check("health panel present (vision row)", (await visionRow.count()) >= 1); check("text model row", (await dialog.getByText("Text model").count()) >= 1); await page.keyboard.press("Escape"); await page.waitForTimeout(500); await context.close(); } /* 2. library: tabs, deck card, menu, folders ------------------------- */ { const { context, page } = await makePage(browser); await seed(page, { decks: [DECK], folders: [{ id: "fold_1", name: "Biology", createdAt: 1 }] }); await page.goto(BASE, { waitUntil: "networkidle" }); await page.waitForTimeout(900); check("library tabs present", await page.getByRole("link", { name: "Study Decks", exact: true }).isVisible()); check("deck card title", await page.getByText("Sky deck").first().isVisible()); check("deck meta line", (await page.getByText(/2 activities/).count()) >= 1); check("folder pills visible with folders", (await page.locator(".folder-pill").count()) >= 1); check("review card has no exam link", (await page.getByRole("link", { name: /Exam mode/ }).count()) === 0); check("review card keeps due + mistakes", (await page.getByText(/Review due ·/).count()) >= 1 && (await page.getByText(/Mistakes ·/).count()) >= 1); await page.getByRole("button", { name: "Deck actions" }).click(); await page.waitForTimeout(300); for (const item of ["Rename", "Export JSON", "Share", "Regenerate deck", "Delete deck"]) { check(`deck menu: ${item}`, (await page.getByRole("button", { name: item, exact: true }).count()) >= 1); } await page.keyboard.press("Escape"); await page.waitForTimeout(400); await page.getByRole("link", { name: "Homework", exact: true }).click(); await page.waitForTimeout(600); check("homework tab empty state", await page.getByRole("link", { name: "Make my homework", exact: true }).isVisible()); await context.close(); } /* 3. study feed ------------------------------------------------------ */ { const { context, page } = await makePage(browser); await seed(page, { decks: [DECK] }); await page.goto(`${BASE}/deck/seed_deck`, { waitUntil: "networkidle" }); await page.waitForTimeout(900); const firstSlide = page.getByRole("region", { name: /Activity 1 of/ }); check("feed: first activity", await firstSlide.getByText("What colour is a clear daytime sky?").isVisible()); await page.getByRole("button", { name: "Green" }).click(); await page.waitForTimeout(500); check("feed: wrong answer shows the correct one", await page.getByText(/Correct answer/i).first().isVisible()); await page.getByRole("button", { name: "Next" }).click(); await page.waitForTimeout(800); await page.getByRole("button", { name: "Scattering" }).click(); await page.waitForTimeout(500); await page.getByRole("button", { name: "Next" }).click(); await page.waitForTimeout(900); check("feed: results appear", await page.getByText(/50%/).first().isVisible()); check("feed: results summary", (await page.getByText(/You got 1 of 2 right/).count()) >= 1); await context.close(); } /* 4. homework entry -------------------------------------------------- */ { const { context, page } = await makePage(browser); await seed(page, { decks: [] }); await page.goto(`${BASE}/homework`, { waitUntil: "networkidle" }); await page.waitForTimeout(800); check("homework: step headline", await page.getByRole("heading", { name: "The assignment" }).isVisible()); check("homework: empty placeholder", await page.getByText("Nothing added yet").isVisible()); await context.close(); } await browser.close(); finish();