53 lines
4.2 KiB
JavaScript
53 lines
4.2 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import fs from 'node:fs';
|
|
import path from 'node:path';
|
|
import test from 'node:test';
|
|
|
|
const root = path.resolve(import.meta.dirname, '../..');
|
|
const page = fs.readFileSync(path.join(root, 'src/web/components/ClientOverviewPage.tsx'), 'utf8');
|
|
const panel = fs.readFileSync(path.join(root, 'src/web/features/connection/ConnectionPanel.tsx'), 'utf8');
|
|
const routing = fs.readFileSync(path.join(root, 'src/web/features/routing/RoutingFeature.tsx'), 'utf8');
|
|
const boundary = fs.readFileSync(path.join(root, 'src/web/features/connection/index.ts'), 'utf8');
|
|
|
|
test('connection feature is the sole always-mounted power panel owner', () => {
|
|
assert.equal(boundary.trim(), "export { ConnectionPanel } from './ConnectionPanel.js';");
|
|
assert.match(page, /import \{ ConnectionPanel \} from '\.\.\/features\/connection\/index\.js'/);
|
|
assert.equal((page.match(/<ConnectionPanel/g) || []).length, 1);
|
|
assert.match(page, /<main[\s\S]*<ConnectionPanel[\s\S]*<GatewayTrafficSummary/);
|
|
assert.doesNotMatch(page, /client-power-section|const powerButton|function toggleConnection|confirmingStop|id="stop-connection"|DURATION_MODE_STORAGE_KEY/);
|
|
assert.match(panel, /\{visible \? <section className=\{`client-power-section\$\{isGateway \? ' is-gateway' : ''\}`\}/);
|
|
assert.match(panel, /<ConfirmationDialog[\s\S]*open=\{confirmingStop\}/);
|
|
assert.match(panel, /<div[\s\S]*client-power-control[\s\S]*\{brandSlot\}[\s\S]*\{powerButton\}[\s\S]*<\/div>/);
|
|
assert.match(page, /brandSlot=\{<HarborBrand[\s\S]*onSetGatewayAuto=\{onSetGatewayAuto\}[\s\S]*\/>\}/);
|
|
assert.match(panel, /<\/section> : brandSlot/);
|
|
assert.ok(panel.indexOf('<ConfirmationDialog') > panel.indexOf('{visible && <section'), 'dialog remains mounted outside the visible section');
|
|
});
|
|
|
|
test('connection feature preserves actions, local preference and opaque neighbor slots', () => {
|
|
assert.doesNotMatch(panel, /from ['"][^'"]*\/api\/|\bapi\./);
|
|
assert.doesNotMatch(panel, /setInterval|setTimeout|useState\([^)]*state|useReducer/);
|
|
assert.match(panel, /connectionAction\(\{ connected, selectedServerId, configExists: configured \}\)/);
|
|
assert.match(panel, /action\?\.type === 'stop'[\s\S]*action\?\.type === 'apply'[\s\S]*action\?\.type === 'restart'/);
|
|
assert.match(panel, /if \(!await onStop\(\)\) return;[\s\S]*setConfirmingStop\(false\)/);
|
|
assert.match(panel, /localStorage\.getItem\(DURATION_MODE_STORAGE_KEY\) === 'words'[\s\S]*localStorage\.setItem\(DURATION_MODE_STORAGE_KEY, nextMode\)/);
|
|
assert.match(panel, /client-state-detail[\s\S]*\{routingSlot\}[\s\S]*client-state-copy[\s\S]*\{serverSlot\}[\s\S]*client-proxies[\s\S]*\{statusSlot\}/);
|
|
assert.match(page, /routingSlot=\{<RoutingPendingStatus[\s\S]*blocked=\{connectionBlocked\}[\s\S]*onRestart=\{onRestart\}/);
|
|
assert.match(routing, /client-route-rules-pending[\s\S]*Перезапустить VPN/);
|
|
assert.match(page, /serverSlot=\{isGateway && <div className="client-gateway-route-summary"/);
|
|
assert.match(page, /statusSlot=\{<>[\s\S]*InlineError[\s\S]*InlineProgress/);
|
|
});
|
|
|
|
test('shared clock, copy feedback and live announcement stay single-owned by the page', () => {
|
|
const pageBody = page.slice(page.indexOf('export function ClientOverviewPage'));
|
|
assert.equal((page.match(/setInterval\(\(\) => setNow\(Date\.now\(\)\), 1000\)/g) || []).length, 1);
|
|
assert.match(page, /export function ClientOverviewPage[\s\S]*const \[copyFeedback, setCopyFeedback\]/);
|
|
assert.doesNotMatch(panel, /const \[copyFeedback, setCopyFeedback\]/);
|
|
assert.equal((pageBody.match(/className="client-live-region"/g) || []).length, 1);
|
|
assert.match(page, /onCopyProxy=\{copyProxy\}/);
|
|
assert.match(page, /copyTimersRef = useRef<Partial<Record<CopyKind[\s\S]*copyTimersRef\.current\[kind\]/);
|
|
assert.match(page, /copyAttemptsRef = useRef<Partial<Record<CopyKind[\s\S]*copyAttemptsRef\.current\[kind\] = attempt[\s\S]*await copyText\(value\)[\s\S]*copyAttemptsRef\.current\[kind\] !== attempt[\s\S]*const pendingTimer = copyTimersRef\.current\[kind\][\s\S]*setCopyFeedback/);
|
|
assert.match(panel, /const feedback = copyFeedback\?\.\[kind\][\s\S]*key=\{feedback\.cycle\}/);
|
|
assert.match(panel, /localProxyUrls\(proxyPort, gatewayAddress\)/);
|
|
assert.match(panel, /onClick=\{\(\) => onCopyProxy\(kind\)\}/);
|
|
});
|