const { test } = require('node:test'); const assert = require('node:assert/strict'); const fs = require('node:fs'); const path = require('node:path'); function read(relativePath) { return fs.readFileSync(path.join(__dirname, '..', relativePath), 'utf8'); } const source = read('public/js/learningHub.js'); const apiModule = read('public/js/learningHub/api.js'); const editorModule = read('public/js/learningHub/tiptapEditor.js'); const feedRendererModule = read('public/js/learningHub/feedRenderer.js'); const sanitizeModule = read('public/js/learningHub/sanitize.js'); test('Learning Hub destroys embedded Tiptap editors before removing CMS rows', () => { assert.match(source, /from '\.\/learningHub\/tiptapEditor\.js'/); assert.match(editorModule, /export function destroyTpEditor\(editor\)/); assert.match(editorModule, /export function destroyQuestionBlockEditors\(block\)/); assert.match(editorModule, /export function clearQuestionBlocks\(container\)/); assert.match(source, /destroyQuestionBlockEditors\(qb\); qb\.remove\(\)/); assert.match(source, /destroyOptionEditor\(row\); row\.remove\(\)/); assert.doesNotMatch(source, /document\.getElementById\('lh-cms-questions'\)\.innerHTML = ''/); assert.doesNotMatch(source, /qContainer\.innerHTML = ''/); }); test('Learning Hub sanitization helpers are isolated for renderer reuse', () => { assert.match(source, /from '\.\/learningHub\/sanitize\.js'/); assert.match(sanitizeModule, /export function esc\(str\)/); assert.match(sanitizeModule, /export function sanitizeHtml\(html\)/); assert.match(sanitizeModule, /DOMPurify\.sanitize/); assert.doesNotMatch(sanitizeModule, /ai-generate|ai-refine|FormData|\/api\/admin\/learning/); assert.doesNotMatch(source, /function sanitizeHtml\(html\)/); }); test('Learning Hub API helpers centralize auth JSON fetches', () => { assert.match(source, /from '\.\/learningHub\/api\.js'/); assert.match(apiModule, /export function getJson\(url\)/); assert.match(apiModule, /getAuthHeaders\(\)/); assert.match(apiModule, /export function sendJson\(url, method, payload\)/); assert.match(apiModule, /export function deleteJson\(url\)/); }); test('Learning Hub Tiptap helpers are isolated from content generation flow', () => { assert.match(editorModule, /export function makeTpEditor/); assert.match(editorModule, /window\.Tiptap/); assert.doesNotMatch(editorModule, /ai-generate|ai-refine|FormData|\/api\/admin\/learning/); }); test('Learning Hub feed renderers are isolated from API and editor flows', () => { assert.match(source, /from '\.\/learningHub\/feedRenderer\.js'/); assert.match(feedRendererModule, /export function renderCategoryPills\(categories\)/); assert.match(feedRendererModule, /export function renderFeed\(items, feedEl\)/); assert.match(feedRendererModule, /export function renderSearchHeader\(count, query\)/); assert.match(feedRendererModule, /export function renderEmptySearchMessage\(\)/); assert.match(feedRendererModule, /fa-presentation-screen/); assert.doesNotMatch(feedRendererModule, /fetch\(|getJson|sendJson|deleteJson|ai-generate|FormData|window\.Tiptap/); assert.doesNotMatch(source, /function renderFeed\(items, feedEl\)/); });