Files
code-crispies/tests/unit/pathManager-start-pause-resume.test.js
Michael Czechowski 6c65381fcb feat: add Guided Learning Paths feature
Implement PathManager to orchestrate multi-module learning journeys:
- Add PathManager class with start/pause/resume functionality
- Create learning-paths.json config with CSS Fundamentals path
- Integrate path progress tracking with LessonEngine
- Add path selection UI to homepage and navigation
- Include JSON schema for learning path validation
- Add comprehensive test suite for PathManager
2026-01-12 20:30:09 +01:00

198 lines
6.3 KiB
JavaScript

/**
* Tests for PathManager start/pause/resume functionality
*/
import { describe, it, expect, beforeEach } from "vitest";
import { PathManager } from "../../src/impl/PathManager.js";
describe("PathManager - Start/Pause/Resume/GetActivePath", () => {
let pathManager;
let mockPaths;
beforeEach(() => {
// Clear localStorage before each test
localStorage.clear();
// Create mock paths
mockPaths = [
{
id: "css-fundamentals",
title: "CSS Fundamentals",
modules: [
{ id: "basic-selectors", lessons: [{}, {}, {}] }
],
estimatedTime: 60
},
{
id: "flexbox-master",
title: "Flexbox Master",
modules: [
{ id: "flex-basics", lessons: [{}, {}] }
],
estimatedTime: 90
}
];
// Create fresh PathManager instance
pathManager = new PathManager();
pathManager.setPaths(mockPaths);
});
describe("getActivePath()", () => {
it("should return null when no path is active", () => {
const result = pathManager.getActivePath();
expect(result).toBeNull();
});
it("should return the active path object after starting a path", () => {
pathManager.startPath("css-fundamentals");
const result = pathManager.getActivePath();
expect(result).not.toBeNull();
expect(result.id).toBe("css-fundamentals");
expect(result.title).toBe("CSS Fundamentals");
});
it("should return null after pausing", () => {
pathManager.startPath("css-fundamentals");
pathManager.pausePath();
const result = pathManager.getActivePath();
expect(result).toBeNull();
});
});
describe("startPath(pathId)", () => {
it("should activate a path and return true", () => {
const result = pathManager.startPath("css-fundamentals");
expect(result).toBe(true);
expect(pathManager.getActivePath()).not.toBeNull();
});
it("should return false for non-existent path", () => {
const result = pathManager.startPath("non-existent");
expect(result).toBe(false);
expect(pathManager.getActivePath()).toBeNull();
});
it("should initialize progress for new path", () => {
pathManager.startPath("css-fundamentals");
const progress = pathManager.getPathProgress("css-fundamentals");
expect(progress.startTimestamp).not.toBeNull();
expect(progress.isStarted).toBe(true);
});
it("should switch active path when starting a different path", () => {
pathManager.startPath("css-fundamentals");
expect(pathManager.getActivePath().id).toBe("css-fundamentals");
pathManager.startPath("flexbox-master");
expect(pathManager.getActivePath().id).toBe("flexbox-master");
});
it("should persist active path to localStorage", () => {
pathManager.startPath("css-fundamentals");
const saved = JSON.parse(localStorage.getItem("codeCrispies.pathProgress"));
expect(saved.activePathId).toBe("css-fundamentals");
});
});
describe("pausePath()", () => {
it("should deactivate the current path and return true", () => {
pathManager.startPath("css-fundamentals");
const result = pathManager.pausePath();
expect(result).toBe(true);
expect(pathManager.getActivePath()).toBeNull();
});
it("should return false when no path is active", () => {
const result = pathManager.pausePath();
expect(result).toBe(false);
});
it("should update lastActivityTimestamp before pausing", () => {
pathManager.startPath("css-fundamentals");
const progressBefore = pathManager.getPathProgress("css-fundamentals");
const timestampBefore = progressBefore.lastActivityTimestamp;
// Small delay to ensure timestamp changes
const now = new Date().toISOString();
pathManager.pausePath();
const progressAfter = pathManager.getPathProgress("css-fundamentals");
expect(progressAfter.lastActivityTimestamp).toBeTruthy();
});
it("should persist inactive state to localStorage", () => {
pathManager.startPath("css-fundamentals");
pathManager.pausePath();
const saved = JSON.parse(localStorage.getItem("codeCrispies.pathProgress"));
expect(saved.activePathId).toBeNull();
});
});
describe("resumePath(pathId)", () => {
it("should reactivate a previously started path and return true", () => {
pathManager.startPath("css-fundamentals");
pathManager.pausePath();
const result = pathManager.resumePath("css-fundamentals");
expect(result).toBe(true);
expect(pathManager.getActivePath().id).toBe("css-fundamentals");
});
it("should return false for a path that was never started", () => {
const result = pathManager.resumePath("flexbox-master");
expect(result).toBe(false);
});
it("should return false for non-existent path", () => {
const result = pathManager.resumePath("non-existent");
expect(result).toBe(false);
});
it("should update lastActivityTimestamp when resuming", () => {
pathManager.startPath("css-fundamentals");
const timestampBefore = pathManager.getPathProgress("css-fundamentals").lastActivityTimestamp;
pathManager.pausePath();
pathManager.resumePath("css-fundamentals");
const timestampAfter = pathManager.getPathProgress("css-fundamentals").lastActivityTimestamp;
expect(timestampAfter).toBeTruthy();
});
it("should persist resumed state to localStorage", () => {
pathManager.startPath("css-fundamentals");
pathManager.pausePath();
pathManager.resumePath("css-fundamentals");
const saved = JSON.parse(localStorage.getItem("codeCrispies.pathProgress"));
expect(saved.activePathId).toBe("css-fundamentals");
});
});
describe("Active path state - Only one path active at a time", () => {
it("should only allow one active path at a time", () => {
pathManager.startPath("css-fundamentals");
expect(pathManager.getActivePath().id).toBe("css-fundamentals");
pathManager.startPath("flexbox-master");
expect(pathManager.getActivePath().id).toBe("flexbox-master");
// Only flexbox-master should be active
const activePath = pathManager.getActivePath();
expect(activePath.id).toBe("flexbox-master");
});
it("should store active path state separately from progress", () => {
pathManager.startPath("css-fundamentals");
const saved = JSON.parse(localStorage.getItem("codeCrispies.pathProgress"));
// Active path ID stored separately
expect(saved).toHaveProperty("activePathId");
expect(saved.activePathId).toBe("css-fundamentals");
// Progress data stored separately
expect(saved).toHaveProperty("pathProgress");
expect(saved.pathProgress).toHaveProperty("css-fundamentals");
});
});
});