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,415 @@
|
||||
import { expect, test } from "@playwright/test";
|
||||
|
||||
import { installMockBridge, TEST_IDENTITIES } from "../helpers/bridge";
|
||||
import { seedActiveIdentity } from "../helpers/onboarding";
|
||||
|
||||
// Community deep links that arrive before machine onboarding complete are
|
||||
// drained from Rust into a persisted transaction and acknowledged immediately.
|
||||
// Invite claiming waits until setup finishes and the final identity is known.
|
||||
|
||||
const DEFAULT_MOCK_PUBKEY = "deadbeef".repeat(8);
|
||||
const COMMUNITY_ONBOARDING_PUBKEY = TEST_IDENTITIES.tyler.pubkey;
|
||||
const TRANSACTION_STORAGE_KEY = "buzz-community-onboarding-transaction.v1";
|
||||
const COMMUNITY_RELAY_URL = "wss://hive.example.com";
|
||||
|
||||
const PENDING_JOIN_LINK = {
|
||||
id: "dl-join-1",
|
||||
kind: "join" as const,
|
||||
relayUrl: "wss://hive.example.com",
|
||||
code: "abc.def",
|
||||
};
|
||||
|
||||
const PENDING_CONNECT_LINK = {
|
||||
id: "dl-connect-1",
|
||||
kind: "connect" as const,
|
||||
relayUrl: "wss://hive.example.com",
|
||||
code: null,
|
||||
};
|
||||
|
||||
const PENDING_ADD_COMMUNITY_LINK = {
|
||||
id: "dl-add-community-1",
|
||||
kind: "add-community" as const,
|
||||
relayUrl: "wss://acme.communities.buzz.xyz",
|
||||
code: null,
|
||||
name: "Acme Team",
|
||||
};
|
||||
|
||||
const SECOND_PENDING_ADD_COMMUNITY_LINK = {
|
||||
id: "dl-add-community-2",
|
||||
kind: "add-community" as const,
|
||||
relayUrl: "wss://beta.communities.buzz.xyz",
|
||||
code: null,
|
||||
name: "Beta Team",
|
||||
};
|
||||
|
||||
test("join deep link is acknowledged without claiming before setup", async ({
|
||||
page,
|
||||
}) => {
|
||||
let claimCalls = 0;
|
||||
await page.route("**/api/invites/claim", async (route) => {
|
||||
claimCalls++;
|
||||
await route.abort();
|
||||
});
|
||||
await installMockBridge(
|
||||
page,
|
||||
{ pendingCommunityDeepLinks: [PENDING_JOIN_LINK] },
|
||||
{ skipCommunitySeed: true, skipOnboardingSeed: true },
|
||||
);
|
||||
await page.goto("/");
|
||||
|
||||
const gate = page.getByTestId("pending-invite-gate");
|
||||
await expect(gate).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole("heading", { name: "Opening community link" }),
|
||||
).toBeVisible();
|
||||
await page.getByTestId("pending-invite-continue").click();
|
||||
await expect(gate).toHaveCount(0);
|
||||
await expect(page.getByTestId("machine-onboarding-gate")).toBeVisible();
|
||||
expect(claimCalls).toBe(0);
|
||||
await expect
|
||||
.poll(() =>
|
||||
page.evaluate(
|
||||
(key) => window.localStorage.getItem(key),
|
||||
TRANSACTION_STORAGE_KEY,
|
||||
),
|
||||
)
|
||||
.toContain('"stage":"claiming"');
|
||||
});
|
||||
|
||||
test("connect deep link shows a static acknowledgment during setup", async ({
|
||||
page,
|
||||
}) => {
|
||||
// No invite code means nothing to confirm against the relay — the gate
|
||||
// acknowledges the link and waits for the user instead of auto-advancing.
|
||||
await installMockBridge(
|
||||
page,
|
||||
{ pendingCommunityDeepLinks: [PENDING_CONNECT_LINK] },
|
||||
{ skipCommunitySeed: true, skipOnboardingSeed: true },
|
||||
);
|
||||
await page.goto("/");
|
||||
|
||||
const gate = page.getByTestId("pending-invite-gate");
|
||||
await expect(gate).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole("heading", { name: "Opening community link" }),
|
||||
).toBeVisible();
|
||||
await expect(gate).toContainText("hive");
|
||||
|
||||
// Continue setup dismisses the gate but keeps the transaction: the
|
||||
// connect resumes in CommunityOnboardingFlow after machine setup.
|
||||
await page.getByTestId("pending-invite-continue").click();
|
||||
await expect(gate).toHaveCount(0);
|
||||
await expect(page.getByTestId("machine-onboarding-gate")).toBeVisible();
|
||||
await expect
|
||||
.poll(() =>
|
||||
page.evaluate(
|
||||
(key) => window.localStorage.getItem(key),
|
||||
TRANSACTION_STORAGE_KEY,
|
||||
),
|
||||
)
|
||||
.toContain('"acknowledged":true');
|
||||
});
|
||||
|
||||
test("add-community deep link starts onboarding when no community is configured", async ({
|
||||
page,
|
||||
}) => {
|
||||
// profileReadError forces the fallback path (error → profile step), so the
|
||||
// test asserts pre-existing-profile behavior without the default mock
|
||||
// identity's has_profile_event:true triggering the skip.
|
||||
await installMockBridge(
|
||||
page,
|
||||
{
|
||||
pendingCommunityDeepLinks: [PENDING_ADD_COMMUNITY_LINK],
|
||||
profileReadError: "no-kind-0",
|
||||
},
|
||||
{ skipCommunitySeed: true },
|
||||
);
|
||||
await page.goto("/");
|
||||
|
||||
await expect(page.getByTestId("community-onboarding-flow")).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole("heading", { name: "Build your profile" }),
|
||||
).toBeVisible();
|
||||
await expect
|
||||
.poll(() =>
|
||||
page.evaluate(
|
||||
(key) => window.localStorage.getItem(key),
|
||||
TRANSACTION_STORAGE_KEY,
|
||||
),
|
||||
)
|
||||
.toContain('"source":"add-community"');
|
||||
await expect
|
||||
.poll(() =>
|
||||
page.evaluate(
|
||||
(key) => window.localStorage.getItem(key),
|
||||
TRANSACTION_STORAGE_KEY,
|
||||
),
|
||||
)
|
||||
.toContain('"communityName":"Acme Team"');
|
||||
});
|
||||
|
||||
test("add-community deep link skips profile step when identity has an existing kind:0 profile", async ({
|
||||
page,
|
||||
}) => {
|
||||
// The default mock identity (deadbeef...) is pre-seeded with
|
||||
// has_profile_event:true. The skip should fire on connecting → clear the
|
||||
// transaction entirely, never showing the profile step.
|
||||
await installMockBridge(
|
||||
page,
|
||||
{ pendingCommunityDeepLinks: [PENDING_ADD_COMMUNITY_LINK] },
|
||||
{ skipCommunitySeed: true },
|
||||
);
|
||||
await page.goto("/");
|
||||
|
||||
// Onboarding flow must disappear — the skip cleared the transaction.
|
||||
await expect(page.getByTestId("community-onboarding-flow")).toHaveCount(0);
|
||||
// handleCommunityOnboardingConnect already added the community when the
|
||||
// transaction reached "connecting", so the app lands in the full UI.
|
||||
await expect(page.getByTestId("sidebar-profile-avatar-button")).toBeVisible();
|
||||
});
|
||||
|
||||
test("add-community deep link opens one editable prefill and acknowledges the queue", async ({
|
||||
page,
|
||||
}) => {
|
||||
await installMockBridge(
|
||||
page,
|
||||
{ pendingCommunityDeepLinks: [PENDING_ADD_COMMUNITY_LINK] },
|
||||
{ seedPreviewFeatures: true },
|
||||
);
|
||||
await page.goto("/");
|
||||
|
||||
await expect(
|
||||
page.getByRole("heading", { name: "Join an existing community" }),
|
||||
).toBeVisible();
|
||||
const communityInput = page.getByLabel("Community URL or invite link");
|
||||
await expect(communityInput).toHaveValue(PENDING_ADD_COMMUNITY_LINK.relayUrl);
|
||||
await expect(page.getByLabel("Name")).toHaveCount(0);
|
||||
|
||||
await page.getByRole("button", { name: "Close" }).click();
|
||||
await expect(
|
||||
page.getByRole("heading", { name: "Join an existing community" }),
|
||||
).toHaveCount(0);
|
||||
|
||||
await page.getByTestId("sidebar-profile-avatar-button").click();
|
||||
await page.getByTestId("community-switcher").click();
|
||||
await page.getByRole("menuitem", { name: "Add a community" }).click();
|
||||
await page.getByTestId("add-community-join").click();
|
||||
await expect(communityInput).toHaveValue("");
|
||||
|
||||
const acknowledgements = await page.evaluate(() =>
|
||||
(window.__BUZZ_E2E_COMMAND_LOG__ ?? []).filter(
|
||||
(entry) => entry.command === "acknowledge_pending_community_deep_link",
|
||||
),
|
||||
);
|
||||
expect(acknowledgements).toEqual([
|
||||
{
|
||||
command: "acknowledge_pending_community_deep_link",
|
||||
payload: { id: PENDING_ADD_COMMUNITY_LINK.id },
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test("queued add-community links open and acknowledge one at a time", async ({
|
||||
page,
|
||||
}) => {
|
||||
await installMockBridge(
|
||||
page,
|
||||
{
|
||||
pendingCommunityDeepLinks: [
|
||||
PENDING_ADD_COMMUNITY_LINK,
|
||||
SECOND_PENDING_ADD_COMMUNITY_LINK,
|
||||
],
|
||||
},
|
||||
{ seedPreviewFeatures: true },
|
||||
);
|
||||
await page.goto("/");
|
||||
|
||||
const communityInput = page.getByLabel("Community URL or invite link");
|
||||
await expect(communityInput).toHaveValue(PENDING_ADD_COMMUNITY_LINK.relayUrl);
|
||||
|
||||
await expect
|
||||
.poll(() =>
|
||||
page.evaluate(() =>
|
||||
(window.__BUZZ_E2E_COMMAND_LOG__ ?? [])
|
||||
.filter(
|
||||
(entry) =>
|
||||
entry.command === "acknowledge_pending_community_deep_link",
|
||||
)
|
||||
.map((entry) => entry.payload),
|
||||
),
|
||||
)
|
||||
.toEqual([{ id: PENDING_ADD_COMMUNITY_LINK.id }]);
|
||||
|
||||
await page.getByRole("button", { name: "Close" }).click();
|
||||
|
||||
await expect(communityInput).toHaveValue(
|
||||
SECOND_PENDING_ADD_COMMUNITY_LINK.relayUrl,
|
||||
);
|
||||
await expect
|
||||
.poll(() =>
|
||||
page.evaluate(() =>
|
||||
(window.__BUZZ_E2E_COMMAND_LOG__ ?? [])
|
||||
.filter(
|
||||
(entry) =>
|
||||
entry.command === "acknowledge_pending_community_deep_link",
|
||||
)
|
||||
.map((entry) => entry.payload),
|
||||
),
|
||||
)
|
||||
.toEqual([
|
||||
{ id: PENDING_ADD_COMMUNITY_LINK.id },
|
||||
{ id: SECOND_PENDING_ADD_COMMUNITY_LINK.id },
|
||||
]);
|
||||
});
|
||||
|
||||
test("deleted public starter channels do not strand community onboarding", async ({
|
||||
page,
|
||||
}) => {
|
||||
const starterError =
|
||||
"starter channels created but metadata not yet available";
|
||||
await seedActiveIdentity(page, TEST_IDENTITIES.tyler);
|
||||
await page.addInitScript(
|
||||
({ pubkey, relayUrl, storageKey }) => {
|
||||
window.localStorage.setItem(
|
||||
`buzz-machine-onboarding-complete.v2:${pubkey}`,
|
||||
"true",
|
||||
);
|
||||
const timestamp = new Date().toISOString();
|
||||
window.localStorage.setItem(
|
||||
storageKey,
|
||||
JSON.stringify({
|
||||
id: "txn-deleted-starters-1",
|
||||
source: "deep-link-join",
|
||||
stage: "team-intro",
|
||||
relayUrl,
|
||||
communityName: "hive",
|
||||
communityId: "e2e-default-community",
|
||||
createdAt: timestamp,
|
||||
updatedAt: timestamp,
|
||||
}),
|
||||
);
|
||||
},
|
||||
{
|
||||
pubkey: COMMUNITY_ONBOARDING_PUBKEY,
|
||||
relayUrl: COMMUNITY_RELAY_URL,
|
||||
storageKey: TRANSACTION_STORAGE_KEY,
|
||||
},
|
||||
);
|
||||
await installMockBridge(
|
||||
page,
|
||||
{ ensureStarterChannelsErrors: [starterError] },
|
||||
{ relayWsUrl: COMMUNITY_RELAY_URL, skipOnboardingSeed: true },
|
||||
);
|
||||
await page.goto("/");
|
||||
|
||||
await page.getByRole("button", { name: "Take me to Buzz" }).click();
|
||||
|
||||
await expect(page.getByTestId("community-onboarding-flow")).toHaveCount(0);
|
||||
await expect(page).toHaveURL(/#\/channels\/[^/]+$/);
|
||||
await expect(page.getByTestId("chat-title")).toContainText("Welcome");
|
||||
await expect(page.getByText(starterError)).toHaveCount(0);
|
||||
expect(
|
||||
await page.evaluate(
|
||||
() =>
|
||||
window.__BUZZ_E2E_COMMANDS__?.filter(
|
||||
(command) => command === "ensure_starter_channels",
|
||||
).length ?? 0,
|
||||
),
|
||||
).toBe(1);
|
||||
});
|
||||
|
||||
test("required Welcome creation failure keeps community onboarding open", async ({
|
||||
page,
|
||||
}) => {
|
||||
const welcomeError = "Channel creation is not permitted.";
|
||||
await seedActiveIdentity(page, TEST_IDENTITIES.tyler);
|
||||
await page.addInitScript(
|
||||
({ pubkey, relayUrl, storageKey }) => {
|
||||
window.localStorage.setItem(
|
||||
`buzz-machine-onboarding-complete.v2:${pubkey}`,
|
||||
"true",
|
||||
);
|
||||
const timestamp = new Date().toISOString();
|
||||
window.localStorage.setItem(
|
||||
storageKey,
|
||||
JSON.stringify({
|
||||
id: "txn-welcome-failure-1",
|
||||
source: "deep-link-join",
|
||||
stage: "team-intro",
|
||||
relayUrl,
|
||||
communityName: "hive",
|
||||
communityId: "e2e-default-community",
|
||||
createdAt: timestamp,
|
||||
updatedAt: timestamp,
|
||||
}),
|
||||
);
|
||||
},
|
||||
{
|
||||
pubkey: COMMUNITY_ONBOARDING_PUBKEY,
|
||||
relayUrl: COMMUNITY_RELAY_URL,
|
||||
storageKey: TRANSACTION_STORAGE_KEY,
|
||||
},
|
||||
);
|
||||
await installMockBridge(
|
||||
page,
|
||||
{ createChannelErrors: [welcomeError] },
|
||||
{ relayWsUrl: COMMUNITY_RELAY_URL, skipOnboardingSeed: true },
|
||||
);
|
||||
await page.goto("/");
|
||||
|
||||
await page.getByRole("button", { name: "Take me to Buzz" }).click();
|
||||
|
||||
await expect(page.getByTestId("community-onboarding-flow")).toBeVisible();
|
||||
await expect(page.getByText(`${welcomeError} Try again.`)).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole("button", { name: "Take me to Buzz" }),
|
||||
).toBeEnabled();
|
||||
await expect(page.getByTestId("chat-title")).toHaveCount(0);
|
||||
});
|
||||
|
||||
test("persisted deep-link invite hands off to Joining after machine onboarding", async ({
|
||||
page,
|
||||
}) => {
|
||||
// Deterministic claim failure (no real relay behind the mock bridge): the
|
||||
// spec asserts the handoff reaches the "Joining …" claiming screen, not
|
||||
// that the claim itself succeeds.
|
||||
await page.route("**/api/invites/claim", (route) => route.abort());
|
||||
await page.addInitScript(
|
||||
({ pubkey, storageKey }) => {
|
||||
window.localStorage.setItem(
|
||||
`buzz-machine-onboarding-complete.v2:${pubkey}`,
|
||||
"true",
|
||||
);
|
||||
const timestamp = new Date().toISOString();
|
||||
window.localStorage.setItem(
|
||||
storageKey,
|
||||
JSON.stringify({
|
||||
id: "txn-deep-link-1",
|
||||
source: "deep-link-join",
|
||||
stage: "claiming",
|
||||
relayUrl: "wss://hive.example.com",
|
||||
inviteCode: "abc.def",
|
||||
communityName: "hive",
|
||||
createdAt: timestamp,
|
||||
updatedAt: timestamp,
|
||||
}),
|
||||
);
|
||||
},
|
||||
{ pubkey: DEFAULT_MOCK_PUBKEY, storageKey: TRANSACTION_STORAGE_KEY },
|
||||
);
|
||||
await installMockBridge(page, undefined, {
|
||||
skipCommunitySeed: true,
|
||||
skipOnboardingSeed: true,
|
||||
});
|
||||
await page.goto("/");
|
||||
|
||||
// Machine onboarding is complete, so the transaction owns the screen.
|
||||
await expect(page.getByTestId("community-onboarding-flow")).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole("heading", { name: "Joining hive" }),
|
||||
).toBeVisible();
|
||||
await expect(page.getByTestId("pending-invite-gate")).toHaveCount(0);
|
||||
|
||||
// The claim was attempted and its failure surfaced with a Retry.
|
||||
await expect(page.getByRole("button", { name: "Retry" })).toBeVisible();
|
||||
});
|
||||
Reference in New Issue
Block a user