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
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:
@@ -0,0 +1,255 @@
|
||||
/**
|
||||
* Screenshot-regression spec for PR #1764: provider/model dropdown fixes.
|
||||
*
|
||||
* Three states that previously regressed on fresh OSS installs:
|
||||
*
|
||||
* 01 – Global agent config provider select renders both Databricks v1 and v2
|
||||
* options (they are always shown on OSS builds; the mock bridge returns an
|
||||
* empty baked env, simulating an OSS install with no BUZZ_AGENT_PROVIDER).
|
||||
*
|
||||
* 02 – Effort dropdown shows "Default (medium)" instead of bare "Inherit" when
|
||||
* no effort is baked and the provider uses a known default effort level.
|
||||
* (provider unset → effortDefault = "medium" → inheritFallbackLabel fires)
|
||||
*
|
||||
* 03 – Edit dialog for a definition with runtime null auto-seeds the app-default
|
||||
* runtime (buzz-agent) and model discovery runs → model combobox is non-empty.
|
||||
* Previously, the seeding effect bailed in edit mode, leaving the runtime
|
||||
* empty and the model dropdown silently blank.
|
||||
*/
|
||||
import { expect, test } from "@playwright/test";
|
||||
|
||||
import { installMockBridge } from "../helpers/bridge";
|
||||
import { waitForAnimations } from "../helpers/animations";
|
||||
|
||||
const SHOTS = "test-results/screenshots-dialogs";
|
||||
|
||||
/**
|
||||
* Open Settings → Agents through the app UI and wait for the defaults card to
|
||||
* finish loading. The CI static server does not provide SPA fallbacks for a
|
||||
* direct `/settings` request.
|
||||
*/
|
||||
async function openAiDefaultsSettings(page: import("@playwright/test").Page) {
|
||||
await page.goto("/", { waitUntil: "domcontentloaded" });
|
||||
await page.getByTestId("open-settings").click();
|
||||
await page.getByTestId("profile-popover-settings").click();
|
||||
await expect(page.getByTestId("settings-view")).toBeVisible();
|
||||
await page.getByTestId("settings-nav-agents").click();
|
||||
await expect(page.getByTestId("settings-global-agent-config")).toBeVisible({
|
||||
timeout: 10_000,
|
||||
});
|
||||
// The card shows a spinner during the async load effect; wait for it to clear.
|
||||
await expect(page.locator(".animate-spin").first()).not.toBeVisible({
|
||||
timeout: 5_000,
|
||||
});
|
||||
}
|
||||
|
||||
test.describe("agent provider dropdown screenshots", () => {
|
||||
test.use({ viewport: { width: 1280, height: 900 } });
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
page.on("pageerror", (err) => {
|
||||
console.error(
|
||||
"PAGE ERROR:",
|
||||
err.message,
|
||||
err.stack?.split("\n").slice(0, 5).join("\n"),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
// Shot 01: OSS provider dropdown includes both Databricks v1 and v2.
|
||||
//
|
||||
// The mock bridge returns get_baked_build_env_keys = [] (OSS), so no
|
||||
// BUZZ_AGENT_PROVIDER is baked and hideProviderIds is empty → v1 appears.
|
||||
test("01-provider-dropdown-oss", async ({ page }) => {
|
||||
await installMockBridge(page);
|
||||
await openAiDefaultsSettings(page);
|
||||
|
||||
const providerSelect = page.getByTestId("global-agent-provider");
|
||||
await expect(providerSelect).toBeVisible({ timeout: 5_000 });
|
||||
|
||||
// Regression: both v1 and v2 must be present on OSS. The defaults editor
|
||||
// renders the app's styled dropdown, so open it and assert option rows.
|
||||
await providerSelect.click();
|
||||
await expect(
|
||||
page.getByTestId("global-agent-provider-option-databricks"),
|
||||
).toHaveText("Databricks");
|
||||
await expect(
|
||||
page.getByTestId("global-agent-provider-option-databricks_v2"),
|
||||
).toHaveText("Databricks v2");
|
||||
await page.keyboard.press("Escape");
|
||||
await expect(
|
||||
page.getByTestId("global-agent-provider-option-databricks"),
|
||||
).toHaveCount(0);
|
||||
|
||||
await waitForAnimations(page);
|
||||
|
||||
// Screenshot the card — provider select is closed but the assertion above
|
||||
// proves both Databricks options are present in the option list.
|
||||
await page
|
||||
.getByTestId("settings-global-agent-config")
|
||||
.screenshot({ path: `${SHOTS}/01-provider-dropdown-oss.png` });
|
||||
});
|
||||
|
||||
// Shot 02: Effort dropdown shows "Default (medium)" (no baked effort,
|
||||
// provider=databricks_v2 with no model → effortDefault = "medium").
|
||||
//
|
||||
// getProviderEffortConfig("databricks_v2", "") hits the blank-model branch:
|
||||
// → { defaultValue: "medium" }
|
||||
// inheritFallbackLabel = "Default (medium)", bakedEffort = null (OSS)
|
||||
// → the zero-value option reads "Default (medium)" instead of bare "Inherit".
|
||||
//
|
||||
// Using databricks_v2 here (rather than no provider) makes this card
|
||||
// visually distinct from shot 01 — the provider select shows "Databricks v2"
|
||||
// as the selected value — so the two PNG hashes differ.
|
||||
test("02-effort-default-label", async ({ page }) => {
|
||||
await installMockBridge(page, {
|
||||
globalAgentConfig: {
|
||||
provider: "databricks_v2",
|
||||
model: null,
|
||||
env_vars: {},
|
||||
},
|
||||
});
|
||||
await openAiDefaultsSettings(page);
|
||||
|
||||
const effortSelect = page.getByTestId(
|
||||
"global-agent-thinking-effort-select",
|
||||
);
|
||||
await expect(effortSelect).toBeVisible({ timeout: 5_000 });
|
||||
|
||||
// Regression: the zero-value option must show the provider's default
|
||||
// effort rather than a bare "Inherit". The styled dropdown renders the
|
||||
// zero value as the closed trigger's label (nothing is persisted) and as
|
||||
// the "empty" option row when opened.
|
||||
await expect(effortSelect).toHaveText("Default (medium)");
|
||||
await effortSelect.click();
|
||||
await expect(
|
||||
page.getByTestId("global-agent-thinking-effort-select-option-empty"),
|
||||
).toHaveText("Default (medium)");
|
||||
await page.keyboard.press("Escape");
|
||||
await expect(
|
||||
page.getByTestId("global-agent-thinking-effort-select-option-empty"),
|
||||
).toHaveCount(0);
|
||||
|
||||
await waitForAnimations(page);
|
||||
|
||||
await page
|
||||
.getByTestId("settings-global-agent-config")
|
||||
.screenshot({ path: `${SHOTS}/02-effort-default-label.png` });
|
||||
});
|
||||
|
||||
// Shot 03: Edit dialog for a definition with null runtime auto-seeds the
|
||||
// default runtime (buzz-agent via getDefaultPersonaRuntime) and model
|
||||
// discovery runs, producing a non-empty model combobox.
|
||||
//
|
||||
// Previously the seeding effect bailed in edit mode ("id" in initialValues),
|
||||
// leaving runtime = "" → modelFieldVisible = false → discovery never ran →
|
||||
// the model dropdown was silently empty.
|
||||
test("03-builtin-edit-runtime-seeded", async ({ page }) => {
|
||||
await installMockBridge(page, {
|
||||
personas: [
|
||||
{
|
||||
displayName: "Null Runtime Agent",
|
||||
systemPrompt: "An agent with no runtime configured.",
|
||||
// runtime/provider/model not set → all null in the mock, so the
|
||||
// edit dialog must auto-seed the app default runtime.
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
await page.goto("/");
|
||||
await page.getByTestId("open-agents-view").click();
|
||||
await expect(page.getByTestId("agents-library-personas")).toBeVisible({
|
||||
timeout: 10_000,
|
||||
});
|
||||
|
||||
// Open the persona's actions menu (visible for non-builtin personas).
|
||||
const actionsBtn = page.getByRole("button", {
|
||||
name: "Open actions for Null Runtime Agent",
|
||||
});
|
||||
await expect(actionsBtn).toBeVisible({ timeout: 8_000 });
|
||||
await actionsBtn.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();
|
||||
|
||||
// Regression: the runtime trigger must not be empty — the auto-seed effect
|
||||
// must have run and selected the app default (buzz-agent in the mock catalog).
|
||||
const runtimeTrigger = dialog.locator("#persona-runtime");
|
||||
await expect(runtimeTrigger).toBeVisible({ timeout: 8_000 });
|
||||
await expect(runtimeTrigger).not.toContainText("No preference", {
|
||||
timeout: 8_000,
|
||||
});
|
||||
|
||||
// Regression: the model combobox must appear (modelFieldVisible = true once
|
||||
// runtime is non-empty) and model discovery must have run, populating it.
|
||||
const modelCombobox = dialog.getByRole("combobox", { name: /model/i });
|
||||
await expect(modelCombobox).toBeVisible({ timeout: 8_000 });
|
||||
|
||||
// Open the picker and assert that a known model from the mock discovery
|
||||
// response is listed. The mock returns "Claude Opus 4.6" for all providers
|
||||
// (see discover_agent_models in e2eBridge.ts). A zero-model discovery
|
||||
// regression would leave the list empty and this assertion would fail.
|
||||
// The picker is a searchable command popover portaled outside the dialog
|
||||
// whose items render as buttons — query at page level.
|
||||
await modelCombobox.click();
|
||||
await expect(
|
||||
page.getByRole("button", { name: /Claude Opus 4\.6/i }),
|
||||
).toBeVisible({ timeout: 5_000 });
|
||||
// Close the popover so the screenshot captures the dialog's resting state.
|
||||
await page.keyboard.press("Escape");
|
||||
await expect(
|
||||
page.getByRole("button", { name: /Claude Opus 4\.6/i }),
|
||||
).toBeHidden();
|
||||
|
||||
await waitForAnimations(page);
|
||||
await dialog.screenshot({
|
||||
path: `${SHOTS}/03-builtin-edit-runtime-seeded.png`,
|
||||
});
|
||||
});
|
||||
|
||||
test("04-codex-definition-exposes-model-without-global-provider-defaults", async ({
|
||||
page,
|
||||
}) => {
|
||||
await installMockBridge(page, {
|
||||
globalAgentConfig: {
|
||||
provider: "databricks_v2",
|
||||
model: "global-databricks-model",
|
||||
env_vars: {},
|
||||
},
|
||||
personas: [
|
||||
{
|
||||
displayName: "Codex Definition",
|
||||
systemPrompt: "A Codex-backed definition.",
|
||||
runtime: "codex",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
await page.goto("/");
|
||||
await page.getByTestId("open-agents-view").click();
|
||||
await page
|
||||
.getByRole("button", { name: "Open actions for Codex Definition" })
|
||||
.click();
|
||||
await page.getByRole("menuitem", { name: "Edit" }).click();
|
||||
|
||||
const dialog = page.getByTestId("persona-dialog");
|
||||
await expect(dialog).toBeVisible({ timeout: 10_000 });
|
||||
await expect(
|
||||
dialog.getByRole("tab", { name: "Customize for this agent" }),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
dialog.getByText("Harness default", { exact: true }),
|
||||
).toBeVisible();
|
||||
await expect(dialog.getByText(/Databricks/i)).toHaveCount(0);
|
||||
|
||||
await dialog.getByRole("tab", { name: "Customize for this agent" }).click();
|
||||
await expect(
|
||||
dialog.getByRole("combobox", { name: /model/i }),
|
||||
).toBeVisible();
|
||||
await expect(dialog.getByText("Model changes apply only")).toHaveCount(0);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user