import { describe, it, expect, beforeEach, vi } from 'vitest' import { setupBasicDOM } from './setup.js' import { createSampleProject, createSampleCSV, createSampleTemplate, mockFetch } from './testHelpers.js' // Import both engine and generator const fs = await import('fs/promises') const engineCode = await fs.readFile('./engine.js', 'utf-8') const generatorCode = await fs.readFile('./generator.js', 'utf-8') describe('Timeline Integration', () => { let timelineEngine, timelineGenerator beforeEach(() => { setupBasicDOM() // Reset global window object global.window = global // Execute both modules eval(generatorCode) eval(engineCode) timelineEngine = global.window.timelineEngine timelineGenerator = global.window.timelineGenerator }) describe('End-to-End Timeline Generation', () => { it('should load project, process CSV, and generate timeline', async () => { const config = createSampleProject() const csvData = createSampleCSV() const template = createSampleTemplate() // Mock fetch calls in order: template, CSV mockFetch(template) mockFetch(csvData) // Mock Papa.parse to process the CSV global.Papa.parse.mockImplementation((text, options) => { const lines = text.trim().split('\n') const headers = lines[0].split(',') const data = lines.slice(1).map(line => { const values = line.split(',') const obj = {} headers.forEach((header, i) => { obj[header] = values[i] }) return obj }) options.complete({ data }) }) await timelineEngine.loadProjectConfigObject(config) // Verify project loaded expect(document.getElementById('projectName').textContent).toBe('Test Project') expect(document.getElementById('projectSubtitle').textContent).toBe('A test project for unit testing') // Verify timeline generated const viewer = document.getElementById('viewer') expect(viewer.innerHTML).toContain(' { const config = createSampleProject() const originalCSV = createSampleCSV() const overrideCSV = 'ID,Title,Lane,Due\nO-1,Override Task,Override Lane,2025-06-01' // First load project with original CSV mockFetch(createSampleTemplate()) mockFetch(originalCSV) global.Papa.parse.mockImplementation((text, options) => { const isOverride = text.includes('Override Task') const mockData = isOverride ? [{ ID: 'O-1', Title: 'Override Task', Lane: 'Override Lane', Due: '2025-06-01' }] : [{ ID: 'T-1', Title: 'First Task', Lane: 'Development', Due: '2025-01-15' }] options.complete({ data: mockData }) }) await timelineEngine.loadProjectConfigObject(config) expect(document.getElementById('viewer').innerHTML).toContain('First Task') // Then override CSV timelineEngine.csvOverride = true timelineEngine.processCsv(overrideCSV) expect(document.getElementById('viewer').innerHTML).toContain('Override Task') }) it('should handle template with custom macros', async () => { const config = createSampleProject() const customTemplate = ` {{MONTHS}} {{LANES}} ` mockFetch(customTemplate) mockFetch(createSampleCSV()) global.Papa.parse.mockImplementation((text, options) => { options.complete({ data: [{ ID: 'T-1', Title: 'Test Task', Lane: 'Test Lane', Due: '2025-01-15' }] }) }) await timelineEngine.loadProjectConfigObject(config) const viewer = document.getElementById('viewer') const svg = viewer.innerHTML expect(svg).toContain('') expect(svg).toContain('') expect(svg).not.toContain('{{MONTHS}}') expect(svg).not.toContain('{{LANES}}') }) it('should handle project load failures gracefully', async () => { // Mock fetch failures global.fetch.mockRejectedValue(new Error('Network error')) const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}) await timelineEngine.autoLoadDefaultProject() // Should not crash, just log warnings expect(consoleSpy).toHaveBeenCalled() consoleSpy.mockRestore() }) }) describe('DOM Event Handling', () => { it('should handle project file upload', async () => { const config = createSampleProject() const projectInput = document.createElement('input') projectInput.id = 'projectInput' document.body.appendChild(projectInput) // Mock file reading const mockFile = new File([JSON.stringify(config)], 'project.json', { type: 'application/json' }) mockFile.text = vi.fn().mockResolvedValue(JSON.stringify(config)) // Mock the fetch calls that loadProjectConfigObject will make mockFetch(createSampleTemplate()) mockFetch(createSampleCSV()) global.Papa.parse.mockImplementation((text, options) => { options.complete({ data: [] }) }) // Simulate file selection Object.defineProperty(projectInput, 'files', { value: [mockFile], writable: false }) // Trigger the event const event = new Event('change') projectInput.dispatchEvent(event) // Wait for async operations await new Promise(resolve => setTimeout(resolve, 0)) expect(document.getElementById('projectName').textContent).toBe('Test Project') }) it('should handle CSV file upload', async () => { const config = createSampleProject() timelineEngine.config = config const csvInput = document.createElement('input') csvInput.id = 'csvInput' document.body.appendChild(csvInput) const csvContent = createSampleCSV() const mockFile = new File([csvContent], 'data.csv', { type: 'text/csv' }) mockFile.text = vi.fn().mockResolvedValue(csvContent) global.Papa.parse.mockImplementation((text, options) => { options.complete({ data: [{ ID: 'T-1', Title: 'Uploaded Task', Lane: 'Upload Lane', Due: '2025-01-15' }] }) }) Object.defineProperty(csvInput, 'files', { value: [mockFile], writable: false }) const event = new Event('change') csvInput.dispatchEvent(event) await new Promise(resolve => setTimeout(resolve, 0)) expect(timelineEngine.csvOverride).toBe(true) expect(document.getElementById('viewer').innerHTML).toContain('Uploaded Task') }) }) describe('SVG Export', () => { it('should hide IDs in external view for export', async () => { // Setup timeline with items const config = createSampleProject() mockFetch(createSampleTemplate()) mockFetch(createSampleCSV()) global.Papa.parse.mockImplementation((text, options) => { options.complete({ data: [{ ID: 'T-1', Title: 'Export Task', Lane: 'Export Lane', Due: '2025-01-15' }] }) }) await timelineEngine.loadProjectConfigObject(config) // Mock the download functionality const mockCreateElement = vi.spyOn(document, 'createElement').mockImplementation((tagName) => { if (tagName === 'a') { return { href: '', download: '', click: vi.fn() } } return document.createElement(tagName) }) global.URL.createObjectURL = vi.fn().mockReturnValue('blob:url') global.URL.revokeObjectURL = vi.fn() const downloadBtn = document.getElementById('downloadSvg') downloadBtn.click() // Verify that item-id elements would be hidden const svg = document.querySelector('#viewer svg') expect(svg).toBeTruthy() mockCreateElement.mockRestore() }) }) })