Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 16 additions & 8 deletions apps/web/DESIGN.md

Large diffs are not rendered by default.

13 changes: 7 additions & 6 deletions apps/web/PRODUCT.md

Large diffs are not rendered by default.

16 changes: 8 additions & 8 deletions apps/web/e2e/access.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,13 +109,13 @@ test("leads from Getting started to the default model configuration, and counts
await openConsole(page, request, "overview", { fresh: true });
const step = page.getByRole("region", { name: "Getting started" }).getByRole("listitem").filter({ hasText: "Set a default model configuration" });
await expect(step).toContainText("To do");
await step.getByRole("button", { name: "Open System" }).click();
await expect(page.getByRole("heading", { name: "System", level: 1 })).toBeVisible();
await step.getByRole("button", { name: "Open Settings" }).click();
await expect(page.getByRole("heading", { name: "Models and credentials", level: 1 })).toBeVisible();
// It arrives on the default harness's action.
const set = page.getByRole("button", { name: "Set the default model configuration for Codex" });
await expect(set).toBeFocused();
// Only the page body scrolls to it: the page header stays in view.
await expect(page.getByRole("heading", { name: "System", level: 1 })).toBeInViewport();
await expect(page.getByRole("heading", { name: "Models and credentials", level: 1 })).toBeInViewport();
await set.click();
const form = page.getByRole("dialog", { name: "Set default model configuration for Codex" });
await form.getByLabel("Base URL").fill("https://model.example/v1");
Expand All @@ -131,10 +131,10 @@ test("shows every page empty on a fresh install, and Getting started hides, come
await openConsole(page, request, "overview", { fresh: true, sandbox: "none", nodes: "none" });
// What each page shows once its reads are done: an empty state, Core's own figures, or sandbox setup.
const loaded = (view: string) => view === "core-metrics" ? page.locator(".kpi-strip").first()
: view === "nodes" ? page.getByText("Set up sandbox hosting in System before adding nodes.")
: view === "system" ? page.getByRole("button", { name: "Manage sandbox configuration" })
: view === "nodes" ? page.getByText("Set up sandbox hosting in Settings before adding nodes.")
: view === "settings" ? page.getByRole("heading", { name: "Where should sandboxes run?" })
: page.locator(".console-empty").first();
for (const view of ["core-metrics", "agent-metrics", "sandbox-metrics", "sessions", "agents", "templates", "skills", "files", "vaults", "projects", "nodes", "system", "overview"]) {
for (const view of ["core-metrics", "agent-metrics", "sandbox-metrics", "sessions", "agents", "templates", "skills", "files", "vaults", "projects", "nodes", "settings", "overview"]) {
await page.goto(`/#${view}`);
await expect(loaded(view)).toBeVisible();
await expect(page.locator('[aria-busy="true"]')).toHaveCount(0);
Expand All @@ -154,7 +154,7 @@ test("shows every page empty on a fresh install, and Getting started hides, come
const step = checklist.getByRole("listitem").filter({ hasText: "Get sandboxes ready" });
await expect(step).toContainText("To do");
await step.getByRole("button", { name: "Set up sandboxes" }).click();
await expect(page).toHaveURL(/#system\?id=sandbox$/);
await expect(page).toHaveURL(/#settings\?id=sandbox$/);
await expect(page.getByRole("heading", { name: "Where should sandboxes run?" })).toBeVisible();
});

Expand All @@ -163,7 +163,7 @@ test("opens Add node from Getting started when no node has joined", async ({ pag
const step = page.getByRole("region", { name: "Getting started" }).getByRole("listitem").filter({ hasText: "Get sandboxes ready" });
await expect(step).toContainText("To do");
await step.getByRole("button", { name: "Add node" }).click();
await expect(page.getByRole("heading", { name: "Nodes", level: 1 })).toBeVisible();
await expect(page.getByRole("heading", { name: "Sandbox and scheduling", level: 1 })).toBeVisible();
await expect(page.getByRole("dialog", { name: "Add node" })).toBeVisible();
});

Expand Down
51 changes: 33 additions & 18 deletions apps/web/e2e/nodes.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ test("issues no command before the installation is read, for a loopback public U
await held;
await route.fulfill({ status: 500, json: { error: { message: "Unavailable.", type: "server_error", code: null, param: null } } });
});
await openConsole(page, request, "nodes", { installation: "local" });
await openConsole(page, request, "settings?id=sandbox", { installation: "local" });
await page.getByRole("button", { name: "Add node" }).click();
const add = page.getByRole("dialog", { name: "Add node" });
await expect(add.getByRole("status")).toHaveText("Checking this installation's public URL…");
Expand Down Expand Up @@ -186,7 +186,7 @@ test("gives the host's uninstall command even when the installation must be read
});

test("sets up own-machine sandboxes page by page, with the Runtime from the distribution", async ({ page, request }) => {
await openConsole(page, request, "system?id=sandbox", { sandbox: "none" });
await openConsole(page, request, "settings?id=sandbox", { sandbox: "none" });
await expect(page.getByRole("heading", { name: "Where should sandboxes run?" })).toBeVisible();
await page.getByRole("button", { name: "Own machines" }).click();
await page.getByRole("button", { name: "microsandbox Recommended" }).click();
Expand All @@ -203,15 +203,14 @@ test("sets up own-machine sandboxes page by page, with the Runtime from the dist
expect(tokenRequested).toBe(false);
expect(await writes(request)).toEqual(["POST /core/v1/sandbox/deployment"]);
await add.getByRole("button", { name: "Close dialog" }).click();
await page.getByRole("button", { name: "System", exact: true }).click();
await page.getByRole("button", { name: "Manage sandbox configuration", exact: true }).click();
await page.getByRole("button", { name: "Settings", exact: true }).click();
// The saved specification carries the Runtime read from the console's manifest.
await page.getByRole("button", { name: "Configuration details", exact: true }).click();
await expect(page.locator(".help-tip-popover").getByText("c0ffee000000", { exact: true })).toBeVisible();
});

test("preselects microsandbox and asks once before switching to Docker", async ({ page, request }) => {
await openConsole(page, request, "system?id=sandbox", { sandbox: "none" });
await openConsole(page, request, "settings?id=sandbox", { sandbox: "none" });
await page.getByRole("button", { name: "Own machines" }).click();
const microsandbox = page.getByRole("button", { name: "microsandbox Recommended" });
const docker = page.getByRole("button", { name: "Docker", exact: true });
Expand Down Expand Up @@ -243,7 +242,7 @@ test("saves E2B without opening Add node, as it has no machines", async ({ page,
page.on("request", (sent) => {
if (sent.method() === "POST" && sent.url().endsWith("/core/v1/sandbox/deployment")) submitted = sent.postDataJSON() as Record<string, unknown>;
});
await openConsole(page, request, "system?id=sandbox", { sandbox: "none" });
await openConsole(page, request, "settings?id=sandbox", { sandbox: "none" });
await page.getByRole("button", { name: "E2B cloud" }).click();
await expect(page.getByLabel("E2B provider")).toHaveValue("sandbase");
await expect(page.getByLabel("Sandbox API URL")).toHaveValue("https://sandbox.sandbase.ai");
Expand All @@ -257,7 +256,7 @@ test("saves E2B without opening Add node, as it has no machines", async ({ page,
await selectFixtureE2BBuild(page);
await page.getByRole("button", { name: "Next" }).click();
await page.getByRole("button", { name: "Save configuration" }).click();
await expect(page.getByRole("heading", { name: "Sandbox configuration", level: 1 })).toBeVisible();
await expect(page.getByRole("heading", { name: "Sandbox and scheduling", level: 1 })).toBeVisible();
await expect(page.getByRole("dialog")).toHaveCount(0);
expect(submitted).toMatchObject({ provider: "e2b", configuration: { template: "template:94be54a1-138c-4f30-bc87-b13686272dbe", api_url: "https://sandbox.sandbase.ai", domain: "sandbox.sandbase.ai" } , credential: { api_key: "fixture-private-key" } });
expect(await writes(request)).toEqual([
Expand All @@ -268,7 +267,7 @@ test("saves E2B without opening Add node, as it has no machines", async ({ page,
});

test("uses the official E2B preset and clears a selected build when the key changes", async ({ page, request }) => {
await openConsole(page, request, "system?id=sandbox", { sandbox: "none" });
await openConsole(page, request, "settings?id=sandbox", { sandbox: "none" });
await page.getByRole("button", { name: "E2B cloud" }).click();
await page.getByLabel("E2B provider").selectOption("official");
await expect(page.getByLabel("Sandbox API URL")).toHaveValue("https://api.e2b.app");
Expand All @@ -289,7 +288,7 @@ test("uses the official E2B preset and clears a selected build when the key chan
});

test("shows the retained E2B build while a replacement key is checked", async ({ page, request }) => {
await openConsole(page, request, "system?id=sandbox", { sandbox: "e2b" });
await openConsole(page, request, "settings?id=sandbox", { sandbox: "e2b" });
await page.getByRole("button", { name: "Change resources" }).click();
const edit = page.getByRole("dialog", { name: "Change resources" });
const saved = "oac-runtime:0f1e2d3c-4b5a-6978-8a9b-0c1d2e3f4a5b";
Expand All @@ -315,7 +314,7 @@ test("edits only the saved backend, preserving a custom size and Runtime", async
await route.fulfill({ json: deployment });
});
await page.route("**/core/v1/sandbox/nodes", (route) => route.fulfill({ json: { data: [] } }));
await openConsole(page, request, "system?id=sandbox");
await openConsole(page, request, "settings?id=sandbox");
await page.getByRole("button", { name: "Change resources" }).click();
await expect(page.getByRole("button", { name: "Own machines" })).toHaveCount(0);
await expect(page.getByRole("button", { name: "E2B cloud" })).toHaveCount(0);
Expand All @@ -326,7 +325,7 @@ test("edits only the saved backend, preserving a custom size and Runtime", async
});

test("keeps the page usable when Core refuses a sandbox change, and shows a safe refusal", async ({ page, request }) => {
await openConsole(page, request, "system?id=sandbox", { sandbox: "none" });
await openConsole(page, request, "settings?id=sandbox", { sandbox: "none" });
await failNext(request, { method: "POST", path: "/sandbox/deployment", status: 403, message: "This console is read-only." });
await page.getByRole("button", { name: "Own machines" }).click();
await page.getByRole("button", { name: "microsandbox Recommended" }).click();
Expand All @@ -345,24 +344,40 @@ test("keeps the page usable when Core refuses a sandbox change, and shows a safe
const added = page.getByRole("dialog", { name: "Add node" });
await expect(added).toBeVisible();
await added.getByRole("button", { name: "Close dialog" }).click();
await page.getByRole("button", { name: "System", exact: true }).click();
await page.getByRole("button", { name: "Manage sandbox configuration", exact: true }).click();
await page.getByRole("button", { name: "Settings", exact: true }).click();
await page.getByRole("button", { name: "Configuration details", exact: true }).click();
await expect(page.locator(".help-tip-popover").getByText("c0ffee000000", { exact: true })).toBeVisible();
});

test("renames a node and sets how many sandboxes run on it at once", async ({ page, request }) => {
await openConsole(page, request, "nodes");
// The Nodes policy area owns all per-node capacity edits.
const capacity = page.getByRole("region", { name: "Capacity and suspension" });
// Nodes links to the sole capacity editor in Settings.
const capacity = page.getByRole("table", { name: "Sandbox nodes" });
await expect(capacity).toContainText("Active / limit");
await expect(capacity).toContainText("5 / 8");
await capacity.getByRole("row").filter({ hasText: "core-01" }).getByRole("button", { name: "Edit node" }).click();
await page.getByRole("row").filter({ hasText: "core-01" }).getByRole("button", { name: "Edit node" }).click();
const edit = page.getByRole("dialog", { name: "Edit node" });
await edit.getByLabel("Name").fill("core-01-large");
await edit.getByLabel("Sandboxes at once").fill("6");
await edit.getByRole("button", { name: "Save" }).click();
await expect(edit).toBeHidden();
await expect(capacity).toContainText("core-01-large");
await expect(capacity).toContainText("5 / 6");
await expect(page.getByRole("region", { name: "Sandbox nodes", exact: true })).toContainText("core-01-large");
await expect(page.getByRole("region", { name: "Sandbox nodes", exact: true })).toContainText("5 / 6");
});


test("node settings history follows the selected node URL", async ({ page, request }) => {
await openConsole(page, request, "nodes");
await page.getByRole("row").filter({ hasText: "core-01" }).getByRole("button", { name: "Edit node", exact: true }).click();
const editor = page.getByRole("dialog", { name: "Edit node", exact: true });
await expect(editor).toBeVisible();
await expect(page).toHaveURL(/#settings\?id=sandbox&node=/);
await editor.getByRole("button", { name: "Cancel", exact: true }).click();
await expect(editor).toBeHidden();
await page.goBack();
await expect(editor).toBeVisible();
await page.goForward();
await expect(page).toHaveURL(/#settings\?id=sandbox$/);
await expect(editor).toBeHidden();
expect(await writes(request)).toEqual([]);
});
3 changes: 2 additions & 1 deletion apps/web/e2e/overview-readiness.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,8 @@ test("local-only address links to System on other pages and blocks Add node", as
await expect(notice).toBeVisible();
await expect(page.getByRole("button", { name: "Add node", exact: true })).toBeDisabled();
await expect(page.getByText("Add node is unavailable while the public address is local only.")).toBeVisible();
await page.getByRole("button", { name: "System", exact: true }).click();
await page.getByRole("button", { name: "Settings", exact: true }).click();
await page.getByRole("button", { name: "Service and deployment", exact: true }).click();
await expect(notice).toBeVisible();
await expect(notice.getByRole("button", { name: "Review the public address" })).toHaveCount(0);
expect(await writes(request)).toEqual([]);
Expand Down
2 changes: 1 addition & 1 deletion apps/web/e2e/provider-observations.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ test("default provider observations are optional details and recover only on Cor
for (const harness of data.data) if (harness.model_configuration) Object.assign(harness.model_configuration, observation);
await route.fulfill({ response, json: data });
});
await openConsole(page, request, "system");
await openConsole(page, request, "settings?id=models");
const card = page.getByRole("article", { name: "Codex", exact: true });
await expect(card).not.toContainText("Last successful use");
await card.getByRole("button", { name: "Usage details", exact: true }).click();
Expand Down
10 changes: 5 additions & 5 deletions apps/web/e2e/public-url.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ test.afterEach(async ({ request }) => expectManagementBoundary(request));
test("sets up sandboxes with Core's address read-only, never sending it", async ({ page, request }) => {
const bodies: string[] = [];
page.on("request", (sent) => { if (sent.url().includes("/core/v1/sandbox/deployment")) bodies.push(sent.postData() ?? ""); });
await openConsole(page, request, "system?id=sandbox", { sandbox: "none" });
await openConsole(page, request, "settings?id=sandbox", { sandbox: "none" });
await page.getByRole("button", { name: "Own machines" }).click();
await page.getByRole("button", { name: "microsandbox Recommended" }).click();
await page.getByRole("button", { name: /^Standard/ }).click();
const review = page.getByRole("definition").filter({ hasText: "https://core.example.com" });
await expect(review).toContainText("Managed in System");
await expect(review).toContainText("Managed in Settings");
await expect(page.getByRole("textbox", { name: "Core address" })).toHaveCount(0);
await page.getByRole("button", { name: "Save configuration" }).click();
// Saved: own machines continue straight to Add node.
Expand All @@ -23,7 +23,7 @@ test("sets up sandboxes with Core's address read-only, never sending it", async
});

test("explains an E2B rejection in the wizard, with a link to domain setup", async ({ page, request }) => {
await openConsole(page, request, "system?id=sandbox", { sandbox: "none", installation: "local" });
await openConsole(page, request, "settings?id=sandbox", { sandbox: "none", installation: "local" });
await page.getByRole("button", { name: "E2B cloud" }).click();
await selectFixtureE2BBuild(page);
await page.getByRole("button", { name: "Next" }).click();
Expand All @@ -32,7 +32,7 @@ test("explains an E2B rejection in the wizard, with a link to domain setup", asy
await page.getByRole("button", { name: "Save configuration" }).click();
const rejection = page.locator(".wizard-rejection");
await expect(rejection).toContainText("E2B sandboxes need a public HTTPS address.");
await expect(rejection.getByRole("button", { name: "Managed in System" })).toBeVisible();
await expect(rejection.getByRole("button", { name: "Managed in Settings" })).toBeVisible();
// Nothing was saved and nothing is uncertain: no dialog, and the wizard stays on its review.
await expect(page.getByRole("dialog")).toHaveCount(0);
await expect(page.getByRole("heading", { name: "Review and save" })).toBeVisible();
Expand All @@ -43,7 +43,7 @@ test("explains an E2B rejection in the wizard, with a link to domain setup", asy
});

test("lists the startup settings on System", async ({ page, request }) => {
await openConsole(page, request, "system");
await openConsole(page, request, "settings?id=service");
const installation = page.getByRole("region", { name: "Installation" });
await expect(installation).toContainText("https://core.example.com/v1");
const startup = page.getByRole("region", { name: "Startup settings" });
Expand Down
Loading
Loading