Spaces:
Paused
Paused
| /** | |
| * Unit tests for summary normalization between UI components and parser functions. | |
| * | |
| * These tests verify that: | |
| * - getFirstNonEmptySummary returns string | null | |
| * - parseAllPhaseSummaries and isAccumulatedSummary expect string | undefined | |
| * - The normalization (summary ?? undefined) correctly converts null to undefined | |
| * | |
| * This ensures the UI components properly bridge the type gap between: | |
| * - getFirstNonEmptySummary (returns string | null) | |
| * - parseAllPhaseSummaries (expects string | undefined) | |
| * - isAccumulatedSummary (expects string | undefined) | |
| */ | |
| import { describe, it, expect } from 'vitest'; | |
| import { parseAllPhaseSummaries, isAccumulatedSummary } from '../../../../ui/src/lib/log-parser.ts'; | |
| import { getFirstNonEmptySummary } from '../../../../ui/src/lib/summary-selection.ts'; | |
| describe('Summary Normalization', () => { | |
| describe('getFirstNonEmptySummary', () => { | |
| it('should return the first non-empty string', () => { | |
| const result = getFirstNonEmptySummary(null, undefined, 'valid summary', 'another'); | |
| expect(result).toBe('valid summary'); | |
| }); | |
| it('should return null when all candidates are empty', () => { | |
| const result = getFirstNonEmptySummary(null, undefined, '', ' '); | |
| expect(result).toBeNull(); | |
| }); | |
| it('should return null when no candidates provided', () => { | |
| const result = getFirstNonEmptySummary(); | |
| expect(result).toBeNull(); | |
| }); | |
| it('should return null for all null/undefined candidates', () => { | |
| const result = getFirstNonEmptySummary(null, undefined, null); | |
| expect(result).toBeNull(); | |
| }); | |
| it('should preserve original string formatting (not trim)', () => { | |
| const result = getFirstNonEmptySummary(' summary with spaces '); | |
| expect(result).toBe(' summary with spaces '); | |
| }); | |
| }); | |
| describe('parseAllPhaseSummaries with normalized input', () => { | |
| it('should handle null converted to undefined via ?? operator', () => { | |
| const summary = getFirstNonEmptySummary(null, undefined); | |
| // This is the normalization: summary ?? undefined | |
| const normalizedSummary = summary ?? undefined; | |
| // TypeScript should accept this without error | |
| const result = parseAllPhaseSummaries(normalizedSummary); | |
| expect(result).toEqual([]); | |
| }); | |
| it('should parse accumulated summary when non-null is normalized', () => { | |
| const rawSummary = | |
| '### Implementation\n\nDid some work\n\n---\n\n### Testing\n\nAll tests pass'; | |
| const summary = getFirstNonEmptySummary(null, rawSummary); | |
| const normalizedSummary = summary ?? undefined; | |
| const result = parseAllPhaseSummaries(normalizedSummary); | |
| expect(result).toHaveLength(2); | |
| expect(result[0].phaseName).toBe('Implementation'); | |
| expect(result[1].phaseName).toBe('Testing'); | |
| }); | |
| }); | |
| describe('isAccumulatedSummary with normalized input', () => { | |
| it('should return false for null converted to undefined', () => { | |
| const summary = getFirstNonEmptySummary(null, undefined); | |
| const normalizedSummary = summary ?? undefined; | |
| const result = isAccumulatedSummary(normalizedSummary); | |
| expect(result).toBe(false); | |
| }); | |
| it('should return true for valid accumulated summary after normalization', () => { | |
| const rawSummary = | |
| '### Implementation\n\nDid some work\n\n---\n\n### Testing\n\nAll tests pass'; | |
| const summary = getFirstNonEmptySummary(rawSummary); | |
| const normalizedSummary = summary ?? undefined; | |
| const result = isAccumulatedSummary(normalizedSummary); | |
| expect(result).toBe(true); | |
| }); | |
| it('should return false for single-phase summary after normalization', () => { | |
| const rawSummary = '### Implementation\n\nDid some work'; | |
| const summary = getFirstNonEmptySummary(rawSummary); | |
| const normalizedSummary = summary ?? undefined; | |
| const result = isAccumulatedSummary(normalizedSummary); | |
| expect(result).toBe(false); | |
| }); | |
| }); | |
| describe('Type safety verification', () => { | |
| it('should demonstrate that null must be normalized to undefined', () => { | |
| // This test documents the type mismatch that requires normalization | |
| const summary: string | null = getFirstNonEmptySummary(null); | |
| const normalizedSummary: string | undefined = summary ?? undefined; | |
| // parseAllPhaseSummaries expects string | undefined, not string | null | |
| // The normalization converts null -> undefined, which is compatible | |
| const result = parseAllPhaseSummaries(normalizedSummary); | |
| expect(result).toEqual([]); | |
| }); | |
| it('should work with the actual usage pattern from components', () => { | |
| // Simulates the actual pattern used in summary-dialog.tsx and agent-output-modal.tsx | |
| const featureSummary: string | null | undefined = null; | |
| const extractedSummary: string | null | undefined = undefined; | |
| const rawSummary = getFirstNonEmptySummary(featureSummary, extractedSummary); | |
| const normalizedSummary = rawSummary ?? undefined; | |
| // Both parser functions should work with the normalized value | |
| const phases = parseAllPhaseSummaries(normalizedSummary); | |
| const hasMultiple = isAccumulatedSummary(normalizedSummary); | |
| expect(phases).toEqual([]); | |
| expect(hasMultiple).toBe(false); | |
| }); | |
| }); | |
| }); | |