import { describe, it, expect, beforeEach } from 'vitest'; import { TocValidator } from '../../../../src/domain/text/validators/TocValidator.js'; import { MarkdownParser } from '../../../../src/core/parser.js'; import type { ValidationContext, DocumentMetadata } from '../../../../src/core/types.js'; describe('TocValidator', () => { let validator: TocValidator; let parser: MarkdownParser; beforeEach(() => { validator = new TocValidator(); parser = new MarkdownParser(); }); function createContext(content: string): ValidationContext { const tree = parser.parse(content); const frontmatter = parser.extractFrontmatter(content); const metadata: DocumentMetadata = { filePath: '/test/file.md', title: typeof frontmatter.title === 'string' ? frontmatter.title : undefined, frontmatter, }; return { tree, content, metadata }; } describe('Validatorインターフェース', () => { it('正しいIDを持つ', () => { expect(validator.id).toBe('text/toc'); }); it('正しい名前を持つ', () => { expect(validator.name).toBe('目次プレースホルダー検証'); }); }); describe('{{ toc }}存在チェック', () => { it('{{ toc }}がない場合、エラーを返す', () => { const content = `--- title: テスト draft: false --- # タイトル 本文です。`; const context = createContext(content); const results = validator.validate(context); const tocError = results.find(r => r.ruleId === 'text/toc/missing'); expect(tocError).toBeDefined(); expect(tocError?.severity).toBe('error'); }); it('{{ toc }}がある場合、missingエラーを返さない', () => { const content = `--- title: テスト draft: false --- {{ toc }} # タイトル 本文です。`; const context = createContext(content); const results = validator.validate(context); const tocError = results.find(r => r.ruleId === 'text/toc/missing'); expect(tocError).toBeUndefined(); }); it('{{toc}}(スペースなし)も認識する', () => { const content = `--- title: テスト draft: false --- {{toc}} # タイトル`; const context = createContext(content); const results = validator.validate(context); const tocError = results.find(r => r.ruleId === 'text/toc/missing'); expect(tocError).toBeUndefined(); }); }); describe('{{ toc }}位置チェック', () => { it('frontmatter直後にない場合、警告を返す', () => { const content = `--- title: テスト draft: false --- # タイトル {{ toc }} 本文です。`; const context = createContext(content); const results = validator.validate(context); const positionWarning = results.find(r => r.ruleId === 'text/toc/wrong-position'); expect(positionWarning).toBeDefined(); expect(positionWarning?.severity).toBe('warning'); }); it('frontmatter直後にある場合、位置警告を返さない', () => { const content = `--- title: テスト draft: false --- {{ toc }} # タイトル`; const context = createContext(content); const results = validator.validate(context); const positionWarning = results.find(r => r.ruleId === 'text/toc/wrong-position'); expect(positionWarning).toBeUndefined(); }); }); describe('正常ケース', () => { it('正しい位置に{{ toc }}がある場合、エラーを返さない', () => { const content = `--- title: 正しいテキスト教材 draft: false --- {{ toc }} # タイトル 本文です。`; const context = createContext(content); const results = validator.validate(context); expect(results.length).toBe(0); }); }); describe('エラーメッセージの品質', () => { it('修正方法(suggestion)を含む', () => { const content = `--- title: テスト draft: false --- # タイトル`; const context = createContext(content); const results = validator.validate(context); const tocError = results.find(r => r.ruleId === 'text/toc/missing'); expect(tocError?.suggestion).toBeDefined(); expect(tocError?.suggestion).toContain('{{ toc }}'); }); }); });