You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
82 lines
3.5 KiB
82 lines
3.5 KiB
const test = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
const fs = require('node:fs');
|
|
const vm = require('node:vm');
|
|
const path = require('node:path');
|
|
|
|
const source = fs.readFileSync(path.join(__dirname, '../frontend/web/static/js/integration.js'), 'utf8');
|
|
const start = source.indexOf(' async function navigateWithinConsole(');
|
|
const end = source.indexOf(' function requestConsoleNavigation(', start);
|
|
const deferred = () => {
|
|
let resolve;
|
|
const promise = new Promise(done => { resolve = done; });
|
|
return { promise, resolve };
|
|
};
|
|
const flush = () => new Promise(resolve => setImmediate(resolve));
|
|
|
|
function harness() {
|
|
const requests = [], visits = [], errors = [], redirects = [];
|
|
const content = {querySelector: () => null, replaceWith() {}, setAttribute() {}, removeAttribute() {}};
|
|
const context = {
|
|
URL, loadedUIVersion: '', currentPath: '/',
|
|
persistentTerminalNavigation: {navigating: false, pendingNavigation: null, sequence: 0},
|
|
sameOriginConsoleURL: target => new URL(target, 'http://device/'),
|
|
fetch: url => {
|
|
const result = deferred();
|
|
requests.push({url, result});
|
|
return result.promise;
|
|
},
|
|
DOMParser: class {parseFromString() {return {body: {dataset: {}}, querySelector: () => content};}},
|
|
document: {body: {dataset: {}}, querySelector: () => content, importNode: node => node, dispatchEvent() {}},
|
|
window: {location: {assign: url => redirects.push(url)}, history: {pushState() {}, replaceState() {}}},
|
|
CustomEvent: class {},
|
|
copyNavigationChrome() {}, isPortDebugPath: () => false,
|
|
initializeCurrentPage: async () => {}, report: error => errors.push(error),
|
|
};
|
|
context.setCurrentPath = url => {context.currentPath = url; visits.push(url);};
|
|
vm.createContext(context);
|
|
vm.runInContext(source.slice(start, end), context);
|
|
const reply = (index, status = 200) => requests[index].result.resolve({status, ok: status === 200, text: async () => ''});
|
|
return {context, requests, visits, errors, redirects, reply, navigate: context.navigateWithinConsole};
|
|
}
|
|
|
|
test('slow page initialization preserves only the last menu click without overlapping page writes', async () => {
|
|
const h = harness(), initial = deferred();
|
|
h.context.initializeCurrentPage = () => initial.promise;
|
|
const first = h.navigate('/network', 'push');
|
|
h.reply(0);
|
|
await flush();
|
|
await h.navigate('/ports/1/parameters', 'push');
|
|
await h.navigate('/logs/system', 'replace');
|
|
assert.equal(h.requests.length, 1);
|
|
initial.resolve();
|
|
await first;
|
|
assert.deepEqual(h.requests.map(r => r.url), ['/network', '/logs/system']);
|
|
h.reply(1);
|
|
await flush();
|
|
assert.deepEqual(h.visits, ['/network', '/logs/system']);
|
|
assert.equal(h.context.persistentTerminalNavigation.navigating, false);
|
|
});
|
|
|
|
test('a failed request still releases the pending destination', async () => {
|
|
const h = harness();
|
|
const first = h.navigate('/network', 'push');
|
|
await h.navigate('/port-status', 'push');
|
|
h.reply(0, 503);
|
|
await assert.rejects(first, /HTTP 503/);
|
|
assert.equal(h.requests[1].url, '/port-status');
|
|
h.reply(1);
|
|
await flush();
|
|
assert.deepEqual(h.visits, ['/port-status']);
|
|
});
|
|
|
|
test('expired login clears queued navigation before redirecting', async () => {
|
|
const h = harness();
|
|
const first = h.navigate('/network', 'push');
|
|
await h.navigate('/port-status', 'push');
|
|
h.reply(0, 401);
|
|
await first;
|
|
assert.equal(h.requests.length, 1);
|
|
assert.equal(h.redirects.length, 1);
|
|
assert.equal(h.context.persistentTerminalNavigation.pendingNavigation, null);
|
|
});
|
|
|