import { describe, it, expect } from 'vitest'; import { Formatter, ConsoleFormatter, JsonFormatter, } from '../../src/presentation/formatters.js'; import type { LintResult, DocumentMetadata } from '../../src/core/types.js'; import type { LintUseCaseOutput } from '../../src/usecases/LintUseCase.js'; describe('Formatter', () => { const sampleMetadata: DocumentMetadata = { filePath: '/test/sample.md', title: 'サンプルドキュメント', frontmatter: { title: 'サンプルドキュメント' }, }; const sampleResults: LintResult[] = [ { ruleId: 'TXT-001', ruleName: 'タイトル必須', severity: 'error', message: 'タイトルが設定されていません', location: { line: 1, column: 1 }, }, { ruleId: 'TXT-002', ruleName: '見出しレベル', severity: 'warning', message: 'H1見出しの後にH3が来ています', detail: '見出しレベルは順番に使用してください', suggestion: 'H2見出しを追加してください', location: { line: 10, column: 1 }, }, { ruleId: 'TXT-003', ruleName: '情報', severity: 'info', message: '推奨事項があります', }, ]; const sampleOutput: LintUseCaseOutput = { results: sampleResults, metadata: sampleMetadata, }; describe('ConsoleFormatter', () => { it('Formatterインターフェースを実装している', () => { const formatter: Formatter = new ConsoleFormatter(); expect(formatter).toBeDefined(); expect(typeof formatter.format).toBe('function'); }); it('空の結果の場合、成功メッセージを返す', () => { const formatter = new ConsoleFormatter(); const output: LintUseCaseOutput = { results: [], metadata: sampleMetadata, }; const result = formatter.format(output); expect(result).toContain('問題'); expect(result).toContain('0'); }); it('エラーを適切にフォーマットする', () => { const formatter = new ConsoleFormatter(); const result = formatter.format(sampleOutput); expect(result).toContain('TXT-001'); expect(result).toContain('✖'); // error アイコン expect(result).toContain('タイトルが設定されていません'); }); it('警告を適切にフォーマットする', () => { const formatter = new ConsoleFormatter(); const result = formatter.format(sampleOutput); expect(result).toContain('TXT-002'); expect(result).toContain('⚠'); // warning アイコン expect(result).toContain('H1見出しの後にH3が来ています'); }); it('情報を適切にフォーマットする', () => { const formatter = new ConsoleFormatter(); const result = formatter.format(sampleOutput); expect(result).toContain('TXT-003'); expect(result).toContain('ℹ'); // info アイコン }); it('位置情報を表示する', () => { const formatter = new ConsoleFormatter(); const result = formatter.format(sampleOutput); expect(result).toContain('1:1'); expect(result).toContain('10:1'); }); it('詳細と提案を表示する', () => { const formatter = new ConsoleFormatter(); const result = formatter.format(sampleOutput); expect(result).toContain('見出しレベルは順番に使用してください'); expect(result).toContain('H2見出しを追加してください'); }); it('サマリーを表示する', () => { const formatter = new ConsoleFormatter(); const result = formatter.format(sampleOutput); // エラー1, 警告1, 情報1(日本語フォーマット) expect(result).toContain('エラー: 1'); expect(result).toContain('警告: 1'); expect(result).toContain('情報: 1'); }); }); describe('JsonFormatter', () => { it('Formatterインターフェースを実装している', () => { const formatter: Formatter = new JsonFormatter(); expect(formatter).toBeDefined(); expect(typeof formatter.format).toBe('function'); }); it('有効なJSONを出力する', () => { const formatter = new JsonFormatter(); const result = formatter.format(sampleOutput); expect(() => JSON.parse(result)).not.toThrow(); }); it('全ての結果を含む', () => { const formatter = new JsonFormatter(); const result = formatter.format(sampleOutput); const parsed = JSON.parse(result); expect(parsed.results).toHaveLength(3); expect(parsed.results[0].ruleId).toBe('TXT-001'); }); it('メタデータを含む', () => { const formatter = new JsonFormatter(); const result = formatter.format(sampleOutput); const parsed = JSON.parse(result); expect(parsed.filePath).toBe('/test/sample.md'); expect(parsed.documentType).toBeDefined(); }); it('サマリーを含む', () => { const formatter = new JsonFormatter(); const result = formatter.format(sampleOutput); const parsed = JSON.parse(result); expect(parsed.summary).toBeDefined(); expect(parsed.summary.total).toBe(3); expect(parsed.summary.errors).toBe(1); expect(parsed.summary.warnings).toBe(1); expect(parsed.summary.infos).toBe(1); }); it('空の結果でも有効なJSONを出力する', () => { const formatter = new JsonFormatter(); const output: LintUseCaseOutput = { results: [], metadata: sampleMetadata, }; const result = formatter.format(output); const parsed = JSON.parse(result); expect(parsed.results).toHaveLength(0); expect(parsed.summary.total).toBe(0); }); it('整形されたJSONを出力する(pretty: true)', () => { const formatter = new JsonFormatter({ pretty: true }); const result = formatter.format(sampleOutput); // 整形されたJSONは改行を含む expect(result).toContain('\n'); }); it('圧縮されたJSONを出力する(pretty: false)', () => { const formatter = new JsonFormatter({ pretty: false }); const result = formatter.format(sampleOutput); // 圧縮されたJSONは改行を含まない(最後の改行を除く) expect(result.trim()).not.toContain('\n'); }); }); });