Spaces:
Paused
Paused
| <html lang="fa" dir="rtl"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Functional Testing - Legal Dashboard</title> | |
| <style> | |
| body { | |
| font-family: 'Arial', sans-serif; | |
| max-width: 1400px; | |
| margin: 0 auto; | |
| padding: 20px; | |
| background: #f5f5f5; | |
| } | |
| .test-section { | |
| background: white; | |
| padding: 20px; | |
| margin: 20px 0; | |
| border-radius: 8px; | |
| box-shadow: 0 2px 4px rgba(0,0,0,0.1); | |
| } | |
| .success { color: #10b981; } | |
| .error { color: #ef4444; } | |
| .info { color: #3b82f6; } | |
| .warning { color: #f59e0b; } | |
| button { | |
| background: #007bff; | |
| color: white; | |
| border: none; | |
| padding: 10px 20px; | |
| border-radius: 4px; | |
| cursor: pointer; | |
| margin: 5px; | |
| font-size: 14px; | |
| } | |
| button:hover { | |
| background: #0056b3; | |
| } | |
| button:disabled { | |
| background: #ccc; | |
| cursor: not-allowed; | |
| } | |
| .workflow-test { | |
| border: 1px solid #ddd; | |
| border-radius: 8px; | |
| padding: 15px; | |
| margin: 10px 0; | |
| background: white; | |
| } | |
| .workflow-test.success { | |
| border-color: #10b981; | |
| background: #f0fdf4; | |
| } | |
| .workflow-test.error { | |
| border-color: #ef4444; | |
| background: #fef2f2; | |
| } | |
| .workflow-test.testing { | |
| border-color: #3b82f6; | |
| background: #eff6ff; | |
| } | |
| .test-results { | |
| max-height: 400px; | |
| overflow-y: auto; | |
| border: 1px solid #ddd; | |
| border-radius: 4px; | |
| padding: 10px; | |
| background: #f8f9fa; | |
| font-family: 'Courier New', monospace; | |
| font-size: 12px; | |
| } | |
| .progress-bar { | |
| width: 100%; | |
| height: 6px; | |
| background: #e5e7eb; | |
| border-radius: 3px; | |
| overflow: hidden; | |
| margin: 10px 0; | |
| } | |
| .progress-fill { | |
| height: 100%; | |
| background: #3b82f6; | |
| transition: width 0.3s ease; | |
| } | |
| .file-upload-area { | |
| border: 2px dashed #ddd; | |
| padding: 30px; | |
| text-align: center; | |
| border-radius: 8px; | |
| margin: 20px 0; | |
| background: #fafafa; | |
| } | |
| .file-upload-area.dragover { | |
| border-color: #3b82f6; | |
| background: #eff6ff; | |
| } | |
| .status-indicator { | |
| display: inline-block; | |
| width: 12px; | |
| height: 12px; | |
| border-radius: 50%; | |
| margin-right: 8px; | |
| } | |
| .status-indicator.success { background: #10b981; } | |
| .status-indicator.error { background: #ef4444; } | |
| .status-indicator.warning { background: #f59e0b; } | |
| .status-indicator.info { background: #3b82f6; } | |
| .status-indicator.testing { | |
| background: #3b82f6; | |
| animation: pulse 1s infinite; | |
| } | |
| @keyframes pulse { | |
| 0% { opacity: 1; } | |
| 50% { opacity: 0.5; } | |
| 100% { opacity: 1; } | |
| } | |
| .summary-stats { | |
| display: grid; | |
| grid-template-columns: repeat(4, 1fr); | |
| gap: 15px; | |
| margin-bottom: 20px; | |
| } | |
| .stat-card { | |
| background: white; | |
| padding: 15px; | |
| border-radius: 8px; | |
| text-align: center; | |
| box-shadow: 0 2px 4px rgba(0,0,0,0.1); | |
| } | |
| .stat-number { | |
| font-size: 2rem; | |
| font-weight: bold; | |
| margin-bottom: 5px; | |
| } | |
| .stat-label { | |
| color: #666; | |
| font-size: 0.9rem; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <h1>🔧 Functional Testing - Legal Dashboard</h1> | |
| <div class="test-section"> | |
| <h2>📊 Test Summary</h2> | |
| <div class="summary-stats"> | |
| <div class="stat-card"> | |
| <div class="stat-number" id="totalWorkflows">0</div> | |
| <div class="stat-label">Total Workflows</div> | |
| </div> | |
| <div class="stat-card"> | |
| <div class="stat-number" id="passedWorkflows">0</div> | |
| <div class="stat-label">Passed</div> | |
| </div> | |
| <div class="stat-card"> | |
| <div class="stat-number" id="failedWorkflows">0</div> | |
| <div class="stat-label">Failed</div> | |
| </div> | |
| <div class="stat-card"> | |
| <div class="stat-number" id="successRate">0%</div> | |
| <div class="stat-label">Success Rate</div> | |
| </div> | |
| </div> | |
| <div class="progress-bar"> | |
| <div class="progress-fill" id="progressBar" style="width: 0%"></div> | |
| </div> | |
| </div> | |
| <div class="test-section"> | |
| <h2>🎛️ Test Controls</h2> | |
| <button type="button" onclick="runAllWorkflows()" id="runAllBtn">Run All Workflows</button> | |
| <button type="button" onclick="testDocumentWorkflow()">Document Workflow</button> | |
| <button type="button" onclick="testUploadWorkflow()">Upload Workflow</button> | |
| <button type="button" onclick="testScrapingWorkflow()">Scraping Workflow</button> | |
| <button type="button" onclick="testAnalyticsWorkflow()">Analytics Workflow</button> | |
| <button type="button" onclick="clearResults()">Clear Results</button> | |
| <button type="button" onclick="exportResults()">Export Results</button> | |
| </div> | |
| <div class="test-section"> | |
| <h2>📁 File Upload Test</h2> | |
| <div class="file-upload-area" id="uploadZone"> | |
| <p><strong>Drag and drop a file here or click to select</strong></p> | |
| <p>Supported formats: PDF, JPG, JPEG, PNG, TIFF</p> | |
| <input type="file" id="testFileInput" accept=".pdf,.jpg,.jpeg,.png,.tiff" style="display: none;"> | |
| <button type="button" onclick="document.getElementById('testFileInput').click()">Select File</button> | |
| </div> | |
| <div id="uploadResults"></div> | |
| </div> | |
| <div class="test-section"> | |
| <h2>🔄 Workflow Tests</h2> | |
| <div id="workflowTests"> | |
| <!-- Workflow tests will be generated here --> | |
| </div> | |
| </div> | |
| <div class="test-section"> | |
| <h2>📋 Test Results</h2> | |
| <div class="test-results" id="testResults"> | |
| <!-- Test results will be displayed here --> | |
| </div> | |
| </div> | |
| <script src="../js/api-client.js"></script> | |
| <script> | |
| class FunctionalTester { | |
| constructor() { | |
| this.baseURL = window.location.origin; | |
| this.results = []; | |
| this.testStats = { | |
| total: 0, | |
| passed: 0, | |
| failed: 0, | |
| successRate: 0 | |
| }; | |
| this.isRunning = false; | |
| this.workflows = [ | |
| { | |
| name: 'Document Management Workflow', | |
| description: 'Test complete document CRUD operations', | |
| steps: [ | |
| { name: 'Get Documents List', action: 'getDocuments' }, | |
| { name: 'Create Test Document', action: 'createDocument' }, | |
| { name: 'Update Document', action: 'updateDocument' }, | |
| { name: 'Search Documents', action: 'searchDocuments' }, | |
| { name: 'Delete Test Document', action: 'deleteDocument' } | |
| ] | |
| }, | |
| { | |
| name: 'File Upload & OCR Workflow', | |
| description: 'Test file upload and OCR processing', | |
| steps: [ | |
| { name: 'Upload Test File', action: 'uploadFile' }, | |
| { name: 'Process OCR', action: 'processOCR' }, | |
| { name: 'Get OCR Status', action: 'getOCRStatus' }, | |
| { name: 'Extract Text', action: 'extractText' } | |
| ] | |
| }, | |
| { | |
| name: 'Dashboard Analytics Workflow', | |
| description: 'Test dashboard and analytics functionality', | |
| steps: [ | |
| { name: 'Get Dashboard Summary', action: 'getDashboardSummary' }, | |
| { name: 'Get Charts Data', action: 'getChartsData' }, | |
| { name: 'Get AI Suggestions', action: 'getAISuggestions' }, | |
| { name: 'Get Performance Metrics', action: 'getPerformanceMetrics' } | |
| ] | |
| }, | |
| { | |
| name: 'Scraping & Rating Workflow', | |
| description: 'Test web scraping and content rating', | |
| steps: [ | |
| { name: 'Get Scraping Status', action: 'getScrapingStatus' }, | |
| { name: 'Get Scraping Statistics', action: 'getScrapingStatistics' }, | |
| { name: 'Get Rating Summary', action: 'getRatingSummary' }, | |
| { name: 'Check Scraping Health', action: 'getScrapingHealth' } | |
| ] | |
| }, | |
| { | |
| name: 'Analytics & Reporting Workflow', | |
| description: 'Test advanced analytics and reporting', | |
| steps: [ | |
| { name: 'Get Analytics Overview', action: 'getAnalyticsOverview' }, | |
| { name: 'Get Performance Analytics', action: 'getPerformanceAnalytics' }, | |
| { name: 'Get Entity Analysis', action: 'getEntityAnalysis' }, | |
| { name: 'Get Quality Analysis', action: 'getQualityAnalysis' } | |
| ] | |
| } | |
| ]; | |
| this.initialize(); | |
| } | |
| initialize() { | |
| this.createWorkflowTests(); | |
| this.setupFileUpload(); | |
| this.updateStats(); | |
| } | |
| createWorkflowTests() { | |
| const container = document.getElementById('workflowTests'); | |
| container.innerHTML = ''; | |
| this.workflows.forEach((workflow, index) => { | |
| const testDiv = document.createElement('div'); | |
| testDiv.className = 'workflow-test'; | |
| testDiv.id = `workflow-${index}`; | |
| testDiv.innerHTML = ` | |
| <div class="status-indicator"></div> | |
| <h3>${workflow.name}</h3> | |
| <p>${workflow.description}</p> | |
| <div class="steps" id="steps-${index}"> | |
| ${workflow.steps.map((step, stepIndex) => ` | |
| <div class="step" id="step-${index}-${stepIndex}"> | |
| <span class="status-indicator"></span> | |
| ${step.name} | |
| </div> | |
| `).join('')} | |
| </div> | |
| <button type="button" onclick="tester.runWorkflow(${index})" class="run-workflow-btn"> | |
| Run Workflow | |
| </button> | |
| `; | |
| container.appendChild(testDiv); | |
| }); | |
| } | |
| setupFileUpload() { | |
| const uploadZone = document.getElementById('uploadZone'); | |
| const fileInput = document.getElementById('testFileInput'); | |
| uploadZone.addEventListener('dragover', (e) => { | |
| e.preventDefault(); | |
| uploadZone.classList.add('dragover'); | |
| }); | |
| uploadZone.addEventListener('dragleave', () => { | |
| uploadZone.classList.remove('dragover'); | |
| }); | |
| uploadZone.addEventListener('drop', (e) => { | |
| e.preventDefault(); | |
| uploadZone.classList.remove('dragover'); | |
| const files = e.dataTransfer.files; | |
| if (files.length > 0) { | |
| this.testFileUpload(files[0]); | |
| } | |
| }); | |
| fileInput.addEventListener('change', (e) => { | |
| if (e.target.files.length > 0) { | |
| this.testFileUpload(e.target.files[0]); | |
| } | |
| }); | |
| } | |
| async runWorkflow(workflowIndex) { | |
| const workflow = this.workflows[workflowIndex]; | |
| const testDiv = document.getElementById(`workflow-${workflowIndex}`); | |
| // Set testing state | |
| testDiv.className = 'workflow-test testing'; | |
| testDiv.querySelector('.status-indicator').className = 'status-indicator testing'; | |
| testDiv.querySelector('.run-workflow-btn').disabled = true; | |
| this.logResult({ | |
| workflow: workflow.name, | |
| status: 'started', | |
| message: `Starting ${workflow.name}` | |
| }); | |
| let allStepsPassed = true; | |
| for (let stepIndex = 0; stepIndex < workflow.steps.length; stepIndex++) { | |
| const step = workflow.steps[stepIndex]; | |
| const stepDiv = document.getElementById(`step-${workflowIndex}-${stepIndex}`); | |
| // Set step testing state | |
| stepDiv.querySelector('.status-indicator').className = 'status-indicator testing'; | |
| try { | |
| const result = await this.executeStep(step.action); | |
| if (result.success) { | |
| stepDiv.querySelector('.status-indicator').className = 'status-indicator success'; | |
| this.logResult({ | |
| workflow: workflow.name, | |
| step: step.name, | |
| status: 'success', | |
| message: `${step.name} completed successfully` | |
| }); | |
| } else { | |
| stepDiv.querySelector('.status-indicator').className = 'status-indicator error'; | |
| allStepsPassed = false; | |
| this.logResult({ | |
| workflow: workflow.name, | |
| step: step.name, | |
| status: 'error', | |
| message: `${step.name} failed: ${result.error}` | |
| }); | |
| } | |
| } catch (error) { | |
| stepDiv.querySelector('.status-indicator').className = 'status-indicator error'; | |
| allStepsPassed = false; | |
| this.logResult({ | |
| workflow: workflow.name, | |
| step: step.name, | |
| status: 'error', | |
| message: `${step.name} failed: ${error.message}` | |
| }); | |
| } | |
| await this.delay(200); // Small delay between steps | |
| } | |
| // Update workflow status | |
| testDiv.className = `workflow-test ${allStepsPassed ? 'success' : 'error'}`; | |
| testDiv.querySelector('.status-indicator').className = `status-indicator ${allStepsPassed ? 'success' : 'error'}`; | |
| testDiv.querySelector('.run-workflow-btn').disabled = false; | |
| this.logResult({ | |
| workflow: workflow.name, | |
| status: allStepsPassed ? 'completed' : 'failed', | |
| message: `${workflow.name} ${allStepsPassed ? 'completed successfully' : 'failed'}` | |
| }); | |
| this.updateStats(); | |
| } | |
| async executeStep(action) { | |
| switch (action) { | |
| case 'getDocuments': | |
| return await this.testGetDocuments(); | |
| case 'createDocument': | |
| return await this.testCreateDocument(); | |
| case 'updateDocument': | |
| return await this.testUpdateDocument(); | |
| case 'searchDocuments': | |
| return await this.testSearchDocuments(); | |
| case 'deleteDocument': | |
| return await this.testDeleteDocument(); | |
| case 'uploadFile': | |
| return await this.testUploadFile(); | |
| case 'processOCR': | |
| return await this.testProcessOCR(); | |
| case 'getOCRStatus': | |
| return await this.testGetOCRStatus(); | |
| case 'extractText': | |
| return await this.testExtractText(); | |
| case 'getDashboardSummary': | |
| return await this.testGetDashboardSummary(); | |
| case 'getChartsData': | |
| return await this.testGetChartsData(); | |
| case 'getAISuggestions': | |
| return await this.testGetAISuggestions(); | |
| case 'getPerformanceMetrics': | |
| return await this.testGetPerformanceMetrics(); | |
| case 'getScrapingStatus': | |
| return await this.testGetScrapingStatus(); | |
| case 'getScrapingStatistics': | |
| return await this.testGetScrapingStatistics(); | |
| case 'getRatingSummary': | |
| return await this.testGetRatingSummary(); | |
| case 'getScrapingHealth': | |
| return await this.testGetScrapingHealth(); | |
| case 'getAnalyticsOverview': | |
| return await this.testGetAnalyticsOverview(); | |
| case 'getPerformanceAnalytics': | |
| return await this.testGetPerformanceAnalytics(); | |
| case 'getEntityAnalysis': | |
| return await this.testGetEntityAnalysis(); | |
| case 'getQualityAnalysis': | |
| return await this.testGetQualityAnalysis(); | |
| default: | |
| return { success: false, error: 'Unknown action' }; | |
| } | |
| } | |
| // Individual step implementations | |
| async testGetDocuments() { | |
| try { | |
| const response = await fetch(`${this.baseURL}/api/documents`); | |
| return { success: response.ok, error: response.ok ? null : `HTTP ${response.status}` }; | |
| } catch (error) { | |
| return { success: false, error: error.message }; | |
| } | |
| } | |
| async testCreateDocument() { | |
| try { | |
| const testDoc = { | |
| title: `Test Document ${Date.now()}`, | |
| content: 'This is a test document for functional testing', | |
| category: 'test', | |
| source: 'functional_test' | |
| }; | |
| const response = await fetch(`${this.baseURL}/api/documents`, { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify(testDoc) | |
| }); | |
| return { success: response.ok, error: response.ok ? null : `HTTP ${response.status}` }; | |
| } catch (error) { | |
| return { success: false, error: error.message }; | |
| } | |
| } | |
| async testUpdateDocument() { | |
| try { | |
| const response = await fetch(`${this.baseURL}/api/documents/1`, { | |
| method: 'PUT', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ title: 'Updated Test Document' }) | |
| }); | |
| return { success: response.ok, error: response.ok ? null : `HTTP ${response.status}` }; | |
| } catch (error) { | |
| return { success: false, error: error.message }; | |
| } | |
| } | |
| async testSearchDocuments() { | |
| try { | |
| const response = await fetch(`${this.baseURL}/api/documents/search?q=test`); | |
| return { success: response.ok, error: response.ok ? null : `HTTP ${response.status}` }; | |
| } catch (error) { | |
| return { success: false, error: error.message }; | |
| } | |
| } | |
| async testDeleteDocument() { | |
| try { | |
| const response = await fetch(`${this.baseURL}/api/documents/1`, { | |
| method: 'DELETE' | |
| }); | |
| return { success: response.ok, error: response.ok ? null : `HTTP ${response.status}` }; | |
| } catch (error) { | |
| return { success: false, error: error.message }; | |
| } | |
| } | |
| async testUploadFile() { | |
| try { | |
| // Create a test file | |
| const testContent = 'This is a test file for functional testing'; | |
| const blob = new Blob([testContent], { type: 'text/plain' }); | |
| const file = new File([blob], 'test.txt', { type: 'text/plain' }); | |
| const formData = new FormData(); | |
| formData.append('file', file); | |
| const response = await fetch(`${this.baseURL}/api/ocr/upload`, { | |
| method: 'POST', | |
| body: formData | |
| }); | |
| return { success: response.ok, error: response.ok ? null : `HTTP ${response.status}` }; | |
| } catch (error) { | |
| return { success: false, error: error.message }; | |
| } | |
| } | |
| async testProcessOCR() { | |
| try { | |
| const response = await fetch(`${this.baseURL}/api/ocr/process`, { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ file_id: 'test_file' }) | |
| }); | |
| return { success: response.ok, error: response.ok ? null : `HTTP ${response.status}` }; | |
| } catch (error) { | |
| return { success: false, error: error.message }; | |
| } | |
| } | |
| async testGetOCRStatus() { | |
| try { | |
| const response = await fetch(`${this.baseURL}/api/ocr/status`); | |
| return { success: response.ok, error: response.ok ? null : `HTTP ${response.status}` }; | |
| } catch (error) { | |
| return { success: false, error: error.message }; | |
| } | |
| } | |
| async testExtractText() { | |
| try { | |
| const response = await fetch(`${this.baseURL}/api/ocr/extract`, { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ file_id: 'test_file' }) | |
| }); | |
| return { success: response.ok, error: response.ok ? null : `HTTP ${response.status}` }; | |
| } catch (error) { | |
| return { success: false, error: error.message }; | |
| } | |
| } | |
| async testGetDashboardSummary() { | |
| try { | |
| const response = await fetch(`${this.baseURL}/api/dashboard/summary`); | |
| return { success: response.ok, error: response.ok ? null : `HTTP ${response.status}` }; | |
| } catch (error) { | |
| return { success: false, error: error.message }; | |
| } | |
| } | |
| async testGetChartsData() { | |
| try { | |
| const response = await fetch(`${this.baseURL}/api/dashboard/charts-data`); | |
| return { success: response.ok, error: response.ok ? null : `HTTP ${response.status}` }; | |
| } catch (error) { | |
| return { success: false, error: error.message }; | |
| } | |
| } | |
| async testGetAISuggestions() { | |
| try { | |
| const response = await fetch(`${this.baseURL}/api/dashboard/ai-suggestions`); | |
| return { success: response.ok, error: response.ok ? null : `HTTP ${response.status}` }; | |
| } catch (error) { | |
| return { success: false, error: error.message }; | |
| } | |
| } | |
| async testGetPerformanceMetrics() { | |
| try { | |
| const response = await fetch(`${this.baseURL}/api/dashboard/performance-metrics`); | |
| return { success: response.ok, error: response.ok ? null : `HTTP ${response.status}` }; | |
| } catch (error) { | |
| return { success: false, error: error.message }; | |
| } | |
| } | |
| async testGetScrapingStatus() { | |
| try { | |
| const response = await fetch(`${this.baseURL}/api/scraping/scrape/status`); | |
| return { success: response.ok, error: response.ok ? null : `HTTP ${response.status}` }; | |
| } catch (error) { | |
| return { success: false, error: error.message }; | |
| } | |
| } | |
| async testGetScrapingStatistics() { | |
| try { | |
| const response = await fetch(`${this.baseURL}/api/scraping/scrape/statistics`); | |
| return { success: response.ok, error: response.ok ? null : `HTTP ${response.status}` }; | |
| } catch (error) { | |
| return { success: false, error: error.message }; | |
| } | |
| } | |
| async testGetRatingSummary() { | |
| try { | |
| const response = await fetch(`${this.baseURL}/api/scraping/rating/summary`); | |
| return { success: response.ok, error: response.ok ? null : `HTTP ${response.status}` }; | |
| } catch (error) { | |
| return { success: false, error: error.message }; | |
| } | |
| } | |
| async testGetScrapingHealth() { | |
| try { | |
| const response = await fetch(`${this.baseURL}/api/scraping/health`); | |
| return { success: response.ok, error: response.ok ? null : `HTTP ${response.status}` }; | |
| } catch (error) { | |
| return { success: false, error: error.message }; | |
| } | |
| } | |
| async testGetAnalyticsOverview() { | |
| try { | |
| const response = await fetch(`${this.baseURL}/api/analytics/overview`); | |
| return { success: response.ok, error: response.ok ? null : `HTTP ${response.status}` }; | |
| } catch (error) { | |
| return { success: false, error: error.message }; | |
| } | |
| } | |
| async testGetPerformanceAnalytics() { | |
| try { | |
| const response = await fetch(`${this.baseURL}/api/analytics/performance`); | |
| return { success: response.ok, error: response.ok ? null : `HTTP ${response.status}` }; | |
| } catch (error) { | |
| return { success: false, error: error.message }; | |
| } | |
| } | |
| async testGetEntityAnalysis() { | |
| try { | |
| const response = await fetch(`${this.baseURL}/api/analytics/entities`); | |
| return { success: response.ok, error: response.ok ? null : `HTTP ${response.status}` }; | |
| } catch (error) { | |
| return { success: false, error: error.message }; | |
| } | |
| } | |
| async testGetQualityAnalysis() { | |
| try { | |
| const response = await fetch(`${this.baseURL}/api/analytics/quality-analysis`); | |
| return { success: response.ok, error: response.ok ? null : `HTTP ${response.status}` }; | |
| } catch (error) { | |
| return { success: false, error: error.message }; | |
| } | |
| } | |
| async testFileUpload(file) { | |
| const resultsDiv = document.getElementById('uploadResults'); | |
| resultsDiv.innerHTML = `<p>Testing file upload: ${file.name} (${file.size} bytes)</p>`; | |
| try { | |
| const formData = new FormData(); | |
| formData.append('file', file); | |
| const startTime = Date.now(); | |
| const response = await fetch(`${this.baseURL}/api/ocr/upload`, { | |
| method: 'POST', | |
| body: formData | |
| }); | |
| const responseTime = Date.now() - startTime; | |
| const responseData = await response.json(); | |
| const success = response.ok; | |
| resultsDiv.innerHTML = ` | |
| <div class="${success ? 'success' : 'error'}"> | |
| <h4>File Upload Test Results</h4> | |
| <p><strong>File:</strong> ${file.name}</p> | |
| <p><strong>Size:</strong> ${file.size} bytes</p> | |
| <p><strong>Status:</strong> ${response.status} ${response.statusText}</p> | |
| <p><strong>Response Time:</strong> ${responseTime}ms</p> | |
| <div class="response-data"> | |
| ${JSON.stringify(responseData, null, 2)} | |
| </div> | |
| </div> | |
| `; | |
| this.logResult({ | |
| workflow: 'File Upload', | |
| status: success ? 'success' : 'error', | |
| message: `File upload ${success ? 'succeeded' : 'failed'}: ${file.name}` | |
| }); | |
| } catch (error) { | |
| resultsDiv.innerHTML = ` | |
| <div class="error"> | |
| <h4>File Upload Test Failed</h4> | |
| <p>Error: ${error.message}</p> | |
| </div> | |
| `; | |
| this.logResult({ | |
| workflow: 'File Upload', | |
| status: 'error', | |
| message: `File upload failed: ${error.message}` | |
| }); | |
| } | |
| this.updateStats(); | |
| } | |
| async runAllWorkflows() { | |
| if (this.isRunning) return; | |
| this.isRunning = true; | |
| document.getElementById('runAllBtn').disabled = true; | |
| document.getElementById('runAllBtn').textContent = 'Running...'; | |
| this.clearResults(); | |
| for (let i = 0; i < this.workflows.length; i++) { | |
| await this.runWorkflow(i); | |
| await this.delay(500); // Delay between workflows | |
| } | |
| this.isRunning = false; | |
| document.getElementById('runAllBtn').disabled = false; | |
| document.getElementById('runAllBtn').textContent = 'Run All Workflows'; | |
| } | |
| logResult(result) { | |
| this.results.push({ | |
| ...result, | |
| timestamp: new Date().toISOString() | |
| }); | |
| const resultsDiv = document.getElementById('testResults'); | |
| const resultEntry = document.createElement('div'); | |
| resultEntry.className = `test-result ${result.status === 'success' || result.status === 'completed' ? 'success' : 'error'}`; | |
| resultEntry.innerHTML = ` | |
| <strong>${result.workflow}</strong>${result.step ? ` - ${result.step}` : ''} - | |
| ${result.status.toUpperCase()} - | |
| ${result.message} | |
| <br><small>${new Date().toLocaleTimeString()}</small> | |
| `; | |
| resultsDiv.appendChild(resultEntry); | |
| resultsDiv.scrollTop = resultsDiv.scrollHeight; | |
| } | |
| updateStats() { | |
| const total = this.results.length; | |
| const passed = this.results.filter(r => | |
| r.status === 'success' || r.status === 'completed' | |
| ).length; | |
| const failed = total - passed; | |
| const successRate = total > 0 ? Math.round((passed / total) * 100) : 0; | |
| this.testStats = { total, passed, failed, successRate }; | |
| document.getElementById('totalWorkflows').textContent = total; | |
| document.getElementById('passedWorkflows').textContent = passed; | |
| document.getElementById('failedWorkflows').textContent = failed; | |
| document.getElementById('successRate').textContent = successRate + '%'; | |
| const progressBar = document.getElementById('progressBar'); | |
| progressBar.style.width = successRate + '%'; | |
| progressBar.style.background = successRate >= 80 ? '#10b981' : successRate >= 60 ? '#f59e0b' : '#ef4444'; | |
| } | |
| clearResults() { | |
| this.results = []; | |
| document.getElementById('testResults').innerHTML = ''; | |
| this.updateStats(); | |
| // Reset all workflow tests | |
| this.workflows.forEach((workflow, index) => { | |
| const testDiv = document.getElementById(`workflow-${index}`); | |
| testDiv.className = 'workflow-test'; | |
| testDiv.querySelector('.status-indicator').className = 'status-indicator'; | |
| testDiv.querySelector('.run-workflow-btn').disabled = false; | |
| workflow.steps.forEach((step, stepIndex) => { | |
| const stepDiv = document.getElementById(`step-${index}-${stepIndex}`); | |
| stepDiv.querySelector('.status-indicator').className = 'status-indicator'; | |
| }); | |
| }); | |
| } | |
| exportResults() { | |
| const data = { | |
| timestamp: new Date().toISOString(), | |
| stats: this.testStats, | |
| results: this.results | |
| }; | |
| const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' }); | |
| const url = URL.createObjectURL(blob); | |
| const a = document.createElement('a'); | |
| a.href = url; | |
| a.download = `functional-test-results-${new Date().toISOString().slice(0, 19).replace(/:/g, '-')}.json`; | |
| a.click(); | |
| URL.revokeObjectURL(url); | |
| } | |
| delay(ms) { | |
| return new Promise(resolve => setTimeout(resolve, ms)); | |
| } | |
| } | |
| // Global tester instance | |
| const tester = new FunctionalTester(); | |
| // Global functions for button clicks | |
| function runAllWorkflows() { | |
| tester.runAllWorkflows(); | |
| } | |
| function testDocumentWorkflow() { | |
| tester.runWorkflow(0); | |
| } | |
| function testUploadWorkflow() { | |
| tester.runWorkflow(1); | |
| } | |
| function testScrapingWorkflow() { | |
| tester.runWorkflow(3); | |
| } | |
| function testAnalyticsWorkflow() { | |
| tester.runWorkflow(4); | |
| } | |
| function clearResults() { | |
| tester.clearResults(); | |
| } | |
| function exportResults() { | |
| tester.exportResults(); | |
| } | |
| console.log('🔧 Functional Tester initialized'); | |
| </script> | |
| </body> | |
| </html> |