77 lines
3.9 KiB
JavaScript
77 lines
3.9 KiB
JavaScript
'use strict';
|
|
|
|
const test = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
const fs = require('node:fs');
|
|
const path = require('node:path');
|
|
const { describeVirtualRows } = require('../sidebar-view');
|
|
|
|
const ROOT = path.resolve(__dirname, '..');
|
|
const read = (name) => fs.readFileSync(path.join(ROOT, name), 'utf8');
|
|
|
|
function syntheticSnapshot(count) {
|
|
const rows = Array.from({ length: count }, (_, index) => ({
|
|
key: `camera:00000000-0000-4000-8000-${String(index).padStart(12, '0')}`,
|
|
id: `00000000-0000-4000-8000-${String(index).padStart(12, '0')}`,
|
|
kind: 'camera', name: `Camera ${index}`, ariaLevel: 3, ariaSelected: index === 4,
|
|
ariaPosInSet: index + 1, ariaSetSize: count,
|
|
}));
|
|
const startIndex = Math.max(0, Math.floor(count / 2) - 20);
|
|
return {
|
|
state: { activeKey: rows[startIndex].key, selectedKey: rows[4].key },
|
|
rows: Object.assign(rows, { hiddenSelected: true }),
|
|
window: { rows: rows.slice(startIndex, startIndex + 60), startIndex, offsetTop: startIndex * 30, totalHeight: count * 30 },
|
|
};
|
|
}
|
|
|
|
test('renderer loads hierarchy foundations in order and exposes an accessible virtual tree', () => {
|
|
const html = read('index.html');
|
|
const treeIndex = html.indexOf('device-tree.js');
|
|
const controllerIndex = html.indexOf('sidebar-controller.js');
|
|
const viewIndex = html.indexOf('sidebar-view.js');
|
|
const rendererIndex = html.indexOf('renderer.js');
|
|
assert.ok(treeIndex >= 0 && treeIndex < controllerIndex && controllerIndex < viewIndex && viewIndex < rendererIndex);
|
|
assert.match(html, /id="deviceList"[^>]*role="tree"[^>]*tabindex="0"[^>]*aria-activedescendant/);
|
|
assert.match(html, /id="deviceResults"[^>]*role="status"[^>]*aria-live="polite"/);
|
|
});
|
|
|
|
test('renderer uses one hierarchy request and no parallel flat discovery', () => {
|
|
const renderer = read('renderer.js');
|
|
assert.match(renderer, /electronAPI\.getDeviceHierarchy\(\)/);
|
|
assert.doesNotMatch(renderer, /electronAPI\.getDevices\(|electronAPI\.getDeviceSites\(|Promise\.all\(\s*\[\s*window\.electronAPI\.get/);
|
|
assert.doesNotMatch(renderer, /\ballDevices\b|\ballSites\b|\bcollapsedSites\b|groupDevicesBySite/);
|
|
assert.match(renderer, /loadGeneration/);
|
|
assert.match(renderer, /generation\s*!==\s*loadGeneration/);
|
|
});
|
|
|
|
test('renderer uses delegated tree events, throttled scroll, and debounced search', () => {
|
|
const renderer = read('renderer.js');
|
|
assert.equal((renderer.match(/deviceList\.addEventListener\('click'/g) || []).length, 1);
|
|
assert.equal((renderer.match(/deviceList\.addEventListener\('keydown'/g) || []).length, 1);
|
|
assert.equal((renderer.match(/deviceList\.addEventListener\('scroll'/g) || []).length, 1);
|
|
assert.match(renderer, /requestAnimationFrame/);
|
|
assert.match(renderer, /setTimeout\([^,]+,\s*125\)/s);
|
|
});
|
|
|
|
test('virtual row descriptors stay bounded for 1,500 and 5,000 visible rows', () => {
|
|
for (const count of [1_500, 5_000]) {
|
|
const descriptors = describeVirtualRows(syntheticSnapshot(count), { activeDeviceIds: new Set() });
|
|
assert.ok(descriptors.length <= 80, `${count} rows mounted ${descriptors.length} descriptors`);
|
|
assert.equal(descriptors[0].role, 'treeitem');
|
|
assert.equal(descriptors[0].aria.level, 3);
|
|
assert.match(descriptors[0].domId, /^apt-tree-row-/);
|
|
}
|
|
});
|
|
|
|
test('view descriptors preserve selection and refresh proxy/status classes without rebuilding rows', () => {
|
|
const snapshot = syntheticSnapshot(1_500);
|
|
const selectedId = snapshot.rows[4].id;
|
|
const before = describeVirtualRows(snapshot, { activeDeviceIds: new Set() });
|
|
const camera = before.find((row) => row.kind === 'camera');
|
|
const after = describeVirtualRows(snapshot, { activeDeviceIds: new Set([camera.deviceId]), isOnline: () => true });
|
|
assert.ok(after.find((row) => row.key === camera.key).classes.includes('proxy-active'));
|
|
assert.ok(after.find((row) => row.key === camera.key).classes.includes('online'));
|
|
assert.equal(snapshot.rows[4].id, selectedId);
|
|
assert.equal(snapshot.rows.hiddenSelected, true);
|
|
});
|