/
Starolat
/
DeepDive
Обзор
Документация
Войти
/
Starolat
/
DeepDive
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
tests/browser-test-runner.html
404 строки
13 KB
Starolat Sergei
chore: sync project state for Gitverse
16 июн 2026, 16:06
16 июн 2026, 16:06
1cb951e
Код
Авторство
О чём код?
<!DOCTYPE html> <html lang="ru"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>DeepDive Tests - Browser Runner</title> <style> * { box-sizing: border-box; } body { font-family: 'Inter', sans-serif; margin: 0; padding: 20px; background: #f5f5f5; } .header { background: #1976d2; color: white; padding: 20px; margin: -20px -20px 20px -20px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } .header h1 { margin: 0; font-size: 24px; } .header p { margin: 5px 0 0 0; opacity: 0.9; } .controls { background: white; padding: 15px; border-radius: 8px; margin-bottom: 20px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); display: flex; gap: 10px; flex-wrap: wrap; } button { padding: 10px 20px; border: none; border-radius: 4px; cursor: pointer; font-size: 14px; transition: background 0.2s; } .btn-primary { background: #1976d2; color: white; } .btn-primary:hover { background: #1565c0; } .btn-secondary { background: #e0e0e0; color: #333; } .btn-secondary:hover { background: #d0d0d0; } .stats { display: flex; gap: 20px; margin-bottom: 20px; } .stat-card { background: white; padding: 15px 25px; border-radius: 8px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); text-align: center; } .stat-value { font-size: 32px; font-weight: bold; } .stat-label { font-size: 12px; color: #666; text-transform: uppercase; } .stat-pass { color: #4caf50; } .stat-fail { color: #f44336; } .stat-total { color: #1976d2; } .results { background: white; border-radius: 8px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); overflow: hidden; } .suite { border-bottom: 1px solid #e0e0e0; } .suite-header { background: #fafafa; padding: 15px 20px; font-weight: 500; cursor: pointer; display: flex; justify-content: space-between; align-items: center; } .suite-header:hover { background: #f0f0f0; } .suite-status { font-size: 12px; padding: 4px 8px; border-radius: 12px; } .suite-pass .suite-status { background: #e8f5e9; color: #2e7d32; } .suite-fail .suite-status { background: #ffebee; color: #c62828; } .test-list { padding: 10px 0; } .test { padding: 10px 20px 10px 40px; display: flex; align-items: center; gap: 10px; } .test:hover { background: #fafafa; } .test-icon { font-size: 16px; } .test-pass .test-icon { color: #4caf50; } .test-fail .test-icon { color: #f44336; } .test-name { flex: 1; } .test-error { color: #f44336; font-size: 12px; margin-top: 5px; font-family: monospace; } .hidden { display: none; } .console-output { background: #1e1e1e; color: #d4d4d4; padding: 15px; font-family: monospace; font-size: 12px; max-height: 300px; overflow-y: auto; } .console-output .log { color: #d4d4d4; } .console-output .info { color: #4fc1ff; } .console-output .warn { color: #ffcc00; } .console-output .error { color: #f44336; } </style> <link rel="stylesheet" href="../assets/fonts/fonts.css"> </head> <body> <div class="header"> <h1>🧪 DeepDive Test Runner</h1> <p>Браузерное тестирование компонентов</p> </div> <div class="controls"> <button class="btn-primary" onclick="runAllTests()">▶️ Запустить все тесты</button> <button class="btn-secondary" onclick="clearResults()">🗑 Очистить</button> </div> <div class="stats" id="stats"> <div class="stat-card"> <div class="stat-value stat-total" id="stat-total">0</div> <div class="stat-label">Всего</div> </div> <div class="stat-card"> <div class="stat-value stat-pass" id="stat-pass">0</div> <div class="stat-label">Пройдено</div> </div> <div class="stat-card"> <div class="stat-value stat-fail" id="stat-fail">0</div> <div class="stat-label">Ошибок</div> </div> </div> <div class="results" id="results"> <div style="padding: 40px; text-align: center; color: #999;"> Нажмите "Запустить все тесты" для начала </div> </div> <script type="module"> // ======================================== // Test Framework (Mini Jest-like) // ======================================== const results = { suites: [], currentSuite: null }; window.describe = (name, fn) => { const suite = { name, tests: [], beforeEachFns: [], afterEachFns: [], passed: 0, failed: 0 }; results.suites.push(suite); results.currentSuite = suite; fn(); results.currentSuite = null; }; window.beforeEach = (fn) => { if (results.currentSuite) { results.currentSuite.beforeEachFns.push(fn); } }; window.afterEach = (fn) => { if (results.currentSuite) { results.currentSuite.afterEachFns.push(fn); } }; window.it = (name, fn) => { results.currentSuite.tests.push({ name, fn, async: fn.constructor.name === 'AsyncFunction', status: 'pending' }); }; window.expect = (actual) => ({ toBe: (expected) => { if (actual !== expected) { throw new Error(`Expected "${expected}", but got "${actual}"`); } }, toEqual: (expected) => { if (JSON.stringify(actual) !== JSON.stringify(expected)) { throw new Error(`Expected ${JSON.stringify(expected)}, but got ${JSON.stringify(actual)}`); } }, toBeTruthy: () => { if (!actual) { throw new Error(`Expected truthy value, but got "${actual}"`); } }, toBeFalsy: () => { if (actual) { throw new Error(`Expected falsy value, but got "${actual}"`); } }, toContain: (item) => { if (!actual.includes(item)) { throw new Error(`Expected array to contain "${item}"`); } }, toHaveLength: (n) => { if (actual.length !== n) { throw new Error(`Expected length ${n}, but got ${actual.length}`); } }, toHaveProperty: (prop) => { if (!(prop in actual)) { throw new Error(`Expected object to have property "${prop}"`); } } }); // ======================================== // Test Runner // ======================================== async function loadComponent(path) { await import(path); } async function runTest(test) { try { const result = test.fn(); // If it's a promise (async function), await it if (result && typeof result.then === 'function') { await result; } test.status = 'passed'; return true; } catch (error) { test.status = 'failed'; test.error = error.message; return false; } } async function runSuite(suite) { suite.passed = 0; suite.failed = 0; for (const test of suite.tests) { for (const fn of suite.beforeEachFns) { await fn(); } const passed = await runTest(test); for (const fn of suite.afterEachFns) { await fn(); } if (passed) suite.passed++; else suite.failed++; } } window.runAllTests = async () => { // Reset results results.suites = []; document.getElementById('results').innerHTML = '<div style="padding: 20px;">⏳ Выполнение тестов...</div>'; // Load test definitions await import('./test-definitions.js'); // Run all suites for (const suite of results.suites) { await runSuite(suite); } renderResults(); updateStats(); }; function renderResults() { const container = document.getElementById('results'); container.innerHTML = ''; results.suites.forEach(suite => { const suiteEl = document.createElement('div'); suiteEl.className = `suite ${suite.failed === 0 ? 'suite-pass' : 'suite-fail'}`; const statusText = suite.failed === 0 ? `✅ ${suite.passed}/${suite.tests.length}` : `❌ ${suite.failed} ошибок`; suiteEl.innerHTML = ` <div class="suite-header" onclick="toggleSuite(this)"> <span>${suite.name}</span> <span class="suite-status">${statusText}</span> </div> <div class="test-list"> ${suite.tests.map(test => ` <div class="test test-${test.status}"> <span class="test-icon">${test.status === 'passed' ? '✅' : '❌'}</span> <span class="test-name">${test.name}</span> ${test.error ? `<div class="test-error">${test.error}</div>` : ''} </div> `).join('')} </div> `; container.appendChild(suiteEl); }); } function updateStats() { let total = 0; let passed = 0; let failed = 0; results.suites.forEach(suite => { total += suite.tests.length; passed += suite.passed; failed += suite.failed; }); document.getElementById('stat-total').textContent = total; document.getElementById('stat-pass').textContent = passed; document.getElementById('stat-fail').textContent = failed; } window.toggleSuite = (header) => { const list = header.nextElementSibling; list.classList.toggle('hidden'); }; window.clearResults = () => { document.getElementById('results').innerHTML = ` <div style="padding: 40px; text-align: center; color: #999;"> Нажмите "Запустить все тесты" для начала </div> `; document.getElementById('stat-total').textContent = '0'; document.getElementById('stat-pass').textContent = '0'; document.getElementById('stat-fail').textContent = '0'; }; // Auto-run on page load (optional) // window.runAllTests(); </script> </body> </html>