import { describe, expect, it } from 'vitest'; import { parseMatrix, validateAgainstCatalog } from '../../scripts/lib/coverage-matrix-parser.mjs'; describe('parseMatrix', () => { it('parses three valid rows including a 4-component ID', () => { const md = `Some intro text. | 0.1.1 | Auth login | RU | src/auth/login.rs | ✅ | covered | | 3.3.1.1 | Voice hotkey | WD | app/test/e2e/specs/voice.spec.ts | 🟡 | partial | | 13.5.3 | Release smoke | MS | docs/RELEASE-MANUAL-SMOKE.md | 🚫 | manual only | Trailing prose.`; const result = parseMatrix(md); expect(result.errors).toEqual([]); expect(result.rows).toHaveLength(3); expect(result.rows[0].id).toBe('0.1.1'); expect(result.rows[1].id).toBe('3.3.1.1'); expect(result.rows[2].status).toBe('🚫'); }); it('flags duplicate IDs and invalid statuses', () => { const md = `| 1.1.1 | First | RU | a.rs | ✅ | ok | | 1.1.1 | Duplicate | VU | b.ts | ✅ | second copy | | 2.2.2 | Bad status | WD | c.spec.ts | ⚠️ | not a real legend |`; const parsed = parseMatrix(md); expect(parsed.errors.some((e: string) => e.includes('invalid status'))).toBe(true); const validation = validateAgainstCatalog(parsed.rows, ['1.1.1', '2.2.2', '9.9.9']); expect(validation.duplicates).toEqual(['1.1.1']); expect(validation.missingFromMatrix).toEqual(['2.2.2', '9.9.9']); }); it('drops rows with invalid status from the rows array (no double-counting)', () => { const md = `| 4.4.4 | OK row | RU | a.rs | ✅ | ok | | 5.5.5 | Bad status | RU | b.rs | ⚠️ | should not appear in rows |`; const parsed = parseMatrix(md); expect(parsed.rows).toHaveLength(1); expect(parsed.rows[0].id).toBe('4.4.4'); expect(parsed.errors).toHaveLength(1); }); it('reports line-numbered errors for invalid ID and status', () => { const md = `header line | 1.1.1 | Good | RU | a.rs | ✅ | ok | | 2.2.2 | Bad status | RU | b.rs | ⚠️ | nope |`; const parsed = parseMatrix(md); expect(parsed.errors[0]).toMatch(/^Line 3 \(2\.2\.2\): invalid status/); }); it('handles a row with empty notes', () => { const md = `| 6.6.6 | No notes | RU | a.rs | ✅ | |`; const parsed = parseMatrix(md); expect(parsed.rows).toHaveLength(1); expect(parsed.rows[0].notes).toBe(''); }); it('matches rows with trailing whitespace after final pipe', () => { const md = `| 7.7.7 | Trailing | RU | a.rs | ✅ | ok | `; const parsed = parseMatrix(md); expect(parsed.rows).toHaveLength(1); expect(parsed.rows[0].id).toBe('7.7.7'); }); it('returns totalRows and uniqueRows from validateAgainstCatalog', () => { const rows = [ { id: '1.1.1', name: 'A', layer: 'RU', path: 'a', status: '✅', notes: '' }, { id: '1.1.1', name: 'B', layer: 'RU', path: 'b', status: '✅', notes: '' }, { id: '2.2.2', name: 'C', layer: 'RU', path: 'c', status: '✅', notes: '' }, ]; const result = validateAgainstCatalog(rows, ['1.1.1', '2.2.2']); expect(result.totalRows).toBe(3); expect(result.uniqueRows).toBe(2); expect(result.duplicates).toEqual(['1.1.1']); expect(result.missingFromMatrix).toEqual([]); }); });