139 lines
5.1 KiB
TypeScript
139 lines
5.1 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import test from "node:test";
|
|
import { getSettingsListTheme, initTheme } from "@earendil-works/pi-coding-agent";
|
|
import { SettingsList, visibleWidth } from "@earendil-works/pi-tui";
|
|
import {
|
|
FAST_BROWSER_SETTING_IDS,
|
|
FAST_BROWSER_SETTING_LABELS,
|
|
FastBrowserSettingsView,
|
|
buildFastBrowserSettingItems,
|
|
fastBrowserSettingValue,
|
|
} from "./settings-view.ts";
|
|
|
|
const stripAnsi = (value: string): string => value.replace(/\u001b\[[0-9;]*m/g, "");
|
|
|
|
test.before(() => initTheme("dark", false));
|
|
|
|
function options(
|
|
draft: Record<string, unknown> = {},
|
|
inherited: Record<string, unknown> = {},
|
|
onEvent: (event: any) => void = () => {},
|
|
) {
|
|
return {
|
|
scope: "project" as const,
|
|
draft,
|
|
inherited,
|
|
onEvent,
|
|
};
|
|
}
|
|
|
|
test("renders native SettingsList values subdued on the right", () => {
|
|
const view = new FastBrowserSettingsView(options(
|
|
{ cdpUrl: "http://127.0.0.1:9333" },
|
|
{ allowedOrigins: ["https://example.test"], textModel: "fixture/small" },
|
|
));
|
|
const lines = view.render(80);
|
|
const endpoint = lines.find((line) => stripAnsi(line).includes("CDP endpoint"));
|
|
assert(endpoint);
|
|
assert.match(stripAnsi(endpoint), /CDP endpoint\s+http:\/\/127\.0\.0\.1:9333/);
|
|
// The native theme wraps an unselected value in its muted ANSI color.
|
|
assert.match(endpoint, /\u001b\[[0-9;]*mhttp:\/\/127\.0\.0\.1:9333\u001b\[[0-9;]*m/);
|
|
|
|
const inherited = fastBrowserSettingValue(
|
|
FAST_BROWSER_SETTING_IDS.textModel,
|
|
{},
|
|
{ textModel: "fixture/small" },
|
|
);
|
|
assert.equal(inherited, "(inherited) fixture/small");
|
|
});
|
|
|
|
test("displays unrestricted origin policy and inherited default values", () => {
|
|
assert.equal(fastBrowserSettingValue(
|
|
FAST_BROWSER_SETTING_IDS.allowedOrigins,
|
|
{ allowedOrigins: "*" },
|
|
{},
|
|
), "All web origins");
|
|
assert.equal(fastBrowserSettingValue(
|
|
FAST_BROWSER_SETTING_IDS.allowedOrigins,
|
|
{},
|
|
{ allowedOrigins: "*" },
|
|
), "(inherited) All web origins");
|
|
});
|
|
|
|
test("shows named profiles as explicit or inherited browser settings", () => {
|
|
const explicit = fastBrowserSettingValue(
|
|
FAST_BROWSER_SETTING_IDS.browserProfile,
|
|
{ browser: { mode: "launch", profile: "evebot" } },
|
|
{},
|
|
);
|
|
const inherited = fastBrowserSettingValue(
|
|
FAST_BROWSER_SETTING_IDS.browserProfile,
|
|
{},
|
|
{ browser: { mode: "launch", profile: "global-profile" } },
|
|
);
|
|
assert.equal(explicit, "evebot");
|
|
assert.equal(inherited, "(inherited) global-profile");
|
|
});
|
|
|
|
test("updates displayed rows with the latest draft after an edit", () => {
|
|
const draft: Record<string, unknown> = {};
|
|
const inherited = { textModel: "global/model" };
|
|
const view = new FastBrowserSettingsView(options(draft, inherited));
|
|
const before = view.render(80).join("\n");
|
|
assert.match(stripAnsi(before), /Auxiliary text model\s+\(inherited\) global\/model/);
|
|
draft.textModel = "project/model";
|
|
view.updateValues(draft, inherited);
|
|
const after = view.render(80).join("\n");
|
|
assert.match(stripAnsi(after), /Auxiliary text model\s+project\/model/);
|
|
});
|
|
|
|
test("keeps long URLs, paths, and CJK labels within narrow terminal widths", () => {
|
|
const view = new FastBrowserSettingsView(options(
|
|
{
|
|
cdpUrl: "http://127.0.0.1:9222",
|
|
python: "/very/long/path/项目/with/a/credential-free/python-executable",
|
|
browser: {
|
|
mode: "launch",
|
|
product: "chromium",
|
|
executablePath: "/very/long/path/项目/with/a/browser-executable",
|
|
startUrl: "https://example.test/非常長い開始URL/with/more/path",
|
|
},
|
|
},
|
|
{ allowedOrigins: ["https://example.test/非常長い起点"] },
|
|
));
|
|
for (const width of [60, 80, 120]) {
|
|
for (const line of view.render(width)) assert(visibleWidth(line) <= width, `${width}: ${stripAnsi(line)}`);
|
|
}
|
|
});
|
|
|
|
test("dispatches by stable id even when a displayed label changes", () => {
|
|
const events: any[] = [];
|
|
const items = buildFastBrowserSettingItems(options({}, {}, (event) => events.push(event)));
|
|
const first = items.find((item) => item.id === FAST_BROWSER_SETTING_IDS.cdpUrl);
|
|
assert(first);
|
|
first.label = "Endpoint label changed";
|
|
const list = new SettingsList(items, 10, getSettingsListTheme(), (id) => {
|
|
events.push({ kind: "edit", id });
|
|
}, () => {});
|
|
list.handleInput("\r");
|
|
assert.deepEqual(events, [{ kind: "edit", id: FAST_BROWSER_SETTING_IDS.cdpUrl }]);
|
|
assert.equal(FAST_BROWSER_SETTING_LABELS.cdpUrl, "CDP endpoint");
|
|
});
|
|
|
|
test("does not render explicit save or discard actions", () => {
|
|
const labels = buildFastBrowserSettingItems(options()).map((item) => item.label);
|
|
assert(!labels.includes("Save settings"));
|
|
assert(!labels.includes("Cancel"));
|
|
const view = new FastBrowserSettingsView(options());
|
|
assert.match(stripAnsi(view.render(120).join("\n")), /Changes save automatically/);
|
|
});
|
|
|
|
test("Escape dispatches cancel without mutating the draft", () => {
|
|
const draft = { textModel: "keep-me" };
|
|
const events: any[] = [];
|
|
const view = new FastBrowserSettingsView(options(draft, {}, (event) => events.push(event)));
|
|
view.handleInput("\u001b");
|
|
assert.deepEqual(events, [{ kind: "cancel" }]);
|
|
assert.deepEqual(draft, { textModel: "keep-me" });
|
|
});
|