feat: import Chinese-localized Buzz source snapshot
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>
This commit is contained in:
2026-08-13 18:34:25 +08:00
parent 61c3fa1df9
commit 9dfa06ffee
3785 changed files with 1085458 additions and 2 deletions
@@ -0,0 +1,231 @@
/**
* E2E spec for the create-agent "Run on" provider config fields.
*
* Pins the fix for the "Typewriter Eraser": WhereToRunSection's probe effect
* used to depend on the whole draft, so every keystroke re-probed the
* provider and every probe resolution reset providerConfig to schema
* defaults — typing into a defaultless field (the k8s "Kubeconfig context")
* looked completely dead, and the provider binary respawned in a loop.
*
* Covers:
* - typing into a defaultless provider field sticks, and the provider is
* probed exactly once for the selection (not once per keystroke or
* Advanced disclosure toggle)
* - the config form is gated on probe resolution (no half-rendered form),
* and defaults prefill exactly once when a slow probe lands
* - collapsing Advanced during an incomplete remote setup keeps the submit
* blocker visible through the Required badge
* - switching provider → local → provider re-probes and resets cleanly
*
* The stale-closure merge on probe resolution (defaults beneath in-flight
* typing) is unreachable through this UI because the fields render only
* after the probe resolves; it is pinned at the unit level in
* whereToRunIntent.test.mjs (applyProbeResult).
*/
import { expect, test } from "@playwright/test";
import { installMockBridge } from "../helpers/bridge";
type Page = import("@playwright/test").Page;
const PROVIDER = {
id: "kubernetes",
binaryPath: "/mock/buzz-backend-kubernetes",
};
const PROBE_RESULT = {
ok: true,
name: "kubernetes",
version: "0.0.0-mock",
config_schema: {
type: "object",
properties: {
context: {
type: "string",
title: "Kubeconfig context",
description: "Context from your kubeconfig.",
},
namespace: {
type: "string",
title: "Namespace",
default: "buzz-agents-mock01",
},
},
required: ["namespace"],
},
};
async function probeInvocations(page: Page): Promise<number> {
return page.evaluate(
() =>
(
window as Window & { __BUZZ_E2E_COMMANDS__?: string[] }
).__BUZZ_E2E_COMMANDS__?.filter(
(command) => command === "probe_backend_provider",
).length ?? 0,
);
}
async function selectRunOnOption(
page: Page,
dialog: import("@playwright/test").Locator,
optionName: string,
) {
const trigger = dialog.locator("#agent-run-on");
await expect(trigger).toHaveAttribute("aria-expanded", "false");
await trigger.press("Enter");
const option = page.getByRole("menuitemradio", {
exact: true,
name: optionName,
});
await expect(option).toBeVisible();
// The shared PersonaDropdownField supports keyboard selection. Using it here
// avoids racing the menu's open animation when this test changes locations
// repeatedly.
await option.press("Enter");
await expect(trigger).toHaveAttribute("aria-expanded", "false");
}
/** Open Advanced in the create-agent dialog and select the mocked provider. */
async function openCreateDialogOnProvider(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 });
const advanced = dialog.getByRole("button", {
name: "Advanced",
exact: true,
});
await expect(advanced).toHaveAttribute("aria-expanded", "false");
await expect(dialog.locator("#agent-run-on")).toHaveCount(0);
await advanced.click();
await expect(advanced).toHaveAttribute("aria-expanded", "true");
const respondTo = dialog.getByTestId("agent-respond-to");
const runOn = dialog.locator("#agent-run-on");
await expect(respondTo).toBeVisible();
await expect(runOn).toBeVisible();
expect(await respondTo.evaluate((element) => element.offsetTop)).toBeLessThan(
await runOn.evaluate((element) => element.offsetTop),
);
await selectRunOnOption(page, dialog, PROVIDER.id);
return dialog;
}
test("typing into a defaultless provider field sticks and probes only once", async ({
page,
}) => {
await installMockBridge(page, {
backendProviders: [PROVIDER],
backendProviderProbeResult: PROBE_RESULT,
});
const dialog = await openCreateDialogOnProvider(page);
const contextField = dialog.locator("#provider-cfg-context");
await expect(contextField).toBeVisible({ timeout: 10_000 });
// Defaults prefilled from the schema; context has none.
await expect(dialog.locator("#provider-cfg-namespace")).toHaveValue(
"buzz-agents-mock01",
);
await expect(contextField).toHaveValue("");
await contextField.fill("prod-us-west");
await expect(contextField).toHaveValue("prod-us-west");
// One selection, one probe — keystrokes and Advanced disclosure toggles
// must not refire executable provider discovery after it has completed.
expect(await probeInvocations(page)).toBe(1);
const advanced = dialog.getByRole("button", {
name: "Advanced",
exact: true,
});
await advanced.click();
await expect(advanced).toHaveAttribute("aria-expanded", "false");
await expect(dialog.locator("#agent-run-on")).toHaveCount(0);
await advanced.click();
await expect(advanced).toHaveAttribute("aria-expanded", "true");
await expect(dialog.locator("#provider-cfg-context")).toHaveValue(
"prod-us-west",
);
expect(await probeInvocations(page)).toBe(1);
});
test("config fields render only after a slow probe resolves, with defaults", async ({
page,
}) => {
// The fields are gated on the probe result (draft.probedProvider), which is
// what makes mid-flight typing unreachable through the UI — the stale-probe
// merge seam (applyProbeResult) is pinned at the unit level instead. This
// spec holds the gate: no half-rendered form before the probe lands, and
// defaults appear exactly once when it does.
await installMockBridge(page, {
backendProviders: [PROVIDER],
backendProviderProbeResult: PROBE_RESULT,
backendProviderProbeDelayMs: 1_000,
});
const dialog = await openCreateDialogOnProvider(page);
// Pre-resolution: the security warning is up, the form is not.
await expect(dialog.getByText("will receive your agent")).toBeVisible();
await expect(dialog.locator("#provider-cfg-context")).toHaveCount(0);
// Post-resolution: fields render with schema defaults prefilled.
await expect(dialog.locator("#provider-cfg-context")).toBeVisible({
timeout: 10_000,
});
await expect(dialog.locator("#provider-cfg-namespace")).toHaveValue(
"buzz-agents-mock01",
);
expect(await probeInvocations(page)).toBe(1);
});
test("collapsed Advanced marks incomplete remote setup as required", async ({
page,
}) => {
await installMockBridge(page, {
backendProviders: [PROVIDER],
backendProviderProbeResult: PROBE_RESULT,
backendProviderProbeDelayMs: 10_000,
});
const dialog = await openCreateDialogOnProvider(page);
const advanced = dialog.getByRole("button", {
name: "Advanced",
exact: true,
});
const submit = dialog.getByTestId("persona-dialog-submit");
await expect(submit).toBeDisabled();
await advanced.click();
await expect(advanced).toHaveAttribute("aria-expanded", "false");
await expect(dialog.locator("#agent-run-on")).toHaveCount(0);
await expect(
dialog.getByTestId("persona-advanced-required-badge"),
).toHaveText("Required");
await expect(submit).toBeDisabled();
});
test("provider → local → provider re-probes and resets the config", async ({
page,
}) => {
await installMockBridge(page, {
backendProviders: [PROVIDER],
backendProviderProbeResult: PROBE_RESULT,
});
const dialog = await openCreateDialogOnProvider(page);
const contextField = dialog.locator("#provider-cfg-context");
await expect(contextField).toBeVisible({ timeout: 10_000 });
await contextField.fill("stale-value");
await selectRunOnOption(page, dialog, "This computer");
await expect(contextField).toHaveCount(0);
await selectRunOnOption(page, dialog, PROVIDER.id);
await expect(dialog.locator("#provider-cfg-context")).toBeVisible({
timeout: 10_000,
});
// Fresh selection = fresh draft: the stale value must not leak back.
await expect(dialog.locator("#provider-cfg-context")).toHaveValue("");
expect(await probeInvocations(page)).toBe(2);
});