const { test } = require('node:test'); const assert = require('node:assert/strict'); const path = require('node:path'); const { pathToFileURL } = require('node:url'); const { JSDOM } = require('jsdom'); async function loadAiPanelController() { return import(pathToFileURL(path.join(__dirname, '..', 'public/js/learningHub/aiPanelController.js')).href); } function makeDom() { return new JSDOM(`
`); } function withGlobals(dom, fn) { const previousDocument = global.document; const previousWindow = global.window; const previousFetch = global.fetch; const previousGetAuthHeaders = global.getAuthHeaders; global.document = dom.window.document; global.window = dom.window; global.getAuthHeaders = function() { return {}; }; return Promise.resolve() .then(fn) .finally(function() { global.document = previousDocument; global.window = previousWindow; global.fetch = previousFetch; global.getAuthHeaders = previousGetAuthHeaders; }); } test('Learning Hub AI panel option state matches content type semantics', async () => { const { applyAiOptionState } = await loadAiPanelController(); const els = { wordsField: { style: {} }, slidesField: { style: {} }, quizRow: { style: {} }, cardTitle: { textContent: '' }, toggleLabel: { style: {} }, toggleCb: { checked: false, disabled: false }, countWrap: { style: {} } }; applyAiOptionState('article', els); assert.equal(els.wordsField.style.display, 'flex'); assert.equal(els.slidesField.style.display, 'none'); assert.equal(els.cardTitle.textContent, 'Quiz Questions (optional)'); assert.equal(els.countWrap.style.display, 'none'); els.toggleCb.checked = true; applyAiOptionState('presentation', els); assert.equal(els.wordsField.style.display, 'none'); assert.equal(els.slidesField.style.display, 'flex'); assert.equal(els.toggleCb.disabled, false); assert.equal(els.countWrap.style.display, 'flex'); els.toggleCb.checked = false; applyAiOptionState('quiz', els); assert.equal(els.wordsField.style.display, 'none'); assert.equal(els.slidesField.style.display, 'none'); assert.equal(els.cardTitle.textContent, 'Quiz Questions'); assert.equal(els.toggleLabel.style.display, 'none'); assert.equal(els.toggleCb.checked, true); assert.equal(els.toggleCb.disabled, true); assert.equal(els.countWrap.style.display, 'flex'); }); test('Learning Hub AI panel opens with defaults, models, auth state, and stable handlers', async () => { const dom = makeDom(); await withGlobals(dom, async function() { const { createAiPanelController } = await loadAiPanelController(); let modelBuilds = 0; let fetches = 0; const paths = []; const panel = document.getElementById('lh-ai-panel'); panel.scrollIntoView = function() {}; window._buildModelOptions = function(sel) { modelBuilds += 1; const opt = document.createElement('option'); opt.value = 'model-a'; opt.textContent = 'Model A'; sel.appendChild(opt); }; global.fetch = function(url) { fetches += 1; assert.equal(url, '/api/auth/me'); return Promise.resolve({ json: function() { return Promise.resolve({ user: { nextcloud_url: 'https://cloud.example', webdav_learning_path: '/Learning' } }); } }); }; const controller = createAiPanelController({ browseWebdav: function() {}, getEditorType: function() { return 'presentation'; }, getWebdavPath: function() { return '/Learning'; }, setWebdavPath: function(pathValue) { paths.push(pathValue); } }); controller.open(); controller.open(); await new Promise(function(resolve) { setImmediate(resolve); }); assert.equal(panel.classList.contains('hidden'), false); assert.equal(document.getElementById('lh-ai-ctype').value, 'presentation'); assert.equal(document.getElementById('lh-ai-words-field').style.display, 'none'); assert.equal(document.getElementById('lh-ai-slides-field').style.display, 'flex'); assert.equal(document.getElementById('lh-ai-tab-webdav').style.display, ''); assert.deepEqual(paths, ['/Learning', '/Learning']); assert.equal(modelBuilds, 1); assert.equal(fetches, 2); const ctype = document.getElementById('lh-ai-ctype'); ctype.value = 'quiz'; ctype.dispatchEvent(new window.Event('change')); assert.equal(document.getElementById('lh-ai-q-toggle').disabled, true); assert.equal(document.getElementById('lh-ai-quiz-card-title').textContent, 'Quiz Questions'); }); }); test('Learning Hub AI panel tab switch lazily browses empty WebDAV panel', async () => { const dom = makeDom(); await withGlobals(dom, async function() { const { createAiPanelController } = await loadAiPanelController(); const browsedPaths = []; const controller = createAiPanelController({ browseWebdav: function(pathValue) { browsedPaths.push(pathValue); }, getEditorType: function() { return 'article'; }, getWebdavPath: function() { return '/Learning/Sub'; }, setWebdavPath: function() {} }); controller.switchTab('webdav'); assert.equal(document.getElementById('lh-ai-tab-webdav').classList.contains('active'), true); assert.equal(document.getElementById('lh-ai-tp-webdav').classList.contains('hidden'), false); assert.deepEqual(browsedPaths, ['/Learning/Sub']); document.getElementById('lh-ai-webdav-list').innerHTML = ''; controller.switchTab('webdav'); assert.deepEqual(browsedPaths, ['/Learning/Sub']); }); });