| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| import { describe, it, expect } from 'vitest'; |
|
|
| |
| |
|
|
| |
| |
| |
| function parsePhaseSummaries(summary: string | undefined): Map<string, string> { |
| const phaseSummaries = new Map<string, string>(); |
|
|
| if (!summary || !summary.trim()) { |
| return phaseSummaries; |
| } |
|
|
| |
| const sections = summary.split(/\n\n---\n\n/); |
|
|
| for (const section of sections) { |
| |
| const headerMatch = section.match(/^###\s+(.+?)(?:\n|$)/); |
| if (headerMatch) { |
| const phaseName = headerMatch[1].trim().toLowerCase(); |
| |
| const content = section.substring(headerMatch[0].length).trim(); |
| phaseSummaries.set(phaseName, content); |
| } |
| } |
|
|
| return phaseSummaries; |
| } |
|
|
| |
| |
| |
| function extractPhaseSummary(summary: string | undefined, phaseName: string): string | null { |
| const phaseSummaries = parsePhaseSummaries(summary); |
| const normalizedPhaseName = phaseName.toLowerCase(); |
| return phaseSummaries.get(normalizedPhaseName) || null; |
| } |
|
|
| |
| |
| |
| function extractImplementationSummary(summary: string | undefined): string | null { |
| if (!summary || !summary.trim()) { |
| return null; |
| } |
|
|
| const phaseSummaries = parsePhaseSummaries(summary); |
|
|
| |
| const implementationContent = phaseSummaries.get('implementation'); |
| if (implementationContent) { |
| return implementationContent; |
| } |
|
|
| |
| for (const [phaseName, content] of phaseSummaries) { |
| if (phaseName.includes('implement')) { |
| return content; |
| } |
| } |
|
|
| |
| |
| |
| if (!summary.includes('### ') && !summary.includes('\n---\n')) { |
| return summary; |
| } |
|
|
| return null; |
| } |
|
|
| |
| |
| |
| function isAccumulatedSummary(summary: string | undefined): boolean { |
| if (!summary || !summary.trim()) { |
| return false; |
| } |
|
|
| |
| const hasMultiplePhases = |
| summary.includes('\n\n---\n\n') && summary.match(/###\s+.+/g)?.length > 0; |
|
|
| return hasMultiplePhases; |
| } |
|
|
| |
| |
| |
| interface PhaseSummaryEntry { |
| |
| phaseName: string; |
| |
| content: string; |
| |
| header: string; |
| } |
|
|
| |
| const DEFAULT_PHASE_NAME = 'Summary'; |
|
|
| |
| |
| |
| |
| function parseAllPhaseSummaries(summary: string | undefined): PhaseSummaryEntry[] { |
| const entries: PhaseSummaryEntry[] = []; |
|
|
| if (!summary || !summary.trim()) { |
| return entries; |
| } |
|
|
| |
| if (!summary.includes('### ')) { |
| |
| return [ |
| { phaseName: DEFAULT_PHASE_NAME, content: summary, header: `### ${DEFAULT_PHASE_NAME}` }, |
| ]; |
| } |
|
|
| |
| const sections = summary.split(/\n\n---\n\n/); |
|
|
| for (const section of sections) { |
| |
| const headerMatch = section.match(/^(###\s+)(.+?)(?:\n|$)/); |
| if (headerMatch) { |
| const header = headerMatch[0].trim(); |
| const phaseName = headerMatch[2].trim(); |
| |
| const content = section.substring(headerMatch[0].length).trim(); |
| entries.push({ phaseName, content, header }); |
| } |
| } |
|
|
| return entries; |
| } |
|
|
| describe('parsePhaseSummaries', () => { |
| describe('basic parsing', () => { |
| it('should parse single phase summary', () => { |
| const summary = `### Implementation |
| |
| ## Changes Made |
| - Created new module |
| - Added unit tests`; |
|
|
| const result = parsePhaseSummaries(summary); |
|
|
| expect(result.size).toBe(1); |
| expect(result.get('implementation')).toBe( |
| '## Changes Made\n- Created new module\n- Added unit tests' |
| ); |
| }); |
|
|
| it('should parse multiple phase summaries', () => { |
| const summary = `### Implementation |
| |
| ## Changes Made |
| - Created new module |
| |
| --- |
| |
| ### Testing |
| |
| ## Test Results |
| - All tests pass`; |
|
|
| const result = parsePhaseSummaries(summary); |
|
|
| expect(result.size).toBe(2); |
| expect(result.get('implementation')).toBe('## Changes Made\n- Created new module'); |
| expect(result.get('testing')).toBe('## Test Results\n- All tests pass'); |
| }); |
|
|
| it('should handle three or more phases', () => { |
| const summary = `### Planning |
| |
| Plan created |
| |
| --- |
| |
| ### Implementation |
| |
| Code written |
| |
| --- |
| |
| ### Testing |
| |
| Tests pass |
| |
| --- |
| |
| ### Refinement |
| |
| Code polished`; |
|
|
| const result = parsePhaseSummaries(summary); |
|
|
| expect(result.size).toBe(4); |
| expect(result.get('planning')).toBe('Plan created'); |
| expect(result.get('implementation')).toBe('Code written'); |
| expect(result.get('testing')).toBe('Tests pass'); |
| expect(result.get('refinement')).toBe('Code polished'); |
| }); |
| }); |
|
|
| describe('edge cases', () => { |
| it('should return empty map for undefined summary', () => { |
| const result = parsePhaseSummaries(undefined); |
| expect(result.size).toBe(0); |
| }); |
|
|
| it('should return empty map for null summary', () => { |
| const result = parsePhaseSummaries(null as unknown as string); |
| expect(result.size).toBe(0); |
| }); |
|
|
| it('should return empty map for empty string', () => { |
| const result = parsePhaseSummaries(''); |
| expect(result.size).toBe(0); |
| }); |
|
|
| it('should return empty map for whitespace-only string', () => { |
| const result = parsePhaseSummaries(' \n\n '); |
| expect(result.size).toBe(0); |
| }); |
|
|
| it('should handle summary without phase headers', () => { |
| const summary = 'Just some regular content without headers'; |
| const result = parsePhaseSummaries(summary); |
| expect(result.size).toBe(0); |
| }); |
|
|
| it('should handle section without header after separator', () => { |
| const summary = `### Implementation |
| |
| Content here |
| |
| --- |
| |
| This section has no header`; |
|
|
| const result = parsePhaseSummaries(summary); |
|
|
| expect(result.size).toBe(1); |
| expect(result.get('implementation')).toBe('Content here'); |
| }); |
| }); |
|
|
| describe('phase name normalization', () => { |
| it('should normalize phase names to lowercase', () => { |
| const summary = `### IMPLEMENTATION |
| |
| Content`; |
|
|
| const result = parsePhaseSummaries(summary); |
| expect(result.has('implementation')).toBe(true); |
| expect(result.has('IMPLEMENTATION')).toBe(false); |
| }); |
|
|
| it('should handle mixed case phase names', () => { |
| const summary = `### Code Review |
| |
| Content`; |
|
|
| const result = parsePhaseSummaries(summary); |
| expect(result.has('code review')).toBe(true); |
| }); |
|
|
| it('should preserve spaces in multi-word phase names', () => { |
| const summary = `### Code Review |
| |
| Content`; |
|
|
| const result = parsePhaseSummaries(summary); |
| expect(result.get('code review')).toBe('Content'); |
| }); |
| }); |
|
|
| describe('content preservation', () => { |
| it('should preserve markdown formatting in content', () => { |
| const summary = `### Implementation |
| |
| ## Heading |
| - **Bold text** |
| - \`code\` |
| \`\`\`typescript |
| const x = 1; |
| \`\`\``; |
|
|
| const result = parsePhaseSummaries(summary); |
| const content = result.get('implementation'); |
|
|
| expect(content).toContain('**Bold text**'); |
| expect(content).toContain('`code`'); |
| expect(content).toContain('```typescript'); |
| }); |
|
|
| it('should preserve unicode in content', () => { |
| const summary = `### Testing |
| |
| Results: ✅ 42 passed, ❌ 0 failed`; |
|
|
| const result = parsePhaseSummaries(summary); |
| expect(result.get('testing')).toContain('✅'); |
| expect(result.get('testing')).toContain('❌'); |
| }); |
|
|
| it('should preserve tables in content', () => { |
| const summary = `### Testing |
| |
| | Test | Result | |
| |------|--------| |
| | Unit | Pass |`; |
|
|
| const result = parsePhaseSummaries(summary); |
| expect(result.get('testing')).toContain('| Test | Result |'); |
| }); |
|
|
| it('should handle empty phase content', () => { |
| const summary = `### Implementation |
| |
| --- |
| |
| ### Testing |
| |
| Content`; |
|
|
| const result = parsePhaseSummaries(summary); |
| expect(result.get('implementation')).toBe(''); |
| expect(result.get('testing')).toBe('Content'); |
| }); |
| }); |
| }); |
|
|
| describe('extractPhaseSummary', () => { |
| describe('extraction by phase name', () => { |
| it('should extract specified phase content', () => { |
| const summary = `### Implementation |
| |
| Implementation content |
| |
| --- |
| |
| ### Testing |
| |
| Testing content`; |
|
|
| expect(extractPhaseSummary(summary, 'Implementation')).toBe('Implementation content'); |
| expect(extractPhaseSummary(summary, 'Testing')).toBe('Testing content'); |
| }); |
|
|
| it('should be case-insensitive for phase name', () => { |
| const summary = `### Implementation |
| |
| Content`; |
|
|
| expect(extractPhaseSummary(summary, 'implementation')).toBe('Content'); |
| expect(extractPhaseSummary(summary, 'IMPLEMENTATION')).toBe('Content'); |
| expect(extractPhaseSummary(summary, 'ImPlEmEnTaTiOn')).toBe('Content'); |
| }); |
|
|
| it('should return null for non-existent phase', () => { |
| const summary = `### Implementation |
| |
| Content`; |
|
|
| expect(extractPhaseSummary(summary, 'NonExistent')).toBeNull(); |
| }); |
| }); |
|
|
| describe('edge cases', () => { |
| it('should return null for undefined summary', () => { |
| expect(extractPhaseSummary(undefined, 'Implementation')).toBeNull(); |
| }); |
|
|
| it('should return null for empty summary', () => { |
| expect(extractPhaseSummary('', 'Implementation')).toBeNull(); |
| }); |
|
|
| it('should handle whitespace in phase name', () => { |
| const summary = `### Code Review |
| |
| Content`; |
|
|
| expect(extractPhaseSummary(summary, 'Code Review')).toBe('Content'); |
| expect(extractPhaseSummary(summary, 'code review')).toBe('Content'); |
| }); |
| }); |
| }); |
|
|
| describe('extractImplementationSummary', () => { |
| describe('exact match', () => { |
| it('should extract implementation phase by exact name', () => { |
| const summary = `### Implementation |
| |
| ## Changes Made |
| - Created feature |
| - Added tests |
| |
| --- |
| |
| ### Testing |
| |
| Tests pass`; |
|
|
| const result = extractImplementationSummary(summary); |
| expect(result).toBe('## Changes Made\n- Created feature\n- Added tests'); |
| }); |
|
|
| it('should be case-insensitive', () => { |
| const summary = `### IMPLEMENTATION |
| |
| Content`; |
|
|
| expect(extractImplementationSummary(summary)).toBe('Content'); |
| }); |
| }); |
|
|
| describe('partial match fallback', () => { |
| it('should find phase containing "implement"', () => { |
| const summary = `### Feature Implementation |
| |
| Content here`; |
|
|
| const result = extractImplementationSummary(summary); |
| expect(result).toBe('Content here'); |
| }); |
|
|
| it('should find phase containing "implementation"', () => { |
| const summary = `### Implementation Phase |
| |
| Content here`; |
|
|
| const result = extractImplementationSummary(summary); |
| expect(result).toBe('Content here'); |
| }); |
| }); |
|
|
| describe('legacy/non-accumulated summary handling', () => { |
| it('should return full summary if no phase headers present', () => { |
| const summary = `## Changes Made |
| - Created feature |
| - Added tests`; |
|
|
| const result = extractImplementationSummary(summary); |
| expect(result).toBe(summary); |
| }); |
|
|
| it('should return null if summary has phase headers but no implementation', () => { |
| const summary = `### Testing |
| |
| Tests pass |
| |
| --- |
| |
| ### Review |
| |
| Review complete`; |
|
|
| const result = extractImplementationSummary(summary); |
| expect(result).toBeNull(); |
| }); |
|
|
| it('should not return full summary if it contains phase headers', () => { |
| const summary = `### Testing |
| |
| Tests pass`; |
|
|
| const result = extractImplementationSummary(summary); |
| expect(result).toBeNull(); |
| }); |
| }); |
|
|
| describe('edge cases', () => { |
| it('should return null for undefined summary', () => { |
| expect(extractImplementationSummary(undefined)).toBeNull(); |
| }); |
|
|
| it('should return null for empty string', () => { |
| expect(extractImplementationSummary('')).toBeNull(); |
| }); |
|
|
| it('should return null for whitespace-only string', () => { |
| expect(extractImplementationSummary(' \n\n ')).toBeNull(); |
| }); |
| }); |
| }); |
|
|
| describe('isAccumulatedSummary', () => { |
| describe('accumulated format detection', () => { |
| it('should return true for accumulated summary with separator and headers', () => { |
| const summary = `### Implementation |
| |
| Content |
| |
| --- |
| |
| ### Testing |
| |
| Content`; |
|
|
| expect(isAccumulatedSummary(summary)).toBe(true); |
| }); |
|
|
| it('should return true for accumulated summary with multiple phases', () => { |
| const summary = `### Phase 1 |
| |
| Content 1 |
| |
| --- |
| |
| ### Phase 2 |
| |
| Content 2 |
| |
| --- |
| |
| ### Phase 3 |
| |
| Content 3`; |
|
|
| expect(isAccumulatedSummary(summary)).toBe(true); |
| }); |
|
|
| it('should return true for accumulated summary with just one phase and separator', () => { |
| |
| const summary = `### Implementation |
| |
| Content |
| |
| --- |
| |
| ### Testing |
| |
| More content`; |
|
|
| expect(isAccumulatedSummary(summary)).toBe(true); |
| }); |
| }); |
|
|
| describe('non-accumulated format detection', () => { |
| it('should return false for summary without separator', () => { |
| const summary = `### Implementation |
| |
| Just content`; |
|
|
| expect(isAccumulatedSummary(summary)).toBe(false); |
| }); |
|
|
| it('should return false for summary with separator but no headers', () => { |
| const summary = `Content |
| |
| --- |
| |
| More content`; |
|
|
| expect(isAccumulatedSummary(summary)).toBe(false); |
| }); |
|
|
| it('should return false for simple text summary', () => { |
| const summary = 'Just a simple summary without any special formatting'; |
| expect(isAccumulatedSummary(summary)).toBe(false); |
| }); |
|
|
| it('should return false for markdown summary without phase headers', () => { |
| const summary = `## Changes Made |
| - Created feature |
| - Added tests`; |
| expect(isAccumulatedSummary(summary)).toBe(false); |
| }); |
| }); |
|
|
| describe('edge cases', () => { |
| it('should return false for undefined summary', () => { |
| expect(isAccumulatedSummary(undefined)).toBe(false); |
| }); |
|
|
| it('should return false for null summary', () => { |
| expect(isAccumulatedSummary(null as unknown as string)).toBe(false); |
| }); |
|
|
| it('should return false for empty string', () => { |
| expect(isAccumulatedSummary('')).toBe(false); |
| }); |
|
|
| it('should return false for whitespace-only string', () => { |
| expect(isAccumulatedSummary(' \n\n ')).toBe(false); |
| }); |
| }); |
| }); |
|
|
| describe('Integration: Full parsing workflow', () => { |
| it('should correctly parse typical server-accumulated pipeline summary', () => { |
| |
| const summary = [ |
| '### Implementation', |
| '', |
| '## Changes', |
| '- Added auth module', |
| '- Created user service', |
| '', |
| '---', |
| '', |
| '### Code Review', |
| '', |
| '## Review Results', |
| '- Style issues fixed', |
| '- Added error handling', |
| '', |
| '---', |
| '', |
| '### Testing', |
| '', |
| '## Test Results', |
| '- 42 tests pass', |
| '- 98% coverage', |
| ].join('\n'); |
|
|
| |
| expect(isAccumulatedSummary(summary)).toBe(true); |
|
|
| |
| const phases = parsePhaseSummaries(summary); |
| expect(phases.size).toBe(3); |
| expect(phases.get('implementation')).toContain('Added auth module'); |
| expect(phases.get('code review')).toContain('Style issues fixed'); |
| expect(phases.get('testing')).toContain('42 tests pass'); |
|
|
| |
| expect(extractPhaseSummary(summary, 'Implementation')).toContain('Added auth module'); |
| expect(extractPhaseSummary(summary, 'Code Review')).toContain('Style issues fixed'); |
| expect(extractPhaseSummary(summary, 'Testing')).toContain('42 tests pass'); |
|
|
| |
| expect(extractImplementationSummary(summary)).toContain('Added auth module'); |
| }); |
|
|
| it('should handle legacy non-pipeline summary correctly', () => { |
| |
| const summary = `## Implementation Complete |
| - Created the feature |
| - All tests pass`; |
|
|
| |
| expect(isAccumulatedSummary(summary)).toBe(false); |
|
|
| |
| const phases = parsePhaseSummaries(summary); |
| expect(phases.size).toBe(0); |
|
|
| |
| expect(extractPhaseSummary(summary, 'Implementation')).toBeNull(); |
|
|
| |
| expect(extractImplementationSummary(summary)).toBe(summary); |
| }); |
|
|
| it('should handle single-step pipeline summary', () => { |
| |
| const summary = `### Implementation |
| |
| ## Changes |
| - Created the feature`; |
|
|
| |
| expect(isAccumulatedSummary(summary)).toBe(false); |
|
|
| |
| const phases = parsePhaseSummaries(summary); |
| expect(phases.size).toBe(1); |
| expect(phases.get('implementation')).toContain('Created the feature'); |
| }); |
| }); |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| describe('parseAllPhaseSummaries', () => { |
| describe('basic parsing', () => { |
| it('should parse single phase summary into array with one entry', () => { |
| const summary = `### Implementation |
| |
| ## Changes Made |
| - Created new module |
| - Added unit tests`; |
|
|
| const result = parseAllPhaseSummaries(summary); |
|
|
| expect(result.length).toBe(1); |
| expect(result[0].phaseName).toBe('Implementation'); |
| expect(result[0].content).toBe('## Changes Made\n- Created new module\n- Added unit tests'); |
| expect(result[0].header).toBe('### Implementation'); |
| }); |
|
|
| it('should parse multiple phase summaries in order', () => { |
| const summary = `### Implementation |
| |
| ## Changes Made |
| - Created new module |
| |
| --- |
| |
| ### Testing |
| |
| ## Test Results |
| - All tests pass`; |
|
|
| const result = parseAllPhaseSummaries(summary); |
|
|
| expect(result.length).toBe(2); |
| |
| expect(result[0].phaseName).toBe('Implementation'); |
| expect(result[0].content).toBe('## Changes Made\n- Created new module'); |
| expect(result[1].phaseName).toBe('Testing'); |
| expect(result[1].content).toBe('## Test Results\n- All tests pass'); |
| }); |
|
|
| it('should parse three or more phases in correct order', () => { |
| const summary = `### Planning |
| |
| Plan created |
| |
| --- |
| |
| ### Implementation |
| |
| Code written |
| |
| --- |
| |
| ### Testing |
| |
| Tests pass |
| |
| --- |
| |
| ### Refinement |
| |
| Code polished`; |
|
|
| const result = parseAllPhaseSummaries(summary); |
|
|
| expect(result.length).toBe(4); |
| expect(result[0].phaseName).toBe('Planning'); |
| expect(result[1].phaseName).toBe('Implementation'); |
| expect(result[2].phaseName).toBe('Testing'); |
| expect(result[3].phaseName).toBe('Refinement'); |
| }); |
| }); |
|
|
| describe('non-accumulated summary handling', () => { |
| it('should return single entry for summary without phase headers', () => { |
| const summary = `## Changes Made |
| - Created feature |
| - Added tests`; |
|
|
| const result = parseAllPhaseSummaries(summary); |
|
|
| expect(result.length).toBe(1); |
| expect(result[0].phaseName).toBe('Summary'); |
| expect(result[0].content).toBe(summary); |
| }); |
|
|
| it('should return single entry for simple text summary', () => { |
| const summary = 'Just a simple summary without any special formatting'; |
|
|
| const result = parseAllPhaseSummaries(summary); |
|
|
| expect(result.length).toBe(1); |
| expect(result[0].phaseName).toBe('Summary'); |
| expect(result[0].content).toBe(summary); |
| }); |
| }); |
|
|
| describe('edge cases', () => { |
| it('should return empty array for undefined summary', () => { |
| const result = parseAllPhaseSummaries(undefined); |
| expect(result.length).toBe(0); |
| }); |
|
|
| it('should return empty array for empty string', () => { |
| const result = parseAllPhaseSummaries(''); |
| expect(result.length).toBe(0); |
| }); |
|
|
| it('should return empty array for whitespace-only string', () => { |
| const result = parseAllPhaseSummaries(' \n\n '); |
| expect(result.length).toBe(0); |
| }); |
|
|
| it('should handle section without header after separator', () => { |
| const summary = `### Implementation |
| |
| Content here |
| |
| --- |
| |
| This section has no header`; |
|
|
| const result = parseAllPhaseSummaries(summary); |
|
|
| expect(result.length).toBe(1); |
| expect(result[0].phaseName).toBe('Implementation'); |
| }); |
| }); |
|
|
| describe('content preservation', () => { |
| it('should preserve markdown formatting in content', () => { |
| const summary = `### Implementation |
| |
| ## Heading |
| - **Bold text** |
| - \`code\` |
| \`\`\`typescript |
| const x = 1; |
| \`\`\``; |
|
|
| const result = parseAllPhaseSummaries(summary); |
| const content = result[0].content; |
|
|
| expect(content).toContain('**Bold text**'); |
| expect(content).toContain('`code`'); |
| expect(content).toContain('```typescript'); |
| }); |
|
|
| it('should preserve unicode in content', () => { |
| const summary = `### Testing |
| |
| Results: ✅ 42 passed, ❌ 0 failed`; |
|
|
| const result = parseAllPhaseSummaries(summary); |
| expect(result[0].content).toContain('✅'); |
| expect(result[0].content).toContain('❌'); |
| }); |
|
|
| it('should preserve tables in content', () => { |
| const summary = `### Testing |
| |
| | Test | Result | |
| |------|--------| |
| | Unit | Pass |`; |
|
|
| const result = parseAllPhaseSummaries(summary); |
| expect(result[0].content).toContain('| Test | Result |'); |
| }); |
|
|
| it('should handle empty phase content', () => { |
| const summary = `### Implementation |
| |
| --- |
| |
| ### Testing |
| |
| Content`; |
|
|
| const result = parseAllPhaseSummaries(summary); |
| expect(result.length).toBe(2); |
| expect(result[0].content).toBe(''); |
| expect(result[1].content).toBe('Content'); |
| }); |
| }); |
|
|
| describe('header preservation', () => { |
| it('should preserve original header text', () => { |
| const summary = `### Code Review |
| |
| Content`; |
|
|
| const result = parseAllPhaseSummaries(summary); |
| expect(result[0].header).toBe('### Code Review'); |
| }); |
|
|
| it('should preserve phase name with original casing', () => { |
| const summary = `### CODE REVIEW |
| |
| Content`; |
|
|
| const result = parseAllPhaseSummaries(summary); |
| expect(result[0].phaseName).toBe('CODE REVIEW'); |
| }); |
| }); |
|
|
| describe('chronological order preservation', () => { |
| it('should maintain order: Alpha before Beta before Gamma', () => { |
| const summary = `### Alpha |
| |
| First |
| |
| --- |
| |
| ### Beta |
| |
| Second |
| |
| --- |
| |
| ### Gamma |
| |
| Third`; |
|
|
| const result = parseAllPhaseSummaries(summary); |
|
|
| expect(result.length).toBe(3); |
| const names = result.map((e) => e.phaseName); |
| expect(names).toEqual(['Alpha', 'Beta', 'Gamma']); |
| }); |
|
|
| it('should preserve typical pipeline order', () => { |
| const summary = [ |
| '### Implementation', |
| '', |
| '## Changes', |
| '- Added auth module', |
| '', |
| '---', |
| '', |
| '### Code Review', |
| '', |
| '## Review Results', |
| '- Style issues fixed', |
| '', |
| '---', |
| '', |
| '### Testing', |
| '', |
| '## Test Results', |
| '- 42 tests pass', |
| ].join('\n'); |
|
|
| const result = parseAllPhaseSummaries(summary); |
|
|
| expect(result.length).toBe(3); |
| expect(result[0].phaseName).toBe('Implementation'); |
| expect(result[1].phaseName).toBe('Code Review'); |
| expect(result[2].phaseName).toBe('Testing'); |
| }); |
| }); |
| }); |
|
|