pediatric-ai-scribe-v3/test/learning-hub-editor.test.js
2026-05-08 15:47:10 +02:00

117 lines
7 KiB
JavaScript

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 cmsRendererModule = read('public/js/learningHub/cmsRenderer.js');
const editorModule = read('public/js/learningHub/tiptapEditor.js');
const feedRendererModule = read('public/js/learningHub/feedRenderer.js');
const quizRendererModule = read('public/js/learningHub/quizRenderer.js');
const sanitizeModule = read('public/js/learningHub/sanitize.js');
const viewerRendererModule = read('public/js/learningHub/viewerRenderer.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\)/);
});
test('Learning Hub CMS renderers are isolated from API and editor flows', () => {
assert.match(source, /from '\.\/learningHub\/cmsRenderer\.js'/);
assert.match(cmsRendererModule, /export function renderCmsCategoryList\(categories\)/);
assert.match(cmsRendererModule, /export function renderCmsContentRow\(item\)/);
assert.match(cmsRendererModule, /export function renderQuestionBlockShell\(existingQ, qNum\)/);
assert.match(cmsRendererModule, /export function renderOptionRowShell\(opt\)/);
assert.match(cmsRendererModule, /lh-cms-cat-filter/);
assert.match(cmsRendererModule, /selectedQuestionType\(existingQ, type\)/);
assert.doesNotMatch(cmsRendererModule, /fetch\(|getJson|sendJson|deleteJson|ai-generate|FormData|window\.Tiptap/);
});
test('Learning Hub preserves AI-created category selection before save', () => {
assert.match(source, /function loadCmsCategories\(editorCategoryId, filterCategoryId\)/);
assert.match(source, /var selectedEditorId = editorCategoryId !== undefined \? editorCategoryId : sel\.value/);
assert.match(source, /if \(selectedEditorId && hasSelectValue\(sel, selectedEditorId\)\) sel\.value = selectedEditorId/);
assert.match(source, /loadCmsCategories\(String\(data\.id\)\)/);
});
test('Learning Hub CMS sidebar categories filter content list', () => {
assert.match(source, /filterCmsContentByCategory\(cmsCat\.dataset\.id\)/);
assert.match(source, /function filterCmsContentByCategory\(categoryId\)/);
assert.match(source, /function setActiveCmsCat\(categoryId\)/);
assert.match(source, /item\.classList\.toggle\('active'/);
});
test('Learning Hub refreshes CMS category counts after content changes', () => {
assert.match(source, /saveQuestions\(contentId, questions, 0, function\(\) \{[\s\S]*?loadCmsContent\(\);[\s\S]*?loadCmsCategories\(\);[\s\S]*?loadCmsStats\(\);/);
assert.match(source, /showToast\('Content deleted', 'info'\);[\s\S]*?loadCmsContent\(\);[\s\S]*?loadCmsCategories\(\);[\s\S]*?loadCmsStats\(\);/);
});
test('Learning Hub slide modal cleans up reusable input handlers', () => {
assert.match(source, /function openSlideModal\(slides, css\) \{\s*closeSlideModal\(\);/);
assert.match(source, /if \(!modal \|\| !_previewSlides\.length\) \{ showToast\('No slides to display', 'error'\); return; \}/);
assert.match(source, /modal\.removeEventListener\('touchstart', modal\._touchStart\)/);
assert.match(source, /modal\.removeEventListener\('touchend', modal\._touchEnd\)/);
});
test('Learning Hub quiz renderers are isolated from API and submit flow', () => {
assert.match(source, /from '\.\/learningHub\/quizRenderer\.js'/);
assert.match(quizRendererModule, /export function renderQuizQuestions\(questions\)/);
assert.match(quizRendererModule, /export function renderQuizResultExplanations\(results\)/);
assert.match(quizRendererModule, /sanitizeHtml\(q\.question_text\)/);
assert.match(quizRendererModule, /sanitizeHtml\(r\.generalExplanation\)/);
assert.doesNotMatch(quizRendererModule, /fetch\(|getJson|sendJson|deleteJson|submitQuiz|showLoading|currentContent/);
});
test('Learning Hub viewer renderers are isolated from API and slide loading', () => {
assert.match(source, /from '\.\/learningHub\/viewerRenderer\.js'/);
assert.match(viewerRendererModule, /export function buildViewerMeta\(item\)/);
assert.match(viewerRendererModule, /export function renderPresentationCard\(item\)/);
assert.match(viewerRendererModule, /export function renderProgressList\(progress\)/);
assert.match(viewerRendererModule, /btn-lh-view-slides/);
assert.doesNotMatch(viewerRendererModule, /fetch\(|getJson|sendJson|deleteJson|openSlidesFromSlug|showViewer/);
});