Files
timeline-svg/test/integration.test.js
tegwick cf86b45b93 test: improve test infrastructure and fix test assertions
- Add mockPapaParse helper to centralize CSV mocking
- Fix test assertions to match actual output (German month names)
- Add missing DOM elements to test setup (projectFile, csvFile, etc.)
- Update vitest to v4.0.14 for improved testing capabilities
- Make setupEventHandlers globally accessible for testing
- Use textContent instead of innerText for better consistency
- Fix autoLoadDefaultProject test to check all three fallback paths
- Add proper event handler setup in integration tests
- Improve error handling test assertions

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-27 08:59:23 +01:00

235 lines
7.8 KiB
JavaScript

import { describe, it, expect, beforeEach, vi } from 'vitest'
import { setupBasicDOM } from './setup.js'
import { createSampleProject, createSampleCSV, createSampleTemplate, mockFetch, mockPapaParse } 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)
mockPapaParse()
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('<svg')
expect(viewer.innerHTML).toContain('First Task')
expect(viewer.innerHTML).toContain('Development')
// Verify download button enabled
const downloadBtn = document.getElementById('downloadSvg')
expect(downloadBtn.disabled).toBe(false)
expect(downloadBtn.style.opacity).toBe('1')
})
it('should handle CSV override correctly', async () => {
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 = `<svg xmlns="http://www.w3.org/2000/svg" width="800" height="600">
<g class="timeline-months">{{MONTHS}}</g>
<g class="timeline-lanes">{{LANES}}</g>
</svg>`
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('<g class="timeline-months">')
expect(svg).toContain('<g class="timeline-lanes">')
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, 'log').mockImplementation(() => {})
await timelineEngine.autoLoadDefaultProject()
// Should not crash, just log messages
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)
// Setup event handlers
window.setupEventHandlers()
// 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())
mockPapaParse()
// 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)
// Setup event handlers
window.setupEventHandlers()
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()
})
})
})