tests/e2e/settings-navigation.spec.ts
Rama main · réplica de lectura del repositorio original.
import {
test,
expect,
fulfillJson,
mockTeamSettings,
TEAM,
TEAM_SETTINGS_PATH,
} from "./helpers/run-checks-fixtures";
import { enableScopedE2EAuth, scopedPath } from "./helpers/auth";
import { capacitySummary } from "./helpers/billing";
test.beforeEach(async ({ page }) => {
await enableScopedE2EAuth(page);
await page.route("**/api/settings/decision-checks", (route) =>
fulfillJson(route, { enabled: true, viewer: { canManage: true } })
);
await page.route("**/api/settings/keys", (route) =>
fulfillJson(route, { keys: [] })
);
await page.route("**/api/settings/api-keys", (route) =>
fulfillJson(route, { keys: [] })
);
await page.route("**/api/mcp-servers", (route) =>
fulfillJson(route, { servers: [] })
);
});
test("Settings drills into page navigation and back restores the main menu", async ({
page,
}) => {
await page.goto(scopedPath("settings/account"));
const nav = page.getByRole("navigation", { name: "Settings", exact: true });
await expect(nav).toBeVisible();
await expect(page.getByRole("tab")).toHaveCount(0);
await page.getByRole("button", { name: "Back to main navigation" }).click();
await expect(page.getByTestId("app-nav-connections")).toBeVisible();
await expect(page).toHaveURL(scopedPath("settings/account"));
await page.getByTestId("app-nav-settings").click();
await expect(nav).toBeVisible();
for (const [label, route] of [
["Teams", "teams"],
["Provider Keys", "keys"],
["Mogplex Keys", "mogplex-keys"],
["Account", "account"],
]) {
await nav.getByRole("link", { name: label, exact: true }).click();
await expect(page).toHaveURL(scopedPath(`settings/${route}`));
await expect(
page.getByRole("heading", { name: label, exact: true }).first()
).toBeVisible();
await expect(
nav.getByRole("link", { name: label, exact: true })
).toHaveAttribute("aria-current", "page");
await expect(page.getByRole("tab")).toHaveCount(0);
}
await page.reload();
await expect(
nav.getByRole("link", { name: "Account", exact: true })
).toHaveAttribute("aria-current", "page");
});
test("Billing has separate settings and usage, with history and reload support", async ({
page,
}) => {
const summary = capacitySummary();
summary.recentCosts = [
{
operationId: "usage-1",
description: "Repository review",
status: "settled",
occurredAt: "2026-09-22T12:00:00Z",
totalCents: 125,
items: [],
},
];
await page.route("**/api/billing/capacity", (route) =>
fulfillJson(route, summary)
);
await page.goto(scopedPath("settings/billing"));
await expect(
page.getByRole("tab", { name: "Billing Settings", exact: true })
).toHaveAttribute("aria-selected", "true");
await expect(
page.getByRole("heading", { name: "Add inference credit" })
).toBeVisible();
await expect(
page.getByRole("heading", { name: "Recent usage costs" })
).toHaveCount(0);
await page.getByRole("tab", { name: "Usage", exact: true }).click();
await expect(page).toHaveURL(scopedPath("settings/billing?tab=usage"));
await expect(
page.getByText("Repository review", { exact: true })
).toBeVisible();
await expect(page.getByText("$1.25", { exact: true })).toBeVisible();
await expect(
page.getByRole("heading", { name: "Add inference credit" })
).toHaveCount(0);
await page.reload();
await expect(
page.getByRole("tab", { name: "Usage", exact: true })
).toHaveAttribute("aria-selected", "true");
await page.goBack();
await expect(
page.getByRole("tab", { name: "Billing Settings", exact: true })
).toHaveAttribute("aria-selected", "true");
await page.goForward();
await expect(
page.getByText("Repository review", { exact: true })
).toBeVisible();
});
test("mobile Settings navigation opens, selects a page and closes", async ({
page,
}) => {
await page.setViewportSize({ width: 390, height: 844 });
await page.route("**/api/billing/capacity", (route) =>
fulfillJson(route, { ...capacitySummary(), recentCosts: [] })
);
await page.goto(scopedPath("settings/account"));
const menuButton = page.getByRole("button", { name: "Open navigation" });
await expect(menuButton.locator("svg")).toBeVisible();
const menuBounds = await menuButton.boundingBox();
expect(menuBounds?.width).toBeGreaterThanOrEqual(36);
expect(menuBounds?.height).toBeGreaterThanOrEqual(36);
await menuButton.click();
const dialog = page.getByRole("dialog", { name: "Navigation" });
await dialog.getByRole("button", { name: "Back to main navigation" }).click();
await expect(
dialog.getByRole("link", { name: "Settings", exact: true })
).toBeFocused();
await dialog.getByRole("link", { name: "Settings", exact: true }).click();
await dialog.getByRole("link", { name: "Billing", exact: true }).click();
await expect(dialog).not.toBeVisible();
await expect(page).toHaveURL(scopedPath("settings/billing"));
await page.getByRole("tab", { name: "Usage", exact: true }).click();
await expect(page.getByText("No usage costs yet.")).toBeVisible();
expect(
await page.evaluate(
() => document.documentElement.scrollWidth <= window.innerWidth
)
).toBe(true);
await page.getByRole("button", { name: "Open navigation" }).click();
await expect(
page
.getByRole("dialog", { name: "Navigation" })
.getByRole("link", { name: "Billing", exact: true })
).toBeVisible();
});
for (const width of [320, 390, 640, 700, 768, 1280]) {
test(`team icon content stays readable at ${width}px`, async ({ page }) => {
await page.setViewportSize({ width, height: 900 });
await mockTeamSettings(page, "owner");
await page.route(`**/api/teams/${TEAM.id}/members`, (route) =>
fulfillJson(route, {
team: { ...TEAM, iconUrl: "/icon.png" },
viewer: { role: "owner", canManage: true },
members: [],
invites: [],
})
);
await page.goto(`${TEAM_SETTINGS_PATH}/members`);
const section = page.locator("section").filter({
has: page.getByText("Team icon", { exact: true }),
});
const caption = section.getByText("PNG, JPG, WEBP, or GIF", {
exact: false,
});
await expect(caption).toBeVisible();
const captionBounds = await caption.boundingBox();
expect(captionBounds?.width).toBeGreaterThanOrEqual(140);
const replace = section.getByRole("button", {
name: "Replace",
exact: true,
});
const remove = section.getByRole("button", { name: "Remove", exact: true });
await expect(replace).toBeVisible();
await expect(remove).toBeVisible();
const replaceBounds = await replace.boundingBox();
const removeBounds = await remove.boundingBox();
expect(replaceBounds).not.toBeNull();
expect(removeBounds).not.toBeNull();
expect(replaceBounds!.y).toBe(removeBounds!.y);
const sectionBounds = await section.boundingBox();
expect(sectionBounds?.height).toBeLessThan(230);
if (width < 640) {
expect(replaceBounds!.y).toBeGreaterThanOrEqual(
captionBounds!.y + captionBounds!.height
);
}
expect(
await page.evaluate(
() => document.documentElement.scrollWidth <= window.innerWidth
)
).toBe(true);
await page.screenshot({ path: test.info().outputPath("team-icon.png") });
});
}
test("team Settings has team pages and limits Audit access", async ({
page,
}) => {
await mockTeamSettings(page, "viewer");
await page.goto(TEAM_SETTINGS_PATH);
await expect(page).toHaveURL(`${TEAM_SETTINGS_PATH}/members`);
const nav = page.getByRole("navigation", { name: "Settings", exact: true });
await expect(
nav.getByRole("link", { name: "Members", exact: true })
).toBeVisible();
await expect(
nav.getByRole("link", { name: "Account", exact: true })
).toHaveCount(0);
await expect(
nav.getByRole("link", { name: "Audit", exact: true })
).toHaveCount(0);
await expect(page.getByRole("tab")).toHaveCount(0);
await page.goto(`${TEAM_SETTINGS_PATH}/audit`);
await expect(
page.getByText("Only a team owner or admin can view the audit log.")
).toBeVisible();
});
test("old key links open the right page and preserve unrelated query values", async ({
page,
}) => {
await page.goto(scopedPath("settings?tab=keys&sub=cli&keep=1"));
await expect(page).toHaveURL(scopedPath("settings/mogplex-keys?keep=1"));
await expect(
page.getByRole("heading", { name: "Mogplex Keys", exact: true })
).toBeVisible();
await page.goto(scopedPath("settings#teams"));
await expect(page).toHaveURL(scopedPath("settings/teams"));
});
test("Settings expands a compact sidebar and preserves the main menu width", async ({
page,
}) => {
await page.addInitScript(() => {
localStorage.setItem("mogplex.appSidebar.width", "64");
localStorage.setItem("mogplex.appSidebar.collapsed", "true");
localStorage.setItem("mogplex-theme", "dark");
});
await page.emulateMedia({ colorScheme: "dark" });
await page.route(/\/api\/settings(?:\?.*)?$/, (route) =>
fulfillJson(route, { theme: "dark" })
);
await page.goto(scopedPath("settings/billing"));
const sidebar = page.getByTestId("app-sidebar");
await expect(sidebar).toHaveAttribute("data-compact", "false");
await expect(page.getByTestId("settings-nav-billing")).toBeVisible();
await page
.getByRole("button", { name: "Back to main navigation" })
.press("Enter");
await expect(sidebar).toHaveAttribute("data-compact", "true");
await page.getByTestId("app-nav-settings").press("Enter");
await expect(sidebar).toHaveAttribute("data-compact", "false");
await expect(page.getByTestId("settings-nav-account")).toHaveAttribute(
"aria-current",
"page"
);
await expect(
page.getByRole("heading", { name: "Account", exact: true })
).toBeVisible();
await expect(page.getByRole("switch", { name: "Run checks" })).toBeEnabled();
});
test("team owners can open Keys and Audit as pages", async ({ page }) => {
await mockTeamSettings(page, "owner");
await page.goto(`${TEAM_SETTINGS_PATH}/members`);
const nav = page.getByRole("navigation", { name: "Settings", exact: true });
await nav.getByRole("link", { name: "Provider Keys", exact: true }).click();
await expect(page).toHaveURL(`${TEAM_SETTINGS_PATH}/keys`);
await expect(
page.getByRole("heading", { name: "Provider Keys", exact: true })
).toBeVisible();
await nav.getByRole("link", { name: "Audit", exact: true }).click();
await expect(page).toHaveURL(`${TEAM_SETTINGS_PATH}/audit`);
await expect(
page.getByRole("heading", { name: "Audit", exact: true })
).toBeVisible();
await expect(page.getByRole("tab")).toHaveCount(0);
await page.reload();
await expect(
nav.getByRole("link", { name: "Audit", exact: true })
).toHaveAttribute("aria-current", "page");
});
test("Back restores focus and reentering Billing opens its Settings menu", async ({
page,
}) => {
await page.route("**/api/skills/catalog", (route) =>
fulfillJson(route, { skills: [] })
);
await page.route("**/api/observability/calls**", (route) =>
fulfillJson(route, { calls: [] })
);
await page.goto(scopedPath("settings/billing"));
await page
.getByRole("button", { name: "Back to main navigation" })
.press("Enter");
await expect(page.getByTestId("app-nav-settings")).toBeFocused();
await page.getByTestId("app-nav-connections").click();
await expect(page).toHaveURL(scopedPath("connections"));
await page.getByRole("link", { name: "Manage billing", exact: true }).click();
await expect(
page.getByRole("navigation", { name: "Settings", exact: true })
).toBeVisible();
await expect(page.getByTestId("settings-nav-billing")).toHaveAttribute(
"aria-current",
"page"
);
await expect(
page.getByRole("separator", { name: "Resize app navigation" })
).toHaveCount(0);
});