95 lines
5.2 KiB
JavaScript
95 lines
5.2 KiB
JavaScript
import assert from 'node:assert/strict';
|
||
import fs from 'node:fs';
|
||
import path from 'node:path';
|
||
import test from 'node:test';
|
||
|
||
import {
|
||
autoServer,
|
||
filterServers,
|
||
groupServers,
|
||
SERVER_RESULT_WINDOW,
|
||
} from '../../src/web/utils/serverPicker.js';
|
||
|
||
const root = path.resolve(import.meta.dirname, '../..');
|
||
const picker = fs.readFileSync(path.join(root, 'src/web/components/ServerPicker.jsx'), 'utf8');
|
||
const overview = fs.readFileSync(path.join(root, 'src/web/components/ClientOverviewPage.jsx'), 'utf8');
|
||
const styles = fs.readFileSync(path.join(root, 'src/web/styles.css'), 'utf8');
|
||
|
||
const fixtures = (count) => Array.from({ length: count }, (_, index) => ({
|
||
id: `srv-${String(count - index).padStart(3, '0')}`,
|
||
label: index < 2 ? 'Duplicate' : `Server ${index}`,
|
||
host: `node-${index}.example`,
|
||
country: index % 2 ? 'NL' : 'DE',
|
||
provider: index % 3 ? 'Harbor' : 'Other',
|
||
protocol: index % 2 ? 'vless' : 'trojan',
|
||
}));
|
||
|
||
test('server picker handles 1, 30 and 300 stable-ID servers with duplicate labels', () => {
|
||
for (const count of [1, 30, 300]) {
|
||
const servers = fixtures(count);
|
||
assert.equal(filterServers(servers, '').length, count);
|
||
assert.equal(new Set(servers.map(({ id }) => id)).size, count);
|
||
}
|
||
assert.equal(filterServers(fixtures(30), 'node-12.example')[0].id, 'srv-018');
|
||
assert.equal(filterServers(fixtures(30), 'trojan').length, 15);
|
||
assert.equal(groupServers(fixtures(30)).length, 2);
|
||
assert.equal(autoServer(fixtures(30)).id, 'srv-001');
|
||
assert.equal(SERVER_RESULT_WINDOW, 60);
|
||
});
|
||
|
||
test('server picker checks health only on manual refresh and bounds the result window', () => {
|
||
assert.doesNotMatch(overview, /pingAll|servers\.ping/);
|
||
assert.doesNotMatch(picker, /checkVisible\(\);/);
|
||
assert.match(picker, /onClick={checkVisible}/);
|
||
assert.match(picker, /\{ \.\.\.current\[id\], checking: true \}/);
|
||
assert.match(picker, /700 - \(performance\.now\(\) - startedAt\)/);
|
||
assert.match(picker, /\{ \.\.\.current\[id\], checking: false \}/);
|
||
assert.match(picker, /\.slice\(page \* SERVER_RESULT_WINDOW, \(page \+ 1\) \* SERVER_RESULT_WINDOW\)/);
|
||
assert.match(picker, /\.slice\(0, 30\)/);
|
||
assert.match(picker, /Math\.min\(index, 7\)/);
|
||
assert.match(picker, /type="search"/);
|
||
assert.match(picker, /harbor-server-favorites/);
|
||
assert.match(picker, /harbor-server-recent/);
|
||
assert.match(picker, /aria-expanded={!isCollapsed}/);
|
||
});
|
||
|
||
test('manual ping uses plain language and keeps results beside server names', () => {
|
||
assert.match(picker, /function ServerHealth/);
|
||
assert.match(picker, /<svg className="client-server-health-checking"/);
|
||
assert.doesNotMatch(picker, />Проверяем…<\/span>/);
|
||
assert.match(picker, /function ServerCheckButton/);
|
||
assert.match(picker, /client-server-check client-tooltip-anchor/);
|
||
assert.match(picker, /<span className="client-tooltip" role="tooltip">/);
|
||
assert.doesNotMatch(picker, /TCP|Не проверен/);
|
||
assert.match(picker, /<strong>\{server\.label\}<\/strong>[\s\S]*?<\/button>[\s\S]*?client-server-meta/);
|
||
assert.match(styles, /\.client-server-row \{[\s\S]*?grid-template-columns: minmax\(0, 1fr\) 120px minmax\(0, 1fr\);/);
|
||
assert.match(styles, /\.client-server-row \.client-server \{[\s\S]*?width: 120px;/);
|
||
assert.match(styles, /\.client-server-health \{[\s\S]*?font-size: 9px;[\s\S]*?font-variant-numeric: tabular-nums;/);
|
||
assert.match(styles, /\.client-server-meta \.client-server-health \{[\s\S]*?place-items: end start;[\s\S]*?padding-bottom: 7px;/);
|
||
assert.match(styles, /\.client-server-health\.is-checking \.client-server-health-checking \{[\s\S]*?animation: client-server-check-spin 700ms linear infinite;/);
|
||
assert.match(styles, /\.client-server-meta \{[\s\S]*?width: 42px;[\s\S]*?margin-left: 4px;/);
|
||
assert.match(styles, /\.client-server-favorite \{[\s\S]*?position: absolute;[\s\S]*?width: 44px;/);
|
||
assert.match(picker, /server={servers\[0\]}[\s\S]*?ping={pings\[servers\[0\]\.id\]}/);
|
||
assert.match(picker, /simpleServers\.map[\s\S]*?ping={pings\[server\.id\]}/);
|
||
});
|
||
|
||
test('server picker starts simple and reveals advanced controls on demand', () => {
|
||
assert.match(picker, /const \[advanced, setAdvanced\] = useState\(false\)/);
|
||
assert.match(picker, /aria-expanded={advanced}/);
|
||
assert.match(picker, /setAdvanced\(\(current\) => !current\)/);
|
||
assert.match(picker, /<span>Поиск и фильтры<\/span>/);
|
||
assert.match(picker, /client-server-toolbar-title">Список серверов/);
|
||
assert.doesNotMatch(picker, /client-server-toolbar-actions/);
|
||
assert.match(styles, /\.client-servers\.is-scalable \{[\s\S]*?width: min\(100%, 300px\);/);
|
||
assert.match(styles, /\.client-server-mode-toggle \{[\s\S]*?grid-row: 2;/);
|
||
assert.match(styles, /\.client-server-check \{[\s\S]*?grid-column: 3;[\s\S]*?grid-row: 1;/);
|
||
assert.match(styles, /\.client-server-auto \{[\s\S]*?width: 220px;[\s\S]*?margin-inline: auto;/);
|
||
assert.match(picker, /client-server-mode-panel is-simple/);
|
||
assert.match(picker, /client-server-mode-panel is-advanced/);
|
||
assert.match(picker, /inert={advanced \? true : undefined}/);
|
||
assert.match(picker, /inert={!advanced \? true : undefined}/);
|
||
assert.match(picker, /const SIMPLE_SERVER_LIMIT = 5/);
|
||
assert.match(picker, /\.slice\(0, SIMPLE_SERVER_LIMIT\)/);
|
||
assert.match(picker, /\{selected && <ServerRow/);
|
||
});
|