Files
harbor-net/test/web/server-picker.test.js
Dmitriy Petrov 7f276b0404
All checks were successful
Build and Deploy Gateway / build-and-push (push) Successful in 16s
Build and Deploy Gateway / deploy (push) Successful in 6s
Refine server picker layout and bump Harbor versions
2026-07-15 00:09:50 +03:00

93 lines
5.0 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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, /client-server-health-checking/);
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]*?padding-bottom: 4px;[\s\S]*?border-bottom: 1px solid var\(--client-accent\);/);
assert.match(styles, /\.client-server-meta \{[\s\S]*?width: 42px;[\s\S]*?margin-left: 8px;/);
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/);
});