mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
**Category:** improvement **User Impact:** Users can create, discover, and import agents from one consistent Add agent dialog. **Problem:** Agent creation, discovery, and import were split across a dropdown and separate dialogs, making the Add agent flow fragmented. The existing E2E suite also continued targeting the deleted dropdown after the flows were unified. **Solution:** Route the new-agent card directly into a unified dialog with dedicated Create, catalog, and Import navigation, then update the affected E2E coverage to exercise that interface and its current empty state. <details> <summary>File changes</summary> **desktop/src/features/agents/ui/AgentDefinitionDialog.tsx** Supports rendering the agent definition form inside the unified Add agent experience while retaining the standalone dialog behavior. **desktop/src/features/agents/ui/AgentDefinitionDialogShell.tsx** Adds the shared shell used to present agent-definition content consistently in embedded and standalone contexts. **desktop/src/features/agents/ui/AgentDialog.tsx** Passes the revised dialog state and close behavior through the existing agent dialog entry point. **desktop/src/features/agents/ui/AgentsView.tsx** Connects the Agents page to the unified Add agent dialog and opens newly added catalog agents in their profile panel. **desktop/src/features/agents/ui/PersonaCatalogDialog.tsx** Combines catalog browsing, agent creation, and snapshot import behind persistent navigation, including dirty-navigation confirmation. **desktop/src/features/agents/ui/UnifiedAgentsSection.tsx** Replaces the new-agent dropdown with a direct Add agent entry point and adjusts the responsive card grid. **desktop/src/features/agents/ui/personaLibraryCopy.ts** Updates catalog-facing copy for the unified experience. **desktop/src/features/agents/ui/usePersonaActions.ts** Returns the resolved local persona after catalog activation so the caller can open the added agent. **desktop/tests/e2e/agent-readiness-screenshots.spec.ts** Opens the embedded create pane directly for readiness screenshots. **desktop/tests/e2e/agents.spec.ts** Covers unified Create, catalog, and Import navigation and asserts the current shared-agent empty state. **desktop/tests/e2e/global-agent-config-screenshots.spec.ts** Updates global configuration screenshot setup for direct create-pane entry. **desktop/tests/e2e/inline-custom-harness.spec.ts** Updates custom harness setup for the embedded create form. **desktop/tests/e2e/persona-env-vars.spec.ts** Updates environment-variable and model-provider scenarios for direct create-pane entry. **desktop/tests/e2e/persona-model-combobox-screenshots.spec.ts** Updates model combobox screenshot setup for direct create-pane entry. **desktop/tests/e2e/smoke.spec.ts** Updates agent-creation smoke coverage for the unified Add agent dialog. **desktop/tests/e2e/where-to-run-config.spec.ts** Updates provider-selection coverage for the embedded create form. </details> ## Reproduction steps 1. Open the Agents page and select the new-agent card. 2. Confirm the Add agent dialog opens directly on Create without an intermediate dropdown. 3. Use the left navigation to browse shared agents and open Import. 4. Select a catalog agent and confirm the dialog closes and the added agent's profile panel opens. 5. Run the affected desktop Playwright smoke and integration specs and confirm all scenarios pass. --------- Signed-off-by: Taylor Ho <taylorkmho@gmail.com> Co-authored-by: Carl <acda9e433d19dcd0e6b6840f7f4b98f3a56f1fab98049d444c087019e6d36560@buzz.block.builderlab.xyz>
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 });
|
|
});
|
|
});
|