import { describe, test, expect, beforeEach, vi } from 'vitest'; import { getSavedSessions, saveSession, deleteSession, formatSessionAsMarkdown, downloadFile } from '../services/sessionStorage'; import type { Scenario, Message, AnalysisResult } from '../types'; describe('Session Storage & Export Service', () => { const mockStorage: Record = {}; const mockScenario: Scenario = { id: 'test-scenario-1', title: 'Workplace Disagreement', description: 'Resolving friction with a manager', category: 'Workplace', difficulty: 'Intermediate', systemInstruction: 'INTERNAL CBT LOGIC: React with skepticism.', initialMessage: 'Why was this project late?', cbtFocus: ['All-or-Nothing Thinking', 'Mind Reading'], characterName: 'Director Vance' }; const mockMessages: Message[] = [ { id: 'm1', role: 'model', text: 'Why was this project late?', timestamp: new Date() }, { id: 'm2', role: 'user', text: 'We encountered unexpected API downtime, but delivered within scope.', timestamp: new Date() } ]; const mockAnalysis: AnalysisResult = { analysis: { distortions: ['Mind Reading'], isAdaptive: true, evidence: 'User acknowledged the delay factually without catastrophic self-blame.' }, rating: { score: 4, reason: 'Strong boundary maintenance and objective attribution.' }, suggestions: { alternativeThought: 'Delays happen due to technical constraints; focus on resolution.', behavioralStep: 'Send an updated post-mortem dashboard to stakeholders.' } }; beforeEach(() => { for (const key in mockStorage) { delete mockStorage[key]; } vi.stubGlobal('localStorage', { getItem: (key: string) => mockStorage[key] || null, setItem: (key: string, val: string) => { mockStorage[key] = val; }, removeItem: (key: string) => { delete mockStorage[key]; }, clear: () => { for (const key in mockStorage) { delete mockStorage[key]; } } }); }); test('getSavedSessions returns empty array when storage is empty', () => { expect(getSavedSessions()).toEqual([]); }); test('getSavedSessions handles corrupted JSON gracefully', () => { mockStorage['cognitive-clarity-saved-sessions'] = 'INVALID_JSON_CORRUPTED'; const sessions = getSavedSessions(); expect(sessions).toEqual([]); }); test('saveSession stores new session and retrieves it', () => { const saved = saveSession(mockScenario, 'text', mockMessages, mockAnalysis); expect(saved.scenarioId).toBe(mockScenario.id); expect(saved.scenarioTitle).toBe(mockScenario.title); expect(saved.mode).toBe('text'); expect(saved.messages.length).toBe(2); expect(saved.finalReport?.rating.score).toBe(4); const all = getSavedSessions(); expect(all.length).toBe(1); expect(all[0].id).toBe(saved.id); }); test('saveSession caps history at 50 sessions', () => { for (let i = 0; i < 55; i++) { saveSession( { ...mockScenario, id: `scenario-${i}` }, 'text', mockMessages ); } const all = getSavedSessions(); expect(all.length).toBe(50); }); test('deleteSession removes specific session by id', () => { const s1 = saveSession(mockScenario, 'text', mockMessages); const s2 = saveSession({ ...mockScenario, id: 's2', title: 'Second' }, 'voice', mockMessages); expect(getSavedSessions().length).toBe(2); deleteSession(s1.id); const remaining = getSavedSessions(); expect(remaining.length).toBe(1); expect(remaining[0].id).toBe(s2.id); }); test('formatSessionAsMarkdown generates structured markdown report with all sections', () => { const md = formatSessionAsMarkdown('Workplace Disagreement', mockAnalysis, mockMessages); expect(md).toContain('# Cognitive Clarity — Clinical CBT Session Report'); expect(md).toContain('**Scenario:** Workplace Disagreement'); expect(md).toContain('✅ Adaptive Growth Observed'); expect(md).toContain('**Mastery Rating:** 4 / 5'); expect(md).toContain('- Mind Reading'); expect(md).toContain('User acknowledged the delay factually'); expect(md).toContain('Delays happen due to technical constraints'); expect(md).toContain('## 3. Session Transcript'); expect(md).toContain('**[AI Partner]**: Why was this project late?'); expect(md).toContain('**[You]**: We encountered unexpected API downtime'); }); test('downloadFile creates object URL and triggers anchor click in DOM', () => { let clicked = false; let appended = false; let removed = false; let revokedUrl = ''; const mockAnchor = { href: '', download: '', click: () => { clicked = true; } }; vi.stubGlobal('URL', { createObjectURL: () => 'blob:mock-url-123', revokeObjectURL: (url: string) => { revokedUrl = url; } }); vi.stubGlobal('document', { createElement: () => mockAnchor, body: { appendChild: () => { appended = true; }, removeChild: () => { removed = true; } } }); downloadFile('# Report', 'report.md', 'text/markdown'); expect(mockAnchor.download).toBe('report.md'); expect(mockAnchor.href).toBe('blob:mock-url-123'); expect(clicked).toBe(true); expect(appended).toBe(true); expect(removed).toBe(true); expect(revokedUrl).toBe('blob:mock-url-123'); }); });