Implementation following plan: - S01: Foundation: schema, section config, and router - S02: Install CodeMirror JavaScript language support - S03: Create JavaScript lesson JSON files (variables, DOM, events) - S04: Register JavaScript lessons in module stores - S05: Add JavaScript validation logic - S06: Add JavaScript mode to LessonEngine preview rendering - S07: Add JavaScript mode to CodeEditor - S08: Update app.js for JavaScript mode support - S09: Update navigation HTML and CSS theming for JavaScript section - S10: Add section grouping headers in sidebar navigation - S11: Update and write tests
177 lines
5.6 KiB
JavaScript
177 lines
5.6 KiB
JavaScript
import { describe, test, expect } from "vitest";
|
|
import { sections, getSection, getSectionList, getModuleSection, getModulesBySection } from "../../src/config/sections.js";
|
|
|
|
describe("Sections Config", () => {
|
|
describe("sections constant", () => {
|
|
test("sections_AllDefined_HasFiveSections", () => {
|
|
expect(Object.keys(sections)).toHaveLength(5);
|
|
expect(sections).toHaveProperty("css");
|
|
expect(sections).toHaveProperty("html");
|
|
expect(sections).toHaveProperty("tailwind");
|
|
expect(sections).toHaveProperty("markdown");
|
|
expect(sections).toHaveProperty("javascript");
|
|
});
|
|
|
|
test("sections_EachSection_HasRequiredFields", () => {
|
|
for (const [key, section] of Object.entries(sections)) {
|
|
expect(section.id).toBe(key);
|
|
expect(section.title).toBeTruthy();
|
|
expect(section.description).toBeTruthy();
|
|
expect(section.color).toMatch(/^#[0-9a-f]{6}$/);
|
|
expect(typeof section.order).toBe("number");
|
|
}
|
|
});
|
|
});
|
|
|
|
describe("getSection", () => {
|
|
test.each([
|
|
["css", "CSS"],
|
|
["html", "HTML"],
|
|
["tailwind", "Tailwind CSS"],
|
|
["markdown", "Markdown"],
|
|
["javascript", "JavaScript"]
|
|
])("getSection_%s_ReturnsCorrectSection", (id, expectedTitle) => {
|
|
const section = getSection(id);
|
|
expect(section).not.toBeNull();
|
|
expect(section.id).toBe(id);
|
|
expect(section.title).toBe(expectedTitle);
|
|
});
|
|
|
|
test("getSection_NonExistentId_ReturnsNull", () => {
|
|
expect(getSection("nonexistent")).toBeNull();
|
|
});
|
|
|
|
test("getSection_Undefined_ReturnsNull", () => {
|
|
expect(getSection(undefined)).toBeNull();
|
|
});
|
|
|
|
test("getSection_EmptyString_ReturnsNull", () => {
|
|
expect(getSection("")).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe("getSectionList", () => {
|
|
test("getSectionList_Default_ReturnsSortedByOrder", () => {
|
|
const list = getSectionList();
|
|
expect(list).toHaveLength(5);
|
|
|
|
// Verify sorted by order
|
|
for (let i = 1; i < list.length; i++) {
|
|
expect(list[i].order).toBeGreaterThan(list[i - 1].order);
|
|
}
|
|
});
|
|
|
|
test("getSectionList_Default_CSSIsFirst", () => {
|
|
const list = getSectionList();
|
|
expect(list[0].id).toBe("css");
|
|
});
|
|
|
|
test("getSectionList_Default_JavaScriptIsLast", () => {
|
|
const list = getSectionList();
|
|
expect(list[list.length - 1].id).toBe("javascript");
|
|
});
|
|
|
|
test("getSectionList_Default_ContainsAllSections", () => {
|
|
const list = getSectionList();
|
|
const ids = list.map((s) => s.id);
|
|
expect(ids).toContain("css");
|
|
expect(ids).toContain("html");
|
|
expect(ids).toContain("tailwind");
|
|
expect(ids).toContain("markdown");
|
|
expect(ids).toContain("javascript");
|
|
});
|
|
});
|
|
|
|
describe("getModuleSection", () => {
|
|
test("getModuleSection_ExplicitSection_UsesExplicitValue", () => {
|
|
const module = { mode: "css", section: "html" };
|
|
expect(getModuleSection(module)).toBe("html");
|
|
});
|
|
|
|
test.each([
|
|
["css", "css"],
|
|
["html", "html"],
|
|
["tailwind", "tailwind"],
|
|
["markdown", "markdown"],
|
|
["javascript", "javascript"]
|
|
])("getModuleSection_Mode%s_InfersCorrectSection", (mode, expectedSection) => {
|
|
const module = { mode };
|
|
expect(getModuleSection(module)).toBe(expectedSection);
|
|
});
|
|
|
|
test("getModuleSection_NoMode_DefaultsToCss", () => {
|
|
expect(getModuleSection({})).toBe("css");
|
|
});
|
|
|
|
test("getModuleSection_UndefinedMode_DefaultsToCss", () => {
|
|
expect(getModuleSection({ mode: undefined })).toBe("css");
|
|
});
|
|
|
|
test("getModuleSection_UnknownMode_DefaultsToCss", () => {
|
|
expect(getModuleSection({ mode: "unknown-mode" })).toBe("css");
|
|
});
|
|
|
|
test("getModuleSection_ExplicitSectionOverridesMode_UsesSection", () => {
|
|
const module = { mode: "html", section: "tailwind" };
|
|
expect(getModuleSection(module)).toBe("tailwind");
|
|
});
|
|
});
|
|
|
|
describe("getModulesBySection", () => {
|
|
const testModules = [
|
|
{ id: "css-basics", mode: "css" },
|
|
{ id: "flexbox", mode: "css" },
|
|
{ id: "html-elements", mode: "html" },
|
|
{ id: "tailwind-intro", mode: "tailwind" },
|
|
{ id: "markdown-basics", mode: "markdown" },
|
|
{ id: "welcome", mode: "css", excludeFromProgress: true },
|
|
{ id: "playground", mode: "css", excludeFromProgress: true }
|
|
];
|
|
|
|
test("getModulesBySection_Css_ReturnsCssModules", () => {
|
|
const result = getModulesBySection(testModules, "css");
|
|
expect(result).toHaveLength(2);
|
|
expect(result.map((m) => m.id)).toEqual(["css-basics", "flexbox"]);
|
|
});
|
|
|
|
test("getModulesBySection_Html_ReturnsHtmlModules", () => {
|
|
const result = getModulesBySection(testModules, "html");
|
|
expect(result).toHaveLength(1);
|
|
expect(result[0].id).toBe("html-elements");
|
|
});
|
|
|
|
test("getModulesBySection_Tailwind_ReturnsTailwindModules", () => {
|
|
const result = getModulesBySection(testModules, "tailwind");
|
|
expect(result).toHaveLength(1);
|
|
expect(result[0].id).toBe("tailwind-intro");
|
|
});
|
|
|
|
test("getModulesBySection_ExcludesFromProgress_FiltersOut", () => {
|
|
const result = getModulesBySection(testModules, "css");
|
|
const ids = result.map((m) => m.id);
|
|
expect(ids).not.toContain("welcome");
|
|
expect(ids).not.toContain("playground");
|
|
});
|
|
|
|
test("getModulesBySection_EmptyModules_ReturnsEmptyArray", () => {
|
|
const result = getModulesBySection([], "css");
|
|
expect(result).toEqual([]);
|
|
});
|
|
|
|
test("getModulesBySection_NonExistentSection_ReturnsEmptyArray", () => {
|
|
const result = getModulesBySection(testModules, "nonexistent");
|
|
expect(result).toEqual([]);
|
|
});
|
|
|
|
test("getModulesBySection_ExplicitSectionOverride_IncludesModule", () => {
|
|
const modules = [
|
|
{ id: "special", mode: "css", section: "html" },
|
|
{ id: "normal-html", mode: "html" }
|
|
];
|
|
const result = getModulesBySection(modules, "html");
|
|
expect(result).toHaveLength(2);
|
|
expect(result.map((m) => m.id)).toContain("special");
|
|
});
|
|
});
|
|
});
|