import { describe, it, expect, vi, beforeEach } from 'vitest' import { exportToPDF, exportToZIP, downloadExport } from '../export' import type { SDKState } from '../types' // Mock jsPDF as a class vi.mock('jspdf', () => { return { default: class MockJsPDF { internal = { pageSize: { getWidth: () => 210, getHeight: () => 297 }, } setFillColor = vi.fn().mockReturnThis() setDrawColor = vi.fn().mockReturnThis() setTextColor = vi.fn().mockReturnThis() setFontSize = vi.fn().mockReturnThis() setFont = vi.fn().mockReturnThis() setLineWidth = vi.fn().mockReturnThis() text = vi.fn().mockReturnThis() line = vi.fn().mockReturnThis() rect = vi.fn().mockReturnThis() roundedRect = vi.fn().mockReturnThis() circle = vi.fn().mockReturnThis() addPage = vi.fn().mockReturnThis() setPage = vi.fn().mockReturnThis() getNumberOfPages = vi.fn(() => 5) splitTextToSize = vi.fn((text: string) => [text]) output = vi.fn(() => new Blob(['mock-pdf'], { type: 'application/pdf' })) }, } }) // Mock JSZip as a class vi.mock('jszip', () => { return { default: class MockJSZip { private mockFolder = { file: vi.fn().mockReturnThis(), folder: vi.fn(() => this.mockFolder), } folder = vi.fn(() => this.mockFolder) generateAsync = vi.fn(() => Promise.resolve(new Blob(['mock-zip'], { type: 'application/zip' }))) }, } }) const createMockState = (overrides: Partial = {}): SDKState => ({ version: '1.0.0', lastModified: new Date('2024-01-15'), tenantId: 'test-tenant', userId: 'test-user', subscription: 'PROFESSIONAL', currentPhase: 1, currentStep: 'use-case-workshop', completedSteps: ['use-case-workshop', 'screening'], checkpoints: { 'CP-UC': { checkpointId: 'CP-UC', passed: true, validatedAt: new Date(), validatedBy: 'SYSTEM', errors: [], warnings: [], }, }, useCases: [ { id: 'uc-1', name: 'Test Use Case', description: 'A test use case for testing', category: 'Marketing', stepsCompleted: 3, steps: [ { id: 's1', name: 'Step 1', completed: true, data: {} }, { id: 's2', name: 'Step 2', completed: true, data: {} }, { id: 's3', name: 'Step 3', completed: true, data: {} }, ], assessmentResult: null, createdAt: new Date(), updatedAt: new Date(), }, ], activeUseCase: 'uc-1', screening: null, modules: [], requirements: [], controls: [ { id: 'ctrl-1', name: 'Test Control', description: 'A test control', type: 'TECHNICAL', category: 'Access Control', implementationStatus: 'IMPLEMENTED', effectiveness: 'HIGH', evidence: [], owner: 'Test Owner', dueDate: null, }, ], evidence: [], checklist: [], risks: [ { id: 'risk-1', title: 'Test Risk', description: 'A test risk', category: 'Security', likelihood: 3, impact: 4, severity: 'HIGH', inherentRiskScore: 12, residualRiskScore: 6, status: 'MITIGATED', mitigation: [ { id: 'mit-1', description: 'Test mitigation', type: 'MITIGATE', status: 'COMPLETED', effectiveness: 80, controlId: 'ctrl-1', }, ], owner: 'Risk Owner', relatedControls: ['ctrl-1'], relatedRequirements: [], }, ], aiActClassification: null, obligations: [], dsfa: null, toms: [], retentionPolicies: [], vvt: [], documents: [], cookieBanner: null, consents: [], dsrConfig: null, escalationWorkflows: [], sbom: null, securityIssues: [], securityBacklog: [], commandBarHistory: [], recentSearches: [], preferences: { language: 'de', theme: 'light', compactMode: false, showHints: true, autoSave: true, autoValidate: true, allowParallelWork: true, }, ...overrides, }) describe('exportToPDF', () => { it('should return a Blob', async () => { const state = createMockState() const result = await exportToPDF(state) expect(result).toBeInstanceOf(Blob) }) it('should create a PDF with the correct type', async () => { const state = createMockState() const result = await exportToPDF(state) expect(result.type).toBe('application/pdf') }) it('should handle empty state', async () => { const emptyState = createMockState({ useCases: [], risks: [], controls: [], completedSteps: [], }) const result = await exportToPDF(emptyState) expect(result).toBeInstanceOf(Blob) }) it('should handle state with multiple risks of different severities', async () => { const state = createMockState({ risks: [ { id: 'risk-1', title: 'Critical Risk', description: 'Critical', category: 'Security', likelihood: 5, impact: 5, severity: 'CRITICAL', inherentRiskScore: 25, residualRiskScore: 15, status: 'IDENTIFIED', mitigation: [], owner: null, relatedControls: [], relatedRequirements: [], }, { id: 'risk-2', title: 'Low Risk', description: 'Low', category: 'Operational', likelihood: 1, impact: 1, severity: 'LOW', inherentRiskScore: 1, residualRiskScore: 1, status: 'ACCEPTED', mitigation: [], owner: null, relatedControls: [], relatedRequirements: [], }, ], }) const result = await exportToPDF(state) expect(result).toBeInstanceOf(Blob) }) }) describe('exportToZIP', () => { it('should return a Blob', async () => { const state = createMockState() const result = await exportToZIP(state) expect(result).toBeInstanceOf(Blob) }) it('should create a ZIP with the correct type', async () => { const state = createMockState() const result = await exportToZIP(state) expect(result.type).toBe('application/zip') }) it('should handle empty state', async () => { const emptyState = createMockState({ useCases: [], risks: [], controls: [], completedSteps: [], }) const result = await exportToZIP(emptyState) expect(result).toBeInstanceOf(Blob) }) it('should respect includeEvidence option', async () => { const state = createMockState() const result = await exportToZIP(state, { includeEvidence: false }) expect(result).toBeInstanceOf(Blob) }) it('should respect includeDocuments option', async () => { const state = createMockState() const result = await exportToZIP(state, { includeDocuments: false }) expect(result).toBeInstanceOf(Blob) }) }) describe('downloadExport', () => { let mockCreateElement: ReturnType let mockAppendChild: ReturnType let mockRemoveChild: ReturnType let mockLink: { href: string; download: string; click: ReturnType } beforeEach(() => { mockLink = { href: '', download: '', click: vi.fn(), } mockCreateElement = vi.spyOn(document, 'createElement').mockReturnValue(mockLink as unknown as HTMLElement) mockAppendChild = vi.spyOn(document.body, 'appendChild').mockImplementation(() => mockLink as unknown as HTMLElement) mockRemoveChild = vi.spyOn(document.body, 'removeChild').mockImplementation(() => mockLink as unknown as HTMLElement) }) it('should download JSON format', async () => { const state = createMockState() await downloadExport(state, 'json') expect(mockLink.download).toContain('.json') expect(mockLink.click).toHaveBeenCalled() }) it('should download PDF format', async () => { const state = createMockState() await downloadExport(state, 'pdf') expect(mockLink.download).toContain('.pdf') expect(mockLink.click).toHaveBeenCalled() }) it('should download ZIP format', async () => { const state = createMockState() await downloadExport(state, 'zip') expect(mockLink.download).toContain('.zip') expect(mockLink.click).toHaveBeenCalled() }) it('should include date in filename', async () => { const state = createMockState() await downloadExport(state, 'json') // Check that filename contains a date pattern expect(mockLink.download).toMatch(/ai-compliance-sdk-\d{4}-\d{2}-\d{2}\.json/) }) it('should throw error for unknown format', async () => { const state = createMockState() await expect(downloadExport(state, 'unknown' as any)).rejects.toThrow('Unknown export format') }) })