170 lines
6.5 KiB
JavaScript
170 lines
6.5 KiB
JavaScript
// Generated, do not edit. sha256:e3545bf95485dc067f72c9e5c3b65d89fe5d9d0cb4f65e196bb3a28c383f4229
|
|||
|
|
import { describe, it, beforeEach, mock } from 'node:test';
|
||
|
|
import assert from 'node:assert/strict';
|
||
|
|
|
||
|
|
import { mkdtempSync } from 'node:fs';
|
||
|
|
import { tmpdir } from 'node:os';
|
||
|
|
import { join } from 'node:path';
|
||
|
|
import { createDbHarness } from '../helpers/db-node.mjs';
|
||
|
|
|
||
|
|
process.env.ASSETAPP_DATA_DIR = mkdtempSync(join(tmpdir(), 'assetapp-control-'));
|
||
|
|
const { setInstallLocale } = await import('../../server/installation.mjs');
|
||
|
|
setInstallLocale('nl');
|
||
|
|
|
||
|
|
const harness = createDbHarness();
|
||
|
|
mock.module('../../server/db.js', { namedExports: harness.exports });
|
||
|
|
|
||
|
|
const echteConfig = await import('../../shared/config.js');
|
||
|
|
mock.module('../../shared/config.js', {
|
||
|
|
namedExports: {
|
||
|
|
...echteConfig,
|
||
|
|
CONFIG: Object.freeze({
|
||
|
|
...echteConfig.CONFIG,
|
||
|
|
COMMANDS: Object.freeze({ ...echteConfig.CONFIG.COMMANDS, CLAIM_SECONDS: 1 }),
|
||
|
|
}),
|
||
|
|
},
|
||
|
|
});
|
||
|
|
|
||
|
|
const { CONTROL_TOOLS } = await import('../../server/mcp/control-tools.mjs');
|
||
|
|
const { claimNextCommand, resolveCommand } = await import('../../server/data/commands.js');
|
||
|
|
const { setPref } = await import('../../server/data/preferences.js');
|
||
|
|
const { TARGETS, targetHash } = await import('../../shared/config.js');
|
||
|
|
|
||
|
|
const tool = CONTROL_TOOLS.find(t => t.name === 'navigate_to');
|
||
|
|
const waar = { scope: tool.scope, run: () => tool.run({}) };
|
||
|
|
|
||
|
|
const zetAan = async (control = true) => {
|
||
|
|
await setPref('ai', 'allow_read', 1);
|
||
|
|
await setPref('ai', 'allow_control', control ? 1 : 0);
|
||
|
|
};
|
||
|
|
|
||
|
|
let busTijdensWachten = [];
|
||
|
|
function browserDoet(uitkomst, detail = null, na = 60) {
|
||
|
|
setTimeout(async () => {
|
||
|
|
busTijdensWachten = harness.exports.all(`SELECT name FROM BusSignal ORDER BY id`).map(r => r.name);
|
||
|
|
const c = await claimNextCommand('tab-1');
|
||
|
|
if (c) await resolveCommand(c.id, 'tab-1', uitkomst, detail);
|
||
|
|
}, na);
|
||
|
|
}
|
||
|
|
|
||
|
|
beforeEach(async () => { harness.recreate(); busTijdensWachten = []; await zetAan(); });
|
||
|
|
|
||
|
|
describe('navigate_to: de poort', () => {
|
||
|
|
it('declareert scope control, en niet read of write', () => {
|
||
|
|
assert.equal(tool.scope, 'control');
|
||
|
|
});
|
||
|
|
|
||
|
|
it('weigert als het besturen uit staat, en verandert dan niets', async () => {
|
||
|
|
await zetAan(false);
|
||
|
|
await assert.rejects(() => tool.run({ target: 'AI_ACTIVITY' }), /niet besturen/);
|
||
|
|
assert.equal(await claimNextCommand('tab-1'), null, 'er is geen opdracht klaargezet');
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
describe('navigate_to: het doel', () => {
|
||
|
|
it('zet een opdracht klaar met het adres van de gekozen plek', async () => {
|
||
|
|
browserDoet('done');
|
||
|
|
const r = await tool.run({ target: 'AI_ACTIVITY' });
|
||
|
|
assert.match(r.text, /Gedaan/);
|
||
|
|
assert.match(r.text, new RegExp(targetHash(TARGETS.AI_ACTIVITY).replace('#', '#')));
|
||
|
|
});
|
||
|
|
|
||
|
|
it('deelt geen enkel administratieveld', async () => {
|
||
|
|
browserDoet('done');
|
||
|
|
const r = await tool.run({ target: 'HOME' });
|
||
|
|
assert.deepEqual(r.fieldsShared, []);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('wijst een onbekende plek af zonder om te vallen, en noemt wat er wél is', async () => {
|
||
|
|
const r = await tool.run({ target: 'DE_KEUKEN' });
|
||
|
|
assert.match(r.text, /geen bekende plek/);
|
||
|
|
assert.match(r.text, /AI_ACTIVITY/);
|
||
|
|
assert.match(r.text, /TAXES - Belastingen/, r.text);
|
||
|
|
assert.equal(await claimNextCommand('tab-1'), null, 'en er staat niets klaar');
|
||
|
|
});
|
||
|
|
|
||
|
|
it('wijst een leeg of verkeerd getypt doel net zo af als een verzonnen doel', async () => {
|
||
|
|
for (const args of [{ target: '' }, { target: 42 }, { target: null }]) {
|
||
|
|
const r = await tool.run(args);
|
||
|
|
assert.match(r.text, /geen bekende plek/, `${JSON.stringify(args)} hoort netjes afgewezen te worden`);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
describe('navigate_to: het signaal gaat vóóruit', () => {
|
||
|
|
it('zet het signaal op de bus vóórdat hij gaat wachten', async () => {
|
||
|
|
browserDoet('done');
|
||
|
|
await tool.run({ target: 'AI_ACTIVITY' });
|
||
|
|
assert.ok(busTijdensWachten.includes('commands-changed'),
|
||
|
|
`de bus was ${JSON.stringify(busTijdensWachten)} toen het tabblad keek`);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
describe('navigate_to: de uitkomst', () => {
|
||
|
|
it('meldt het als de gebruiker het tegenhoudt, mét de reden', async () => {
|
||
|
|
browserDoet('rejected', 'Tegengehouden door de gebruiker.');
|
||
|
|
const r = await tool.run({ target: 'AI_ACTIVITY' });
|
||
|
|
assert.match(r.text, /Niet uitgevoerd/);
|
||
|
|
assert.match(r.text, /Tegengehouden door de gebruiker/);
|
||
|
|
assert.match(r.text, /niet genavigeerd/);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('geeft de schuld niet aan de gebruiker als het tabblad zelf afwees', async () => {
|
||
|
|
browserDoet('rejected', 'De opdracht wees niet naar een geldige plek in de app.');
|
||
|
|
const r = await tool.run({ target: 'HOME' });
|
||
|
|
assert.doesNotMatch(r.text, /de gebruiker/i);
|
||
|
|
assert.match(r.text, /geldige plek/);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('zegt dat de app niet openstaat als niemand de opdracht oppakt', async () => {
|
||
|
|
const r = await tool.run({ target: 'HOME' });
|
||
|
|
assert.match(r.text, /niet open/);
|
||
|
|
assert.match(r.text, /niet genavigeerd/);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
describe('navigate_to zonder target: waar de gebruiker kijkt', () => {
|
||
|
|
it('staat achter dezelfde schuif als navigeren', async () => {
|
||
|
|
assert.equal(waar.scope, 'control');
|
||
|
|
await zetAan(false);
|
||
|
|
await assert.rejects(() => waar.run({}), /niet besturen/);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('geeft de NAAM terug die navigate_to kent, niet alleen het adres', async () => {
|
||
|
|
browserDoet('done', targetHash(TARGETS.CHARTS));
|
||
|
|
const r = await waar.run({});
|
||
|
|
assert.match(r.text, /CHARTS/);
|
||
|
|
assert.match(r.text, new RegExp(targetHash(TARGETS.CHARTS)));
|
||
|
|
});
|
||
|
|
|
||
|
|
it('noemt de belastingpagina bij naam en niet alleen bij adres', async () => {
|
||
|
|
browserDoet('done', targetHash(TARGETS.TAXES));
|
||
|
|
const r = await waar.run({});
|
||
|
|
assert.match(r.text, /Belastingen/, r.text);
|
||
|
|
assert.match(r.text, /TAXES/);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('geeft bij een plek buiten de registry het adres, en zegt erbij dat het geen naam is', async () => {
|
||
|
|
browserDoet('done', '#/events/2026/detail');
|
||
|
|
const r = await waar.run({});
|
||
|
|
assert.match(r.text, /#\/events\/2026\/detail/);
|
||
|
|
assert.match(r.text, /geen van de plekken/);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('deelt geen enkel administratieveld', async () => {
|
||
|
|
browserDoet('done', targetHash(TARGETS.HOME));
|
||
|
|
const r = await waar.run({});
|
||
|
|
assert.deepEqual(r.fieldsShared, []);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('zegt dat de app niet openstaat als niemand antwoordt', async () => {
|
||
|
|
const r = await waar.run({});
|
||
|
|
assert.match(r.text, /niet open/);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('verzint niets als het tabblad wel antwoordt maar zonder plek', async () => {
|
||
|
|
browserDoet('done', '');
|
||
|
|
const r = await waar.run({});
|
||
|
|
assert.match(r.text, /liet niet weten/);
|
||
|
|
});
|
||
|
|
});
|