153 lines
4.1 KiB
TypeScript
153 lines
4.1 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import type {
|
|
ApplyConfigurationResult,
|
|
PingServerResponse,
|
|
} from "../api/tauriCommands";
|
|
import type { ComponentStatus } from "../domain/types";
|
|
import {
|
|
connectionCheckView,
|
|
noticeFromConfigurationApply,
|
|
pingSummary,
|
|
routeChainSegments,
|
|
} from "./viewModel";
|
|
|
|
const runningProxiFyre: ComponentStatus = {
|
|
id: "proxyfier",
|
|
name: "ProxiFyre",
|
|
state: "running",
|
|
installed: true,
|
|
running: true,
|
|
path: "C:\\Tools\\ProxiFyre\\ProxiFyre.exe",
|
|
problems: [],
|
|
actions: [],
|
|
};
|
|
|
|
describe("App view helpers", () => {
|
|
it("describes the external SOCKS5 route without Local sing-box", () => {
|
|
const segments = routeChainSegments({
|
|
routeMode: "external",
|
|
proxyInput: "proxy.example.test:1080",
|
|
proxyfier: runningProxiFyre,
|
|
singbox: undefined,
|
|
singBoxStatus: null,
|
|
selectedServer: null,
|
|
appCount: 2,
|
|
isDetectingComponents: false,
|
|
});
|
|
|
|
expect(segments.map((segment) => segment.id)).toEqual([
|
|
"apps",
|
|
"proxifyre",
|
|
"endpoint",
|
|
"exit",
|
|
]);
|
|
expect(segments[2]).toMatchObject({
|
|
value: "proxy.example.test:1080",
|
|
tone: "ok",
|
|
});
|
|
expect(segments[3].details).toContain(
|
|
"Local sing-box не нужен для этого маршрута.",
|
|
);
|
|
});
|
|
|
|
it("summarizes the fastest successful ping", () => {
|
|
const results = [
|
|
{ tag: "slow", ok: true, latency: 90 },
|
|
{ tag: "failed", ok: false },
|
|
{ tag: "fast", ok: true, latency: 20 },
|
|
] as PingServerResponse[];
|
|
|
|
expect(pingSummary(results)).toBe("Ответили 2/3; быстрее fast: 20 ms.");
|
|
});
|
|
|
|
it("builds an expandable route-check result from every backend probe", () => {
|
|
const view = connectionCheckView({
|
|
routeMode: "external",
|
|
proxyInput: "192.168.50.111:8080",
|
|
proxyCheck: {
|
|
tag: "external",
|
|
server: "192.168.50.111",
|
|
serverPort: 8080,
|
|
ok: true,
|
|
latency: 14,
|
|
probes: [
|
|
{
|
|
id: "cloudflare-trace",
|
|
name: "Cloudflare Trace",
|
|
url: "https://cloudflare.com/cdn-cgi/trace",
|
|
ok: true,
|
|
status: 200,
|
|
latency: 21,
|
|
ip: "203.0.113.10",
|
|
},
|
|
{
|
|
id: "ipify",
|
|
name: "ipify",
|
|
url: "https://api.ipify.org",
|
|
ok: false,
|
|
error: "timeout",
|
|
},
|
|
],
|
|
},
|
|
singbox: undefined,
|
|
singBoxStatus: null,
|
|
selectedServer: null,
|
|
isDetectingComponents: false,
|
|
isProxyChecking: false,
|
|
});
|
|
|
|
expect(view).toMatchObject({
|
|
checked: true,
|
|
endpoint: "192.168.50.111:8080",
|
|
tone: "warning",
|
|
});
|
|
expect(view.probes.map((probe) => probe.id)).toEqual([
|
|
"tcp",
|
|
"cloudflare-trace",
|
|
"ipify",
|
|
]);
|
|
expect(view.details).toContainEqual({
|
|
label: "Cloudflare Trace",
|
|
value: "IP 203.0.113.10 · HTTP 200 · 21 ms",
|
|
});
|
|
expect(view.probes[1]).toMatchObject({
|
|
ip: "203.0.113.10",
|
|
latency: "21 ms",
|
|
status: "Доступна",
|
|
});
|
|
});
|
|
|
|
it("shows the result surface while the route check is running", () => {
|
|
const view = connectionCheckView({
|
|
routeMode: "external",
|
|
proxyInput: "192.168.50.111:8080",
|
|
proxyCheck: null,
|
|
singbox: undefined,
|
|
singBoxStatus: null,
|
|
selectedServer: null,
|
|
isDetectingComponents: false,
|
|
isProxyChecking: true,
|
|
});
|
|
|
|
expect(view).toMatchObject({ checked: false, loading: true });
|
|
});
|
|
|
|
it("distinguishes rolled-back and partial apply failures", () => {
|
|
const base: ApplyConfigurationResult = {
|
|
success: false,
|
|
changed: false,
|
|
partialState: false,
|
|
message: "Helper failed; previous files restored.",
|
|
generatedConfigPath:
|
|
"C:\\ProgramData\\ProxyWarden\\generated\\proxifyre-app-config.json",
|
|
restartRequired: [],
|
|
phases: [],
|
|
};
|
|
|
|
expect(noticeFromConfigurationApply(base).title).toBe("Изменения отменены");
|
|
expect(
|
|
noticeFromConfigurationApply({ ...base, partialState: true }).title,
|
|
).toBe("Проверь состояние файлов");
|
|
});
|
|
});
|