const { test } = require('node:test'); const assert = require('node:assert/strict'); const fs = require('node:fs/promises'); const path = require('node:path'); const MarkdownIt = require('markdown-it'); let modulePromise; async function loadCitationModule() { if (!modulePromise) { modulePromise = fs.readFile(path.join(__dirname, '..', 'public', 'js', 'assistant', 'citations.js'), 'utf8') .then((source) => import('data:text/javascript;charset=utf-8,' + encodeURIComponent(source))); } return modulePromise; } const sources = [ { title: 'Nelson Textbook of Pediatrics' }, { title: 'Pediatric Asthma Guideline' }, { resource: 'Emergency Medicine Review' } ]; test('renders citation clusters as links to matching source cards', async () => { const { renderAssistantMarkdown } = await loadCitationModule(); const html = renderAssistantMarkdown('Give magnesium for severe exacerbation [1, 2].', sources); assert.match(html, /href="#assistant-source-1"/); assert.match(html, /href="#assistant-source-2"/); assert.match(html, /title="Source 1: Nelson Textbook of Pediatrics"/); assert.match(html, /data-source-number="1"/); assert.match(html, /data-source-number="2"/); assert.match(html, /]*>src<\/a> ]*>src<\/a>/); }); test('can render citation labels as numbers for PDF export', async () => { const { renderAssistantMarkdown } = await loadCitationModule(); const html = renderAssistantMarkdown('Give magnesium for severe exacerbation [1, 2].', sources, { citationLabel: 'number' }); assert.match(html, /]*>1<\/a> ]*>2<\/a>/); }); test('leaves unknown citation clusters untouched rather than guessing', async () => { const { renderAssistantMarkdown } = await loadCitationModule(); const html = renderAssistantMarkdown('Dose statement [4].', sources); assert.match(html, /\[4\]/); assert.doesNotMatch(html, /assistant-source-4/); }); test('does not merge separate citations across separate claims', async () => { const { renderAssistantMarkdown } = await loadCitationModule(); const html = renderAssistantMarkdown('Use oxygen [1]. Give bronchodilator [2].', sources); assert.match(html, /oxygen ]*data-source-number="1"[^>]*>src<\/a>/); assert.match(html, /bronchodilator ]*data-source-number="2"[^>]*>src<\/a>/); assert.doesNotMatch(html, /data-source-number="1"[^>]*>src<\/a>]*data-source-number="2"/); }); test('sorts adjacent citation tokens into one safe Vancouver cluster', async () => { const { renderAssistantMarkdown } = await loadCitationModule(); const html = renderAssistantMarkdown('Deteriorating course [1][4][2][3].', [ { title: 'A' }, { title: 'B' }, { title: 'C' }, { title: 'D' } ]); assert.match(html, /data-source-number="1"[^>]*>src<\/a> ]*data-source-number="2"[^>]*>src<\/a> ]*data-source-number="3"[^>]*>src<\/a> ]*data-source-number="4"[^>]*>src<\/a>/); assert.doesNotMatch(html, /\] { const { renderAssistantMarkdown } = await loadCitationModule(); const html = renderAssistantMarkdown('Deteriorating course [1][4][2][3', [ { title: 'A' }, { title: 'B' }, { title: 'C' }, { title: 'D' } ]); assert.match(html, /data-source-number="1"[^>]*>src<\/a> ]*data-source-number="2"[^>]*>src<\/a> ]*data-source-number="3"[^>]*>src<\/a> ]*data-source-number="4"[^>]*>src<\/a>/); }); test('does not normalize adjacent citations if any source number is unknown', async () => { const { renderAssistantMarkdown } = await loadCitationModule(); const html = renderAssistantMarkdown('Unsupported claim [1][9].', sources); assert.match(html, /\[9\]/); assert.doesNotMatch(html, /assistant-source-9/); }); test('escapes source titles inside citation link titles', async () => { const { renderAssistantMarkdown } = await loadCitationModule(); const html = renderAssistantMarkdown('Unsafe title [1].', [{ title: 'Bad "Title"