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,175 @@
|
||||
import { expect, test } from "@playwright/test";
|
||||
import type { Page } from "@playwright/test";
|
||||
|
||||
import { waitForAnimations } from "../helpers/animations";
|
||||
import { installMockBridge } from "../helpers/bridge";
|
||||
|
||||
const SHOTS = "test-results/channel-sort";
|
||||
|
||||
// Mock-mode current-user pubkey and relay (see e2eBridge DEFAULT_MOCK_PUBKEY /
|
||||
// DEFAULT_RELAY_WS_URL). Sort preferences persist under the relay-scoped key
|
||||
// buzz-channel-sort.v1:<pubkey>:<encoded-relay>.
|
||||
const MOCK_PUBKEY = "deadbeef".repeat(8);
|
||||
const MOCK_RELAY_ENCODED = encodeURIComponent("ws://localhost:3000");
|
||||
const SORT_STORAGE_KEY = `buzz-channel-sort.v1:${MOCK_PUBKEY}:${MOCK_RELAY_ENCODED}`;
|
||||
|
||||
function seedSortState(page: Page, groups: Record<string, string>) {
|
||||
return page.addInitScript(
|
||||
({ key, groups }) => {
|
||||
window.localStorage.setItem(key, JSON.stringify({ version: 1, groups }));
|
||||
},
|
||||
{ key: SORT_STORAGE_KEY, groups },
|
||||
);
|
||||
}
|
||||
|
||||
async function openApp(page: Page) {
|
||||
await page.goto("/");
|
||||
await page.getByTestId("channel-general").click();
|
||||
await expect(page.getByTestId("chat-title")).toHaveText("general");
|
||||
}
|
||||
|
||||
function streamNames(page: Page) {
|
||||
return page
|
||||
.getByTestId("stream-list")
|
||||
.locator("[data-testid^='channel-']")
|
||||
.evaluateAll((nodes) =>
|
||||
nodes
|
||||
.map((n) => n.getAttribute("data-testid") ?? "")
|
||||
.filter(
|
||||
(id) =>
|
||||
!id.startsWith("channel-unread") &&
|
||||
!id.startsWith("channel-working") &&
|
||||
!id.startsWith("channel-dm-count"),
|
||||
)
|
||||
.map((id) => id.replace(/^channel-/, "")),
|
||||
);
|
||||
}
|
||||
|
||||
test.describe("per-group channel sort", () => {
|
||||
test("01 — Channels group defaults to A–Z", async ({ page }) => {
|
||||
await installMockBridge(page);
|
||||
await openApp(page);
|
||||
|
||||
const names = await streamNames(page);
|
||||
const sorted = [...names].sort((a, b) => a.localeCompare(b));
|
||||
expect(names).toEqual(sorted);
|
||||
});
|
||||
|
||||
test("02 — sort trigger switches Channels to Recent and persists", async ({
|
||||
page,
|
||||
}) => {
|
||||
await installMockBridge(page);
|
||||
await openApp(page);
|
||||
|
||||
// Hover the Channels header to reveal the action cluster, then open the
|
||||
// sort dropdown and choose Recent.
|
||||
const streamList = page.getByTestId("stream-list");
|
||||
await expect(streamList).toBeVisible();
|
||||
await page.getByText("Channels", { exact: true }).hover();
|
||||
const trigger = page.getByTestId("section-actions-channels");
|
||||
await expect(trigger).toBeVisible();
|
||||
await waitForAnimations(page);
|
||||
await page.screenshot({ path: `${SHOTS}/01-channels-sort-ingress.png` });
|
||||
await trigger.click();
|
||||
// Sort is now a submenu flyout — open it before the radio items render.
|
||||
await page.getByRole("menuitem", { name: "Sort" }).click();
|
||||
await expect(
|
||||
page.getByRole("menuitemradio", { name: "Recent" }),
|
||||
).toBeVisible();
|
||||
await waitForAnimations(page);
|
||||
await page.screenshot({ path: `${SHOTS}/02-channels-sort-open.png` });
|
||||
await page.getByRole("menuitemradio", { name: "Recent" }).click();
|
||||
|
||||
// Mock recency: all-replies (far future) > deep-history (1m) > general
|
||||
// (5m) > agents (15m) > sales (30m) > engineering (42m) > design (120m),
|
||||
// then no-activity channels alphabetically. The list is virtualized, so
|
||||
// only assert on the rendered prefix.
|
||||
await expect
|
||||
.poll(async () => (await streamNames(page)).slice(0, 3))
|
||||
.toEqual(["all-replies", "deep-history", "general"]);
|
||||
const names = await streamNames(page);
|
||||
const recencyOrder = [
|
||||
"all-replies",
|
||||
"deep-history",
|
||||
"general",
|
||||
"agents",
|
||||
"sales",
|
||||
"engineering",
|
||||
"design",
|
||||
];
|
||||
const rendered = recencyOrder.filter((n) => names.includes(n));
|
||||
expect(names.slice(0, rendered.length)).toEqual(rendered);
|
||||
await waitForAnimations(page);
|
||||
await page.screenshot({ path: `${SHOTS}/03-channels-recent.png` });
|
||||
|
||||
// Persisted for this identity.
|
||||
const stored = await page.evaluate((key) => {
|
||||
return JSON.parse(window.localStorage.getItem(key) ?? "null");
|
||||
}, SORT_STORAGE_KEY);
|
||||
expect(stored).toEqual({ version: 1, groups: { channels: "recent" } });
|
||||
|
||||
// Survives reload.
|
||||
await page.reload();
|
||||
await expect(page.getByTestId("stream-list")).toBeVisible();
|
||||
await expect
|
||||
.poll(async () => (await streamNames(page)).slice(0, 2))
|
||||
.toEqual(["all-replies", "deep-history"]);
|
||||
});
|
||||
|
||||
test("03 — group preferences are independent (seeded Channels=recent leaves Forums A–Z)", async ({
|
||||
page,
|
||||
}) => {
|
||||
await seedSortState(page, { channels: "recent" });
|
||||
await installMockBridge(page);
|
||||
await openApp(page);
|
||||
|
||||
// Channels reflects the seeded Recent order…
|
||||
await expect
|
||||
.poll(async () => (await streamNames(page)).slice(0, 2))
|
||||
.toEqual(["all-replies", "deep-history"]);
|
||||
|
||||
// …while Forums (unset) stays alphabetical.
|
||||
const forumNames = await page
|
||||
.getByTestId("forum-list")
|
||||
.locator("[data-testid^='channel-']")
|
||||
.evaluateAll((nodes) =>
|
||||
nodes
|
||||
.map((n) => n.getAttribute("data-testid") ?? "")
|
||||
.filter(
|
||||
(id) =>
|
||||
!id.startsWith("channel-unread") &&
|
||||
!id.startsWith("channel-working"),
|
||||
)
|
||||
.map((id) => id.replace(/^channel-/, "")),
|
||||
);
|
||||
const sortedForums = [...forumNames].sort((a, b) => a.localeCompare(b));
|
||||
expect(forumNames).toEqual(sortedForums);
|
||||
await waitForAnimations(page);
|
||||
await page.screenshot({ path: `${SHOTS}/04-independent-groups.png` });
|
||||
});
|
||||
|
||||
test("04 — DM group has its own sort trigger", async ({ page }) => {
|
||||
await installMockBridge(page);
|
||||
await openApp(page);
|
||||
|
||||
const dmList = page.getByTestId("dm-list");
|
||||
await expect(dmList).toBeVisible();
|
||||
await page.getByText("Direct messages", { exact: true }).hover();
|
||||
const trigger = page.getByTestId("section-actions-dms");
|
||||
await expect(trigger).toBeVisible();
|
||||
await trigger.click();
|
||||
// Sort is now a submenu flyout — open it before the radio items render.
|
||||
await page.getByRole("menuitem", { name: "Sort" }).click();
|
||||
await expect(
|
||||
page.getByRole("menuitemradio", { name: "A–Z" }),
|
||||
).toBeVisible();
|
||||
await waitForAnimations(page);
|
||||
await page.screenshot({ path: `${SHOTS}/05-dm-sort-open.png` });
|
||||
await page.getByRole("menuitemradio", { name: "Recent" }).click();
|
||||
|
||||
const stored = await page.evaluate((key) => {
|
||||
return JSON.parse(window.localStorage.getItem(key) ?? "null");
|
||||
}, SORT_STORAGE_KEY);
|
||||
expect(stored).toEqual({ version: 1, groups: { dms: "recent" } });
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user