Changeset 0.37.17 (#229)
Co-authored-by: gobha <jasafpro@gmail.com> Co-committed-by: gobha <jasafpro@gmail.com>
This commit is contained in:
262
packages/sdk-test-runner/js/ui.js
Normal file
262
packages/sdk-test-runner/js/ui.js
Normal file
@@ -0,0 +1,262 @@
|
||||
/**
|
||||
* 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();
|
||||
};
|
||||
|
||||
})();
|
||||
Reference in New Issue
Block a user