108 lines
3.5 KiB
TypeScript
108 lines
3.5 KiB
TypeScript
import { describe, it, expect } from 'vitest'
|
|
import { processCitations } from '../../utils/citationParser'
|
|
import type { SourceMetadata } from '../../types'
|
|
|
|
const mockSources: SourceMetadata[] = [
|
|
{
|
|
filename: 'NEC4 ACC.pdf',
|
|
upload_date: '2024-01-15',
|
|
content_summary: 'Summary',
|
|
chunk_index: 0,
|
|
page_number: 3,
|
|
chunk_file_path: 'chunk_0.pdf',
|
|
},
|
|
{
|
|
filename: 'meeting_notes.docx',
|
|
upload_date: '2024-01-16',
|
|
content_summary: 'Minutes',
|
|
chunk_index: 1,
|
|
page_number: null,
|
|
chunk_file_path: 'chunk_1.pdf',
|
|
},
|
|
{
|
|
filename: 'report.pdf',
|
|
upload_date: '2024-01-17',
|
|
content_summary: 'Report',
|
|
chunk_index: 2,
|
|
page_number: 5,
|
|
chunk_file_path: 'chunk_2.pdf',
|
|
},
|
|
]
|
|
|
|
describe('processCitations', () => {
|
|
it('returns original text when no sources provided', () => {
|
|
const text = 'This has [NEC4 ACC.pdf, page 3] citation.'
|
|
expect(processCitations(text, [])).toBe(text)
|
|
})
|
|
|
|
it('replaces matched citation with markdown link', () => {
|
|
const text = 'Clause info [NEC4 ACC.pdf, page 3] is important.'
|
|
const result = processCitations(text, mockSources)
|
|
expect(result).toContain('](')
|
|
expect(result).toContain('/pdf-viewer')
|
|
expect(result).toMatch(/\[NEC4 ACC\.pdf, page 3\]\([^)]+\)/)
|
|
})
|
|
|
|
it('handles filename-only citation for DOCX (no page)', () => {
|
|
const text = 'Notes [meeting_notes.docx] from meeting.'
|
|
const result = processCitations(text, mockSources)
|
|
expect(result).toContain('/pdf-viewer')
|
|
expect(result).toContain('meeting_notes.docx')
|
|
})
|
|
|
|
it('leaves unmatched citations as plain text', () => {
|
|
const text = 'Unknown source [unknown_file.pdf, page 10] here.'
|
|
const result = processCitations(text, mockSources)
|
|
expect(result).toBe(text)
|
|
})
|
|
|
|
it('handles multiple citations in same text', () => {
|
|
const text = 'A [NEC4 ACC.pdf, page 3] and B [report.pdf, page 5].'
|
|
const result = processCitations(text, mockSources)
|
|
const linkCount = (result.match(/\[.+?\]\(/g) || []).length
|
|
expect(linkCount).toBe(2)
|
|
})
|
|
|
|
it('does not break existing markdown links', () => {
|
|
const text = 'See [label](http://example.com) and [NEC4 ACC.pdf, page 3].'
|
|
const result = processCitations(text, mockSources)
|
|
expect(result).toContain('[label](http://example.com)')
|
|
expect(result).toContain('/pdf-viewer')
|
|
})
|
|
|
|
it('does not break markdown images', () => {
|
|
const text = ' and [NEC4 ACC.pdf, page 3].'
|
|
const result = processCitations(text, mockSources)
|
|
expect(result).toContain('![diagram]')
|
|
expect(result).toContain('/pdf-viewer')
|
|
})
|
|
|
|
it('matches case-insensitively', () => {
|
|
const text = 'Cite [nec4 acc.pdf, page 3] lowercase.'
|
|
const result = processCitations(text, mockSources)
|
|
expect(result).toContain('/pdf-viewer')
|
|
})
|
|
|
|
it('leaves plain bracket text without matching source', () => {
|
|
const text = 'Some [plain bracket text] without source.'
|
|
const result = processCitations(text, mockSources)
|
|
expect(result).toBe(text)
|
|
})
|
|
|
|
it('skips sources without chunk_file_path', () => {
|
|
const sourcesWithoutPath = [
|
|
{
|
|
filename: 'no_path.pdf',
|
|
upload_date: '2024-01-18',
|
|
content_summary: 'Summary',
|
|
chunk_index: 0,
|
|
page_number: 1,
|
|
chunk_file_path: null,
|
|
},
|
|
]
|
|
const text = 'Source [no_path.pdf, page 1] missing path.'
|
|
const result = processCitations(text, sourcesWithoutPath)
|
|
expect(result).toBe(text)
|
|
})
|
|
})
|