Files
yaak-mountain-loop/apps/yaak-proxy/lib/systemProxy.test.ts
T
Gregory SchierandClaude Fable 5.2 35058cc3be Yaak Proxy: prose pass over the UI copy
Apply the approved wording table: labels stay short, explanations are one
full sentence about the user's traffic, and detail moves behind an info
icon on the first-run card, the body banners, the Settings Helper row and
the rejected card. One word per thing: traffic and flows, recorded,
truncated, domains, searches, the CLI, Retry.

Backend messages that reach the window change with them, with the tests
and harness fixtures that assert on the old text. The two working files
for the pass are removed from the branch.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-11 08:41:49 -07:00

167 lines
5.5 KiB
TypeScript

import type { HelperState } from "@yaakapp-internal/proxy-lib";
import { describe, expect, test } from "vite-plus/test";
import {
helperPollInterval,
helperRow,
recordingLabel,
type SystemProxyState,
systemProxySwitch,
} from "./systemProxy";
const off: SystemProxyState = { routing: false, port: 9090, pending: null, error: null };
describe("systemProxySwitch", () => {
test("says whether this Mac's traffic is routed now", () => {
expect(systemProxySwitch({ ...off, routing: true })).toEqual({
checked: true,
description: "Traffic from every app will be recorded.",
failed: false,
});
expect(systemProxySwitch(off)).toEqual({
checked: false,
description: "Only traffic sent to port 9090 will be recorded.",
failed: false,
});
});
test("shows the new value while the password prompt is up", () => {
expect(systemProxySwitch({ ...off, pending: true })).toEqual({
checked: true,
description: "Waiting for macOS",
failed: false,
});
});
test("waits for no password when the helper makes the change", () => {
expect(systemProxySwitch({ ...off, pending: false, helper: true })).toEqual({
checked: false,
description: "Changing the system proxy",
failed: false,
});
});
test("snaps back on a cancelled prompt and says so", () => {
expect(systemProxySwitch({ ...off, error: "The password prompt was cancelled." })).toEqual({
checked: false,
description: "The password prompt was cancelled.",
failed: true,
});
expect(systemProxySwitch({ ...off, error: "networksetup failed" }).description).toBe(
"networksetup failed",
);
});
});
describe("helperRow", () => {
test("offers each next step, and says what the helper is for", () => {
expect(helperRow("not_registered", "admin_prompt")).toMatchObject({
description: "The helper is not installed.",
action: "install",
});
expect(helperRow("requires_approval", "admin_prompt")).toMatchObject({
description: "The helper is awaiting approval.",
action: "approve",
});
const enabled = helperRow("enabled", "helper");
expect(enabled).toMatchObject({
description: "The helper is installed.",
action: "unregister",
});
expect(enabled.tooltip).toMatch(/without a password prompt/);
});
test("says when the password prompt still has settings to put back", () => {
expect(helperRow("enabled", "admin_prompt").tooltip).toBe(
"One more password prompt is needed to restore the system proxy settings.",
);
});
test("offers to install when macOS can't find it, rather than blaming a dev build", () => {
expect(helperRow("not_found", "admin_prompt")).toMatchObject({
description: "macOS could not find the helper.",
action: "install",
});
});
test("keeps every row short", () => {
const states: HelperState[] = [
"unavailable",
"not_found",
"not_registered",
"requires_approval",
"enabled",
];
for (const state of states) {
for (const backend of ["helper", "admin_prompt"] as const) {
const row = helperRow(state, backend);
expect(row.description.length).toBeLessThanOrEqual(60);
expect(row.tooltip.length).toBeLessThanOrEqual(80);
}
}
});
test("has a row for every state the backend sends", () => {
// A missing key here fails the typecheck, so a new state can't be left out.
const states: Record<HelperState, true> = {
unavailable: true,
not_found: true,
not_registered: true,
requires_approval: true,
enabled: true,
};
for (const state of Object.keys(states) as HelperState[]) {
for (const backend of ["helper", "admin_prompt", "sudo"] as const) {
expect(helperRow(state, backend).description).toMatch(/helper.*\.$/);
}
}
});
test("still gives a row for a state this build doesn't know", () => {
const row = helperRow("from_the_future" as HelperState, "admin_prompt");
expect(row).toEqual({
description: "The helper is from_the_future.",
tooltip: "The helper changes the system proxy without a password prompt.",
action: null,
});
});
test("says plainly that a dev build cannot use it", () => {
expect(helperRow("unavailable", "admin_prompt")).toMatchObject({
description: "The helper is not available in dev builds.",
action: null,
});
});
});
describe("helperPollInterval", () => {
const now = 100_000;
test("polls every 2 seconds while approval is pending", () => {
expect(helperPollInterval("requires_approval", null, now)).toBe(2000);
});
test("stops once enabled or not registered, even just after registering", () => {
expect(helperPollInterval("enabled", now - 500, now)).toBe(false);
expect(helperPollInterval("not_registered", now - 500, now)).toBe(false);
});
test("polls briefly after registering, until approval shows", () => {
expect(helperPollInterval("not_found", now - 500, now)).toBe(2000);
expect(helperPollInterval(undefined, now - 500, now)).toBe(2000);
expect(helperPollInterval("not_found", now - 10_000, now)).toBe(false);
});
test("never polls in other states", () => {
for (const helper of ["unavailable", "not_found", undefined] as const) {
expect(helperPollInterval(helper, null, now)).toBe(false);
}
});
});
describe("recordingLabel", () => {
test("offers the opposite of the current state", () => {
expect(recordingLabel(true)).toBe("Pause Recording");
expect(recordingLabel(false)).toBe("Resume Recording");
});
});