feat(sv-card): open AI Usage dashboard via in-Void #/ai-usage route
This commit is contained in:
@@ -30,7 +30,7 @@ async function load() {
|
|||||||
el('div', { class: 'sv-row' }, el('span', { class: 'k' }, 'runs · err'), mono(`${l.runs} · ${(l.top.error_rate * 100).toFixed(0)}%`)))
|
el('div', { class: 'sv-row' }, el('span', { class: 'k' }, 'runs · err'), mono(`${l.runs} · ${(l.top.error_rate * 100).toFixed(0)}%`)))
|
||||||
: el('span', { class: 'muted' }, 'No local runs yet')
|
: el('span', { class: 'muted' }, 'No local runs yet')
|
||||||
),
|
),
|
||||||
el('a', { class: 'aiu-link', href: 'http://192.168.1.212:8080/', target: '_blank', rel: 'noopener' }, 'Full dashboard ↗')
|
el('a', { class: 'aiu-link', href: '#/ai-usage' }, 'Full dashboard ↗')
|
||||||
);
|
);
|
||||||
} catch { mount(body, el('span', { class: 'muted' }, 'No usage data')); }
|
} catch { mount(body, el('span', { class: 'muted' }, 'No usage data')); }
|
||||||
}
|
}
|
||||||
|
|||||||
28
tests/frontend/ai_usage_card.test.js
Normal file
28
tests/frontend/ai_usage_card.test.js
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import { describe, it, expect, vi, beforeAll, afterAll } from 'vitest';
|
||||||
|
import { JSDOM } from 'jsdom';
|
||||||
|
|
||||||
|
vi.mock('../../public/api.js', () => ({ api: { get: vi.fn().mockResolvedValue({
|
||||||
|
ok: true,
|
||||||
|
claude: { today: { input: 1, output: 2, cache: 3, turns: 4 }, week: { input: 5, output: 6 }, top_model: 'claude-opus-4-8' },
|
||||||
|
local: { top: { model: 'llama', p50_ms: 100, p95_ms: 200, error_rate: 0 }, runs: 3 }
|
||||||
|
}) } }));
|
||||||
|
|
||||||
|
let card;
|
||||||
|
beforeAll(async () => {
|
||||||
|
const dom = new JSDOM('<!doctype html><html><body></body></html>', { url: 'http://localhost/' });
|
||||||
|
global.window = dom.window; global.document = dom.window.document;
|
||||||
|
global.Node = dom.window.Node; global.location = dom.window.location;
|
||||||
|
card = (await import('../../public/views/cards/ai_usage.js')).default;
|
||||||
|
});
|
||||||
|
afterAll(() => { delete global.window; delete global.document; delete global.Node; delete global.location; });
|
||||||
|
|
||||||
|
describe('AI Usage SV card link', () => {
|
||||||
|
it('points the Full dashboard link at the in-Void #/ai-usage route', async () => {
|
||||||
|
const e = document.createElement('div');
|
||||||
|
card.mount(e);
|
||||||
|
await new Promise(r => setTimeout(r, 0)); // let load() resolve
|
||||||
|
const link = e.querySelector('a.aiu-link');
|
||||||
|
expect(link.getAttribute('href')).toBe('#/ai-usage');
|
||||||
|
expect(link.hasAttribute('target')).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user