This repository has been archived on 2026-04-03. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
core/packages/sdk-test-runner/js/ui.js
gobha 96a4f16bc5 Changeset 0.37.17 (#229)
Co-authored-by: gobha <jasafpro@gmail.com>
Co-committed-by: gobha <jasafpro@gmail.com>
2026-03-24 16:50:00 +00:00

263 lines
8.7 KiB
JavaScript

/**
* 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();
};
})();