44 lines
3.1 KiB
JavaScript
44 lines
3.1 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 { createModel, exactConfirmation } = require('../../renderer/modules/etahen-link-ux');
|
|
|
|
test('health model distinguishes etaHEN, agent and Chimera uncertainty', () => {
|
|
const unknown = createModel({ runtime: { state: 'unknown' } });
|
|
assert.equal(unknown.etaHen.state, 'unknown'); assert.equal(unknown.agent.state, 'unknown'); assert.equal(unknown.chimera.state, 'unknown');
|
|
const online = createModel({ runtime: { state: 'online', identity: { version: '2.5b' } }, agent: { state: 'offline', paired: true }, lastInventory: { completedAt: 123 } });
|
|
assert.equal(online.etaHen.state, 'online'); assert.equal(online.agent.state, 'offline'); assert.equal(online.chimera.state, 'online');
|
|
});
|
|
|
|
test('staged remains distinct from installed and recovery dominates presentation', () => {
|
|
const staged = createModel({ job: { state: 'staged', stagedCount: 2, installedCount: 99 } });
|
|
assert.equal(staged.transfer.stagedCount, 2); assert.equal(staged.transfer.installedCount, 0); assert.equal(staged.recoveryRequired, false);
|
|
const recovery = createModel({ job: { state: 'recovery_required' } });
|
|
assert.equal(recovery.recoveryRequired, true); assert.equal(recovery.approvalRequired, false);
|
|
});
|
|
|
|
test('mutating confirmation requires local intent, recovery and exact identities', () => {
|
|
const input = { action: 'INSTALL', target: 'artifact_12345678', runtimeEpoch: 'runtime_12345678', localIntent: true, recoveryAvailable: true };
|
|
assert.equal(exactConfirmation(input), 'CONFIRM INSTALL artifact_12345678 runtime_12345678');
|
|
assert.throws(() => exactConfirmation({ ...input, localIntent: false }), /local console intent/i);
|
|
assert.throws(() => exactConfirmation({ ...input, recoveryAvailable: false }), /recovery/i);
|
|
});
|
|
|
|
test('rendering contract is keyboard-labelled, responsive and reduced-motion safe', () => {
|
|
const moduleSource = fs.readFileSync(path.join(__dirname, '../../renderer/modules/etahen-link-ux.js'), 'utf8');
|
|
const css = fs.readFileSync(path.join(__dirname, '../../renderer/styles/etahen-link.css'), 'utf8');
|
|
assert.match(moduleSource, /aria-labelledby="etahen-link-title"/); assert.match(moduleSource, /role="status" aria-live="polite"/); assert.match(moduleSource, /event\.key === 'Enter'/);
|
|
assert.match(css, /@media \(max-width: 700px\)/); assert.match(css, /prefers-reduced-motion: reduce/); assert.match(css, /focus-visible/);
|
|
});
|
|
|
|
test('English and Dutch Link catalogs have exact non-empty key parity', () => {
|
|
const en = JSON.parse(fs.readFileSync(path.join(__dirname, '../../build/lang/en.json'), 'utf8')).strings;
|
|
const nl = JSON.parse(fs.readFileSync(path.join(__dirname, '../../build/lang/nl.json'), 'utf8')).strings;
|
|
const keys = Object.keys(en).filter(key => key.startsWith('etahenLink.')).sort();
|
|
assert.ok(keys.length >= 12); assert.deepEqual(keys, Object.keys(nl).filter(key => key.startsWith('etahenLink.')).sort());
|
|
for (const key of keys) { assert.ok(en[key].trim()); assert.ok(nl[key].trim()); }
|
|
});
|