/** * End-to-End Scenario Testing * Tests complete user journeys from start to finish */ class E2EScenarioTester { constructor() { this.results = []; this.currentScenario = null; this.testContainer = null; } /** * Initialize E2E testing environment */ async initialize() { console.log('šŸŽ¬ Initializing E2E Scenario Tests...'); // Create results container this.testContainer = document.createElement('div'); this.testContainer.id = 'e2e-test-container'; this.testContainer.style.cssText = ` position: fixed; bottom: 10px; right: 10px; width: 400px; max-height: 300px; overflow-y: auto; background: linear-gradient(135deg, #ff9a56 0%, #ff6b9d 100%); color: white; border-radius: 12px; padding: 15px; z-index: 10002; font-family: system-ui, sans-serif; font-size: 12px; box-shadow: 0 8px 32px rgba(0,0,0,0.3); `; document.body.appendChild(this.testContainer); this.log('āœ… E2E test environment ready'); } /** * Run all E2E scenarios */ async runAllScenarios() { await this.initialize(); this.log('šŸŽ¬ Starting End-to-End scenario testing...'); // Scenario 1: Complete Learning Session await this.runScenario('Complete Learning Session', async () => { return await this.testCompleteLearningSession(); }); // Scenario 2: Vocabulary Discovery Journey await this.runScenario('Vocabulary Discovery Journey', async () => { return await this.testVocabularyDiscoveryJourney(); }); // Scenario 3: Smart Guide Full Flow await this.runScenario('Smart Guide Full Flow', async () => { return await this.testSmartGuideFullFlow(); }); // Scenario 4: Progress Tracking Flow await this.runScenario('Progress Tracking Flow', async () => { return await this.testProgressTrackingFlow(); }); // Scenario 5: Error Recovery Flow await this.runScenario('Error Recovery Flow', async () => { return await this.testErrorRecoveryFlow(); }); this.generateE2EReport(); } /** * Test complete learning session flow */ async testCompleteLearningSession() { this.log('šŸ“š Testing complete learning session...'); const steps = [ { name: 'Load application', test: () => window.app && window.app.getStatus().status === 'running' }, { name: 'Access flashcard learning', test: async () => { // Try to navigate to flashcards const router = window.app?.getCore()?.router; if (router) { router.navigate('/games/flashcard'); await this.wait(1000); return true; } return false; } }, { name: 'Start flashcard session', test: async () => { const flashcard = window.app?.getModule?.('flashcardLearning'); if (!flashcard) return false; try { await flashcard.start(); await this.wait(500); return flashcard._isActive; } catch (error) { this.log(`āš ļø Flashcard start error: ${error.message}`); return false; } } }, { name: 'Interact with flashcard', test: async () => { // Simulate rating a card const ratingBtn = document.querySelector('[data-rating="good"], .confidence-btn'); if (ratingBtn) { this.simulateClick(ratingBtn); await this.wait(300); return true; } return false; } }, { name: 'Complete session', test: async () => { // Try to end the session const flashcard = window.app?.getModule?.('flashcardLearning'); if (flashcard && flashcard._isActive) { await flashcard.stop(); return !flashcard._isActive; } return true; // Already stopped } } ]; return await this.runSteps(steps); } /** * Test vocabulary discovery journey */ async testVocabularyDiscoveryJourney() { this.log('šŸ“– Testing vocabulary discovery journey...'); const steps = [ { name: 'Check prerequisite engine', test: () => window.prerequisiteEngine && typeof window.prerequisiteEngine.markWordDiscovered === 'function' }, { name: 'Open vocabulary modal', test: async () => { if (typeof window.showVocabularyKnowledge === 'function') { try { await window.showVocabularyKnowledge(); await this.wait(500); const modal = document.getElementById('vocabularyModal'); return modal && modal.style.display !== 'none'; } catch (error) { this.log(`āš ļø Vocabulary modal error: ${error.message}`); return false; } } return false; } }, { name: 'Verify progress bars', test: () => { const modal = document.getElementById('vocabularyModal'); if (!modal) return false; const progressBars = modal.querySelectorAll('.progress-bar'); return progressBars.length >= 2; } }, { name: 'Test word discovery', test: () => { const testWord = 'journey_test_word'; window.prerequisiteEngine.markWordDiscovered(testWord); return window.prerequisiteEngine.isDiscovered(testWord); } }, { name: 'Test word mastery', test: () => { const testWord = 'journey_test_word'; window.prerequisiteEngine.markWordMastered(testWord); return window.prerequisiteEngine.isMastered(testWord); } }, { name: 'Close modal', test: async () => { const modal = document.getElementById('vocabularyModal'); if (!modal) return true; const closeBtn = modal.querySelector('.close'); if (closeBtn) { this.simulateClick(closeBtn); await this.wait(300); return modal.style.display === 'none' || !document.body.contains(modal); } return true; } } ]; return await this.runSteps(steps); } /** * Test smart guide full flow */ async testSmartGuideFullFlow() { this.log('🧠 Testing smart guide full flow...'); const steps = [ { name: 'Check intelligent sequencer', test: () => { const sequencer = window.app?.getCore()?.intelligentSequencer; return sequencer && typeof sequencer.startGuidedSession === 'function'; } }, { name: 'Start guided session', test: () => { const sequencer = window.app?.getCore()?.intelligentSequencer; if (!sequencer) return false; try { const session = sequencer.startGuidedSession({ bookId: 'sbs', chapterId: 'sbs-7-8', targetLength: 3 }); return session && session.id; } catch (error) { this.log(`āš ļø Session start error: ${error.message}`); return false; } } }, { name: 'Get exercise recommendation', test: async () => { const sequencer = window.app?.getCore()?.intelligentSequencer; if (!sequencer) return false; try { const recommendation = await sequencer.getNextExercise(); return recommendation && recommendation.type; } catch (error) { this.log(`āš ļø Recommendation error: ${error.message}`); return false; } } }, { name: 'Record exercise completion', test: () => { const sequencer = window.app?.getCore()?.intelligentSequencer; if (!sequencer) return false; try { sequencer.recordExerciseCompletion( { type: 'text', difficulty: 'medium', bookId: 'sbs', chapterId: 'sbs-7-8' }, { timeSpent: 30000, accuracy: 0.8, completed: true } ); return true; } catch (error) { this.log(`āš ļø Recording error: ${error.message}`); return false; } } }, { name: 'Get performance insights', test: () => { const sequencer = window.app?.getCore()?.intelligentSequencer; if (!sequencer) return false; try { const insights = sequencer.getPerformanceInsights(); return insights && insights.overallStats; } catch (error) { this.log(`āš ļø Insights error: ${error.message}`); return false; } } }, { name: 'Stop guided session', test: () => { const sequencer = window.app?.getCore()?.intelligentSequencer; if (!sequencer) return false; try { sequencer.stopGuidedSession(); return !sequencer.isGuiding(); } catch (error) { this.log(`āš ļø Stop session error: ${error.message}`); return false; } } } ]; return await this.runSteps(steps); } /** * Test progress tracking flow */ async testProgressTrackingFlow() { this.log('šŸ“Š Testing progress tracking flow...'); const steps = [ { name: 'Load persisted vocabulary data', test: async () => { if (typeof window.loadPersistedVocabularyData === 'function') { try { const data = await window.loadPersistedVocabularyData('sbs-7-8'); return data !== null; } catch (error) { this.log(`āš ļø Data loading error: ${error.message}`); return false; } } return false; } }, { name: 'Calculate vocabulary progress', test: async () => { if (typeof window.calculateVocabularyProgress === 'function') { try { const progress = await window.calculateVocabularyProgress( 'sbs-7-8', {}, window.prerequisiteEngine ); return progress && typeof progress.discoveredCount === 'number'; } catch (error) { this.log(`āš ļø Progress calculation error: ${error.message}`); return false; } } return false; } }, { name: 'Export mastery state', test: () => { if (!window.prerequisiteEngine) return false; try { const state = window.prerequisiteEngine.exportMasteryState(); return state && state.masteredWords && state.timestamp; } catch (error) { this.log(`āš ļø Export error: ${error.message}`); return false; } } }, { name: 'Import mastery state', test: () => { if (!window.prerequisiteEngine) return false; try { const testState = { masteredWords: ['test1', 'test2'], masteredPhrases: [], masteredGrammar: [], timestamp: new Date().toISOString() }; window.prerequisiteEngine.importMasteryState(testState); return window.prerequisiteEngine.isMastered('test1'); } catch (error) { this.log(`āš ļø Import error: ${error.message}`); return false; } } } ]; return await this.runSteps(steps); } /** * Test error recovery flow */ async testErrorRecoveryFlow() { this.log('šŸ”§ Testing error recovery flow...'); const steps = [ { name: 'Test invalid content loading', test: async () => { if (!window.contentLoader) return false; try { await window.contentLoader.getChapterContent('invalid', 'invalid-chapter'); return false; // Should have thrown an error } catch (error) { // Expected error return true; } } }, { name: 'Test AI validation without API', test: async () => { if (!window.llmValidator) return false; try { await window.llmValidator.validateAnswer('test', 'test', { timeout: 1000 }); return false; // Should have thrown an error } catch (error) { // Expected error without API keys return error.message.includes('provider') || error.message.includes('API'); } } }, { name: 'Test invalid module access', test: () => { try { const invalidModule = window.app?.getModule?.('nonexistent-module'); return invalidModule === null || invalidModule === undefined; } catch (error) { // Expected error return true; } } }, { name: 'Test graceful UI failures', test: () => { try { // Try to access non-existent UI element const element = document.getElementById('nonexistent-element'); return element === null; } catch (error) { // Should not throw error return false; } } } ]; return await this.runSteps(steps); } /** * Run a series of test steps */ async runSteps(steps) { let passed = 0; let failed = 0; for (const step of steps) { try { const result = await step.test(); if (result) { passed++; this.log(` āœ… ${step.name}`); } else { failed++; this.log(` āŒ ${step.name}`); } } catch (error) { failed++; this.log(` āŒ ${step.name}: ${error.message}`); } } const success = failed === 0; const percentage = Math.round((passed / (passed + failed)) * 100); return { success, passed, failed, percentage, total: passed + failed }; } /** * Run a scenario */ async runScenario(name, testFn) { this.currentScenario = name; this.log(`\nšŸŽ¬ Scenario: ${name}`); try { const result = await testFn(); this.results.push({ name, ...result, timestamp: new Date().toISOString() }); if (result.success) { this.log(`āœ… Scenario completed: ${result.percentage}% success`); } else { this.log(`āŒ Scenario failed: ${result.percentage}% success`); } } catch (error) { this.results.push({ name, success: false, passed: 0, failed: 1, percentage: 0, total: 1, error: error.message, timestamp: new Date().toISOString() }); this.log(`āŒ Scenario error: ${error.message}`); } } /** * Generate E2E test report */ generateE2EReport() { const totalScenarios = this.results.length; const successfulScenarios = this.results.filter(r => r.success).length; const overallSuccess = Math.round((successfulScenarios / totalScenarios) * 100); this.log('\nšŸŽ¬ E2E SCENARIO REPORT'); this.log('====================='); this.log(`Total Scenarios: ${totalScenarios}`); this.log(`Successful: ${successfulScenarios} āœ…`); this.log(`Failed: ${totalScenarios - successfulScenarios} āŒ`); this.log(`Overall Success: ${overallSuccess}%`); if (overallSuccess >= 80) { this.log('šŸŽ‰ EXCELLENT - Complete user flows work well!'); } else if (overallSuccess >= 60) { this.log('šŸ‘ GOOD - Most user flows work'); } else { this.log('āš ļø NEEDS IMPROVEMENT - Major user flow issues'); } // Add close button if (this.testContainer) { const closeBtn = document.createElement('button'); closeBtn.textContent = 'āœ– Close'; closeBtn.style.cssText = ` position: absolute; top: 8px; right: 8px; background: rgba(255,255,255,0.2); color: white; border: 1px solid rgba(255,255,255,0.3); border-radius: 4px; padding: 4px 8px; cursor: pointer; font-size: 10px; `; closeBtn.onclick = () => this.testContainer.remove(); this.testContainer.appendChild(closeBtn); } } /** * Utility methods */ wait(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } simulateClick(element) { if (!element) return; const event = new MouseEvent('click', { bubbles: true, cancelable: true }); element.dispatchEvent(event); if (element.onclick) { element.onclick.call(element, event); } } log(message) { console.log(message); if (this.testContainer) { const div = document.createElement('div'); div.textContent = message; div.style.marginBottom = '2px'; div.style.fontSize = '11px'; div.style.lineHeight = '1.3'; this.testContainer.appendChild(div); this.testContainer.scrollTop = this.testContainer.scrollHeight; } } } // Make available globally window.E2EScenarioTester = E2EScenarioTester; window.runE2ETests = async () => { const tester = new E2EScenarioTester(); await tester.runAllScenarios(); return tester.results; }; console.log('šŸŽ¬ E2E Scenario Test Suite loaded. Run with: runE2ETests()');