mirror of
https://github.com/block/buzz.git
synced 2026-10-27 20:41:57 +00:00
"Create agent" button takes you to a UI to **invite** your existing agents to a channel. Rewording the button to make this clear. Before <img width="481" height="251" alt="Screenshot 2026-08-21 at 1 58 40 pm" src="https://github.com/user-attachments/assets/1b64f722-62b8-46fa-a6c4-dafcf2bdfaa7" /> After (Sorry about different elements being in hover state in the screenshots) <img width="481" height="313" alt="Screenshot 2026-08-21 at 1 58 16 pm" src="https://github.com/user-attachments/assets/f68a9c9b-2276-4ef5-ace8-c4017977b1e8" /> ## Summary - Clarify that the empty-channel intro action adds existing agents to the channel. - Assert the exact action title and description in the existing E2E coverage while preserving the separate Welcome create-agent flow. ### Related issue None found. ### Testing - `../node_modules/.bin/biome check src/features/channels/ui/useChannelIntro.tsx tests/e2e/channels.spec.ts` passed. - `./node_modules/.bin/tsc && ./node_modules/.bin/vite build --mode e2e` passed. - The isolated Playwright smoke case `empty channel shows intro actions` passed (1/1) after installing the repo-pinned Chromium. - `env -u BUZZ_AGENT_PROVIDER just ci` passed. - Screenshots not captured; this is a copy-only UI change. --------- Signed-off-by: Matt Toohey <contact@matttoohey.com>
161 lines
5.5 KiB
TypeScript
161 lines
5.5 KiB
TypeScript
import { expect, test, type Locator, type Page } from "@playwright/test";
|
|
|
|
import { installMockBridge } from "../helpers/bridge";
|
|
import { waitForAnimations } from "../helpers/animations";
|
|
|
|
const THEMES = ["buzz", "catppuccin-mocha"] as const;
|
|
|
|
async function seedTheme(page: Page, theme: (typeof THEMES)[number]) {
|
|
await page.addInitScript((value) => {
|
|
window.localStorage.setItem("buzz-theme", value);
|
|
}, theme);
|
|
}
|
|
|
|
async function expectSecondarySurface(tooltip: Locator) {
|
|
const colors = await tooltip.evaluate((element) => {
|
|
const sample = document.createElement("div");
|
|
sample.style.cssText =
|
|
"position:fixed;visibility:hidden;background:hsl(var(--secondary));color:hsl(var(--secondary-foreground))";
|
|
document.body.append(sample);
|
|
const tooltipStyle = getComputedStyle(element);
|
|
const sampleStyle = getComputedStyle(sample);
|
|
const result = {
|
|
background: tooltipStyle.backgroundColor,
|
|
color: tooltipStyle.color,
|
|
expectedBackground: sampleStyle.backgroundColor,
|
|
expectedColor: sampleStyle.color,
|
|
};
|
|
sample.remove();
|
|
return result;
|
|
});
|
|
|
|
expect(colors.background).toBe(colors.expectedBackground);
|
|
expect(colors.color).toBe(colors.expectedColor);
|
|
}
|
|
|
|
async function expectMutedSupportingText(
|
|
trigger: Locator,
|
|
expectedText: string | RegExp,
|
|
) {
|
|
await trigger.hover();
|
|
const footer = trigger
|
|
.page()
|
|
.getByRole("tooltip")
|
|
.locator('[data-buzz-tooltip-metadata-type=""]');
|
|
await expect(footer).toHaveText(expectedText);
|
|
await expect(footer).toHaveClass(/text-secondary-foreground\/80/);
|
|
await expect(footer).not.toHaveClass(/text-primary-foreground/);
|
|
}
|
|
|
|
for (const theme of THEMES) {
|
|
test(`simple and rich tooltips use the secondary surface — ${theme}`, async ({
|
|
page,
|
|
}) => {
|
|
await seedTheme(page, theme);
|
|
await installMockBridge(page, {
|
|
personas: [
|
|
{
|
|
id: "tooltip-reviewer",
|
|
displayName: "Tooltip Reviewer",
|
|
systemPrompt: "Review tooltip contrast.",
|
|
},
|
|
],
|
|
teams: [
|
|
{
|
|
id: "tooltip-team",
|
|
name: "Contrast Crew",
|
|
description: "Checks semantic surface contrast",
|
|
personaIds: ["tooltip-reviewer"],
|
|
},
|
|
],
|
|
});
|
|
await page.goto("/");
|
|
|
|
await page.getByTestId("channel-random").click();
|
|
await page.getByTestId("channel-members-trigger").hover();
|
|
const simpleTooltip = page.getByRole("tooltip");
|
|
await expect(simpleTooltip).toHaveText("Channel members");
|
|
await expectSecondarySurface(simpleTooltip);
|
|
|
|
await page.mouse.move(0, 0);
|
|
await page.getByTestId("channel-intro-action-create-agent").click();
|
|
const dialog = page.getByTestId("add-channel-bot-dialog");
|
|
await expect(dialog).toBeVisible();
|
|
await dialog.getByRole("button", { name: "Contrast Crew" }).hover();
|
|
|
|
const richTooltip = page.getByRole("tooltip");
|
|
await expect(richTooltip).toContainText("Checks semantic surface contrast");
|
|
await expect(richTooltip).toContainText("Tooltip Reviewer");
|
|
await expectSecondarySurface(richTooltip);
|
|
await expect(
|
|
richTooltip.getByText("Checks semantic surface contrast"),
|
|
).toHaveClass(/text-secondary-foreground\/80/);
|
|
await expect(richTooltip.getByText("Tooltip Reviewer")).toHaveClass(
|
|
/text-secondary-foreground/,
|
|
);
|
|
await expect(
|
|
richTooltip.getByTestId("team-tooltip-persona-chip"),
|
|
).toHaveClass(/bg-secondary-foreground\/10/);
|
|
await expect(
|
|
richTooltip.getByTestId("team-tooltip-persona-avatar"),
|
|
).toHaveClass(/bg-secondary-foreground\/20.*text-secondary-foreground/);
|
|
|
|
await waitForAnimations(page);
|
|
await dialog.screenshot({
|
|
path: `test-results/tooltip-semantics/${theme}-team-tooltip.png`,
|
|
});
|
|
});
|
|
|
|
test(`link tooltip supporting text uses the muted secondary foreground — ${theme}`, async ({
|
|
page,
|
|
}) => {
|
|
await seedTheme(page, theme);
|
|
await installMockBridge(page);
|
|
await page.goto("/");
|
|
await page.getByTestId("channel-general").click();
|
|
const channelId = "9a1657ac-f7aa-5db0-b632-d8bbeb6dfb50";
|
|
const owner = "a".repeat(64);
|
|
const issueId = "b".repeat(64);
|
|
const links = [
|
|
`buzz://channel/${channelId}`,
|
|
`buzz://message?channel=${channelId}&id=mock-general-welcome`,
|
|
`buzz://issue?id=${issueId}&owner=${owner}&d=buzz-world`,
|
|
].join(" ");
|
|
const composer = page.getByTestId("message-input");
|
|
await composer.evaluate((element, text) => {
|
|
const clipboardData = new DataTransfer();
|
|
clipboardData.setData("text/plain", text);
|
|
element.dispatchEvent(
|
|
new ClipboardEvent("paste", {
|
|
bubbles: true,
|
|
cancelable: true,
|
|
clipboardData,
|
|
}),
|
|
);
|
|
}, links);
|
|
|
|
const composerChips = composer.locator('[data-composer-buzz-link=""]');
|
|
await expect(composerChips).toHaveCount(3);
|
|
await page.getByTestId("send-message").click();
|
|
|
|
const row = page.getByTestId("message-row").last();
|
|
await expect(row).toBeVisible();
|
|
await expectMutedSupportingText(
|
|
row.getByRole("button", { name: "Open channel general" }),
|
|
/Public channel · Active (just now|\d+[mhdw] ago)/,
|
|
);
|
|
await page.mouse.move(0, 0);
|
|
await expectMutedSupportingText(
|
|
row.getByRole("button", { name: "Open message in channel general" }),
|
|
/#general · .+ · (just now|\d+[mhdw] ago)/,
|
|
);
|
|
await page.mouse.move(0, 0);
|
|
await expectMutedSupportingText(
|
|
row.getByRole("button", {
|
|
name: /Open issue .* in repository buzz-world/,
|
|
}),
|
|
"Issue · buzz-world",
|
|
);
|
|
});
|
|
}
|