9dfa06ffee
Docker image / Build (linux/amd64) (push) Has been cancelled
Docker image / Build (linux/arm64) (push) Has been cancelled
Docker image / Merge release multi-arch manifest (push) Has been cancelled
Docker image / Merge debug multi-arch manifest (push) Has been cancelled
Docker image / Build public push gateway (linux/amd64) (push) Has been cancelled
Docker image / Build public push gateway (linux/arm64) (push) Has been cancelled
Docker image / Publish public push gateway image (push) Has been cancelled
Sprig image / Build (linux/amd64) (push) Has been cancelled
Sprig image / Build (linux/arm64) (push) Has been cancelled
Sprig image / Merge multi-arch manifest (push) Has been cancelled
Harbor Buzz Orchestra / Python tests and lint (push) Has been cancelled
CI / Detect Changed Paths (push) Has been cancelled
CI / Rust Lint (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Desktop Core (push) Has been cancelled
CI / Desktop Smoke E2E (1) (push) Has been cancelled
CI / Desktop Smoke E2E (2) (push) Has been cancelled
CI / Desktop Smoke E2E (3) (push) Has been cancelled
CI / Desktop Smoke E2E (4) (push) Has been cancelled
CI / Desktop (push) Has been cancelled
CI / Desktop E2E Relay (push) Has been cancelled
CI / Desktop E2E Integration (1/2) (push) Has been cancelled
CI / Desktop E2E Integration (2/2) (push) Has been cancelled
CI / Desktop E2E Integration (push) Has been cancelled
CI / Backend Integration (relay e2e) (push) Has been cancelled
CI / Relay E2E (push) Has been cancelled
CI / Web (push) Has been cancelled
CI / Mobile (push) Has been cancelled
CI / Security (push) Has been cancelled
CI / Dead Token Reference Guard (push) Has been cancelled
CI / Server Cross-Compile (aarch64-unknown-linux-musl) (push) Has been cancelled
CI / Server Cross-Compile (x86_64-unknown-linux-musl) (push) Has been cancelled
CI / Windows Rust (x86_64-pc-windows-msvc) (push) Has been cancelled
CI / Desktop Build (macOS) (push) Has been cancelled
helm chart / lint + unittest + render matrix (push) Has been cancelled
helm chart / install on kind (gated) (push) Has been cancelled
helm chart / publish chart to GHCR (push) Has been cancelled
Mesh Lifecycle / Relay-Driven Mesh Lifecycle Smoke (push) Has been cancelled
Sprig / Build (aarch64-unknown-linux-musl) (push) Has been cancelled
Sprig / Build (x86_64-unknown-linux-musl) (push) Has been cancelled
Sprig / Publish rolling release (push) Has been cancelled
Sprig / Publish tagged release (push) Has been cancelled
Signed-off-by: cls_宁波本机 <908705107@qq.com>
194 lines
7.0 KiB
TypeScript
194 lines
7.0 KiB
TypeScript
/**
|
|
* E2E spec for the inline "Add custom harness…" entry in the agent dialogs.
|
|
*
|
|
* Registering a custom harness used to be reachable only from Settings →
|
|
* Agents, so anyone whose first touchpoint was "New agent" never learned the
|
|
* path existed. The harness dropdowns now carry the entry directly.
|
|
*
|
|
* Covers, on all three surfaces (create, edit definition, edit instance):
|
|
* - choosing the entry opens the registration form
|
|
* - saving registers the harness and selects it in the dropdown
|
|
*
|
|
* Create and instance edit additionally assert the sentinel never becomes the
|
|
* selection; create alone covers dismissing the form leaving the previous
|
|
* selection untouched. The three surfaces share the same routing, so those
|
|
* checks are not repeated on every one.
|
|
*/
|
|
import { expect, test } from "@playwright/test";
|
|
|
|
import { installMockBridge } from "../helpers/bridge";
|
|
|
|
const ADD_ENTRY = "Add custom harness…";
|
|
const HARNESS_LABEL = "My Weird Agent";
|
|
const HARNESS_COMMAND = "my-weird-acp";
|
|
|
|
type Page = import("@playwright/test").Page;
|
|
type Locator = import("@playwright/test").Locator;
|
|
|
|
/** Open a PersonaDropdownField (button trigger + menuitemradio options). */
|
|
async function openDropdown(trigger: Locator) {
|
|
await expect(trigger).toBeVisible({ timeout: 10_000 });
|
|
await trigger.click();
|
|
}
|
|
|
|
/** Register a harness through the inline form and wait for it to close. */
|
|
async function registerHarness(page: Page) {
|
|
const form = page.getByTestId("custom-harness-form");
|
|
await expect(form).toBeVisible({ timeout: 8_000 });
|
|
await page.fill("#ch-label", HARNESS_LABEL);
|
|
await page.fill("#ch-command", HARNESS_COMMAND);
|
|
// The id auto-derives from the label; it is what the dropdown selects on.
|
|
await expect(page.locator("#ch-id")).toHaveValue("my-weird-agent");
|
|
await form.getByRole("button", { name: "Save", exact: true }).click();
|
|
await expect(page.getByTestId("add-custom-harness-dialog")).not.toBeVisible({
|
|
timeout: 8_000,
|
|
});
|
|
}
|
|
|
|
/** Open the create-agent dialog (AgentDefinitionDialog, create mode). */
|
|
async function openCreateDialog(page: Page) {
|
|
await page.goto("/", { waitUntil: "domcontentloaded" });
|
|
await page.getByTestId("open-agents-view").click();
|
|
await page.getByTestId("new-agent-card").click();
|
|
const dialog = page.getByTestId("persona-dialog");
|
|
await expect(dialog).toBeVisible({ timeout: 10_000 });
|
|
await dialog.getByRole("tab", { name: "Customize for this agent" }).click();
|
|
return dialog;
|
|
}
|
|
|
|
/** Open the edit dialog for a saved definition (same dialog, edit mode). */
|
|
async function openDefinitionEditDialog(page: Page, name: string) {
|
|
await page.goto("/", { waitUntil: "domcontentloaded" });
|
|
await page.getByTestId("open-agents-view").click();
|
|
await expect(page.getByTestId("agents-library-personas")).toBeVisible({
|
|
timeout: 10_000,
|
|
});
|
|
await page.getByRole("button", { name: `Open actions for ${name}` }).click();
|
|
await page.getByRole("menuitem", { name: "Edit" }).click();
|
|
const dialog = page.getByTestId("persona-dialog");
|
|
await expect(dialog).toBeVisible({ timeout: 10_000 });
|
|
await dialog.getByRole("tab", { name: "Customize for this agent" }).click();
|
|
return dialog;
|
|
}
|
|
|
|
test.describe("inline add custom harness", () => {
|
|
test("create dialog registers a harness inline and selects it", async ({
|
|
page,
|
|
}) => {
|
|
await installMockBridge(page);
|
|
const dialog = await openCreateDialog(page);
|
|
|
|
const harness = dialog.locator("#persona-runtime");
|
|
await openDropdown(harness);
|
|
await page.getByRole("menuitemradio", { name: ADD_ENTRY }).click();
|
|
|
|
// The sentinel opens the form; it must never become the selection.
|
|
await expect(page.getByTestId("add-custom-harness-dialog")).toBeVisible({
|
|
timeout: 8_000,
|
|
});
|
|
await expect(harness).not.toContainText(ADD_ENTRY);
|
|
|
|
await registerHarness(page);
|
|
|
|
// The saved harness is now the selected harness.
|
|
await expect(harness).toContainText(HARNESS_LABEL, { timeout: 8_000 });
|
|
});
|
|
|
|
test("dismissing the form leaves the create dialog's harness unchanged", async ({
|
|
page,
|
|
}) => {
|
|
await installMockBridge(page);
|
|
const dialog = await openCreateDialog(page);
|
|
|
|
const harness = dialog.locator("#persona-runtime");
|
|
await expect(harness).toBeVisible({ timeout: 10_000 });
|
|
const before = await harness.textContent();
|
|
|
|
await openDropdown(harness);
|
|
await page.getByRole("menuitemradio", { name: ADD_ENTRY }).click();
|
|
await expect(page.getByTestId("add-custom-harness-dialog")).toBeVisible({
|
|
timeout: 8_000,
|
|
});
|
|
|
|
await page.keyboard.press("Escape");
|
|
await expect(
|
|
page.getByTestId("add-custom-harness-dialog"),
|
|
).not.toBeVisible();
|
|
|
|
// No harness was registered, so the prior selection must survive.
|
|
await expect(harness).toHaveText(before ?? "");
|
|
});
|
|
|
|
test("definition edit dialog registers a harness inline and selects it", async ({
|
|
page,
|
|
}) => {
|
|
await installMockBridge(page, {
|
|
personas: [
|
|
{
|
|
displayName: "Editable Agent",
|
|
systemPrompt: "An agent whose harness gets replaced.",
|
|
},
|
|
],
|
|
});
|
|
const dialog = await openDefinitionEditDialog(page, "Editable Agent");
|
|
|
|
const harness = dialog.locator("#persona-runtime");
|
|
await openDropdown(harness);
|
|
await page.getByRole("menuitemradio", { name: ADD_ENTRY }).click();
|
|
await expect(page.getByTestId("add-custom-harness-dialog")).toBeVisible({
|
|
timeout: 8_000,
|
|
});
|
|
|
|
await registerHarness(page);
|
|
|
|
await expect(harness).toContainText(HARNESS_LABEL, { timeout: 8_000 });
|
|
});
|
|
|
|
test("instance edit dialog registers a harness inline and keeps Custom command", async ({
|
|
page,
|
|
}) => {
|
|
await installMockBridge(page, {
|
|
managedAgents: [
|
|
{
|
|
pubkey:
|
|
"npub1e2e00000000000000000000000000000000000000000000000000000000",
|
|
name: "Instance Agent",
|
|
status: "stopped",
|
|
channelNames: ["agents"],
|
|
},
|
|
],
|
|
});
|
|
|
|
await page.goto("/", { waitUntil: "domcontentloaded" });
|
|
await page.getByTestId("open-agents-view").click();
|
|
await page
|
|
.getByRole("button", { name: "Instance Agent agent profile" })
|
|
.click();
|
|
await expect(page.getByTestId("user-profile-panel")).toBeVisible({
|
|
timeout: 10_000,
|
|
});
|
|
await page.getByTestId("user-profile-edit-agent").click();
|
|
await expect(page.getByTestId("edit-agent-dialog")).toBeVisible({
|
|
timeout: 10_000,
|
|
});
|
|
|
|
const provider = page.locator("#edit-agent-runtime");
|
|
await openDropdown(provider);
|
|
|
|
// "Custom command" is a different feature (ad-hoc command override) and
|
|
// must survive alongside the new entry.
|
|
await expect(
|
|
page.getByRole("menuitemradio", { name: "Custom command" }),
|
|
).toBeVisible();
|
|
await page.getByRole("menuitemradio", { name: ADD_ENTRY }).click();
|
|
await expect(page.getByTestId("add-custom-harness-dialog")).toBeVisible({
|
|
timeout: 8_000,
|
|
});
|
|
await expect(provider).not.toContainText(ADD_ENTRY);
|
|
|
|
await registerHarness(page);
|
|
|
|
await expect(provider).toContainText(HARNESS_LABEL, { timeout: 8_000 });
|
|
});
|
|
});
|