Files
buzz/desktop/tests/e2e/inbox-title-overlap.spec.ts
cynfria cec5c8fd92 fix(desktop): let inbox title and message author names truncate under narrow panes (#7550)
## Summary

Fixes two instances of the same dead-truncate pattern in the desktop
app, where a flex item's implicit `min-width: auto` prevented `truncate`
from engaging, so long text painted over adjacent controls instead of
ellipsizing:

- **Inbox detail title** (`InboxDetailPane.tsx`): the clickable
context-title button sized to its text instead of shrinking with the
pane, overlapping the header controls (open-in-channel, members, huddle,
more menu). Fixed by adding `max-w-full`.
- **Message author names** (`MessageHeader.tsx` /
`UserProfilePopover.tsx`): the `UserProfilePopover` inline-flex trigger
wrapper refused to shrink below the name's nowrap width, running long
author names under the hover action bar and off the pane edge. Fixed by
adding a `triggerClassName` prop to `UserProfilePopover` and passing
`min-w-0 max-w-full` at the author call site.

Two other suspected instances (project file breadcrumb, drafts pane
title) were stress-tested and already truncate correctly — no change.

### Related issue

N/A — none found.

### Testing

- New Playwright regression tests for both fixes
(`inbox-title-overlap.spec.ts`, `message-author-overlap.spec.ts`,
registered in the smoke project), each proven to discriminate: they fail
with the fix reverted (real measured overlap) and assert the ellipsis
actually engages with non-zero title width, so they can't pass
vacuously.
- Typecheck, lint, and full desktop unit suite green (pre-push hooks);
full desktop e2e smoke suite run earlier: 1402 passed, 3 pre-existing
unrelated failures (each fails identically with the fix reverted).

**Inbox title — before** (long title paints under the header controls):

![Inbox title before: title text overlaps the header control
icons](https://github.com/user-attachments/assets/d5a10f98-114f-4466-8012-468616b82807)

**Inbox title — after** (truncates with ellipsis, controls stay clear):

![Inbox title after: title truncates with an ellipsis before the
controls](https://github.com/user-attachments/assets/80520f7c-1120-4dbc-929d-1ef5eceb874b)

**Author name — before** (long name runs past the header row edge):

![Author name before: name glyphs bleed past the action
bar](https://github.com/user-attachments/assets/be9743ae-ef51-4db0-8c46-a0649e98f0d5)

**Author name — after** (clean cutoff):

![Author name after: name truncates cleanly inside the header
row](https://github.com/user-attachments/assets/24cc0412-ca99-40c4-add6-13380b3ae065)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Signed-off-by: cynfria <yescynthia@gmail.com>
Signed-off-by: Tree Trunks <6ba22921d9dc2ad0aa6ecdf63787ddd24726e266d866da31af69f2e4e146ace5@buzz.block.builderlab.xyz>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Tree Trunks <6ba22921d9dc2ad0aa6ecdf63787ddd24726e266d866da31af69f2e4e146ace5@buzz.block.builderlab.xyz>
2026-09-10 10:00:15 -07:00

147 lines
5.0 KiB
TypeScript

/**
* Regression test: a long inbox detail title must truncate instead of
* running underneath the header controls (open-in-channel, members,
* huddle, more menu) when the detail pane is narrow.
*
* Run: pnpm build:e2e && pnpm exec playwright test --project=smoke \
* tests/e2e/inbox-title-overlap.spec.ts
* Output: test-results/inbox-title-overlap/
*/
import { expect, test } from "@playwright/test";
import type { RelayEvent } from "../../src/shared/api/types";
import { waitForAnimations } from "../helpers/animations";
import { installMockBridge, TEST_IDENTITIES } from "../helpers/bridge";
const SHOTS = "test-results/inbox-title-overlap";
const ENGINEERING_CHANNEL_ID = "1c7e1c02-87bb-5e88-b2da-5a7a9432d0c9";
type MockWindow = Window & {
__BUZZ_E2E_EMIT_MOCK_MESSAGE__?: (input: {
channelName: string;
content: string;
parentEventId?: string | null;
pubkey?: string;
mentionPubkeys?: string[];
id?: string;
}) => RelayEvent;
__BUZZ_E2E_PUSH_MOCK_FEED_ITEM__?: (item: {
category: "mention" | "needs_action" | "activity" | "agent_activity";
channel_id: string | null;
channel_name: string;
content: string;
created_at: number;
id: string;
kind: number;
pubkey: string;
tags: string[][];
}) => unknown;
};
test.describe("inbox detail title overflow", () => {
// Narrow window matching the report; the list pane is widened to its
// resizable maximum so the detail pane is squeezed to its 300px minimum
// and "Message in #engineering" cannot fit next to the header controls.
test.use({ viewport: { width: 1046, height: 838 } });
test("long title truncates instead of overlapping header controls", async ({
page,
}) => {
await page.addInitScript(() => {
window.sessionStorage.setItem(
"buzz.desktop.home-inbox-list-width",
"520",
);
});
await installMockBridge(page, { mode: "mock" });
await page.goto("/", { waitUntil: "domcontentloaded" });
await expect(page.getByTestId("home-inbox-list")).toBeVisible({
timeout: 10_000,
});
await page.waitForFunction(() => {
const win = window as MockWindow;
return (
typeof win.__BUZZ_E2E_EMIT_MOCK_MESSAGE__ === "function" &&
typeof win.__BUZZ_E2E_PUSH_MOCK_FEED_ITEM__ === "function"
);
});
const mentionId = "ab".repeat(32);
await page.evaluate(
({ channelId, currentPubkey, id, senderPubkey }) => {
const win = window as MockWindow;
const emit = win.__BUZZ_E2E_EMIT_MOCK_MESSAGE__;
const push = win.__BUZZ_E2E_PUSH_MOCK_FEED_ITEM__;
if (!emit || !push) {
throw new Error("Mock bridge helpers are not installed.");
}
const event = emit({
channelName: "engineering",
content:
"Hey, can you look at the release checklist before tomorrow?",
id,
mentionPubkeys: [currentPubkey],
pubkey: senderPubkey,
});
push({
category: "mention",
channel_id: channelId,
channel_name: "engineering",
content: event.content,
created_at: event.created_at,
id: event.id,
kind: event.kind,
pubkey: event.pubkey,
tags: event.tags,
});
},
{
channelId: ENGINEERING_CHANNEL_ID,
currentPubkey: TEST_IDENTITIES.tyler.pubkey,
id: mentionId,
senderPubkey: TEST_IDENTITIES.alice.pubkey,
},
);
const row = page.getByTestId(`home-inbox-item-${mentionId}`);
await expect(row).toBeVisible();
await row.click();
const detail = page.getByTestId("home-inbox-detail");
await expect(detail).toBeVisible();
const title = detail.getByTestId("home-inbox-context-title");
await expect(title).toHaveText("Message in #engineering");
await waitForAnimations(page);
const titleBox = await title.boundingBox();
const controlsBox = await detail
.getByTestId("home-inbox-open-context")
.boundingBox();
if (!titleBox || !controlsBox) {
throw new Error("Header title or controls not rendered.");
}
// Guard against a vacuous pass: the title must still render with real
// width, and the squeeze must actually engage — the inner span's
// ellipsis is active only when its content overflows. If layout
// constants or fonts ever change so the full title fits, this fails
// loudly instead of silently no longer testing truncation.
expect(titleBox.width).toBeGreaterThan(0);
const isTruncated = await title.evaluate((element) => {
const span = element.querySelector("span");
return span ? span.scrollWidth > span.clientWidth : false;
});
expect(isTruncated).toBe(true);
await page.screenshot({
clip: { height: 96, width: 1046, x: 0, y: 0 },
path: `${SHOTS}/01-header.png`,
});
// The title's right edge must stop short of the first header control.
expect(titleBox.x + titleBox.width).toBeLessThanOrEqual(controlsBox.x + 1);
});
});