import assert from 'node:assert/strict'; import test from 'node:test'; import { connectionAction, copyText, formatConnectionDuration, localProxyUrls, subscriptionDomain, subscriptionDaysLeft, subscriptionUsage, } from '../../src/web/utils/clientControls.js'; test('connection button chooses the only valid client action', () => { assert.deepEqual(connectionAction({ connected: true }), { type: 'stop' }); assert.deepEqual(connectionAction({ selectedTag: 'nl-amsterdam' }), { type: 'apply', selectedTag: 'nl-amsterdam', }); assert.deepEqual(connectionAction({ configExists: true }), { type: 'restart' }); assert.equal(connectionAction({}), null); }); test('connection duration is derived from the backend start time', () => { const startedAt = '2026-07-10T10:00:00.000Z'; const now = Date.parse('2026-07-11T12:03:04.900Z'); assert.equal(formatConnectionDuration(startedAt, now), '26:03:04'); assert.equal(formatConnectionDuration(null, now), '00:00:00'); }); test('saved subscription is reduced to its public domain', () => { assert.equal(subscriptionDomain('sub.example.com/…'), 'sub.example.com'); assert.equal(subscriptionDomain(''), ''); }); test('local proxy exposes both supported URLs', () => { assert.deepEqual(localProxyUrls(18080), { socks5: 'socks5://127.0.0.1:18080', http: 'http://127.0.0.1:18080', }); }); test('local proxy defaults to the macOS client port', () => { assert.deepEqual(localProxyUrls(), { socks5: 'socks5://127.0.0.1:8082', http: 'http://127.0.0.1:8082', }); }); test('gateway proxy URLs use its network address', () => { assert.deepEqual(localProxyUrls(8080, '192.168.50.111'), { socks5: 'socks5://192.168.50.111:8080', http: 'http://192.168.50.111:8080', }); }); test('copy uses the synchronous native path available on gateway HTTP', async () => { const textarea = { style: {}, setAttribute() {}, select() { this.selected = true; }, remove() { this.removed = true; }, }; const documentRef = { body: { append(node) { node.appended = true; } }, createElement: () => textarea, execCommand: (command) => command === 'copy', }; await copyText('192.168.50.111', { documentRef }); assert.equal(textarea.value, '192.168.50.111'); assert.equal(textarea.selected, true); assert.equal(textarea.removed, true); }); test('subscription usage combines traffic and caps progress', () => { assert.deepEqual(subscriptionUsage({ upload: 30, download: 80, total: 100, expire: 2 }), { upload: 30, download: 80, total: 100, used: 110, percent: 100, expiresAt: new Date(2000), }); }); test('subscription expiry uses Russian day forms', () => { const now = Date.parse('2026-07-11T00:00:00Z'); assert.equal(subscriptionDaysLeft(new Date('2026-07-12T00:00:00Z'), now), 'остался 1 день'); assert.equal(subscriptionDaysLeft(new Date('2026-07-13T00:00:00Z'), now), 'осталось 2 дня'); assert.equal(subscriptionDaysLeft(new Date('2026-07-16T00:00:00Z'), now), 'осталось 5 дней'); });