const test = require('node:test'); const assert = require('node:assert/strict'); const fs = require('node:fs'); const path = require('node:path'); const vm = require('node:vm'); const { JSDOM } = require('jsdom'); const { marked } = require('marked'); const read = file => fs.readFileSync(path.join(__dirname, '..', file), 'utf8'); function ui(t) { const dom = new JSDOM('
' + read('public/components/assistant.html') + '
', { url: 'https://example.test', runScripts: 'outside-only' }); const window = dom.window; window.eval(read('public/js/accountBoundary.js')); assert.equal(window.AccountBoundary.enter({ id: 'synthetic-rendering-owner' }, true), true); window.marked = marked; window.DOMPurify = require('dompurify')(window); window.matchMedia = () => ({ matches: true }); const toasts = []; const context = { window, document: window.document, console, URL, Blob, TextDecoder, AbortController, setTimeout() {}, clearTimeout() {}, showToast: (...args) => toasts.push(args), EMPTY_PROMPT_SETS: [[]], createAssistantImageStore: () => ({ clear() {}, renderGeneratedImage: () => '' }), fetchSavedAssistantChats: async () => ({ success: true, chats: [] }), saveAssistantChat: async () => ({ success: true }) }; vm.createContext(context); for (const file of ['assistant/citations.js', 'assistant/sources.js', 'assistant/sharing.js', 'generatedImages.js', 'assistant/export.js', 'clinicalAssistant.js']) { vm.runInContext(read('public/js/' + file).replace(/^import[\s\S]*?from ['"][^'"]+['"];\s*/gm, '').replace(/^export /gm, ''), context); } appBinding(t, context); t.after(() => window.close()); return { context, document: window.document, window, toasts }; } // The production page binds delegated clicks in bindEvents; the vm harness mirrors it. function appBinding(t, context) { context.bindEvents(); } const longExcerpt = 'Synthetic excerpt. ' + 'x'.repeat(15000); function openModal(app, n = 1) { const cite = app.document.querySelector('.assistant-cite[data-source-number="' + n + '"]'); assert.ok(cite, 'citation chip exists'); cite.click(); return app.document.querySelector('.assistant-source-modal'); } test('citation chips open an OWUI-style source modal with title, page and 10k excerpt preview', t => { const app = ui(t); const sources = [{ number: 1, title: 'Nelson Textbook of Pediatrics', page: 7, excerpt: longExcerpt }]; app.context.restoreSavedChat({ version: 2, messages: [ { role: 'user', content: 'Question' }, { role: 'assistant', content: 'Answer [1].', sources } ], lastAnswer: 'Answer [1].', sources }); const modal = openModal(app); assert.ok(modal, 'modal opens'); assert.equal(modal.getAttribute('role'), 'dialog'); assert.match(modal.textContent, /Nelson Textbook of Pediatrics/); assert.match(modal.textContent, /page 7/); const excerpt = modal.querySelector('[data-assistant-source-excerpt]'); assert.ok(excerpt); assert.ok(excerpt.textContent.length >= 10000 && excerpt.textContent.length <= 10200, '10k preview shown'); const showAll = modal.querySelector('[data-assistant-source-show-all]'); assert.ok(showAll, 'Show all offered for long excerpts'); showAll.click(); assert.ok(excerpt.textContent.length >= 15000, 'Show all reveals the full excerpt'); assert.equal(showAll.hidden, true, 'toggle hides once expanded'); // Sidebar switching is preserved alongside the modal. assert.match(app.document.querySelector('#assistant-source-1').textContent, /Nelson Textbook of Pediatrics.*page 7/is); }); test('source modal closes via button, backdrop and Escape', t => { const app = ui(t); const sources = [{ number: 1, title: 'Synthetic source', page: 3, excerpt: 'Short excerpt.' }]; app.context.restoreSavedChat({ version: 2, messages: [ { role: 'user', content: 'Question' }, { role: 'assistant', content: 'Answer [1].', sources } ], lastAnswer: 'Answer [1].', sources }); let modal = openModal(app); assert.equal(modal.querySelector('[data-assistant-source-show-all]'), null, 'no Show all for short excerpts'); modal.querySelector('.modal-close').click(); assert.equal(app.document.querySelector('.assistant-source-modal'), null); modal = openModal(app); modal.click(); // backdrop is the modal element itself assert.equal(app.document.querySelector('.assistant-source-modal'), null); modal = openModal(app); app.window.document.dispatchEvent(new app.window.KeyboardEvent('keydown', { key: 'Escape', bubbles: true })); assert.equal(app.document.querySelector('.assistant-source-modal'), null); }); test('per-turn source maps select the correct source for reused citation numbers', t => { const app = ui(t); const first = [{ number: 1, title: 'First turn reference', page: 11, excerpt: 'First turn context.' }]; const second = [{ number: 1, title: 'Second turn reference', page: 23, excerpt: 'Second turn context.' }]; app.context.restoreSavedChat({ version: 2, messages: [ { role: 'user', content: 'First question' }, { role: 'assistant', content: 'First answer. [1]', sources: first }, { role: 'user', content: 'Second question' }, { role: 'assistant', content: 'Second answer. [1]', sources: second } ], lastAnswer: 'Second answer. [1]', sources: second }); const citations = app.document.querySelectorAll('#assistant-messages .assistant-cite'); assert.equal(citations.length, 2); citations[0].click(); assert.match(app.document.querySelector('.assistant-source-modal').textContent, /First turn reference/); assert.match(app.document.querySelector('.assistant-source-modal').textContent, /page 11/); app.document.querySelector('.assistant-source-modal .modal-close').click(); citations[1].click(); const modal = app.document.querySelector('.assistant-source-modal'); assert.match(modal.textContent, /Second turn reference/); assert.match(modal.textContent, /page 23/); assert.doesNotMatch(modal.textContent, /First turn reference/); });