const assert = require('node:assert/strict'); const path = require('node:path'); class FakeElement { constructor(id = '') { this.id = id; this.value = id === 'task' ? 'detect' : ''; this.checked = false; this.disabled = false; this.style = {}; this.children = []; this.listeners = {}; this.className = ''; this.textContent = ''; this.scrollTop = 0; this.scrollHeight = 0; this.classList = { add() {}, remove() {}, contains() { return false; } }; } addEventListener(name, handler) { this.listeners[name] = handler; } appendChild(child) { this.children.push(child); this.lastElementChild = child; return child; } getContext() { return {}; } remove() {} set innerHTML(value) { this._innerHTML = value; this.children = []; } get innerHTML() { return this._innerHTML || ''; } } const elements = new Map(); const element = id => { if (!elements.has(id)) elements.set(id, new FakeElement(id)); return elements.get(id); }; let domReady; global.document = { addEventListener(name, handler) { if (name === 'DOMContentLoaded') domReady = handler; }, querySelectorAll() { return []; }, getElementById: element, createElement() { return new FakeElement(); }, head: new FakeElement('head'), body: new FakeElement('body') }; const storage = new Map(); global.localStorage = { getItem(key) { return storage.has(key) ? storage.get(key) : null; }, setItem(key, value) { storage.set(key, value); }, removeItem(key) { storage.delete(key); } }; global.confirm = () => true; global.window = {location: {protocol: 'http:', host: '127.0.0.1:8000'}}; class FakeChart { static instances = []; constructor(_context, config) { this.data = config.data; this.options = config.options; FakeChart.instances.push(this); } destroy() {} update() {} } global.Chart = FakeChart; class FakeWebSocket { static instances = []; constructor(url) { this.url = url; FakeWebSocket.instances.push(this); } } global.WebSocket = FakeWebSocket; const response = (ok, data) => ({ ok, async json() { return data; } }); global.fetch = async url => { if (url === '/api/datasets' || url === '/api/models' || url === '/api/sessions') { return response(true, []); } if (url === '/api/sessions/last_run') return response(false, {}); if (url === '/api/config/defaults') { return response(true, { dataset: 'coco8.yaml', model: 'yolo11n.pt', task: 'detect', workers: 8, patience: 100, augmentation: {enabled: true, close_mosaic: 10}, mlflow: {enabled: false}, split: {enabled: false} }); } return response(false, {}); }; require(path.resolve(__dirname, '../src/yolo_webui/static/app.js')); async function flushPromises() { await new Promise(resolve => setImmediate(resolve)); await new Promise(resolve => setImmediate(resolve)); } (async () => { assert.equal(typeof domReady, 'function'); domReady(); await flushPromises(); element('workers').value = '0'; element('patience').value = '0'; element('close-mosaic').value = '0'; element('auto-augment').value = 'none'; element('config-form').listeners.input(); const savedConfig = JSON.parse(storage.get('draft_config')); assert.equal(savedConfig.workers, 0); assert.equal(savedConfig.patience, 0); assert.equal(savedConfig.augmentation.close_mosaic, 0); assert.equal(savedConfig.augmentation.auto_augment, 'none'); assert.equal(FakeWebSocket.instances.length, 2); const socket = FakeWebSocket.instances[0]; socket.onmessage({ data: JSON.stringify({ type: 'init', status: 'idle', logs: [], metrics: [ {epoch: 1, mAP50: 0.5}, {epoch: 2, loss: 0.2} ] }) }); const chart = FakeChart.instances.at(-1); assert.deepEqual(chart.data.labels, [1, 2]); assert.deepEqual(chart.data.datasets.map(item => item.label), ['mAP50', 'loss']); assert.deepEqual(chart.data.datasets[0].data, [0.5, null]); assert.deepEqual(chart.data.datasets[1].data, [null, 0.2]); })().catch(error => { console.error(error); process.exitCode = 1; });