import * as assert from 'assert'; import * as vscode from 'vscode'; import { reportToMarkdown } from '../src/utils/report'; import type { MergedReport } from '../src/merger/merger'; import type { LinterDiagnostic } from '../src/types'; import type { AIFinding } from '../src/ai/schema'; function range(line: number): vscode.Range { return new vscode.Range(line, 0, line, 0); } function baseReport(overrides: Partial = {}): MergedReport { return { linterDiagnostics: [], customRuleDiagnostics: [], translatedDiagnostics: [], aiFindings: [], linterCount: 0, customRuleCount: 0, aiCount: 0, errors: [], degraded: false, duration: 1200, filePath: '/virtual/app.js', language: 'javascript', adapterNames: ['eslint'], fixableLinterIndices: [], aiFixableLinterIndices: [], fixableCustomIndices: [], aiFixAvailable: true, ...overrides, }; } function linterDiag(overrides: Partial): LinterDiagnostic { return { severity: 'error', ruleId: 'eslint:no-var', message: 'Unexpected var', range: range(2), ...overrides, }; } function aiFinding(overrides: Partial): AIFinding { return { ruleId: 'ai:sec-1', severity: 'error', category: 'security', title: 'XSS risk', description: 'sanitize input', suggestion: 'escape output', line: 0, ...overrides, }; } suite('Report Tests', () => { test('renders header metadata', () => { const md = reportToMarkdown(baseReport()); assert.match(md, /\/virtual\/app\.js/); assert.match(md, /javascript/); assert.match(md, /1\.2s/); assert.match(md, /eslint/); }); test('renders empty report with no-problems banner', () => { const md = reportToMarkdown(baseReport()); assert.match(md, /✅/); assert.ok(!md.includes('eslint:no-var')); }); test('renders linter diagnostics with severity emoji and suggestion', () => { const report = baseReport({ linterDiagnostics: [ linterDiag({}), linterDiag({ severity: 'warning', ruleId: 'eslint:eqeqeq', message: 'Use ===', range: range(3), suggestion: 'use strict equality' }), linterDiag({ severity: 'info', ruleId: 'eslint:prefer-template', message: 'prefer template', range: range(4) }), linterDiag({ severity: 'fatal' as LinterDiagnostic['severity'], ruleId: 'eslint:unknown', message: 'other', range: range(5) }), ], linterCount: 4, }); const md = reportToMarkdown(report); assert.match(md, /eslint:no-var/); assert.match(md, /Unexpected var/); assert.match(md, /🔴/); assert.match(md, /🟡/); assert.match(md, /🔵/); assert.match(md, /⚪/); assert.match(md, /use strict equality/); }); test('renders custom rule diagnostics', () => { const report = baseReport({ customRuleDiagnostics: [ { severity: 'warning', ruleId: 'custom:team-1', message: 'use strict', range: range(1) } as LinterDiagnostic, ], customRuleCount: 1, }); const md = reportToMarkdown(report); assert.match(md, /custom:team-1/); assert.match(md, /use strict/); assert.match(md, /🟡/); }); test('renders ai findings with category and suggestion', () => { const report = baseReport({ aiFindings: [aiFinding({}), aiFinding({ severity: 'warning', category: 'performance', ruleId: 'ai:perf-1', title: 'slow loop', description: 'nested loop', suggestion: 'hoist' })], aiCount: 2, }); const md = reportToMarkdown(report); assert.match(md, /\[AI\]/); assert.match(md, /security/); assert.match(md, /XSS risk/); assert.match(md, /sanitize input/); assert.match(md, /escape output/); assert.match(md, /slow loop/); }); test('renders degraded banner and errors section', () => { const report = baseReport({ degraded: true, errors: ['[pmd] tool-unavailable'] }); const md = reportToMarkdown(report); assert.match(md, /⚠️/); assert.match(md, /\[pmd\] tool-unavailable/); }); test('omits tools line when no adapters', () => { const md = reportToMarkdown(baseReport({ adapterNames: [] })); assert.ok(!md.includes('eslint,')); }); });