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> |