/** * SDK Test Runner — UI * * Renders: summary bar, domain filter, live results table, export. * Follows ICD runner visual conventions. */ (function () { 'use strict'; var T = window.SDKR; if (!T) return; // ─── Verdict Colors ──────────────────────────────────────── var COLORS = { PASS: 'var(--success, #22c55e)', SDK_BUG: 'var(--warning, #f59e0b)', ICD_BUG: 'var(--danger, #ef4444)', SHAPE_BUG: '#a855f7', SKIP: 'var(--text3, #888)', ERROR: 'var(--danger, #ef4444)' }; var LABELS = { PASS: 'PASS', SDK_BUG: 'SDK BUG', ICD_BUG: 'ICD BUG', SHAPE_BUG: 'SHAPE', SKIP: 'SKIP', ERROR: 'ERROR' }; // ─── DOM Helpers ────────────────────────────────────────── function el(tag, attrs, children) { var e = document.createElement(tag); if (attrs) Object.keys(attrs).forEach(function (k) { if (k === 'className') e.className = attrs[k]; else if (k === 'style' && typeof attrs[k] === 'object') Object.assign(e.style, attrs[k]); else if (k.indexOf('on') === 0) e.addEventListener(k.slice(2).toLowerCase(), attrs[k]); else e.setAttribute(k, attrs[k]); }); if (children) { if (!Array.isArray(children)) children = [children]; children.forEach(function (c) { if (typeof c === 'string') e.appendChild(document.createTextNode(c)); else if (c) e.appendChild(c); }); } return e; } function esc(s) { return String(s || ''); } // ─── Shell ──────────────────────────────────────────────── T.renderShell = function () { var mount = T.mount; mount.innerHTML = ''; // Header var header = el('div', { className: 'sdkr-header' }, [ el('h1', {}, 'SDK Test Runner'), el('span', { className: 'sdkr-version' }, 'v' + (T.manifest.version || '?')) ]); mount.appendChild(header); // Description mount.appendChild(el('p', { className: 'sdkr-desc' }, 'Dual-path validation: SDK call → raw ICD fetch. ' + 'SDK fail + ICD pass = SDK bug. Both fail = ICD bug.')); // ── Fixtures Panel ── var fixturePanel = el('div', { className: 'sdkr-fixtures' }); var fixRow = el('div', { className: 'sdkr-fixture-row' }); fixRow.appendChild(el('span', { className: 'sdkr-fixture-label' }, 'FIXTURES:')); // Provider dropdown var provSelect = el('select', { className: 'sdkr-select' }); provSelect.appendChild(el('option', { value: '' }, '— provider —')); ['openrouter', 'openai', 'anthropic', 'venice'].forEach(function (p) { provSelect.appendChild(el('option', { value: p }, p)); }); fixRow.appendChild(provSelect); // API key input var keyInput = el('input', { type: 'password', className: 'sdkr-input', placeholder: 'API key (optional — enables model tests)' }); fixRow.appendChild(keyInput); // Fixture status var fixStatus = el('span', { className: 'sdkr-fixture-status' }, ''); fixRow.appendChild(fixStatus); fixturePanel.appendChild(fixRow); var fixNote = el('div', { className: 'sdkr-fixture-note' }, 'Provide a provider + API key to unlock embedding/model tests. ' + 'Without a key, those tests are skipped. Key is never stored.'); fixturePanel.appendChild(fixNote); mount.appendChild(fixturePanel); // ── Fixture wiring: store user input for provisionFixtures to use ── T._fixtureInput = { provSelect: provSelect, keyInput: keyInput, statusEl: fixStatus }; // Controls row var controlRow = el('div', { className: 'sdkr-controls' }); // Domain filter checkboxes var filterBox = el('div', { className: 'sdkr-filters' }); filterBox.appendChild(el('span', { className: 'sdkr-filter-label' }, 'Domains: ')); var domainNames = Object.keys(T.domains).sort(); var domainChecks = {}; domainNames.forEach(function (name) { var label = el('label', { className: 'sdkr-check' }); var cb = el('input', { type: 'checkbox', checked: 'checked' }); domainChecks[name] = cb; label.appendChild(cb); label.appendChild(document.createTextNode(' ' + name)); filterBox.appendChild(label); }); controlRow.appendChild(filterBox); // Buttons var btnRow = el('div', { className: 'sdkr-btn-row' }); var runBtn = el('button', { className: 'sdkr-btn sdkr-btn-primary', onClick: onRun }, 'Run All'); var abortBtn = el('button', { className: 'sdkr-btn sdkr-btn-danger', onClick: function () { T.abort(); } }, 'Abort'); abortBtn.style.display = 'none'; var exportBtn = el('button', { className: 'sdkr-btn', onClick: onExport }, 'Export JSON'); btnRow.appendChild(runBtn); btnRow.appendChild(abortBtn); btnRow.appendChild(exportBtn); controlRow.appendChild(btnRow); mount.appendChild(controlRow); // Summary bar var summaryBar = el('div', { className: 'sdkr-summary' }); mount.appendChild(summaryBar); // Results table var tableWrap = el('div', { className: 'sdkr-table-wrap' }); var table = el('table', { className: 'sdkr-table' }); var thead = el('thead', {}, [ el('tr', {}, [ el('th', {}, '#'), el('th', {}, 'Domain'), el('th', {}, 'Group'), el('th', {}, 'Test'), el('th', {}, 'Verdict'), el('th', {}, 'ms'), el('th', {}, 'Detail') ]) ]); table.appendChild(thead); var tbody = el('tbody', {}); table.appendChild(tbody); tableWrap.appendChild(table); mount.appendChild(tableWrap); // ── Callbacks ── function updateSummary() { var s = T.stats; var total = T.results.length; summaryBar.innerHTML = ''; var items = [ ['PASS', s.pass], ['SDK_BUG', s.sdk_bug], ['ICD_BUG', s.icd_bug], ['SHAPE_BUG', s.shape_bug], ['SKIP', s.skip] ]; items.forEach(function (item) { var badge = el('span', { className: 'sdkr-badge', style: { background: item[1] > 0 ? COLORS[item[0]] : 'var(--bg2, #333)', color: item[1] > 0 ? '#fff' : 'var(--text3, #888)' } }, LABELS[item[0]] + ': ' + item[1]); summaryBar.appendChild(badge); }); summaryBar.appendChild(el('span', { className: 'sdkr-badge', style: { background: 'var(--bg2, #333)' } }, 'Total: ' + total)); } T.onResult = function (entry) { var idx = T.results.length; var v = entry.verdict; var detail = ''; if (entry.sdkErr) detail += 'SDK: ' + esc(entry.sdkErr); if (entry.rawErr) detail += (detail ? ' | ' : '') + 'ICD: ' + esc(entry.rawErr); if (entry.note) detail += (detail ? ' | ' : '') + esc(entry.note); var row = el('tr', { className: 'sdkr-row sdkr-row-' + v.toLowerCase() }, [ el('td', {}, String(idx)), el('td', {}, esc(entry.domain)), el('td', {}, esc(entry.group)), el('td', { className: 'sdkr-test-name' }, esc(entry.name)), el('td', {}, [ el('span', { className: 'sdkr-verdict', style: { color: COLORS[v] || '#fff' } }, LABELS[v] || v) ]), el('td', { className: 'sdkr-ms' }, String(entry.durationMs)), el('td', { className: 'sdkr-detail' }, detail) ]); tbody.appendChild(row); updateSummary(); // Auto-scroll row.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); }; T.onStart = function () { tbody.innerHTML = ''; runBtn.disabled = true; runBtn.textContent = 'Running...'; abortBtn.style.display = ''; updateSummary(); }; T.onComplete = function () { runBtn.disabled = false; runBtn.textContent = 'Run All'; abortBtn.style.display = 'none'; updateSummary(); }; function onRun() { var selected = []; domainNames.forEach(function (n) { if (domainChecks[n].checked) selected.push(n); }); T.runAll(selected.length === domainNames.length ? null : selected); } function onExport() { var data = { timestamp: new Date().toISOString(), version: T.manifest.version, stats: T.stats, results: T.results }; var blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' }); var a = document.createElement('a'); a.href = URL.createObjectURL(blob); a.download = 'sdk-test-results-' + Date.now() + '.json'; a.click(); URL.revokeObjectURL(a.href); } updateSummary(); }; })();