148 lines
5.0 KiB
JavaScript
148 lines
5.0 KiB
JavaScript
async function makeRequest(test) {
|
|
const apiUrl = document.getElementById('apiUrl').value;
|
|
const endpoint = typeof test.endpoint === 'function' ? test.endpoint() : test.endpoint;
|
|
const url = `${apiUrl}${endpoint}`;
|
|
|
|
const options = {
|
|
method: test.method,
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
}
|
|
};
|
|
|
|
if (test.auth && authToken) {
|
|
options.headers['Authorization'] = `Bearer ${authToken}`;
|
|
}
|
|
|
|
if (test.body) {
|
|
options.body = JSON.stringify(typeof test.body === 'function' ? test.body() : test.body);
|
|
}
|
|
|
|
const response = await fetch(url, options);
|
|
const data = await response.json().catch(() => ({}));
|
|
|
|
return { data, status: response.status };
|
|
}
|
|
|
|
async function runTest(categoryIdx, testIdx) {
|
|
const test = tests[categoryIdx].tests[testIdx];
|
|
const testId = `test-${categoryIdx}-${testIdx}`;
|
|
const testEl = document.getElementById(testId);
|
|
const contentEl = document.getElementById(`${testId}-content`);
|
|
const toggleEl = document.getElementById(`${testId}-toggle`);
|
|
const resultEl = testEl.querySelector('.test-result');
|
|
|
|
if (test.skip && test.skip()) {
|
|
testEl.querySelector('.test-status').textContent = 'SKIPPED';
|
|
testEl.querySelector('.test-status').className = 'test-status pending';
|
|
resultEl.style.display = 'block';
|
|
resultEl.className = 'test-result';
|
|
resultEl.innerHTML = '⚠️ Prerequisites not met';
|
|
return 'skip';
|
|
}
|
|
|
|
testEl.className = 'test-case running';
|
|
testEl.querySelector('.test-status').textContent = 'RUNNING';
|
|
testEl.querySelector('.test-status').className = 'test-status running';
|
|
resultEl.style.display = 'none';
|
|
|
|
try {
|
|
const { data, status } = await makeRequest(test);
|
|
|
|
const expectFail = test.expectFail || false;
|
|
const passed = test.expect(data, status);
|
|
|
|
const success = expectFail ? !passed || status >= 400 : passed;
|
|
|
|
testEl.className = success ? 'test-case pass' : 'test-case fail';
|
|
testEl.querySelector('.test-status').textContent = success ? 'PASS' : 'FAIL';
|
|
testEl.querySelector('.test-status').className = `test-status ${success ? 'pass' : 'fail'}`;
|
|
|
|
// Determine status code class
|
|
let statusClass = 'status-5xx';
|
|
if (status >= 200 && status < 300) statusClass = 'status-2xx';
|
|
else if (status >= 300 && status < 400) statusClass = 'status-3xx';
|
|
else if (status >= 400 && status < 500) statusClass = 'status-4xx';
|
|
|
|
// Check expected fields if defined
|
|
let expectedFieldsHTML = '';
|
|
if (test.expectedFields) {
|
|
const fieldChecks = test.expectedFields.map(field => {
|
|
const exists = field.split('.').reduce((obj, key) => obj?.[key], data) !== undefined;
|
|
const icon = exists ? '✓' : '✗';
|
|
const className = exists ? 'pass' : 'fail';
|
|
return `<div class="field-check ${className}">${icon} ${field}</div>`;
|
|
}).join('');
|
|
|
|
expectedFieldsHTML = `
|
|
<div class="expected-section">
|
|
<div class="expected-label">Expected Fields:</div>
|
|
${fieldChecks}
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
resultEl.style.display = 'block';
|
|
resultEl.className = 'test-result';
|
|
resultEl.innerHTML = `
|
|
<div style="margin-bottom: 8px;">
|
|
<span class="response-status ${statusClass}">HTTP ${status}</span>
|
|
<span style="color: #666;">${success ? '✓ Test passed' : '✗ Test failed'}</span>
|
|
</div>
|
|
${expectedFieldsHTML}
|
|
<div style="color: #666; font-size: 12px; margin-bottom: 4px;">Response:</div>
|
|
<div>${JSON.stringify(data, null, 2)}</div>
|
|
`;
|
|
|
|
if (success && test.onSuccess) {
|
|
test.onSuccess(data);
|
|
}
|
|
|
|
return success ? 'pass' : 'fail';
|
|
} catch (error) {
|
|
testEl.className = 'test-case fail';
|
|
testEl.querySelector('.test-status').textContent = 'ERROR';
|
|
testEl.querySelector('.test-status').className = 'test-status fail';
|
|
|
|
resultEl.style.display = 'block';
|
|
resultEl.className = 'test-error';
|
|
resultEl.innerHTML = `
|
|
<div style="font-weight: bold; margin-bottom: 8px;">❌ Network/Request Error</div>
|
|
<div>${error.message}</div>
|
|
${error.stack ? `<div style="margin-top: 8px; font-size: 11px; opacity: 0.7;">${error.stack}</div>` : ''}
|
|
`;
|
|
return 'fail';
|
|
}
|
|
}
|
|
|
|
async function runAllTests(event) {
|
|
resetState();
|
|
|
|
const button = event.target;
|
|
button.disabled = true;
|
|
button.textContent = '⏳ Running Tests...';
|
|
|
|
let totalTests = 0;
|
|
let passedTests = 0;
|
|
let failedTests = 0;
|
|
|
|
for (let i = 0; i < tests.length; i++) {
|
|
for (let j = 0; j < tests[i].tests.length; j++) {
|
|
const result = await runTest(i, j);
|
|
if (result !== 'skip') {
|
|
totalTests++;
|
|
if (result === 'pass') passedTests++;
|
|
if (result === 'fail') failedTests++;
|
|
}
|
|
}
|
|
}
|
|
|
|
document.getElementById('summary').style.display = 'flex';
|
|
document.getElementById('totalTests').textContent = totalTests;
|
|
document.getElementById('passedTests').textContent = passedTests;
|
|
document.getElementById('failedTests').textContent = failedTests;
|
|
|
|
button.disabled = false;
|
|
button.textContent = '▶ Run All Tests';
|
|
}
|