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.

95 lines
4.5 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 ['/', '/port-status', '/password', '/ports/1/parameters', '/ports/48/advanced']) {
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-16', path: '/ports/16/parameters', label: '포트 16 설정'}, dashboard]);
});
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(), []);
});