The New Archon (Beta) - The Operating System for AI Coding Assistants!

This commit is contained in:
Cole Medin
2025-08-13 07:58:24 -05:00
parent 13e1fc6a0e
commit 59084036f6
603 changed files with 131376 additions and 417 deletions

View File

@@ -0,0 +1,227 @@
import { render, screen, fireEvent, waitFor } from '@testing-library/react'
import { describe, test, expect, vi, beforeEach } from 'vitest'
import React from 'react'
// Mock the dependencies
vi.mock('../../contexts/ToastContext', () => ({
useToast: () => ({
showToast: vi.fn()
})
}))
vi.mock('../../services/projectService', () => ({
projectService: {
getProjectDocuments: vi.fn().mockResolvedValue([])
}
}))
vi.mock('../../services/knowledgeBaseService', () => ({
knowledgeBaseService: {
getItems: vi.fn().mockResolvedValue([])
}
}))
// Create a minimal DocsTab component for testing
const DocsTabTest = () => {
const [documents, setDocuments] = React.useState([
{
id: 'doc-1',
title: 'Document 1',
content: { type: 'prp' },
document_type: 'prp',
updated_at: '2025-07-30T12:00:00Z'
},
{
id: 'doc-2',
title: 'Document 2',
content: { type: 'technical' },
document_type: 'technical',
updated_at: '2025-07-30T13:00:00Z'
},
{
id: 'doc-3',
title: 'Document 3',
content: { type: 'business' },
document_type: 'business',
updated_at: '2025-07-30T14:00:00Z'
}
])
const [selectedDocument, setSelectedDocument] = React.useState(documents[0])
const { showToast } = { showToast: vi.fn() }
return (
<div>
<div className="flex gap-3 overflow-x-auto pb-2 scrollbar-thin scrollbar-thumb-gray-300 dark:scrollbar-thumb-gray-700">
{documents.map(doc => (
<div
key={doc.id}
data-testid={`document-card-${doc.id}`}
className={`flex-shrink-0 w-48 p-4 rounded-lg cursor-pointer ${
selectedDocument?.id === doc.id ? 'border-2 border-blue-500' : 'border border-gray-200'
}`}
onClick={() => setSelectedDocument(doc)}
>
<div className={`text-xs ${doc.document_type}`}>{doc.document_type}</div>
<h4>{doc.title}</h4>
{selectedDocument?.id !== doc.id && (
<button
data-testid={`delete-${doc.id}`}
onClick={(e) => {
e.stopPropagation()
if (confirm(`Delete "${doc.title}"?`)) {
setDocuments(prev => prev.filter(d => d.id !== doc.id))
if (selectedDocument?.id === doc.id) {
setSelectedDocument(documents.find(d => d.id !== doc.id) || null)
}
showToast('Document deleted', 'success')
}
}}
>
Delete
</button>
)}
</div>
))}
<div
data-testid="new-document-card"
className="flex-shrink-0 w-48 h-32 rounded-lg border-2 border-dashed"
onClick={() => console.log('New document')}
>
New Document
</div>
</div>
{selectedDocument && (
<div data-testid="selected-document">
Selected: {selectedDocument.title}
</div>
)}
</div>
)
}
describe('DocsTab Document Cards Integration', () => {
beforeEach(() => {
vi.clearAllMocks()
})
test('renders all document cards', () => {
render(<DocsTabTest />)
expect(screen.getByTestId('document-card-doc-1')).toBeInTheDocument()
expect(screen.getByTestId('document-card-doc-2')).toBeInTheDocument()
expect(screen.getByTestId('document-card-doc-3')).toBeInTheDocument()
expect(screen.getByTestId('new-document-card')).toBeInTheDocument()
})
test('shows active state on selected document', () => {
render(<DocsTabTest />)
const doc1 = screen.getByTestId('document-card-doc-1')
expect(doc1.className).toContain('border-blue-500')
const doc2 = screen.getByTestId('document-card-doc-2')
expect(doc2.className).not.toContain('border-blue-500')
})
test('switches between documents', () => {
render(<DocsTabTest />)
// Initially doc-1 is selected
expect(screen.getByTestId('selected-document')).toHaveTextContent('Selected: Document 1')
// Click on doc-2
fireEvent.click(screen.getByTestId('document-card-doc-2'))
// Now doc-2 should be selected
expect(screen.getByTestId('selected-document')).toHaveTextContent('Selected: Document 2')
// Check active states
expect(screen.getByTestId('document-card-doc-1').className).not.toContain('border-blue-500')
expect(screen.getByTestId('document-card-doc-2').className).toContain('border-blue-500')
})
test('deletes document with confirmation', () => {
const confirmSpy = vi.spyOn(window, 'confirm').mockReturnValue(true)
render(<DocsTabTest />)
// Click delete on doc-2
const deleteButton = screen.getByTestId('delete-doc-2')
fireEvent.click(deleteButton)
expect(confirmSpy).toHaveBeenCalledWith('Delete "Document 2"?')
// Document should be removed
expect(screen.queryByTestId('document-card-doc-2')).not.toBeInTheDocument()
confirmSpy.mockRestore()
})
test('cancels delete when user declines', () => {
const confirmSpy = vi.spyOn(window, 'confirm').mockReturnValue(false)
render(<DocsTabTest />)
// Click delete on doc-2
const deleteButton = screen.getByTestId('delete-doc-2')
fireEvent.click(deleteButton)
// Document should still be there
expect(screen.getByTestId('document-card-doc-2')).toBeInTheDocument()
confirmSpy.mockRestore()
})
test('selects next document when deleting active document', () => {
const confirmSpy = vi.spyOn(window, 'confirm').mockReturnValue(true)
render(<DocsTabTest />)
// doc-1 is initially selected
expect(screen.getByTestId('selected-document')).toHaveTextContent('Selected: Document 1')
// Switch to doc-2
fireEvent.click(screen.getByTestId('document-card-doc-2'))
expect(screen.getByTestId('selected-document')).toHaveTextContent('Selected: Document 2')
// Delete doc-2 (currently selected)
const deleteButton = screen.getByTestId('delete-doc-2')
fireEvent.click(deleteButton)
// Should automatically select another document
expect(screen.getByTestId('selected-document')).toHaveTextContent('Selected: Document')
expect(screen.queryByTestId('document-card-doc-2')).not.toBeInTheDocument()
confirmSpy.mockRestore()
})
test('does not show delete button on active card', () => {
render(<DocsTabTest />)
// doc-1 is active, should not have delete button
expect(screen.queryByTestId('delete-doc-1')).not.toBeInTheDocument()
// doc-2 is not active, should have delete button
expect(screen.getByTestId('delete-doc-2')).toBeInTheDocument()
})
test('horizontal scroll container has correct classes', () => {
const { container } = render(<DocsTabTest />)
const scrollContainer = container.querySelector('.overflow-x-auto')
expect(scrollContainer).toBeInTheDocument()
expect(scrollContainer?.className).toContain('scrollbar-thin')
expect(scrollContainer?.className).toContain('scrollbar-thumb-gray-300')
})
test('document cards maintain fixed width', () => {
render(<DocsTabTest />)
const cards = screen.getAllByTestId(/document-card-doc-/)
cards.forEach(card => {
expect(card.className).toContain('flex-shrink-0')
expect(card.className).toContain('w-48')
})
})
})

View File

@@ -0,0 +1,227 @@
import { render, screen, fireEvent } from '@testing-library/react'
import { describe, test, expect, vi } from 'vitest'
import React from 'react'
import { DocumentCard, NewDocumentCard } from '../../../src/components/project-tasks/DocumentCard'
import type { ProjectDoc } from '../../../src/components/project-tasks/DocumentCard'
describe('DocumentCard', () => {
const mockDocument: ProjectDoc = {
id: 'doc-1',
title: 'Test Document',
content: { test: 'content' },
document_type: 'prp',
updated_at: '2025-07-30T12:00:00Z',
}
const mockHandlers = {
onSelect: vi.fn(),
onDelete: vi.fn(),
}
beforeEach(() => {
vi.clearAllMocks()
})
test('renders document card with correct content', () => {
render(
<DocumentCard
document={mockDocument}
isActive={false}
onSelect={mockHandlers.onSelect}
onDelete={mockHandlers.onDelete}
isDarkMode={false}
/>
)
expect(screen.getByText('Test Document')).toBeInTheDocument()
expect(screen.getByText('prp')).toBeInTheDocument()
expect(screen.getByText('7/30/2025')).toBeInTheDocument()
})
test('shows correct icon and color for different document types', () => {
const documentTypes = [
{ type: 'prp', expectedClass: 'text-blue-600' },
{ type: 'technical', expectedClass: 'text-green-600' },
{ type: 'business', expectedClass: 'text-purple-600' },
{ type: 'meeting_notes', expectedClass: 'text-orange-600' },
]
documentTypes.forEach(({ type, expectedClass }) => {
const { container, rerender } = render(
<DocumentCard
document={{ ...mockDocument, document_type: type }}
isActive={false}
onSelect={mockHandlers.onSelect}
onDelete={mockHandlers.onDelete}
isDarkMode={false}
/>
)
const badge = container.querySelector(`.${expectedClass}`)
expect(badge).toBeInTheDocument()
})
})
test('applies active styles when selected', () => {
const { container } = render(
<DocumentCard
document={mockDocument}
isActive={true}
onSelect={mockHandlers.onSelect}
onDelete={mockHandlers.onDelete}
isDarkMode={false}
/>
)
const card = container.firstChild as HTMLElement
expect(card.className).toContain('border-blue-500')
expect(card.className).toContain('scale-105')
})
test('calls onSelect when clicked', () => {
render(
<DocumentCard
document={mockDocument}
isActive={false}
onSelect={mockHandlers.onSelect}
onDelete={mockHandlers.onDelete}
isDarkMode={false}
/>
)
const card = screen.getByText('Test Document').closest('div')
fireEvent.click(card!)
expect(mockHandlers.onSelect).toHaveBeenCalledWith(mockDocument)
})
test('shows delete button on hover', () => {
const { container } = render(
<DocumentCard
document={mockDocument}
isActive={false}
onSelect={mockHandlers.onSelect}
onDelete={mockHandlers.onDelete}
isDarkMode={false}
/>
)
const card = container.firstChild as HTMLElement
// Delete button should not be visible initially
expect(screen.queryByLabelText('Delete Test Document')).not.toBeInTheDocument()
// Hover over the card
fireEvent.mouseEnter(card)
// Delete button should now be visible
expect(screen.getByLabelText('Delete Test Document')).toBeInTheDocument()
// Mouse leave
fireEvent.mouseLeave(card)
// Delete button should be hidden again
expect(screen.queryByLabelText('Delete Test Document')).not.toBeInTheDocument()
})
test('does not show delete button on active card', () => {
const { container } = render(
<DocumentCard
document={mockDocument}
isActive={true}
onSelect={mockHandlers.onSelect}
onDelete={mockHandlers.onDelete}
isDarkMode={false}
/>
)
const card = container.firstChild as HTMLElement
fireEvent.mouseEnter(card)
expect(screen.queryByLabelText('Delete Test Document')).not.toBeInTheDocument()
})
test('confirms before deleting', () => {
const confirmSpy = vi.spyOn(window, 'confirm').mockReturnValue(true)
const { container } = render(
<DocumentCard
document={mockDocument}
isActive={false}
onSelect={mockHandlers.onSelect}
onDelete={mockHandlers.onDelete}
isDarkMode={false}
/>
)
const card = container.firstChild as HTMLElement
fireEvent.mouseEnter(card)
const deleteButton = screen.getByLabelText('Delete Test Document')
fireEvent.click(deleteButton)
expect(confirmSpy).toHaveBeenCalledWith('Delete "Test Document"?')
expect(mockHandlers.onDelete).toHaveBeenCalledWith('doc-1')
confirmSpy.mockRestore()
})
test('cancels delete when user declines', () => {
const confirmSpy = vi.spyOn(window, 'confirm').mockReturnValue(false)
const { container } = render(
<DocumentCard
document={mockDocument}
isActive={false}
onSelect={mockHandlers.onSelect}
onDelete={mockHandlers.onDelete}
isDarkMode={false}
/>
)
const card = container.firstChild as HTMLElement
fireEvent.mouseEnter(card)
const deleteButton = screen.getByLabelText('Delete Test Document')
fireEvent.click(deleteButton)
expect(confirmSpy).toHaveBeenCalled()
expect(mockHandlers.onDelete).not.toHaveBeenCalled()
confirmSpy.mockRestore()
})
test('applies dark mode styles correctly', () => {
const { container } = render(
<DocumentCard
document={mockDocument}
isActive={false}
onSelect={mockHandlers.onSelect}
onDelete={mockHandlers.onDelete}
isDarkMode={true}
/>
)
const card = container.firstChild as HTMLElement
expect(card.className).toContain('dark:')
})
})
describe('NewDocumentCard', () => {
test('renders new document card', () => {
const onClick = vi.fn()
render(<NewDocumentCard onClick={onClick} />)
expect(screen.getByText('New Document')).toBeInTheDocument()
})
test('calls onClick when clicked', () => {
const onClick = vi.fn()
render(<NewDocumentCard onClick={onClick} />)
const card = screen.getByText('New Document').closest('div')
fireEvent.click(card!)
expect(onClick).toHaveBeenCalledTimes(1)
})
})

View File

@@ -0,0 +1,272 @@
import { describe, test, expect } from 'vitest'
// Test the PRP to Markdown conversion logic
describe('MilkdownEditor PRP Conversion', () => {
// Helper function to format values (extracted from component)
const formatValue = (value: any, indent = ''): string => {
if (Array.isArray(value)) {
return value.map(item => `${indent}- ${formatValue(item, indent + ' ')}`).join('\n') + '\n'
}
if (typeof value === 'object' && value !== null) {
let result = ''
Object.entries(value).forEach(([key, val]) => {
const formattedKey = key.replace(/_/g, ' ')
.split(' ')
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
.join(' ')
if (typeof val === 'string' || typeof val === 'number') {
result += `${indent}**${formattedKey}:** ${val}\n\n`
} else {
result += `${indent}### ${formattedKey}\n\n${formatValue(val, indent)}`
}
})
return result
}
return String(value)
}
// Simplified version of convertPRPToMarkdown for testing
const convertPRPToMarkdown = (content: any, docTitle = 'Test Doc'): string => {
let markdown = `# ${content.title || docTitle}\n\n`
// Metadata section
if (content.version || content.author || content.date || content.status) {
markdown += `## Metadata\n\n`
if (content.version) markdown += `- **Version:** ${content.version}\n`
if (content.author) markdown += `- **Author:** ${content.author}\n`
if (content.date) markdown += `- **Date:** ${content.date}\n`
if (content.status) markdown += `- **Status:** ${content.status}\n`
markdown += '\n'
}
// Goal section
if (content.goal) {
markdown += `## Goal\n\n${content.goal}\n\n`
}
// Why section
if (content.why) {
markdown += `## Why\n\n`
if (Array.isArray(content.why)) {
content.why.forEach(item => markdown += `- ${item}\n`)
} else {
markdown += `${content.why}\n`
}
markdown += '\n'
}
// What section
if (content.what) {
markdown += `## What\n\n`
if (typeof content.what === 'string') {
markdown += `${content.what}\n\n`
} else if (content.what.description) {
markdown += `${content.what.description}\n\n`
if (content.what.success_criteria) {
markdown += `### Success Criteria\n\n`
content.what.success_criteria.forEach((criterion: string) => {
markdown += `- [ ] ${criterion}\n`
})
markdown += '\n'
}
}
}
// Handle all other sections dynamically
const handledKeys = [
'title', 'version', 'author', 'date', 'status', 'goal', 'why', 'what',
'document_type'
]
Object.entries(content).forEach(([key, value]) => {
if (!handledKeys.includes(key) && value) {
const sectionTitle = key.replace(/_/g, ' ')
.split(' ')
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
.join(' ')
markdown += `## ${sectionTitle}\n\n`
markdown += formatValue(value)
markdown += '\n'
}
})
return markdown
}
test('converts basic PRP structure to markdown', () => {
const prp = {
title: 'Test PRP',
version: '1.0',
author: 'Test Author',
date: '2025-07-30',
status: 'draft',
goal: 'Test goal'
}
const markdown = convertPRPToMarkdown(prp)
expect(markdown).toContain('# Test PRP')
expect(markdown).toContain('## Metadata')
expect(markdown).toContain('- **Version:** 1.0')
expect(markdown).toContain('- **Author:** Test Author')
expect(markdown).toContain('- **Date:** 2025-07-30')
expect(markdown).toContain('- **Status:** draft')
expect(markdown).toContain('## Goal\n\nTest goal')
})
test('handles array why section', () => {
const prp = {
title: 'Test PRP',
why: ['Reason 1', 'Reason 2', 'Reason 3']
}
const markdown = convertPRPToMarkdown(prp)
expect(markdown).toContain('## Why')
expect(markdown).toContain('- Reason 1')
expect(markdown).toContain('- Reason 2')
expect(markdown).toContain('- Reason 3')
})
test('handles string why section', () => {
const prp = {
title: 'Test PRP',
why: 'Single reason for the change'
}
const markdown = convertPRPToMarkdown(prp)
expect(markdown).toContain('## Why')
expect(markdown).toContain('Single reason for the change')
})
test('handles complex what section with success criteria', () => {
const prp = {
title: 'Test PRP',
what: {
description: 'Main description of what we are building',
success_criteria: [
'Criterion 1',
'Criterion 2',
'Criterion 3'
]
}
}
const markdown = convertPRPToMarkdown(prp)
expect(markdown).toContain('## What')
expect(markdown).toContain('Main description of what we are building')
expect(markdown).toContain('### Success Criteria')
expect(markdown).toContain('- [ ] Criterion 1')
expect(markdown).toContain('- [ ] Criterion 2')
expect(markdown).toContain('- [ ] Criterion 3')
})
test('handles dynamic sections', () => {
const prp = {
title: 'Test PRP',
user_personas: {
developer: {
name: 'Developer Dan',
goals: ['Write clean code', 'Ship features fast']
}
},
technical_requirements: {
frontend: 'React 18',
backend: 'FastAPI',
database: 'PostgreSQL'
}
}
const markdown = convertPRPToMarkdown(prp)
expect(markdown).toContain('## User Personas')
expect(markdown).toContain('### Developer')
expect(markdown).toContain('**Name:** Developer Dan')
expect(markdown).toContain('## Technical Requirements')
expect(markdown).toContain('**Frontend:** React 18')
expect(markdown).toContain('**Backend:** FastAPI')
})
test('formats nested objects correctly', () => {
const value = {
level1: {
level2: {
level3: 'Deep value'
}
}
}
const formatted = formatValue(value)
expect(formatted).toContain('### Level1')
expect(formatted).toContain('### Level2')
expect(formatted).toContain('**Level3:** Deep value')
})
test('formats arrays correctly', () => {
const value = ['Item 1', 'Item 2', { nested: 'Nested item' }]
const formatted = formatValue(value)
expect(formatted).toContain('- Item 1')
expect(formatted).toContain('- Item 2')
expect(formatted).toContain('**Nested:** Nested item')
})
test('handles empty content', () => {
const prp = {}
const markdown = convertPRPToMarkdown(prp, 'Default Title')
expect(markdown).toBe('# Default Title\n\n')
})
test('skips null and undefined values', () => {
const prp = {
title: 'Test PRP',
null_field: null,
undefined_field: undefined,
empty_string: '',
valid_field: 'Valid content'
}
const markdown = convertPRPToMarkdown(prp)
expect(markdown).not.toContain('Null Field')
expect(markdown).not.toContain('Undefined Field')
expect(markdown).not.toContain('Empty String')
expect(markdown).toContain('## Valid Field')
expect(markdown).toContain('Valid content')
})
test('converts snake_case to Title Case', () => {
const prp = {
title: 'Test PRP',
user_journey_mapping: 'Content',
api_endpoint_design: 'More content'
}
const markdown = convertPRPToMarkdown(prp)
expect(markdown).toContain('## User Journey Mapping')
expect(markdown).toContain('## Api Endpoint Design')
})
test('preserves markdown formatting in content', () => {
const prp = {
title: 'Test PRP',
description: '**Bold text** and *italic text* with `code`'
}
const markdown = convertPRPToMarkdown(prp)
expect(markdown).toContain('**Bold text** and *italic text* with `code`')
})
})

View File

@@ -0,0 +1,186 @@
import { render, screen, fireEvent } from '@testing-library/react'
import { describe, test, expect, vi } from 'vitest'
import React from 'react'
import { PRPViewer } from '../../../src/components/prp/PRPViewer'
import type { PRPContent } from '../../../src/components/prp/types/prp.types'
describe('PRPViewer', () => {
const mockContent: PRPContent = {
title: 'Test PRP',
version: '1.0',
author: 'Test Author',
date: '2025-07-30',
status: 'draft',
goal: 'Test goal with [Image #1] placeholder',
why: 'Test reason with [Image #2] reference',
what: {
description: 'Test description with [Image #3] and [Image #4]',
success_criteria: ['Criterion 1', 'Criterion 2 with [Image #5]']
},
context: {
background: 'Background with [Image #6]',
objectives: ['Objective 1', 'Objective 2']
}
}
test('renders without [Image #N] placeholders', () => {
render(<PRPViewer content={mockContent} />)
// Check that [Image #N] placeholders are replaced
expect(screen.queryByText(/\[Image #\d+\]/)).not.toBeInTheDocument()
// Check that content is present
expect(screen.getByText(/Test goal/)).toBeInTheDocument()
expect(screen.getByText(/Test reason/)).toBeInTheDocument()
expect(screen.getByText(/Test description/)).toBeInTheDocument()
})
test('processes nested content with image placeholders', () => {
const { container } = render(<PRPViewer content={mockContent} />)
// Check that the content has been processed
const htmlContent = container.innerHTML
// Should not contain raw [Image #N] text
expect(htmlContent).not.toMatch(/\[Image #\d+\]/)
// Should contain processed markdown image syntax
expect(htmlContent).toContain('Image 1')
expect(htmlContent).toContain('Image 2')
})
test('renders metadata section correctly', () => {
render(<PRPViewer content={mockContent} />)
expect(screen.getByText('Test PRP')).toBeInTheDocument()
expect(screen.getByText('1.0')).toBeInTheDocument()
expect(screen.getByText('Test Author')).toBeInTheDocument()
expect(screen.getByText('draft')).toBeInTheDocument()
})
test('handles empty content gracefully', () => {
render(<PRPViewer content={{} as PRPContent} />)
// Should render without errors
expect(screen.getByText(/Metadata/)).toBeInTheDocument()
})
test('handles null content', () => {
render(<PRPViewer content={null as any} />)
expect(screen.getByText('No PRP content available')).toBeInTheDocument()
})
test('handles string content in objects', () => {
const stringContent = {
title: 'String Test',
description: 'This has [Image #1] in it'
}
render(<PRPViewer content={stringContent as any} />)
// Should process the image placeholder
expect(screen.queryByText(/\[Image #1\]/)).not.toBeInTheDocument()
expect(screen.getByText(/This has/)).toBeInTheDocument()
})
test('handles array content with image placeholders', () => {
const arrayContent = {
title: 'Array Test',
items: [
'Item 1 with [Image #1]',
'Item 2 with [Image #2]',
{ nested: 'Nested with [Image #3]' }
]
}
render(<PRPViewer content={arrayContent as any} />)
// Should process all image placeholders
expect(screen.queryByText(/\[Image #\d+\]/)).not.toBeInTheDocument()
})
test('renders collapsible sections', () => {
render(<PRPViewer content={mockContent} />)
// Find collapsible sections
const contextSection = screen.getByText('Context').closest('div')
expect(contextSection).toBeInTheDocument()
// Should have chevron icon for collapsible sections
const chevrons = screen.getAllByTestId('chevron-icon')
expect(chevrons.length).toBeGreaterThan(0)
})
test('toggles section visibility', () => {
render(<PRPViewer content={mockContent} />)
// Find a collapsible section header
const contextHeader = screen.getByText('Context').closest('button')
// The section should be visible initially (defaultOpen for first 5 sections)
expect(screen.getByText(/Background with/)).toBeInTheDocument()
// Click to collapse
fireEvent.click(contextHeader!)
// Content should be hidden
expect(screen.queryByText(/Background with/)).not.toBeInTheDocument()
// Click to expand
fireEvent.click(contextHeader!)
// Content should be visible again
expect(screen.getByText(/Background with/)).toBeInTheDocument()
})
test('applies dark mode styles', () => {
const { container } = render(<PRPViewer content={mockContent} isDarkMode={true} />)
const viewer = container.querySelector('.prp-viewer')
expect(viewer?.className).toContain('dark')
})
test('uses section overrides when provided', () => {
const CustomSection = ({ data, title }: any) => (
<div data-testid="custom-section">
<h3>{title}</h3>
<p>Custom rendering of: {JSON.stringify(data)}</p>
</div>
)
const overrides = {
context: CustomSection
}
render(<PRPViewer content={mockContent} sectionOverrides={overrides} />)
expect(screen.getByTestId('custom-section')).toBeInTheDocument()
expect(screen.getByText(/Custom rendering of/)).toBeInTheDocument()
})
test('sorts sections by group', () => {
const complexContent = {
title: 'Complex PRP',
// These should be sorted in a specific order
validation_gates: { test: 'validation' },
user_personas: { test: 'personas' },
context: { test: 'context' },
user_flows: { test: 'flows' },
success_metrics: { test: 'metrics' }
}
const { container } = render(<PRPViewer content={complexContent as any} />)
// Get all section titles in order
const sectionTitles = Array.from(
container.querySelectorAll('h3')
).map(el => el.textContent)
// Context should come before personas
const contextIndex = sectionTitles.findIndex(t => t?.includes('Context'))
const personasIndex = sectionTitles.findIndex(t => t?.includes('Personas'))
expect(contextIndex).toBeLessThan(personasIndex)
})
})