import { describe, expect, test } from 'vitest'; import { __testing } from '@/app/reader/components/ReviewModeController'; import type { BookDoc } from '@/libs/document'; describe('ReviewModeController marks', () => { test('marks bilingual source and target paragraphs by file and index', () => { const doc = document.implementation.createHTMLDocument('chapter'); doc.body.innerHTML = [ '
原文一
', '译文一
', '原文二
', '译文二
', ].join(''); __testing.applyReviewMarks( doc, 'OEBPS/Text/chapter.xhtml', [ { id: 'R00002', file: 'Text/chapter.xhtml', ja_p_index: 2, cn_p_index: 3, jp_html: '原文二', jp_text: '原文二', cn_html: '译文二', current_html: '新译文二', }, ], 'R00002', ); const paragraphs = Array.from(doc.querySelectorAll('p')); expect(paragraphs[2]?.getAttribute('data-readest-review-row-id')).toBe('R00002'); expect(paragraphs[2]?.classList.contains('readest-review-source')).toBe(true); expect(paragraphs[3]?.getAttribute('data-readest-review-row-id')).toBe('R00002'); expect(paragraphs[3]?.classList.contains('readest-review-target')).toBe(true); expect(paragraphs[3]?.innerHTML).toBe('新译文二'); }); test('uses section id when foliate sections do not expose href', () => { const bookDoc = { sections: [{ id: 'OEBPS/Text/chapter.xhtml' }], } as unknown as BookDoc; expect(__testing.sectionHrefAt(bookDoc, 0)).toBe('OEBPS/Text/chapter.xhtml'); }); test('clearReviewMarks restores original target HTML', () => { const doc = document.implementation.createHTMLDocument('chapter'); doc.body.innerHTML = '原文
旧译文
'; __testing.applyReviewMarks( doc, 'chapter.xhtml', [ { id: 'R00001', file: 'chapter.xhtml', ja_p_index: 0, cn_p_index: 1, jp_html: '原文', jp_text: '原文', cn_html: '旧译文', current_html: '新译文', }, ], '', ); __testing.clearReviewMarks(doc); const target = doc.querySelectorAll('p')[1]; expect(target?.innerHTML).toBe('旧译文'); expect(target?.hasAttribute('data-readest-review-row-id')).toBe(false); }); test('strips event handlers and dangerous links from target HTML', () => { const doc = document.implementation.createHTMLDocument('chapter'); doc.body.innerHTML = '原文
译文
'; __testing.applyReviewMarks( doc, 'chapter.xhtml', [ { id: 'R00001', file: 'chapter.xhtml', ja_p_index: 0, cn_p_index: 1, jp_html: '原文', jp_text: '原文', cn_html: '译文', current_html: '新译文', }, ], 'R00001', ); const target = doc.querySelectorAll('p')[1]; expect(target?.innerHTML).toBe('新译文'); }); test('updates active classes without rewriting translated paragraph HTML', () => { const doc = document.implementation.createHTMLDocument('chapter'); doc.body.innerHTML = '原文一
译文一
原文二
译文二
'; __testing.applyReviewMarks( doc, 'chapter.xhtml', [ { id: 'R00001', file: 'chapter.xhtml', ja_p_index: 0, cn_p_index: 1, jp_html: '原文一', jp_text: '原文一', cn_html: '译文一', current_html: '新译文一', }, { id: 'R00002', file: 'chapter.xhtml', ja_p_index: 2, cn_p_index: 3, jp_html: '原文二', jp_text: '原文二', cn_html: '译文二', current_html: '新译文二', }, ], 'R00001', ); const target = doc.querySelectorAll('p')[1]!; target.dataset['localState'] = 'kept'; __testing.applyActiveReviewMark(doc, 'R00002'); expect(target.innerHTML).toBe('新译文一'); expect(target.dataset['localState']).toBe('kept'); expect(target.classList.contains('readest-review-active')).toBe(false); expect(doc.querySelectorAll('p')[3]?.classList.contains('readest-review-active')).toBe(true); }); });