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.
108 lines
5.3 KiB
108 lines
5.3 KiB
const test = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
const tabs = require('../frontend/web/static/js/workspace-tabs.js');
|
|
|
|
function storage() {
|
|
const values = new Map();
|
|
return { getItem: key => values.get(key) || null, setItem: (key, value) => values.set(key, value), removeItem: key => values.delete(key) };
|
|
}
|
|
const dashboard = { key: 'dashboard', path: '/', label: '대시보드' };
|
|
const users = { key: 'users', path: '/admin/users', label: '사용자 계정' };
|
|
const port = { key: 'old-port-key', path: '/ports/16/parameters', label: 'Port 16' };
|
|
|
|
test('tabs survive page navigation and refresh within the same login', () => {
|
|
const data = storage();
|
|
const scope = () => ({id: 'login-1', user: 'admin', role: 'administrator'});
|
|
tabs.createStore(() => data, scope).write([dashboard, users]);
|
|
assert.deepEqual(tabs.createStore(() => data, scope).read(), [dashboard, users]);
|
|
});
|
|
|
|
test('administrator tabs never carry over to a port user login', () => {
|
|
const data = storage();
|
|
let scope = {id: 'admin-login', user: 'admin', role: 'administrator'};
|
|
const store = tabs.createStore(() => data, () => scope);
|
|
store.write([dashboard, users]);
|
|
scope = {id: 'operator-login', user: 'operator', role: 'port_operator'};
|
|
assert.deepEqual(store.read(), []);
|
|
store.write([dashboard, users, port]);
|
|
assert.deepEqual(store.read().map(tab => tab.path), ['/', '/ports/16/parameters']);
|
|
});
|
|
|
|
test('logging in again as the same user resets previous tabs', () => {
|
|
const data = storage();
|
|
let scope = {id: 'first-login', user: 'operator', role: 'port_operator'};
|
|
const store = tabs.createStore(() => data, () => scope);
|
|
store.write([dashboard, port]);
|
|
scope = {...scope, id: 'second-login'};
|
|
assert.deepEqual(store.read(), []);
|
|
});
|
|
|
|
test('role or account changes also invalidate a stale scope', () => {
|
|
for (const changed of [{user: 'someone-else'}, {role: 'port_operator'}]) {
|
|
const data = storage();
|
|
let scope = {id: 'login', user: 'admin', role: 'administrator'};
|
|
const store = tabs.createStore(() => data, () => scope);
|
|
store.write([dashboard, users]);
|
|
scope = {...scope, ...changed};
|
|
assert.deepEqual(store.read(), []);
|
|
}
|
|
});
|
|
|
|
test('closing tabs persists; logout clears the store', () => {
|
|
const data = storage();
|
|
const store = tabs.createStore(() => data, () => ({id: 'login', user: 'admin', role: 'administrator'}));
|
|
store.write([dashboard, users]);
|
|
store.write(store.read().filter(tab => tab.key !== users.key));
|
|
assert.deepEqual(store.read(), [dashboard]);
|
|
store.clear();
|
|
assert.deepEqual(store.read(), []);
|
|
assert.equal(data.getItem(tabs.storageKey), null);
|
|
});
|
|
|
|
test('operator tabs reject admin/restart routes and malformed or external URLs', () => {
|
|
for (const path of ['/admin/users', '/network/interface-1', '/port-debug', '/ports/restart', '/ports/49/parameters', '/ports/0/advanced', 'https://example.com', '//example.com', 'javascript:alert(1)', '/api/v1/auth/users', '/ports/../admin/users', '/logout', '/login', '/ports/1/parameters?unsafe=1']) {
|
|
assert.equal(tabs.allowedPath(path, 'port_operator'), false, path);
|
|
}
|
|
for (const path of ['/ports/48/advanced', '/ports/1/data-log']) assert.equal(tabs.allowedPath(path, 'port_operator'), false, path);
|
|
for (const path of ['/', '/port-status', '/password', '/ports/1/parameters', '/ports/48/operation-2']) {
|
|
assert.equal(tabs.allowedPath(path, 'port_operator'), true, path);
|
|
}
|
|
});
|
|
|
|
test('legacy/malformed records and absent workspace identities are not restored', () => {
|
|
const data = storage();
|
|
const scope = () => ({id: 'login', user: 'operator', role: 'port_operator'});
|
|
for (const record of ['bad json', JSON.stringify([dashboard, users]), 'null', '{}']) {
|
|
data.setItem(tabs.storageKey, record);
|
|
assert.deepEqual(tabs.createStore(() => data, scope).read(), []);
|
|
}
|
|
const store = tabs.createStore(() => data, () => ({id: '', user: '', role: ''}));
|
|
store.write([dashboard]);
|
|
assert.deepEqual(store.read(), []);
|
|
});
|
|
|
|
test('port subpages normalize to one tab and corrupt entries are dropped', () => {
|
|
const result = tabs.sanitizeTabs([null, {}, port, {...port, path: '/ports/16/advanced'}, dashboard], 'port_operator');
|
|
assert.deepEqual(result, [{key: 'port-settings', path: '/ports/16/parameters', label: '포트 16 설정'}, dashboard]);
|
|
});
|
|
|
|
test('old per-port tabs collapse in place and one settings tab survives refresh', () => {
|
|
const data = storage();
|
|
const scope = () => ({id: 'login', user: 'admin', role: 'administrator'});
|
|
const store = tabs.createStore(() => data, scope);
|
|
data.setItem(tabs.storageKey, JSON.stringify({...scope(), tabs: [dashboard, port, users,
|
|
{...port, key: 'port-26', path: '/ports/26/operation-2'}]}));
|
|
const restored = store.read();
|
|
assert.deepEqual(restored.map(tab => tab.key), ['dashboard', 'port-settings', 'users']);
|
|
restored[1] = {...restored[1], path: '/ports/26/parameters', label: '포트 26 설정'};
|
|
store.write(restored);
|
|
assert.deepEqual(tabs.createStore(() => data, scope).read(), restored);
|
|
});
|
|
|
|
test('storage denied by browser falls back to working in-memory tabs', () => {
|
|
const store = tabs.createStore(() => { throw new Error('Storage denied'); }, () => ({id: 'login', user: 'operator', role: 'port_operator'}));
|
|
store.write([dashboard, port]);
|
|
assert.equal(store.read().length, 2);
|
|
store.clear();
|
|
assert.deepEqual(store.read(), []);
|
|
});
|
|
|