mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
**Category:** improvement **User Impact:** Buzz channel, message, repository, pull request, and issue links now open reliably and display recognizable context in the desktop app. **Problem:** Buzz links could appear as raw or ambiguous URLs, and navigation links received during startup or community transitions could be dropped before the UI was ready. Repository and issue shares in particular required hover context to understand at a glance. **Solution:** Queue desktop channel/message navigation until the UI is ready, then render bare Buzz permalinks as icon-prefixed chips with concise entity context while preserving user-authored Markdown labels as ordinary links. <details> <summary>File changes</summary> **desktop/src-tauri/src/deep_link.rs** Adds validated channel-link parsing and a deduplicated, acknowledged queue so navigation survives frontend startup. **desktop/src-tauri/src/lib.rs** Registers the pending-navigation state and commands with the desktop application. **desktop/src/features/communities/useCommunityInit.ts** Resets queued navigation safely across community boundaries without leaking stale destinations. **desktop/src/features/messages/lib/channelLink.test.mjs** Covers valid, malformed, and canonical channel permalink forms. **desktop/src/features/messages/lib/channelLink.ts** Defines strict parsing and detection for `buzz://channel/<uuid>` links. **desktop/src/features/messages/lib/composerMessageLinkNode.test.mjs** Extends composer-node coverage for normalized Buzz link content. **desktop/src/features/messages/lib/composerMessageLinkNode.ts** Keeps composer link-node handling aligned with the expanded Buzz link surface. **desktop/src/features/messages/lib/remarkChannelDeepLinks.test.mjs** Verifies bare channel URLs become renderable deep-link nodes without touching code. **desktop/src/features/messages/lib/remarkChannelDeepLinks.ts** Transforms eligible bare channel links into dedicated Markdown nodes. **desktop/src/features/messages/lib/remarkEntityLinks.test.mjs** Covers bare repository, pull-request, and issue detection and code-span exclusions. **desktop/src/features/messages/lib/remarkEntityLinks.ts** Adds dedicated Markdown nodes for bare Buzz project entities. **desktop/src/shared/deep-link.test.mjs** Exercises queued navigation, acknowledgement, serialization, and community-switch behavior. **desktop/src/shared/deep-link.ts** Serializes pending deep-link drains and acknowledges destinations only after successful navigation. **desktop/src/shared/styles/globals/markdown.css** Aligns permalink icon geometry and spacing with agent mention chips. **desktop/src/shared/ui/markdown.test.mjs** Adds integration coverage for every permalink chip, authored labels, fallbacks, icons, and static rendering. **desktop/src/shared/ui/markdown.tsx** Routes channel and entity nodes through the shared presentation path while preserving authored link text. **desktop/src/shared/ui/markdown/BuzzLinkChip.tsx** Introduces the shared interactive/static permalink chip and authored-label inline-link components. **desktop/src/shared/ui/markdown/ChannelDeepLink.tsx** Renders channel shares and references with Hash icons, names, and shortened-ID fallbacks. **desktop/src/shared/ui/markdown/MessageLinkPill.tsx** Renders ordinary message shares with message icons and channel/message context while retaining sent-from-thread behavior. **desktop/src/shared/ui/markdown/entityLinks.tsx** Maps repositories, pull requests, and issues to Projects-aligned icons and contextual labels. **desktop/src/shared/ui/markdown/nodeCache.ts** Includes entity-link rendering in cached Markdown node handling. **desktop/src/shared/ui/markdown/utils.ts** Allows validated channel links through the Buzz URL transform. **desktop/src/shared/useMessageDeepLinks.ts** Drains queued navigation links safely and clears them during teardown. **desktop/src/testing/e2eBridge.ts** Extends the mock bridge with pending-navigation command behavior. **desktop/tests/e2e/community-rail.spec.ts** Verifies queued links do not cross community boundaries. **desktop/tests/e2e/navigation.spec.ts** Covers channel/message deep-link navigation during startup and active sessions. **desktop/tests/helpers/bridge.ts** Adds reusable deep-link mock state and acknowledgement helpers. </details> ## Reproduction steps 1. Run the desktop app and open a channel containing bare `buzz://channel`, `buzz://message`, `buzz://repo`, `buzz://pr`, and `buzz://issue` URLs. 2. Confirm each bare URL renders as one cohesive chip with a type icon, a useful name or shortened identifier, and no duplicated channel `#` character. 3. Add an authored Markdown link such as `[design discussion](buzz://issue?...)` and confirm the supplied label remains an ordinary link rather than becoming a chip. 4. Select channel and message links and confirm they navigate correctly in warm and cold-start states. ## Screenshots / demos Houston dark theme with custom purple accent (`#a855f7`), captured from rebased visual implementation `ad411cc06`; current head `0aafa144f` only adjusts E2E expectations for the visible mention-label behavior shown here. **Composer — channel, message, repository, pull request, and issue pills**  **Message list — channel, message, repository, pull request, and issue pills**  --------- Signed-off-by: Taylor Ho <taylorkmho@gmail.com> Signed-off-by: Carl <acda9e433d19dcd0e6b6840f7f4b98f3a56f1fab98049d444c087019e6d36560@buzz.block.builderlab.xyz> Co-authored-by: Carl <acda9e433d19dcd0e6b6840f7f4b98f3a56f1fab98049d444c087019e6d36560@buzz.block.builderlab.xyz>
572 lines
18 KiB
TypeScript
572 lines
18 KiB
TypeScript
import { expect, test, type Browser } from "@playwright/test";
|
|
|
|
import {
|
|
installRelayBridge,
|
|
openChannelBrowser,
|
|
openCreateChannelDialog,
|
|
TEST_IDENTITIES,
|
|
} from "../helpers/bridge";
|
|
import { openSettings } from "../helpers/settings";
|
|
import { assertRelaySeeded } from "../helpers/seed";
|
|
|
|
const isCi = Boolean(process.env.CI);
|
|
const relaySeedHookTimeoutMs = isCi ? 90_000 : 30_000;
|
|
|
|
async function createStream(
|
|
page: import("@playwright/test").Page,
|
|
channelName: string,
|
|
description?: string,
|
|
) {
|
|
await openCreateChannelDialog(page);
|
|
await page.getByTestId("create-channel-name").fill(channelName);
|
|
if (description !== undefined) {
|
|
await page.getByTestId("create-channel-description").fill(description);
|
|
}
|
|
await page.getByTestId("create-channel-submit").click();
|
|
|
|
await expect(page.getByTestId("stream-list")).toContainText(channelName);
|
|
await expect(page.getByTestId("chat-title")).toHaveText(channelName);
|
|
}
|
|
|
|
async function openChannelManagement(page: import("@playwright/test").Page) {
|
|
await page.getByTestId("channel-management-trigger").click();
|
|
await expect(page.getByTestId("channel-management-sheet")).toBeVisible();
|
|
}
|
|
|
|
async function openChannelEditDialog(page: import("@playwright/test").Page) {
|
|
await page.getByTestId("channel-management-edit").click();
|
|
await expect(
|
|
page.getByRole("dialog", {
|
|
name: /Edit (?:public|private) channel/,
|
|
}),
|
|
).toBeVisible();
|
|
}
|
|
|
|
async function closeChannelManagement(page: import("@playwright/test").Page) {
|
|
await page.getByTestId("auxiliary-panel-close").click();
|
|
await expect(page.getByTestId("channel-management-sheet")).not.toBeVisible();
|
|
}
|
|
|
|
async function assertDesktopNotificationsEnabled(
|
|
page: import("@playwright/test").Page,
|
|
) {
|
|
await openSettings(page, "notifications");
|
|
await expect(page.getByTestId("settings-notifications")).toBeVisible();
|
|
await expect(page.getByTestId("notifications-desktop-state")).toContainText(
|
|
"On",
|
|
);
|
|
await page.getByTestId("settings-back-to-app").click();
|
|
}
|
|
|
|
async function sendChannelMessage(
|
|
page: import("@playwright/test").Page,
|
|
{
|
|
channelName,
|
|
content,
|
|
kind,
|
|
mentionPubkeys,
|
|
}: {
|
|
channelName: string;
|
|
content: string;
|
|
kind?: number | null;
|
|
mentionPubkeys?: string[];
|
|
},
|
|
) {
|
|
await page.evaluate(
|
|
async ({
|
|
channelName: targetChannelName,
|
|
content,
|
|
kind,
|
|
mentionPubkeys,
|
|
}) => {
|
|
const tauriWindow = window as Window & {
|
|
__TAURI_INTERNALS__?: {
|
|
invoke: (
|
|
command: string,
|
|
payload?: Record<string, unknown>,
|
|
) => Promise<unknown>;
|
|
};
|
|
};
|
|
|
|
const invoke = tauriWindow.__TAURI_INTERNALS__?.invoke;
|
|
if (!invoke) {
|
|
throw new Error("Tauri invoke bridge is unavailable.");
|
|
}
|
|
|
|
const payload = (await invoke("get_channels")) as {
|
|
channels: Array<{ id: string; name: string }> | null;
|
|
};
|
|
const channels = payload.channels ?? [];
|
|
const channel = channels.find(({ name }) => name === targetChannelName);
|
|
if (!channel) {
|
|
throw new Error(`Channel not found: ${targetChannelName}`);
|
|
}
|
|
|
|
await invoke("send_channel_message", {
|
|
channelId: channel.id,
|
|
content,
|
|
parentEventId: null,
|
|
mediaTags: null,
|
|
mentionPubkeys: mentionPubkeys ?? null,
|
|
kind: kind ?? null,
|
|
});
|
|
},
|
|
{ channelName, content, kind, mentionPubkeys },
|
|
);
|
|
}
|
|
|
|
async function joinChannel(
|
|
page: import("@playwright/test").Page,
|
|
channelName: string,
|
|
) {
|
|
await page.evaluate(async (targetChannelName) => {
|
|
const tauriWindow = window as Window & {
|
|
__TAURI_INTERNALS__?: {
|
|
invoke: (
|
|
command: string,
|
|
payload?: Record<string, unknown>,
|
|
) => Promise<unknown>;
|
|
};
|
|
};
|
|
|
|
const invoke = tauriWindow.__TAURI_INTERNALS__?.invoke;
|
|
if (!invoke) {
|
|
throw new Error("Tauri invoke bridge is unavailable.");
|
|
}
|
|
|
|
const payload = (await invoke("get_channels")) as {
|
|
channels: Array<{ id: string; name: string }> | null;
|
|
};
|
|
const channels = payload.channels ?? [];
|
|
const channel = channels.find(({ name }) => name === targetChannelName);
|
|
if (!channel) {
|
|
throw new Error(`Channel not found: ${targetChannelName}`);
|
|
}
|
|
|
|
await invoke("join_channel", {
|
|
channelId: channel.id,
|
|
});
|
|
}, channelName);
|
|
}
|
|
|
|
async function getLoggedNotifications(page: import("@playwright/test").Page) {
|
|
return page.evaluate(() => {
|
|
const win = window as Window & {
|
|
__BUZZ_E2E_NOTIFICATIONS__?: Array<{
|
|
body: string | null;
|
|
title: string;
|
|
}>;
|
|
};
|
|
|
|
return win.__BUZZ_E2E_NOTIFICATIONS__ ?? [];
|
|
});
|
|
}
|
|
|
|
async function getLoggedNotificationCount(
|
|
page: import("@playwright/test").Page,
|
|
) {
|
|
return (await getLoggedNotifications(page)).length;
|
|
}
|
|
|
|
async function expectLoggedNotifications(
|
|
page: import("@playwright/test").Page,
|
|
expected: Array<{ body: string | null; title: string }>,
|
|
) {
|
|
await expect.poll(() => getLoggedNotifications(page)).toEqual(expected);
|
|
}
|
|
|
|
test.beforeAll(async () => {
|
|
test.setTimeout(relaySeedHookTimeoutMs);
|
|
await assertRelaySeeded();
|
|
});
|
|
|
|
test("create channel and verify in sidebar", async ({ page }) => {
|
|
const channelName = `integration-e2e-${Date.now()}`;
|
|
|
|
await installRelayBridge(page, "tyler");
|
|
await page.goto("/");
|
|
await openCreateChannelDialog(page);
|
|
await page.getByTestId("create-channel-name").fill(channelName);
|
|
await page.getByTestId("create-channel-submit").click();
|
|
|
|
await expect(page.getByTestId("stream-list")).toContainText(channelName);
|
|
await expect(page.getByTestId("chat-title")).toHaveText(channelName);
|
|
});
|
|
|
|
test("two users see the same channel", async ({
|
|
browser,
|
|
}: {
|
|
browser: Browser;
|
|
}) => {
|
|
const channelName = `shared-channel-${Date.now()}`;
|
|
const contextOne = await browser.newContext();
|
|
const contextTwo = await browser.newContext();
|
|
const pageOne = await contextOne.newPage();
|
|
const pageTwo = await contextTwo.newPage();
|
|
|
|
try {
|
|
await installRelayBridge(pageOne, "tyler");
|
|
await installRelayBridge(pageTwo, "alice");
|
|
|
|
await pageOne.goto("/");
|
|
await openCreateChannelDialog(pageOne);
|
|
await pageOne.getByTestId("create-channel-name").fill(channelName);
|
|
await pageOne.getByTestId("create-channel-submit").click();
|
|
await expect(pageOne.getByTestId("stream-list")).toContainText(channelName);
|
|
|
|
await pageTwo.goto("/");
|
|
await openChannelBrowser(pageTwo);
|
|
await expect(pageTwo.getByTestId("channel-browser-dialog")).toBeVisible();
|
|
await pageTwo
|
|
.getByTestId(`browse-channel-${channelName}`)
|
|
.getByRole("button", { name: "Join" })
|
|
.click();
|
|
await expect(pageTwo.getByTestId("stream-list")).toContainText(channelName);
|
|
} finally {
|
|
await contextOne.close();
|
|
await contextTwo.close();
|
|
}
|
|
});
|
|
|
|
test("message delivery across users", async ({
|
|
browser,
|
|
}: {
|
|
browser: Browser;
|
|
}) => {
|
|
const message = `Cross-user message ${Date.now()}`;
|
|
const contextOne = await browser.newContext();
|
|
const contextTwo = await browser.newContext();
|
|
const pageOne = await contextOne.newPage();
|
|
const pageTwo = await contextTwo.newPage();
|
|
|
|
try {
|
|
await installRelayBridge(pageOne, "tyler");
|
|
await installRelayBridge(pageTwo, "alice");
|
|
|
|
await pageOne.goto("/");
|
|
await pageTwo.goto("/");
|
|
|
|
await pageOne.getByTestId("channel-general").click();
|
|
await pageTwo.getByTestId("channel-general").click();
|
|
await expect(pageOne.getByTestId("chat-title")).toHaveText("general");
|
|
await expect(pageTwo.getByTestId("chat-title")).toHaveText("general");
|
|
|
|
await pageOne.getByTestId("message-input").fill(message);
|
|
await pageOne.getByTestId("send-message").click();
|
|
|
|
await expect(pageTwo.getByTestId("message-timeline")).toContainText(
|
|
message,
|
|
);
|
|
} finally {
|
|
await contextOne.close();
|
|
await contextTwo.close();
|
|
}
|
|
});
|
|
|
|
test("live mentions refetch the home feed without waiting for polling", async ({
|
|
browser,
|
|
}: {
|
|
browser: Browser;
|
|
}) => {
|
|
const stamp = Date.now();
|
|
const targetContext = await browser.newContext();
|
|
const senderContext = await browser.newContext();
|
|
const targetPage = await targetContext.newPage();
|
|
const senderPage = await senderContext.newPage();
|
|
|
|
try {
|
|
await installRelayBridge(targetPage, "tyler");
|
|
await installRelayBridge(senderPage, "alice");
|
|
|
|
await targetPage.goto("/");
|
|
await senderPage.goto("/");
|
|
await assertDesktopNotificationsEnabled(targetPage);
|
|
|
|
await targetPage.getByTestId("channel-general").click();
|
|
await expect(targetPage.getByTestId("chat-title")).toHaveText("general");
|
|
|
|
const message = `Heads up @tyler live mention ${stamp}`;
|
|
await sendChannelMessage(senderPage, {
|
|
channelName: "general",
|
|
content: message,
|
|
mentionPubkeys: [TEST_IDENTITIES.tyler.pubkey],
|
|
});
|
|
|
|
await expect(targetPage.getByTestId("message-timeline")).toContainText(
|
|
message,
|
|
);
|
|
|
|
await expectLoggedNotifications(targetPage, [
|
|
{
|
|
body: message,
|
|
title: "alice mentioned you in #general",
|
|
},
|
|
]);
|
|
|
|
// The Inbox feed should have been refetched live (the original purpose
|
|
// of this test). The home badge stays at 0 while the user is actively
|
|
// reading #general — reading in-channel advances the NIP-RS marker past
|
|
// the new mention — so the assertion that the refetch happened is the
|
|
// Inbox-list content, not the badge.
|
|
await targetPage
|
|
.getByTestId("app-sidebar")
|
|
.getByRole("button", { name: "Inbox" })
|
|
.click();
|
|
await expect(targetPage.getByTestId("home-inbox-list")).toBeVisible();
|
|
await expect(targetPage.getByTestId("home-inbox-list")).toContainText(
|
|
message.replace("@tyler", "tyler"),
|
|
);
|
|
await expect(targetPage.getByTestId("sidebar-home-count")).toHaveCount(0);
|
|
await expect.poll(() => getLoggedNotificationCount(targetPage)).toBe(1);
|
|
} finally {
|
|
await targetContext.close();
|
|
await senderContext.close();
|
|
}
|
|
});
|
|
|
|
test("live forum mentions refetch the home feed without waiting for polling", async ({
|
|
browser,
|
|
}: {
|
|
browser: Browser;
|
|
}) => {
|
|
const stamp = Date.now();
|
|
const targetContext = await browser.newContext();
|
|
const senderContext = await browser.newContext();
|
|
const targetPage = await targetContext.newPage();
|
|
const senderPage = await senderContext.newPage();
|
|
|
|
try {
|
|
await installRelayBridge(targetPage, "tyler");
|
|
await installRelayBridge(senderPage, "alice");
|
|
|
|
await targetPage.goto("/");
|
|
await senderPage.goto("/");
|
|
await assertDesktopNotificationsEnabled(targetPage);
|
|
|
|
await targetPage.getByTestId("channel-general").click();
|
|
await expect(targetPage.getByTestId("chat-title")).toHaveText("general");
|
|
await joinChannel(senderPage, "watercooler");
|
|
|
|
const message = `Forum ping @tyler ${stamp}`;
|
|
await sendChannelMessage(senderPage, {
|
|
channelName: "watercooler",
|
|
content: message,
|
|
kind: 45001,
|
|
mentionPubkeys: [TEST_IDENTITIES.tyler.pubkey],
|
|
});
|
|
|
|
await expect(targetPage.getByTestId("sidebar-home-count")).toHaveText("1");
|
|
|
|
await expectLoggedNotifications(targetPage, [
|
|
{
|
|
body: message,
|
|
title: "alice mentioned you in #watercooler",
|
|
},
|
|
]);
|
|
|
|
await targetPage
|
|
.getByTestId("app-sidebar")
|
|
.getByRole("button", { name: "Inbox" })
|
|
.click();
|
|
await expect(targetPage.getByTestId("home-inbox-list")).toBeVisible();
|
|
await expect(targetPage.getByTestId("home-inbox-list")).toBeVisible();
|
|
await expect(targetPage.getByTestId("home-inbox-list")).toContainText(
|
|
message.replace("@tyler", "tyler"),
|
|
);
|
|
await expect(targetPage.getByTestId("sidebar-home-count")).toHaveCount(0);
|
|
await expect.poll(() => getLoggedNotificationCount(targetPage)).toBe(1);
|
|
} finally {
|
|
await targetContext.close();
|
|
await senderContext.close();
|
|
}
|
|
});
|
|
|
|
test("DM channel appears in sidebar", async ({ page }) => {
|
|
await installRelayBridge(page, "tyler");
|
|
await page.goto("/");
|
|
|
|
await expect(page.getByTestId("dm-list")).toContainText("alice-tyler");
|
|
});
|
|
|
|
test("send message to DM", async ({ page }) => {
|
|
const message = `DM message ${Date.now()}`;
|
|
|
|
await installRelayBridge(page, "tyler");
|
|
await page.goto("/");
|
|
await page.getByTestId("channel-alice-tyler").click();
|
|
await expect(page.getByTestId("chat-title")).toHaveText("alice-tyler");
|
|
|
|
await page.getByTestId("message-input").fill(message);
|
|
await page.getByTestId("send-message").click();
|
|
|
|
await expect(page.getByTestId("message-timeline")).toContainText(message);
|
|
});
|
|
|
|
test("forum channel appears in sidebar", async ({ page }) => {
|
|
await installRelayBridge(page, "tyler");
|
|
await page.goto("/");
|
|
|
|
await expect(page.getByTestId("forum-list")).toContainText("watercooler");
|
|
});
|
|
|
|
test("create channel with description", async ({ page }) => {
|
|
const channelName = `desc-channel-${Date.now()}`;
|
|
const description = `Description for ${channelName}`;
|
|
|
|
await installRelayBridge(page, "tyler");
|
|
await page.goto("/");
|
|
await createStream(page, channelName, description);
|
|
|
|
await expect(page.getByTestId("chat-title")).toHaveAttribute(
|
|
"title",
|
|
description,
|
|
);
|
|
});
|
|
|
|
test("multiple channels independent", async ({ page }) => {
|
|
const channelA = `channel-a-${Date.now()}`;
|
|
const channelB = `channel-b-${Date.now()}`;
|
|
const messageA = `Message in A ${Date.now()}`;
|
|
|
|
await installRelayBridge(page, "tyler");
|
|
await page.goto("/");
|
|
|
|
await openCreateChannelDialog(page);
|
|
await page.getByTestId("create-channel-name").fill(channelA);
|
|
await page.getByTestId("create-channel-submit").click();
|
|
await expect(page.getByTestId("chat-title")).toHaveText(channelA);
|
|
|
|
await openCreateChannelDialog(page);
|
|
await page.getByTestId("create-channel-name").fill(channelB);
|
|
await page.getByTestId("create-channel-submit").click();
|
|
await expect(page.getByTestId("chat-title")).toHaveText(channelB);
|
|
|
|
// Navigate to channel A and send a message
|
|
await page.getByTestId(`channel-${channelA}`).click();
|
|
await expect(page.getByTestId("chat-title")).toHaveText(channelA);
|
|
await page.getByTestId("message-input").fill(messageA);
|
|
await page.getByTestId("send-message").click();
|
|
await expect(page.getByTestId("message-timeline")).toContainText(messageA);
|
|
|
|
// Switch to channel B — message from A should not appear
|
|
await page.getByTestId(`channel-${channelB}`).click();
|
|
await expect(page.getByTestId("chat-title")).toHaveText(channelB);
|
|
await expect(page.getByTestId("message-timeline")).not.toContainText(
|
|
messageA,
|
|
);
|
|
});
|
|
|
|
test("manage sheet updates channel details through the relay", async ({
|
|
page,
|
|
}) => {
|
|
const stamp = Date.now();
|
|
const initialName = `manage-integration-${stamp}`;
|
|
const renamedChannel = `manage-renamed-${stamp}`;
|
|
const initialDescription = `Initial description ${stamp}`;
|
|
const updatedDescription = `Updated description ${stamp}`;
|
|
|
|
await installRelayBridge(page, "tyler");
|
|
await page.goto("/");
|
|
await createStream(page, initialName, initialDescription);
|
|
|
|
await openChannelManagement(page);
|
|
await openChannelEditDialog(page);
|
|
const editDialog = page.getByRole("dialog", {
|
|
name: /Edit (?:public|private) channel/,
|
|
});
|
|
|
|
await editDialog.getByTestId("channel-management-name").fill(renamedChannel);
|
|
await editDialog
|
|
.getByTestId("channel-management-description")
|
|
.fill(updatedDescription);
|
|
await expect(editDialog.getByTestId("channel-management-topic")).toHaveCount(
|
|
0,
|
|
);
|
|
await expect(
|
|
editDialog.getByTestId("channel-management-purpose"),
|
|
).toHaveCount(0);
|
|
await editDialog.getByTestId("channel-management-save-changes").click();
|
|
await expect(editDialog).toHaveCount(0);
|
|
|
|
await expect(page.getByTestId("chat-title")).toHaveText(renamedChannel);
|
|
await expect(page.getByTestId("stream-list")).toContainText(renamedChannel);
|
|
|
|
await closeChannelManagement(page);
|
|
await page.reload();
|
|
|
|
await page.getByTestId(`channel-${renamedChannel}`).click();
|
|
await expect(page.getByTestId("chat-title")).toHaveText(renamedChannel);
|
|
await expect(page.getByTestId("chat-title")).toHaveAttribute(
|
|
"title",
|
|
updatedDescription,
|
|
);
|
|
|
|
await openChannelManagement(page);
|
|
await openChannelEditDialog(page);
|
|
const reopenedEditDialog = page.getByRole("dialog", {
|
|
name: /Edit (?:public|private) channel/,
|
|
});
|
|
|
|
await expect(
|
|
reopenedEditDialog.getByTestId("channel-management-name"),
|
|
).toHaveValue(renamedChannel);
|
|
await expect(
|
|
reopenedEditDialog.getByTestId("channel-management-description"),
|
|
).toHaveValue(updatedDescription);
|
|
await expect(
|
|
reopenedEditDialog.getByTestId("channel-management-topic"),
|
|
).toHaveCount(0);
|
|
await expect(
|
|
reopenedEditDialog.getByTestId("channel-management-purpose"),
|
|
).toHaveCount(0);
|
|
});
|
|
|
|
test("manage sheet archive and unarchive survives a reload through the relay", async ({
|
|
page,
|
|
}) => {
|
|
const channelName = `archive-integration-${Date.now()}`;
|
|
|
|
await installRelayBridge(page, "tyler");
|
|
await page.goto("/");
|
|
await createStream(page, channelName, "Archive integration channel");
|
|
|
|
await openChannelManagement(page);
|
|
await page.getByTestId("channel-management-archive").click();
|
|
await expect(page.getByTestId("channel-management-unarchive")).toBeVisible();
|
|
await closeChannelManagement(page);
|
|
|
|
await expect(page.getByTestId("stream-list")).not.toContainText(channelName);
|
|
await expect(page.getByTestId("message-input")).toHaveAttribute(
|
|
"contenteditable",
|
|
"false",
|
|
);
|
|
await expect(page.getByTestId("send-message")).toBeDisabled();
|
|
|
|
await page.reload();
|
|
|
|
await expect(page.getByTestId("stream-list")).not.toContainText(channelName);
|
|
await openChannelBrowser(page);
|
|
await expect(page.getByTestId("channel-browser-dialog")).toBeVisible();
|
|
await expect(page.getByTestId(`browse-channel-${channelName}`)).toContainText(
|
|
"archived",
|
|
);
|
|
await page.getByTestId(`browse-channel-${channelName}`).click();
|
|
await expect(page.getByTestId("channel-browser-dialog")).not.toBeVisible();
|
|
await expect(page.getByTestId("chat-title")).toHaveText(channelName);
|
|
await expect(page.getByTestId("message-input")).toHaveAttribute(
|
|
"contenteditable",
|
|
"false",
|
|
);
|
|
|
|
await openChannelManagement(page);
|
|
await page.getByTestId("channel-management-unarchive").click();
|
|
await expect(page.getByTestId("channel-management-archive")).toBeVisible();
|
|
await closeChannelManagement(page);
|
|
|
|
await expect(page.getByTestId("stream-list")).toContainText(channelName);
|
|
await expect(page.getByTestId("message-input")).toHaveAttribute(
|
|
"contenteditable",
|
|
"true",
|
|
);
|
|
});
|