|
|
<!DOCTYPE html> |
|
|
<html lang="en"> |
|
|
<head> |
|
|
<meta charset="UTF-8"> |
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
|
<title>Test Runner | MedMIS</title> |
|
|
<script src="https://cdn.tailwindcss.com"></script> |
|
|
<script src="https://unpkg.com/feather-icons"></script> |
|
|
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script> |
|
|
<style> |
|
|
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap'); |
|
|
body { |
|
|
font-family: 'Inter', sans-serif; |
|
|
} |
|
|
.test-card { |
|
|
transition: all 0.2s ease; |
|
|
} |
|
|
.test-card:hover { |
|
|
transform: translateY(-2px); |
|
|
} |
|
|
.status-running { |
|
|
background-color: #f59e0b20; |
|
|
color: #f59e0b; |
|
|
} |
|
|
.status-passed { |
|
|
background-color: #10b98120; |
|
|
color: #10b981; |
|
|
} |
|
|
.status-failed { |
|
|
background-color: #ef444420; |
|
|
color: #ef4444; |
|
|
} |
|
|
</style> |
|
|
</head> |
|
|
<body class="bg-gray-900 text-white min-h-screen"> |
|
|
<div class="container mx-auto px-4 py-8"> |
|
|
<header class="mb-8"> |
|
|
<div class="flex items-center mb-4"> |
|
|
<button onclick="history.back()" class="mr-4 p-2 rounded-lg bg-gray-700 hover:bg-gray-600 transition"> |
|
|
<i data-feather="arrow-left" class="w-5 h-5"></i> |
|
|
</button> |
|
|
<div> |
|
|
<h1 class="text-3xl font-bold">Test Runner</h1> |
|
|
<p class="text-gray-300">Automated testing for MedMIS workflows</p> |
|
|
</div> |
|
|
</div> |
|
|
</header> |
|
|
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6 mb-8"> |
|
|
|
|
|
<div class="lg:col-span-2 bg-gray-800 rounded-xl p-6"> |
|
|
<h2 class="text-xl font-semibold mb-4 flex items-center"> |
|
|
<i data-feather="list" class="w-5 h-5 mr-2"></i> |
|
|
Test Scenarios |
|
|
</h2> |
|
|
<div class="space-y-3" id="testScenarios"> |
|
|
<div class="test-card bg-gray-700 rounded-lg p-4 cursor-pointer hover:bg-gray-600"> |
|
|
<div class="flex justify-between items-start"> |
|
|
<div> |
|
|
<h3 class="font-medium">Patient Flow</h3> |
|
|
<p class="text-sm text-gray-400">Registration → Queue → Study → Report</p> |
|
|
</div> |
|
|
<button class="run-test bg-blue-600 hover:bg-blue-700 text-white py-1 px-3 rounded text-sm" data-test="patientFlow"> |
|
|
Run |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
<div class="test-card bg-gray-700 rounded-lg p-4 cursor-pointer hover:bg-gray-600"> |
|
|
<div class="flex justify-between items-start"> |
|
|
<div> |
|
|
<h3 class="font-medium">SLA Compliance</h3> |
|
|
<p class="text-sm text-gray-400">Priority-based timing and scoring</p> |
|
|
</div> |
|
|
<button class="run-test bg-blue-600 hover:bg-blue-700 text-white py-1 px-3 rounded text-sm" data-test="slaCompliance"> |
|
|
Run |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
<div class="test-card bg-gray-700 rounded-lg p-4 cursor-pointer hover:bg-gray-600"> |
|
|
<div class="flex justify-between items-start"> |
|
|
<div> |
|
|
<h3 class="font-medium">Integration Flow</h3> |
|
|
<p class="text-sm text-gray-400">Service-to-service event routing</p> |
|
|
</div> |
|
|
<button class="run-test bg-blue-600 hover:bg-blue-700 text-white py-1 px-3 rounded text-sm" data-test="integrationFlow"> |
|
|
Run |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="bg-gray-800 rounded-xl p-6"> |
|
|
<h2 class="text-xl font-semibold mb-4 flex items-center"> |
|
|
<i data-feather="check-circle" class="w-5 h-5 mr-2"></i> |
|
|
Test Results |
|
|
</h2> |
|
|
<div id="testResults" class="space-y-3"> |
|
|
<div class="bg-gray-700 rounded-lg p-4"> |
|
|
<div class="flex justify-between mb-2"> |
|
|
<span class="font-medium">Last Run:</span> |
|
|
<span class="text-gray-400">Not run yet</span> |
|
|
</div> |
|
|
<div class="flex justify-between"> |
|
|
<span class="font-medium">Status:</span> |
|
|
<span class="text-gray-400">Idle</span> |
|
|
</div> |
|
|
</div> |
|
|
<div id="testLogs" class="bg-gray-900 rounded-lg p-4 h-64 overflow-y-auto text-sm font-mono"> |
|
|
|
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="bg-gray-800 rounded-xl p-6 mb-8"> |
|
|
<h2 class="text-xl font-semibold mb-4 flex items-center"> |
|
|
<i data-feather="terminal" class="w-5 h-5 mr-2"></i> |
|
|
API Test Console |
|
|
</h2> |
|
|
<div class="grid grid-cols-1 md:grid-cols-4 gap-4 mb-4"> |
|
|
<div> |
|
|
<label class="block text-gray-300 text-sm mb-1">Method</label> |
|
|
<select id="apiMethod" class="w-full bg-gray-700 rounded-lg px-3 py-2"> |
|
|
<option>GET</option> |
|
|
<option>POST</option> |
|
|
<option>PUT</option> |
|
|
<option>DELETE</option> |
|
|
</select> |
|
|
</div> |
|
|
<div class="md:col-span-3"> |
|
|
<label class="block text-gray-300 text-sm mb-1">Endpoint</label> |
|
|
<div class="flex"> |
|
|
<span class="bg-gray-700 rounded-l-lg px-3 py-2">/api/</span> |
|
|
<input type="text" id="apiEndpoint" class="flex-1 bg-gray-700 rounded-r-lg px-3 py-2" placeholder="endpoint"> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
<div class="mb-4"> |
|
|
<label class="block text-gray-300 text-sm mb-1">Request Body</label> |
|
|
<textarea id="apiBody" class="w-full bg-gray-700 rounded-lg px-3 py-2 h-32 font-mono text-sm" placeholder="JSON body"></textarea> |
|
|
</div> |
|
|
<button id="runApiTest" class="w-full bg-blue-600 hover:bg-blue-700 text-white py-2 px-4 rounded-lg transition"> |
|
|
Execute API Test |
|
|
</button> |
|
|
<div id="apiResponse" class="mt-4 bg-gray-900 rounded-lg p-4 h-64 overflow-y-auto text-sm font-mono hidden"> |
|
|
<div class="text-gray-400">Response will appear here...</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="bg-gray-800 rounded-xl p-6"> |
|
|
<h2 class="text-xl font-semibold mb-4 flex items-center"> |
|
|
<i data-feather="database" class="w-5 h-5 mr-2"></i> |
|
|
Database Configuration |
|
|
</h2> |
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6"> |
|
|
<div> |
|
|
<h3 class="font-medium mb-2">Test Database</h3> |
|
|
<div class="bg-gray-700 rounded-lg p-4"> |
|
|
<div class="flex items-center justify-between mb-2"> |
|
|
<span>Current:</span> |
|
|
<span class="font-mono bg-gray-800 px-2 py-1 rounded">SQLite</span> |
|
|
</div> |
|
|
<button id="switchDb" class="w-full bg-purple-600 hover:bg-purple-700 text-white py-2 px-4 rounded-lg transition mt-2"> |
|
|
Switch to PostgreSQL |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
<div> |
|
|
<h3 class="font-medium mb-2">Sample Data</h3> |
|
|
<div class="space-y-2"> |
|
|
<button class="w-full bg-gray-700 hover:bg-gray-600 text-white py-2 px-4 rounded-lg transition text-left flex items-center"> |
|
|
<i data-feather="user" class="w-4 h-4 mr-2"></i> |
|
|
Load Test Patients |
|
|
</button> |
|
|
<button class="w-full bg-gray-700 hover:bg-gray-600 text-white py-2 px-4 rounded-lg transition text-left flex items-center"> |
|
|
<i data-feather="book" class="w-4 h-4 mr-2"></i> |
|
|
Load Test Studies |
|
|
</button> |
|
|
<button class="w-full bg-gray-700 hover:bg-gray-600 text-white py-2 px-4 rounded-lg transition text-left flex items-center"> |
|
|
<i data-feather="file-text" class="w-4 h-4 mr-2"></i> |
|
|
Load Test Reports |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<script> |
|
|
feather.replace(); |
|
|
|
|
|
|
|
|
document.querySelectorAll('.run-test').forEach(button => { |
|
|
button.addEventListener('click', function() { |
|
|
const testName = this.dataset.test; |
|
|
const testLogs = document.getElementById('testLogs'); |
|
|
testLogs.innerHTML = ''; |
|
|
|
|
|
|
|
|
const testResults = document.getElementById('testResults'); |
|
|
testResults.querySelector('.bg-gray-700 div:last-child span').textContent = 'Running'; |
|
|
testResults.querySelector('.bg-gray-700 div:last-child span').className = 'text-yellow-400'; |
|
|
|
|
|
logMessage(`Starting ${testName} test...`); |
|
|
|
|
|
|
|
|
setTimeout(() => { |
|
|
switch(testName) { |
|
|
case 'patientFlow': |
|
|
simulatePatientFlowTest(); |
|
|
break; |
|
|
case 'slaCompliance': |
|
|
simulateSlaTest(); |
|
|
break; |
|
|
case 'integrationFlow': |
|
|
simulateIntegrationTest(); |
|
|
break; |
|
|
} |
|
|
}, 500); |
|
|
}); |
|
|
}); |
|
|
|
|
|
function logMessage(message) { |
|
|
const testLogs = document.getElementById('testLogs'); |
|
|
const line = document.createElement('div'); |
|
|
line.textContent = `[${new Date().toLocaleTimeString()}] ${message}`; |
|
|
testLogs.appendChild(line); |
|
|
testLogs.scrollTop = testLogs.scrollHeight; |
|
|
} |
|
|
|
|
|
function simulatePatientFlowTest() { |
|
|
logMessage("1. Creating test patient..."); |
|
|
setTimeout(() => { |
|
|
logMessage("✓ Patient created (ID: PT-123456)"); |
|
|
|
|
|
logMessage("2. Adding to queue..."); |
|
|
setTimeout(() => { |
|
|
logMessage("✓ Added to queue (Q-789012)"); |
|
|
|
|
|
logMessage("3. Starting study..."); |
|
|
setTimeout(() => { |
|
|
logMessage("✓ Study created (ST-345678)"); |
|
|
|
|
|
logMessage("4. Generating report..."); |
|
|
setTimeout(() => { |
|
|
logMessage("✓ Report finalized (RP-901234)"); |
|
|
|
|
|
logMessage("5. Checking scores..."); |
|
|
setTimeout(() => { |
|
|
logMessage("✓ Scores updated (+20 radiologist, +100 referrer)"); |
|
|
|
|
|
|
|
|
const testResults = document.getElementById('testResults'); |
|
|
testResults.querySelector('.bg-gray-700 div:first-child span').textContent = new Date().toLocaleString(); |
|
|
testResults.querySelector('.bg-gray-700 div:last-child span').textContent = 'Passed'; |
|
|
testResults.querySelector('.bg-gray-700 div:last-child span').className = 'text-green-400'; |
|
|
|
|
|
logMessage("✅ Test completed successfully"); |
|
|
}, 800); |
|
|
}, 800); |
|
|
}, 800); |
|
|
}, 800); |
|
|
}, 800); |
|
|
} |
|
|
|
|
|
|
|
|
document.getElementById('runApiTest').addEventListener('click', function() { |
|
|
const method = document.getElementById('apiMethod').value; |
|
|
const endpoint = document.getElementById('apiEndpoint').value; |
|
|
const body = document.getElementById('apiBody').value; |
|
|
const responseDiv = document.getElementById('apiResponse'); |
|
|
|
|
|
responseDiv.classList.remove('hidden'); |
|
|
responseDiv.innerHTML = '<div class="text-gray-400">Testing endpoint...</div>'; |
|
|
|
|
|
|
|
|
setTimeout(() => { |
|
|
let response; |
|
|
try { |
|
|
const parsedBody = body ? JSON.parse(body) : {}; |
|
|
response = { |
|
|
status: 200, |
|
|
data: { |
|
|
method, |
|
|
endpoint, |
|
|
body: parsedBody, |
|
|
timestamp: new Date().toISOString(), |
|
|
traceId: 't-' + Math.random().toString(36).substr(2, 8) |
|
|
} |
|
|
}; |
|
|
|
|
|
responseDiv.innerHTML = `<div class="text-green-400 mb-2">Request successful (200)</div> |
|
|
<pre>${JSON.stringify(response, null, 2)}</pre>`; |
|
|
} catch (e) { |
|
|
responseDiv.innerHTML = `<div class="text-red-400 mb-2">Error: ${e.message}</div>`; |
|
|
} |
|
|
}, 1000); |
|
|
}); |
|
|
|
|
|
|
|
|
document.getElementById('switchDb').addEventListener('click', function() { |
|
|
const button = this; |
|
|
const currentDb = button.textContent.includes('PostgreSQL') ? 'PostgreSQL' : 'SQLite'; |
|
|
const newDb = currentDb === 'SQLite' ? 'PostgreSQL' : 'SQLite'; |
|
|
|
|
|
logMessage(`Switching database from ${currentDb} to ${newDb}...`); |
|
|
setTimeout(() => { |
|
|
button.textContent = `Switch to ${currentDb}`; |
|
|
button.parentElement.querySelector('span.font-mono').textContent = newDb; |
|
|
logMessage(`✓ Database switched to ${newDb}`); |
|
|
}, 1500); |
|
|
}); |
|
|
</script> |
|
|
</body> |
|
|
</html> |