Files
paseo/packages/app/e2e/settings-host-page.spec.ts
T
Mohamed BoudraandGitHub 0e3eb9cf75 Flatten settings sidebar with a host picker (#1241)
* Flatten settings sidebar with a host picker

Settings had two implicit tiers: app-level sections as flat sidebar rows,
and per-host settings hidden behind a drill-in to one monolithic host page.
This flattens both into a single sidebar with an "App" group and a "Host"
group, where the host's settings are top-level rows scoped by a host picker.

- Split the monolithic HostPage into four section pages: Connections,
  Orchestration (was the unnamed inject-tools + system-prompt block),
  Providers, and Daemon (status, updates, restart, remove).
- Host picker reuses the canonical <Combobox> sidebar host switcher; defaults
  to the local host so a single-local-host user never thinks about "the daemon".
- Host sections become real sub-routes (/settings/hosts/[id]/[section]); the
  old /settings/hosts/[id] redirects to the connections section, so existing
  deep links (pairing, workspace, open-project) keep working.
- "Open providers" from the project screen now lands on the Providers section.

* Fix e2e host-settings navigation for the flat sidebar

The flat-settings refactor moved host settings onto separate section pages,
but three e2e helpers still expected the old monolithic host page:

- openDesktopSettings: the daemon-lifecycle card now lives on the Daemon
  section — navigate there before asserting it.
- acp catalog: "Add provider" now lives on the Providers section — open that
  section before opening the add-provider modal.
- openAddHostFlow: "Add host" is now an item inside the host picker (Combobox),
  not a standalone button — open the picker first.

Test-only; no product changes. Fixes the 8 playwright failures on this branch.
2026-05-31 12:55:08 +08:00

163 lines
5.2 KiB
TypeScript

import { test } from "./fixtures";
import { gotoAppShell, openSettings } from "./helpers/app";
import { getE2EDaemonPort } from "./helpers/daemon-port";
import { TEST_HOST_LABEL } from "./helpers/daemon-registry";
import { getServerId } from "./helpers/server-id";
import {
expectSettingsHeader,
openSettingsHost,
openHostSection,
expectHostLabelDisplayed,
clickEditHostLabel,
expectHostLabelEditMode,
expectHostConnectionsCard,
expectHostInjectMcpCard,
expectHostActionCards,
expectHostProvidersCard,
expectHostNoLocalOnlyRows,
expectRetiredSidebarSectionsAbsent,
expectHostPageVisible,
expectLocalHostEntryFirst,
} from "./helpers/settings";
test.describe("Settings host page", () => {
test("connections section shows the seeded connection endpoint", async ({ page }) => {
const serverId = getServerId();
const port = getE2EDaemonPort();
await gotoAppShell(page);
await openSettings(page);
await openSettingsHost(page, serverId);
await expectSettingsHeader(page, "Connections");
await expectHostConnectionsCard(page, port);
});
test("orchestration section shows the inject MCP toggle", async ({ page }) => {
const serverId = getServerId();
await gotoAppShell(page);
await openSettings(page);
await openSettingsHost(page, serverId);
await openHostSection(page, serverId, "orchestration");
await expectSettingsHeader(page, "Orchestration");
await expectHostInjectMcpCard(page);
});
test("providers section shows the providers card", async ({ page }) => {
const serverId = getServerId();
await gotoAppShell(page);
await openSettings(page);
await openSettingsHost(page, serverId);
await expectHostProvidersCard(page, serverId);
await expectSettingsHeader(page, "Providers");
});
test("daemon section shows the host label and restart/remove action cards", async ({ page }) => {
const serverId = getServerId();
await gotoAppShell(page);
await openSettings(page);
await openSettingsHost(page, serverId);
await openHostSection(page, serverId, "daemon");
await expectSettingsHeader(page, "Daemon");
await expectHostLabelDisplayed(page);
await expectHostActionCards(page, serverId);
});
test("clicking the label pencil reveals the inline editor", async ({ page }) => {
const serverId = getServerId();
await gotoAppShell(page);
await openSettings(page);
await openSettingsHost(page, serverId);
await openHostSection(page, serverId, "daemon");
await expectHostLabelDisplayed(page);
await clickEditHostLabel(page);
await expectHostLabelEditMode(page, TEST_HOST_LABEL);
});
test("daemon section does not render pair-device or daemon-lifecycle rows for a remote daemon", async ({
page,
}) => {
const serverId = getServerId();
await gotoAppShell(page);
await openSettings(page);
await openSettingsHost(page, serverId);
await openHostSection(page, serverId, "daemon");
// TODO: add local-daemon fixture for positive Pair/Daemon coverage.
await expectHostNoLocalOnlyRows(page);
});
test("settings sidebar exposes the flat App and Host section rows", async ({ page }) => {
await gotoAppShell(page);
await openSettings(page);
await expectRetiredSidebarSectionsAbsent(page);
});
test("navigating to /settings/hosts/[serverId] redirects to the connections section", async ({
page,
}) => {
const serverId = getServerId();
await gotoAppShell(page);
await page.goto(`/settings/hosts/${encodeURIComponent(serverId)}`);
await expectHostPageVisible(page, serverId);
await expectSettingsHeader(page, "Connections");
await openHostSection(page, serverId, "daemon");
await expectHostLabelDisplayed(page);
await expectHostActionCards(page, serverId);
});
test("sidebar pins the local daemon host first with a Local marker", async ({ page }) => {
const serverId = getServerId();
// Simulate the Electron desktop bridge so `useIsLocalDaemon` resolves the
// seeded host to the local daemon. `manageBuiltInDaemon: false` (returned
// from get_desktop_settings) bypasses the desktop bootstrap flow so only
// the sidebar's status query runs against the seeded test daemon.
await page.addInitScript((localServerId) => {
(window as unknown as { paseoDesktop: unknown }).paseoDesktop = {
platform: "darwin",
invoke: async (command: string) => {
if (command === "desktop_daemon_status") {
return {
serverId: localServerId,
status: "running",
listen: null,
hostname: null,
pid: null,
home: "",
version: null,
desktopManaged: true,
error: null,
};
}
if (command === "get_desktop_settings") {
return {
releaseChannel: "stable",
daemon: { manageBuiltInDaemon: false, keepRunningAfterQuit: true },
};
}
return null;
},
getPendingOpenProject: async () => null,
events: { on: async () => () => undefined },
};
}, serverId);
await gotoAppShell(page);
await openSettings(page);
await expectLocalHostEntryFirst(page, serverId);
});
});