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,234 @@
import { expect, test } from "@playwright/test";
import { installMockBridge } from "../helpers/bridge";
const RELAY_UNREACHABLE = "relay unreachable: connection refused";
// Minimal teal 8×8 PNG as a data URL — satisfies avatarDataUrl's data:image/ guard.
const MOCK_AVATAR_DATA_URL =
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAE0lEQVR4nGOQ2rrsPz7MMDIUAACluJ0BkoZ9dQAAAABJRU5ErkJggg==";
// Self-profile cache key: buzz-self-profile.v1:<relay>:<pubkey>
const MOCK_PUBKEY = "deadbeef".repeat(8);
const MOCK_RELAY_URL = "ws://localhost:3000";
const SELF_PROFILE_CACHE_KEY = `buzz-self-profile.v1:${MOCK_RELAY_URL}:${MOCK_PUBKEY}`;
async function settle(page: import("@playwright/test").Page) {
await page.evaluate(() =>
// Tolerate cancelled animations: a SkeletonReveal animation cancelled
// mid-flight (skeleton → live content swap) rejects `.finished` with an
// AbortError. allSettled lets the animations that DO finish settle instead
// of aborting the whole wait on the first cancel.
Promise.allSettled(document.getAnimations().map((a) => a.finished)),
);
}
type ConnectionState =
| "idle"
| "connecting"
| "connected"
| "reconnecting"
| "stalled"
| "disconnected";
/** Directly drive the relay client singleton state via the E2E test seam. */
async function driveConnectionDegraded(
page: import("@playwright/test").Page,
state: ConnectionState = "reconnecting",
) {
if (state !== "connected") {
// When driving to a degraded state, wait for the relay to reach "connected"
// first. Without this guard, the mock relay's async auth handshake can race
// the state override and write "connected" back over it.
await page.waitForFunction(() => {
const win = window as Window & {
__BUZZ_E2E_GET_RELAY_CONNECTION_STATE__?: () => string;
__BUZZ_E2E_SET_RELAY_CONNECTION_STATE__?: unknown;
};
return (
typeof win.__BUZZ_E2E_SET_RELAY_CONNECTION_STATE__ === "function" &&
typeof win.__BUZZ_E2E_GET_RELAY_CONNECTION_STATE__ === "function" &&
win.__BUZZ_E2E_GET_RELAY_CONNECTION_STATE__() === "connected"
);
});
} else {
// When driving to "connected", just wait for the seam to be installed —
// no need to gate on current state since we're overriding it directly.
await page.waitForFunction(
() =>
typeof (
window as Window & {
__BUZZ_E2E_SET_RELAY_CONNECTION_STATE__?: unknown;
}
).__BUZZ_E2E_SET_RELAY_CONNECTION_STATE__ === "function",
);
}
await page.evaluate((s) => {
const setter = (
window as Window & {
__BUZZ_E2E_SET_RELAY_CONNECTION_STATE__?: (state: string) => void;
}
).__BUZZ_E2E_SET_RELAY_CONNECTION_STATE__;
if (!setter) throw new Error("E2E relay state setter not installed.");
setter(s);
}, state);
}
test.describe("relay connectivity", () => {
test("01 — sidebar unreachable card", async ({ page }) => {
await installMockBridge(page, { channelsReadError: RELAY_UNREACHABLE });
await page.goto("/");
// Wait for the E2E seam to be installed (bridge init is complete), then force
// the relay into a degraded state. The card is gated on !isRelayConnectionConnected:
// a stale channelsReadError alone (with state still "connected") no longer pins
// the card — connected state is now authoritative so stale query errors don't
// hold the UI open after an auto-reconnect.
//
// Use "reconnecting" (rather than "disconnected"): the card shows via
// isRelayConnectionStateDegraded for reconnecting/stalled states, so the test
// doesn't race the channels-query retry window. useRelayConnection debounces
// non-healthy states by 2 s, so await with timeout 5 s.
await page.waitForFunction(
() =>
typeof (
window as Window & {
__BUZZ_E2E_SET_RELAY_CONNECTION_STATE__?: unknown;
}
).__BUZZ_E2E_SET_RELAY_CONNECTION_STATE__ === "function",
);
await driveConnectionDegraded(page, "reconnecting");
const relayCard = page.getByTestId("sidebar-relay-unreachable");
await expect(relayCard).toBeVisible({
timeout: 5_000,
});
await expect(relayCard).toContainText("Can't reach the relay");
await expect(relayCard).toContainText("Click to connect");
await expect(page.getByTestId("sidebar-reconnect")).toBeVisible();
await settle(page);
// Clip to sidebar width (256px) so the card and channel list are both visible.
});
test("02 — sidebar reconnect card while reconnecting", async ({ page }) => {
await installMockBridge(page);
await page.goto("/");
// Wait for a healthy boot, then force the relay into "reconnecting" state.
await expect(page.getByTestId("channel-general")).toBeVisible();
await driveConnectionDegraded(page);
// useRelayConnection debounces non-healthy states by 2 s before surfacing.
const relayCard = page.getByTestId("sidebar-relay-unreachable");
await expect(relayCard).toBeVisible({
timeout: 5_000,
});
await expect(relayCard).toContainText("Can't reach the relay");
await expect(relayCard).toContainText("Click to connect");
await expect(page.getByTestId("sidebar-reconnect")).toBeVisible();
await settle(page);
// Clip to the sidebar, where degraded relay state is now surfaced.
});
test("03 — canvas unreachable in management sheet", async ({ page }) => {
await installMockBridge(page, { canvasReadError: RELAY_UNREACHABLE });
await page.goto("/");
await page.getByTestId("channel-general").click();
await expect(page.getByTestId("chat-title")).toHaveText("general");
await page.getByTestId("channel-management-trigger").click();
await expect(page.getByTestId("channel-management-sheet")).toBeVisible();
await page.getByTestId("channel-canvas-ingress").click();
// ChannelCanvas shows the destructive error paragraph in the drill-in view
// when the query fails.
const canvasSection = page.getByTestId("channel-canvas-section");
await canvasSection.scrollIntoViewIfNeeded();
await expect(
canvasSection.getByText("Can't reach the relay."),
).toBeVisible();
// Await Radix sheet animations before measuring the settled state.
const sheet = page.getByTestId("channel-management-sheet");
await sheet.evaluate((el) =>
Promise.all(
el
.closest("[data-state]")
?.getAnimations()
.map((a) => a.finished) ?? [],
),
);
await settle(page);
// Capture the whole sheet so the error renders in its Canvas-section context.
});
test("04 — cached identity shown offline (avatar + display name)", async ({
page,
}) => {
// Seed the self-profile cache BEFORE installMockBridge so addInitScript
// runs in order and the cache is present when React mounts.
await page.addInitScript(
({ key, cache }) => {
window.localStorage.setItem(key, JSON.stringify(cache));
},
{
key: SELF_PROFILE_CACHE_KEY,
cache: {
version: 1,
displayName: "Tyler Durden",
avatarUrl: "http://localhost:3999/avatar.png",
avatarDataUrl: MOCK_AVATAR_DATA_URL,
updatedAt: 1_700_000_000_000,
},
},
);
await installMockBridge(page, { profileReadError: RELAY_UNREACHABLE });
await page.goto("/");
// The profile card should show the cached display name.
const profileCard = page.getByTestId("sidebar-profile-card");
await expect(profileCard).toContainText("Tyler Durden");
await settle(page);
});
test("05 — no-cache npub fallback when offline", async ({ page }) => {
// No cache seeded — profile card falls back to the mock identity npub name.
await installMockBridge(page, { profileReadError: RELAY_UNREACHABLE });
await page.goto("/");
const profileCard = page.getByTestId("sidebar-profile-card");
// Default mock identity display name is "npub1mock...".
await expect(profileCard).toContainText("npub1mock");
await settle(page);
});
test("06 — sidebar card shows connected after external relay recovery", async ({
page,
}) => {
await installMockBridge(page);
await page.goto("/");
await expect(page.getByTestId("channel-general")).toBeVisible();
await driveConnectionDegraded(page);
const relayCard = page.getByTestId("sidebar-relay-unreachable");
await expect(relayCard).toBeVisible({
timeout: 5_000,
});
await expect(relayCard).toContainText("Can't reach the relay");
await expect(relayCard).toContainText("Click to connect");
await driveConnectionDegraded(page, "connected");
await expect(relayCard).toContainText("Connected");
await expect(relayCard).not.toContainText("Click to connect");
await page.waitForTimeout(3_000);
await expect(relayCard).toContainText("Connected");
await expect(relayCard).toBeHidden({
timeout: 5_000,
});
});
});