Files
buzz/desktop/tests/e2e/entity-link-recipient-cards.spec.ts
Wes c213d90d5c fix(desktop): bound startup request bursts and recover quota refusals (#7790)
Prepared by Brain on Wes’s behalf.

## Summary

Reduce startup request amplification and let quota-refused background
work recover without changing relay quotas or bypassing the existing
Send gate.

- Coalesce nest regeneration into one worker plus the latest pending
generation. Keep the stale-write fence; bound the archive read so an old
workspace cannot strand newer work.
- Claim pending channel subscriptions before awaiting setup; cancel
removed/unmounted entries and preserve still-owned subscriptions across
reconnect handoff.
- Recheck extended cooldown deadlines before retry dispatch; distinguish
an explicit zero-second hint from a missing hint.
- Recover native finite/unread queries within their original caller
deadline and a three-retry budget, preserving verified unique events,
complete filters and terminal errors.
- Pace renderer initial/live-CLOSED REQs at one per 250ms: visible
channel and explicitly interactive TTS/read-state/observer owners first,
then live-only auxiliaries, then background history. Publication and
finite queries bypass this queue. Reconnect replay now promotes explicit
interactive owners alongside the visible channel with stable
registration-order ties; its existing capped batches and cooldown remain
intact.

**Scope at `867495426`:** 39 source/test files, including 15 production
files (+665/-235) and 24 test/fixture files. The diagnostic recorder,
HTTP instrumentation, trace launcher and raw captures are deliberately
excluded. No profile reset, grouping, quota increase or new global
scheduler.

## Before / after evidence

These are observations from instrumented, human-operated development
Tauri/Vite runs against a production relay, plus explicitly identified
controlled regressions—not a packaged-release startup benchmark.

| Measurement | Before | After | Evidence boundary |
| --- | --- | --- | --- |
| Archive snapshot requests (kind 13535) | 290 | 3 | Recorder-only
baseline → nest-only intermediate candidate; unequal 67.436s / 39.273s
captures and hydration differences |
| Archive responses: HTTP 200 / 429 | 277 / 13 | 3 / 0 | Same
baseline/nest-only captures; not a claim that all quota pressure
disappeared |
| Successful archive responses’ advertised content-length sum |
21,208,505 bytes | 229,695 bytes | Header values, not actual transferred
bytes |
| 154-channel StrictMode replay | 308 REQs | 154 REQs | Controlled real
React hook → real session → mocked Tauri transport regression |
| Add one channel while 154 setups are pending | 309 REQs | 155 REQs |
Same production-boundary regression; ordinary mount remains 154 → 154 |
| Broad renderer request burst / spacing | 110 broad REQs within
10.766ms in pre-drain capture | 250.061ms minimum / 252.131ms median
between broad REQ writes in final longer capture | Different attended
runs; pacing rather than a universal request-rate cap |
| Native unread query outcomes | Baseline: 8 EOSE / 146 quota CLOSED out
of 154 queries | Final longer run: all 156 observed queries reach EOSE;
eight refused queries each recover after two refusals | Different
runs/workloads; query completion, not badge rendering |

### Timing metrics

| Observation | Measured value |
| --- | ---: |
| Baseline’s two accepted chat EVENT socket writes → positive OK |
173.340ms / 85.556ms |
| Nest-only intermediate candidate’s two accepted writes → positive OK |
277.854ms / 78.775ms |
| Final candidate, short run, accepted chat writes → positive OK |
80.636ms / 77.506ms |
| Final candidate, longer run, accepted chat writes → positive OK |
120.712ms / 86.895ms |
| Last initial EOSE among 163 observed broad channel registrations,
longer run | 64.550s after trace start |
| Last EOSE among 156 observed native unread queries, longer run |
73.972s after trace start |
| Native retry waits after 3s then 0s hints, longer run |
3000.7–3002.8ms, then 2001.5–2002.6ms |
| Renderer finite-query retry after a 2s hint, longer run | 2061.365ms |

Both Sends in each final run were accepted, with no recorded negative
OK. Every quota-refused query in the longer run subsequently completed
the same query. Its two Sends happened early (17.878s and 21.655s), not
near the end. These small samples do not support a percentage latency
improvement.

**Tradeoffs and limits**
- 296 background subscriptions imply about 74s of paced registration
before extra cooldowns/high-priority work. The longer capture observed
163 registrations; it did not independently enumerate the expected
membership set or record a queue-empty marker.
- This is not a global 4-REQ/s cap: finite queries, native requests and
reconnect replay are outside the drain and still share relay quota.
Quota refusals recover rather than disappear.
- Socket-write → OK starts after composer submission and any publisher
gate wait. No click-to-visible, launch-to-usable or badge-paint latency
is claimed.
- Native EOSE is not proof of command return, classification, React
state application or visible unread badges. Existing per-channel kinds,
since floors, limits, h-less routing, unread lease/scope guard and
eight-query concurrency remain intact.
- The attended measurements belong to the instrumented candidate, not a
fresh release run of this fixes-only commit. At the initial packaging
checkpoint (`1bf3d060`), eighteen of twenty files were byte-identical;
the other two removed diagnostic gate calls and isolated the nest test’s
temporary paths. The subsequent interactive priority/reconnect and
readiness fixes have controlled hook/session/browser evidence, not
another attended voice/performance run.

### Evidence provenance (captures retained locally, not uploaded)

| Capture | SHA-256 |
| --- | --- |
| Baseline `STARTUP_20260921T194814Z_98811.jsonl` |
`f4f0a89ad9aea91084bd6a7432510488166de4cbc906600d88c26d557f380994` |
| Nest-only `STARTUP_20260921T202610Z_28642.jsonl` |
`ce517b02a3f717c36d96b36bb8616e8fa80b03b12b356bacfd40475925ea3f3e` |
| Final short `STARTUP_20260922T013628Z_13506.jsonl` |
`bfea6f3cf80cd7b7e4e676632a3df2f16271fade5bfa0edec72b7bcbbe7839ff` |
| Final longer `STARTUP_20260922T014637Z_15846.jsonl` |
`f4abece613d108c4c2fe3cb808e633a0486bdc9993ca92323822dc00c3fae6ec` |

Final instrumented build fingerprint:
`b0b4fc27d793c3e643f4e10289b5afdb4620ee2e13a8b33fa0595dd3417fd5ca`. All
observed dropped-record counters in these final captures were zero.
Source manifests and offline correlation analyses are retained locally;
no raw captures, profile data or agent sessions are part of this PR.

## Validation

Current head: `86749542657a60abdfe82eed5cc576d3e162fe5d`, based on
`77729abfb692b25a0f4ec4a69add86af2e32c0dd`.

Review follow-ups:
- `8842dbd8`: explicit interactive TTS admission using the existing
main-window owner, independent of companion/timeline selection.
Real-hook/session/IPC coverage with 296 cold owners proves initial and
CLOSED priority, unchanged filters/cooldown/pacing, background progress
and deduplicated speech.
- `8cb088106`: reconnect preserves interactive priority even with no
visible channel. Stable ordering among foreground ties; real
hook/session reconnect regressions cover cooldown, capped batching, all
297 owners, deduplication and unmount cancellation. Independent reviewer
mutation restored the prior replay implementation, failed both reconnect
rows, then passed the restored candidate.
- `68e76d7c`: read-state and observer reuse interactive priority. Stop
and ModelPicker wait on the existing observer owner inside send
callbacks, after installing result listeners/deadlines. No new owner,
timer or scheduler; inherited CLOSED/fallback/error semantics remain,
rather than a new strict EOSE guarantee. The real Stop panel browser
probe holds observer IPC across the click and checks
observer-before-control order and correlated outcome. ModelPicker
component ordering is source-reviewed only.
- Mock live admission now replays matching stored history with
per-filter time/limit constraints, deduplication, untagged auxiliary
channel fallback and authoritative explicit h-tags. Kind-specific
fixture admission waits and retry-resolving hover actions fix
startup-dependent test setup without weakening event/badge assertions.
- `48e9c0dad` and `867495426`: steady-state scroll-prepend setup
observes live admission, subsequent head dispatch, successful/idle query
completion and initial bottom geometry. Phase-2 genuine-growth and ≤32px
anchor assertions are unchanged. This isolates steady-state prepending:
it **does not fix or certify production early-scroll/head-reset
behavior**.

Current validation:
- Normal push hooks at `867495426`: **6,582/6,582 renderer tests**, zero
failed/skipped/cancelled; lint/static checks, typecheck and differential
file-size gate passed. Native source is unchanged from the earlier
checkpoint; no new native run is claimed for these renderer/test
follow-ups.
- E2E build passed at `48e9c0dad` plus the exact subsequently committed
test-only delta (spec blob `469be2eed868c677ba80b859cc871208000fee60`).
The completed affected-smoke run passed **153/153** across
agent-control, badge, entity links, messaging, profile status and scroll
history; the full scroll file additionally passed twice (**36/36**). No
full CI-smoke pass is claimed from this six-file local run.
- Pinky independently reviewed production and test follow-ups, including
the final scroll spec blob `469be2eed868c677ba80b859cc871208000fee60`,
with no blocking finding. Review is source evidence, not an independent
test run.
- **Not merge-ready:** new-head required CI must complete, exact-range
security review requires authorization, and GitHub currently reports
REVIEW_REQUIRED; fresh exact-head reviewer approval is still needed. No
approval, dismissal or merge performed. No new packaged/native audio,
launch-to-usable or badge-paint observation is claimed.

Initial fixes-only checkpoint validation
(`1bf3d06024d284a63326fdd353baced0842aeb81`):

- Pre-commit formatting hooks passed; worktree clean.
- Renderer package hooks: 6,570 tests passed, zero
failed/skipped/cancelled; lint/static checks, TypeScript and
differential file-size gate passed.
- Native package hook passed: default and mesh-enabled all-target
Clippy, then the complete native workspace test suite. App library:
3,222 passed / 0 failed / 19 ignored; terminal/integration suites
passed. The real nest trigger/stalled-body test passed on macOS.
- Independent whole-change source review: no blocking findings at this
commit; all 20 file hashes matched the committed blobs and
review/validation manifests.
- Regression coverage includes real hook/session cancellation,
StrictMode/membership re-entry, reconnect ownership, extended/zero
cooldowns, burst pacing, auxiliary readiness (including EOSE before IPC
settles), native socket refusal/retry/deadline/cancellation, unread
fetch/classification and real nest trigger/HTTP/file persistence.
- Earlier isolated renderer mutations that started readiness before
enqueue or removed auxiliary priority each failed the real presence
regression; restored controls passed. No native mutation claim.
- Nest fixture uses explicit app-data/nest paths instead of assuming
Windows known-folder APIs obey HOME/APPDATA. Windows runtime execution
is not claimed; CI/build results remain separate.

Originating conversation:
buzz://message?channel=46ced308-71df-495d-a144-4ebf08334703&id=bc43c3422a3b0b2141268d0fa149b898421c824992b662aed8b73c4eee50404f

No approval or merge recommendation is implied by opening this PR.

---------

Signed-off-by: Brain <1a02c72794dcd0f07058a353bc3a81f4028b8c77c92c87fce6d5c8b85970a20b@buzz.block.builderlab.xyz>
Co-authored-by: Brain <1a02c72794dcd0f07058a353bc3a81f4028b8c77c92c87fce6d5c8b85970a20b@buzz.block.builderlab.xyz>
2026-09-22 18:31:44 +00:00

754 lines
27 KiB
TypeScript

import { expect, test } from "@playwright/test";
import { waitForAnimations } from "../helpers/animations";
import {
installBridge,
installMockBridge,
TEST_IDENTITIES,
} from "../helpers/bridge";
const SHOTS = "test-results/entity-link-recipient-cards";
// Regression coverage for Buzz-native entity links after standalone cards were
// removed. Raw and authored entity links keep their inline navigation and
// relay-backed metadata tooltips, while external sender snapshots still render.
const ALICE_PUBKEY = TEST_IDENTITIES.alice.pubkey;
const DEFAULT_MOCK_PUBKEY = "deadbeef".repeat(8);
const REPO_ADDRESS = `30617:${ALICE_PUBKEY}:relay-tools`;
const PR_ID = `e0${"ca4d".repeat(15)}ff`; // 64-hex event id
const PR_SUBJECT = "Restore recipient-side entity cards";
const ISSUE_ID = `f0${"1a2b".repeat(15)}ee`; // 64-hex event id
const ISSUE_SUBJECT =
"Smoke test: issue tracking on relay-tools with a deliberately long title";
const EXTERNAL_HREF = "https://example.com/entity-chip-control";
const RELAY_ORIGIN = "http://localhost:3000";
const CLONE_HREF = `${RELAY_ORIGIN}/git/${ALICE_PUBKEY}/relay-tools.git`;
test("agent-style Buzz links stay chip-only with metadata tooltips", async ({
page,
}) => {
await page.addInitScript(
({ repoAddress, prId, issueId, alicePubkey, prSubject, issueSubject }) => {
const createdAt = Math.floor(Date.now() / 1000) - 60;
window.__BUZZ_E2E_EXTRA_PROJECT_EVENTS__ = [
{
id: prId,
kind: 1618, // KIND_GIT_PULL_REQUEST
pubkey: alicePubkey,
created_at: createdAt,
content: "PR body",
tags: [
["a", repoAddress],
["subject", prSubject],
["c", "abc123".padEnd(40, "0")],
["branch-name", "fix/entity-cards"],
["clone", "https://github.com/block/relay-tools.git"],
],
},
{
id: issueId,
kind: 1621, // KIND_GIT_ISSUE
pubkey: alicePubkey,
created_at: createdAt,
content: "Issue body",
tags: [
["a", repoAddress],
["subject", issueSubject],
],
},
];
},
{
repoAddress: REPO_ADDRESS,
prId: PR_ID,
issueId: ISSUE_ID,
alicePubkey: ALICE_PUBKEY,
prSubject: PR_SUBJECT,
issueSubject: ISSUE_SUBJECT,
},
);
await installMockBridge(page);
await page.setViewportSize({ width: 900, height: 800 });
await page.goto("/", { waitUntil: "domcontentloaded" });
await page.getByTestId("channel-general").click();
await page.waitForFunction(
() => typeof window.__BUZZ_E2E_EMIT_MOCK_MESSAGE__ === "function",
);
// Simulate an agent/CLI sender: plain kind-9 message with angle-bracket
// buzz:// URLs in a Markdown list and NO link-preview snapshot tags.
await page.evaluate(
({ prId, issueId, alicePubkey, externalHref, cloneHref }) => {
window.__BUZZ_E2E_EMIT_MOCK_MESSAGE__?.({
channelName: "general",
pubkey: alicePubkey,
content: [
"PR is up — review when you can:",
"",
`- Pull request with enough leading context to wrap: <buzz://pr?id=${prId}&owner=${alicePubkey}&d=relay-tools>`,
`- Issue: <buzz://issue?id=${issueId}&owner=${alicePubkey}&d=relay-tools>`,
`- Repository: <buzz://repo?owner=${alicePubkey}&d=relay-tools>`,
`- Labeled issue: [triage this issue](buzz://issue?id=${issueId}&owner=${alicePubkey}&d=relay-tools)`,
`- Labeled clone: [clone relay-tools](${cloneHref})`,
`- Missing repo: <buzz://repo?owner=${alicePubkey}&d=missing-repo>`,
`- External control: <${externalHref}>`,
].join("\n"),
extraTags: [
[
"link-preview",
"snapshot",
"1",
externalHref,
"External preview survives",
"Example",
"Sender-authored external metadata",
"",
"",
"",
"",
],
],
});
},
{
prId: PR_ID,
issueId: ISSUE_ID,
alicePubkey: ALICE_PUBKEY,
externalHref: EXTERNAL_HREF,
cloneHref: CLONE_HREF,
},
);
const row = page
.getByTestId("message-row")
.filter({ hasText: "PR is up" })
.last();
await expect(row).toBeVisible();
await expect(row.locator('[data-link-preview^="buzz-"]')).toHaveCount(0);
const externalCard = row.locator('[data-link-preview="generic-link"]');
await expect(externalCard).toBeVisible();
await expect(externalCard).toContainText("External preview survives");
const prChip = row.getByRole("button", {
name: /Open pull request .* in repository relay-tools/,
});
await expect(prChip).toHaveAccessibleName(
`Open pull request ${PR_ID.slice(0, 8)} in repository relay-tools: relay-tools · ${PR_SUBJECT}`,
);
await expect(prChip).not.toHaveAttribute("title");
await expect(prChip).toHaveClass(/wrapping-inline-chip/);
await expect(prChip).toHaveCSS("display", "inline");
await expect(prChip.locator(".truncate")).toHaveCount(0);
await expect(prChip).toHaveText("relay-tools");
await expect(prChip).not.toContainText(PR_SUBJECT);
await expect(prChip).not.toContainText(PR_ID.slice(0, 8));
// Fetched subjects stay out of the inline chip, so metadata resolution cannot
// resize the surrounding message.
await prChip.hover();
const prTooltip = page.getByRole("tooltip");
const prContext = prTooltip.locator(
'[data-buzz-tooltip-metadata-content=""]',
);
await expect(prContext).toHaveText(PR_SUBJECT);
await expect(prContext).toHaveClass(/line-clamp-3/);
await expect(prContext).toHaveCSS("overflow-wrap", "anywhere");
const prFooter = prTooltip.locator('[data-buzz-tooltip-metadata-type=""]');
await expect(prFooter).toHaveText("Pull request · relay-tools");
await expect(prFooter).toHaveCSS("overflow-wrap", "anywhere");
await expect(prFooter).toHaveCSS("white-space", "normal");
const tooltipSemanticColors = await prTooltip.evaluate((element) => {
const styles = getComputedStyle(element);
const probe = document.createElement("span");
probe.style.backgroundColor = "hsl(var(--popover))";
probe.style.color = "hsl(var(--popover-foreground))";
document.body.append(probe);
const semanticStyles = getComputedStyle(probe);
const result = {
actual: [styles.backgroundColor, styles.color],
expected: [semanticStyles.backgroundColor, semanticStyles.color],
};
probe.remove();
return result;
});
expect(tooltipSemanticColors.actual).toEqual(tooltipSemanticColors.expected);
await expect(prChip).toHaveText("relay-tools");
const issueChip = row.locator('[data-buzz-link-kind="issue"]');
// The issue chip is the repository name alone — resolved metadata never
// reaches the inline label, so it neither absorbs the title nor falls back
// to the event hash.
await expect(issueChip).toHaveText("relay-tools");
await expect(issueChip).toHaveAccessibleName(
`Open issue ${ISSUE_ID.slice(0, 8)} in repository relay-tools: relay-tools · ${ISSUE_SUBJECT}`,
);
await expect(issueChip).not.toContainText(ISSUE_SUBJECT);
await expect(issueChip).not.toContainText(ISSUE_ID.slice(0, 8));
await expect(issueChip).toHaveClass(/wrapping-inline-chip/);
await expect(issueChip).toHaveCSS("display", "inline");
await expect(issueChip.locator(".truncate")).toHaveCount(0);
await issueChip.hover();
const issueTooltip = page.getByRole("tooltip");
const issueContext = issueTooltip.locator(
'[data-buzz-tooltip-metadata-content=""]',
);
await expect(issueContext).toHaveText(ISSUE_SUBJECT);
await expect(issueContext).toHaveClass(/line-clamp-3/);
await expect(issueContext).toHaveCSS("overflow-wrap", "anywhere");
await expect(issueContext).toHaveCSS("white-space", "normal");
await expect
.poll(() =>
issueContext.evaluate(
(element) => element.scrollHeight <= element.clientHeight,
),
)
.toBe(true);
await expect(
issueTooltip.locator('[data-buzz-tooltip-metadata-type=""]'),
).toHaveText("Issue · relay-tools");
const labeledIssue = row
.getByRole("button", { name: /Open issue .* in repository relay-tools:/ })
.filter({ hasText: "triage this issue" });
await expect(labeledIssue).toBeVisible();
await labeledIssue.hover();
await expect(
page
.getByRole("tooltip")
.locator('[data-buzz-tooltip-metadata-content=""]'),
).toHaveText(ISSUE_SUBJECT);
// Repository metadata remains available from its inline chip.
const repoChip = row.getByRole("button", {
name: "Open repository relay-tools",
exact: true,
});
await repoChip.hover();
const repoTooltip = page.getByRole("tooltip");
await expect(
repoTooltip.locator('[data-buzz-tooltip-metadata-content=""]'),
).toContainText("Operator tooling and admin CLI for relay deployments.");
await expect(
repoTooltip.locator('[data-buzz-tooltip-metadata-type=""]'),
).toHaveText("Repository");
const labeledClone = row
.getByRole("button", { name: "Open repository relay-tools" })
.filter({ hasText: "clone relay-tools" });
await expect(labeledClone).toBeVisible();
await expect(labeledClone).toHaveClass(/underline/);
await expect(labeledClone).not.toHaveClass(/wrapping-inline-chip/);
await expect(labeledClone).toHaveAttribute("title", CLONE_HREF);
await labeledClone.hover();
const cloneTooltip = page.getByRole("tooltip");
await expect(
cloneTooltip.locator('[data-buzz-tooltip-metadata-content=""]'),
).toContainText("Operator tooling and admin CLI for relay deployments.");
await labeledClone.click();
await expect(page.locator("[data-project-detail-screen]")).toBeVisible();
await page.getByTestId("channel-general").click();
const missingRepoChip = row.getByRole("button", {
name: "Open repository missing-repo",
});
await expect(missingRepoChip).not.toHaveClass(/buzz-link-unavailable/);
const missingRepoColors = await missingRepoChip.evaluate((element) => {
const styles = getComputedStyle(element);
const probe = document.createElement("span");
probe.style.backgroundColor = "hsl(var(--primary) / 0.15)";
probe.style.color = "hsl(var(--primary))";
document.body.append(probe);
const semanticStyles = getComputedStyle(probe);
const result = {
actual: [styles.backgroundColor, styles.color],
expected: [semanticStyles.backgroundColor, semanticStyles.color],
};
probe.remove();
return result;
});
expect(missingRepoColors.actual).toEqual(missingRepoColors.expected);
// Definitive metadata misses keep the tooltip's byline without repeating
// the chip's stable identity.
await missingRepoChip.hover();
const missingRepoTooltip = page.getByRole("tooltip");
await expect(
missingRepoTooltip.locator('[data-buzz-tooltip-metadata-content=""]'),
).toHaveCount(0);
const missingRepoFooter = missingRepoTooltip.locator(
'[data-buzz-tooltip-metadata-type=""]',
);
await expect(missingRepoFooter).toHaveText("Repository");
await expect(missingRepoFooter).toHaveClass(/text-secondary-foreground\/80/);
await expect(missingRepoFooter).not.toHaveClass(/text-primary-foreground/);
await waitForAnimations(page);
await page.screenshot({
animations: "disabled",
path: `${SHOTS}/01-recipient-entity-chips.png`,
});
});
test("issue chip width is metadata-independent while the title loads", async ({
page,
}) => {
await page.addInitScript(
({ repoAddress, issueId, alicePubkey, issueSubject }) => {
window.__BUZZ_E2E_EXTRA_PROJECT_EVENTS__ = [
{
id: issueId,
kind: 1621, // KIND_GIT_ISSUE
pubkey: alicePubkey,
created_at: Math.floor(Date.now() / 1000) - 60,
content: "Issue body",
tags: [
["a", repoAddress],
["subject", issueSubject],
],
},
];
},
{
repoAddress: REPO_ADDRESS,
issueId: ISSUE_ID,
alicePubkey: ALICE_PUBKEY,
issueSubject: ISSUE_SUBJECT,
},
);
await installMockBridge(page);
await page.goto("/", { waitUntil: "domcontentloaded" });
await page.getByTestId("channel-general").click();
// No relay rate limit here: a rate-limited entity fetch caches a negative
// result and never recovers, which would hide the resolved-title half of
// this invariant. Natural relay latency supplies the pending window.
await page.waitForFunction(
() => typeof window.__BUZZ_E2E_EMIT_MOCK_MESSAGE__ === "function",
);
await page.evaluate(
({ issueId, alicePubkey }) => {
window.__BUZZ_E2E_EMIT_MOCK_MESSAGE__?.({
channelName: "general",
pubkey: alicePubkey,
content: `Issue link: buzz://issue?id=${issueId}&owner=${alicePubkey}&d=relay-tools`,
});
},
{ issueId: ISSUE_ID, alicePubkey: ALICE_PUBKEY },
);
const issueChip = page.getByRole("button", {
name: /Open issue .* in repository relay-tools/,
});
await expect(issueChip).toHaveText("relay-tools");
await issueChip.hover();
const tooltipContent = page
.getByRole("tooltip")
.locator('[data-buzz-tooltip-metadata-content=""]');
const widths = new Set<number>();
const tooltipSamples: string[] = [];
await expect
.poll(
async () => {
const box = await issueChip.boundingBox();
if (box) widths.add(Math.round(box.width));
const text = (await tooltipContent.count())
? ((await tooltipContent.textContent()) ?? "")
: "";
tooltipSamples.push(text);
return text;
},
{ timeout: 15_000 },
)
.toBe(ISSUE_SUBJECT);
// At least one sample predates the resolved title, so the widths below span
// the load transition rather than only its settled end.
expect(tooltipSamples.length).toBeGreaterThan(1);
expect(tooltipSamples[0]).not.toBe(ISSUE_SUBJECT);
// One width the whole way, and the label never left the repository name.
expect(Array.from(widths)).toHaveLength(1);
await expect(issueChip).toHaveText("relay-tools");
});
test("entity tooltip uses project context while relay metadata is delayed", async ({
page,
}) => {
await installMockBridge(page);
await page.goto("/", { waitUntil: "domcontentloaded" });
await page.getByTestId("channel-general").click();
await page.waitForFunction(() =>
window.__BUZZ_E2E_HAS_MOCK_LIVE_SUBSCRIPTION__?.({
channelName: "general",
kind: 39005,
}),
);
await page.evaluate(() =>
window.__BUZZ_E2E_ACTIVATE_RELAY_RATE_LIMIT__?.(300),
);
await page.waitForFunction(
() => typeof window.__BUZZ_E2E_EMIT_MOCK_MESSAGE__ === "function",
);
await page.evaluate(
({ issueId, owner }) => {
window.__BUZZ_E2E_EMIT_MOCK_MESSAGE__?.({
channelName: "general",
content: `Delayed issue: buzz://issue?id=${issueId}&owner=${owner}&d=buzz`,
});
},
{ issueId: ISSUE_ID, owner: DEFAULT_MOCK_PUBKEY },
);
const issueChip = page.getByRole("button", {
name: /Open issue .* in repository buzz/,
});
await issueChip.hover();
await expect(
page
.getByRole("tooltip")
.locator('[data-buzz-tooltip-metadata-content=""]'),
).toHaveText("buzz · The complete Buzz community platform.");
});
test("desktop composer and sent message keep Buzz entities chip-only", async ({
page,
}) => {
await installMockBridge(page);
await page.setViewportSize({ width: 900, height: 800 });
await page.goto("/", { waitUntil: "domcontentloaded" });
await page.getByTestId("channel-general").click();
const repoLink = `buzz://repo?owner=${ALICE_PUBKEY}&d=relay-tools`;
await page.getByTestId("message-input").fill(`Check out ${repoLink}`);
// Buzz-native links do not enter the standalone composer-preview surface.
await expect(page.locator("[data-composer-link-previews]")).toHaveCount(0);
await waitForAnimations(page);
await page.screenshot({
animations: "disabled",
path: `${SHOTS}/02-composer-entity-chip-only.png`,
});
await page.getByTestId("send-message").click();
const row = page.getByTestId("message-row").last();
await expect(row.locator('[data-link-preview^="buzz-"]')).toHaveCount(0);
const repoChip = row.getByRole("button", {
name: "Open repository relay-tools",
});
await expect(repoChip).toBeVisible();
await repoChip.hover();
await expect(
page
.getByRole("tooltip")
.locator('[data-buzz-tooltip-metadata-content=""]'),
).toContainText("Operator tooling and admin CLI for relay deployments.");
await waitForAnimations(page);
await page.screenshot({
animations: "disabled",
path: `${SHOTS}/03-sent-entity-chip-tooltip.png`,
});
});
// The composer and the renderer must classify a same-relay clone URL
// identically. Both read the active relay origin: without it the composer
// treats the URL as an external link, shows a loading standalone card, and
// enters sender-snapshot fetching — a preview the sent message then
// contradicts by rendering the same URL as a repository chip.
test("composer classifies a same-relay clone URL as a repository chip, not a card", async ({
page,
}) => {
// The default mock relay is http://localhost:3000, and generic external
// previews are HTTPS-only — an http clone URL is unclassifiable either way,
// so the mismatch cannot appear. Point the bridge at an https relay so the
// composer's classification is the only variable.
const relayHttpUrl = "https://relay.e2e.example";
const cloneHref = `${relayHttpUrl}/git/${DEFAULT_MOCK_PUBKEY}/buzz.git`;
await installBridge(page, {
mode: "mock",
relayHttpUrl,
relayWsUrl: relayHttpUrl.replace(/^https/, "wss"),
// Serve external metadata so a misclassified clone URL produces a
// persistent standalone card rather than a card that loads and then
// disappears on a null result — the assertions below would otherwise pass
// by racing the loading window instead of by correct classification.
mock: {
linkPreviewMetadata: {
title: "Misclassified as external",
siteName: "relay.e2e.example",
description: "Sender-snapshot metadata for an external link",
imageDataUrl: null,
imageDomain: null,
},
},
});
await page.goto("/", { waitUntil: "domcontentloaded" });
await page.getByTestId("channel-general").click();
await page.getByTestId("message-input").fill(`Clone it: ${cloneHref}`);
// Past the composer's 350ms resolution debounce: a same-relay clone URL must
// never reach the standalone preview surface, loading card included.
await expect(page.locator("[data-composer-link-previews]")).toHaveCount(0);
await page.waitForTimeout(600);
await expect(page.locator("[data-composer-link-previews]")).toHaveCount(0);
await page.getByTestId("send-message").click();
const row = page.getByTestId("message-row").last();
await expect(row.locator("[data-link-preview]")).toHaveCount(0);
const repoChip = row.getByRole("button", { name: "Open repository buzz" });
await expect(repoChip).toBeVisible();
await repoChip.hover();
await expect(
page
.getByRole("tooltip")
.locator('[data-buzz-tooltip-metadata-content=""]'),
).toContainText("Relay, desktop, and mobile clients");
// The chip navigates in-app, proving the clone URL resolved onto the
// canonical buzz://repo target rather than being handed to the OS.
await repoChip.click();
await expect(page.locator("[data-project-detail-screen]")).toBeVisible();
});
test("reopening the same entity link reapplies its workspace state", async ({
page,
}) => {
const repoAddress = `30617:${DEFAULT_MOCK_PUBKEY}:buzz`;
await page.addInitScript(
({ issueId, issueSubject, prId, prSubject, repoAddress, owner }) => {
const createdAt = Math.floor(Date.now() / 1000) - 60;
window.__BUZZ_E2E_EXTRA_PROJECT_EVENTS__ = [
{
id: prId,
kind: 1618, // KIND_GIT_PULL_REQUEST
pubkey: owner,
created_at: createdAt,
content: "PR body",
tags: [
["a", repoAddress],
["subject", prSubject],
["c", "abc123".padEnd(40, "0")],
["branch-name", "fix/reopen-entity-link"],
],
},
{
id: issueId,
kind: 1621, // KIND_GIT_ISSUE
pubkey: owner,
created_at: createdAt,
content: "Issue body",
tags: [
["a", repoAddress],
["subject", issueSubject],
],
},
];
},
{
issueId: ISSUE_ID,
issueSubject: ISSUE_SUBJECT,
prId: PR_ID,
prSubject: PR_SUBJECT,
repoAddress,
owner: DEFAULT_MOCK_PUBKEY,
},
);
await installMockBridge(page);
await page.goto("/", { waitUntil: "domcontentloaded" });
await expect(page.getByTestId("open-projects-view")).toBeVisible();
const repoLink = `buzz://repo?owner=${DEFAULT_MOCK_PUBKEY}&d=buzz&tab=prs`;
const prLink = `buzz://pr?id=${PR_ID}&owner=${DEFAULT_MOCK_PUBKEY}&d=buzz`;
const issueLink = `buzz://issue?id=${ISSUE_ID}&owner=${DEFAULT_MOCK_PUBKEY}&d=buzz`;
const emitEntityLink = async (link: string) => {
await page.waitForFunction(
() => typeof window.__TAURI_INTERNALS__?.invoke === "function",
);
await page.evaluate(
(href) =>
window.__TAURI_INTERNALS__?.invoke?.("plugin:event|emit", {
event: "deep-link-entity",
payload: href,
}),
link,
);
};
await emitEntityLink(repoLink);
const pullRequestsTab = page.getByRole("tab", {
name: "Review",
exact: true,
});
await expect(pullRequestsTab).toHaveAttribute("aria-selected", "true");
const breadcrumb = page.getByRole("navigation", {
name: "Project breadcrumb",
});
await breadcrumb.getByRole("button").nth(1).click();
await expect(page.getByTestId("project-channel-home")).toBeVisible();
await emitEntityLink(repoLink);
await expect(pullRequestsTab).toHaveAttribute("aria-selected", "true");
const filesTab = page.getByRole("tab", { name: "Files", exact: true });
await filesTab.click();
await expect(filesTab).toHaveAttribute("aria-selected", "true");
await emitEntityLink(repoLink);
await expect(pullRequestsTab).toHaveAttribute("aria-selected", "true");
await emitEntityLink(prLink);
const prHeading = page
.getByTestId("project-pull-request-detail")
.getByRole("heading", { name: PR_SUBJECT });
await expect(prHeading).toBeVisible();
await breadcrumb.getByRole("button", { name: "Review", exact: true }).click();
await expect(prHeading).toHaveCount(0);
await emitEntityLink(prLink);
await expect(prHeading).toBeVisible();
await emitEntityLink(issueLink);
const issueHeading = page
.getByTestId("project-issue-detail")
.getByRole("heading", { name: ISSUE_SUBJECT });
await expect(issueHeading).toBeVisible();
await breadcrumb.getByRole("button", { name: "Tasks", exact: true }).click();
await expect(issueHeading).toHaveCount(0);
await emitEntityLink(issueLink);
await expect(issueHeading).toBeVisible();
});
test("deleted reply links identify deletion and fall back to their thread root", async ({
page,
}) => {
const deletedReplyId = "c".repeat(64);
const threadRootId = "b".repeat(64);
const channelId = "9dae0116-799b-5071-a0a8-fdd30a91a35d";
const link = `buzz://message?channel=${channelId}&id=${deletedReplyId}&thread=${threadRootId}`;
await installMockBridge(page, { deletedEventIds: [deletedReplyId] });
await page.goto("/", { waitUntil: "domcontentloaded" });
await page.waitForFunction(
() => typeof window.__BUZZ_E2E_EMIT_MOCK_MESSAGE__ === "function",
);
await page.evaluate(
({ id }) => {
window.__BUZZ_E2E_EMIT_MOCK_MESSAGE__?.({
channelName: "random",
content: "Surviving thread root",
id,
});
},
{ id: threadRootId },
);
await page.getByTestId("channel-general").click();
await page.getByTestId("message-input").fill(`Deleted reply ${link}`);
await page.getByTestId("send-message").click();
const deletedLink = page
.getByTestId("message-row")
.filter({ hasText: "Deleted reply" })
.last()
.getByRole("button", {
name: "Open thread in channel random; linked message was deleted",
});
await expect(deletedLink).toHaveAttribute(
"data-message-link-state",
"deleted",
);
await deletedLink.click();
await expect(page.getByTestId("chat-title")).toHaveText("random");
await expect(page).toHaveURL(new RegExp(`thread=${threadRootId}`));
await expect(page.getByRole("heading", { name: "Thread" })).toBeVisible();
});
test("deleted top-level message links identify deletion and fall back to channel navigation", async ({
page,
}) => {
await page.addInitScript(() => {
window.localStorage.setItem("buzz-theme", "buzz-dark");
window.localStorage.setItem("buzz-accent-color", "#c0a2f1");
});
const missingMessageId = "d".repeat(64);
const channelId = "9dae0116-799b-5071-a0a8-fdd30a91a35d";
const link = `buzz://message?channel=${channelId}&id=${missingMessageId}`;
await installMockBridge(page, { deletedEventIds: [missingMessageId] });
await page.goto("/", { waitUntil: "domcontentloaded" });
await page.getByTestId("channel-general").click();
await page.getByTestId("message-input").fill(`Missing link ${link}`);
await page.getByTestId("send-message").click();
const linkMessage = page
.getByTestId("message-row")
.filter({ hasText: "Missing link" })
.last();
const deletedLink = linkMessage.getByRole("button", {
name: "Open channel random; linked message was deleted",
});
await expect(deletedLink).toHaveText("random");
await expect(deletedLink).toHaveAttribute(
"data-message-link-state",
"deleted",
);
await expect(deletedLink).toHaveClass(/buzz-link-deleted/);
await expect(deletedLink).not.toHaveClass(/buzz-link-unavailable/);
const unavailableColors = await linkMessage.evaluate((message) => {
const probe = document.createElement("span");
probe.className = "mention-chip buzz-link-unavailable";
message.querySelector(".message-markdown")?.append(probe);
const styles = getComputedStyle(probe);
const colors = {
backgroundColor: styles.backgroundColor,
color: styles.color,
};
probe.remove();
return colors;
});
await expect
.poll(() =>
deletedLink.evaluate((element) => {
const styles = getComputedStyle(element);
return {
backgroundColor: styles.backgroundColor,
color: styles.color,
};
}),
)
.toEqual(unavailableColors);
await waitForAnimations(page);
await linkMessage.screenshot({
path: `${SHOTS}/deleted-message-link-after.png`,
});
await deletedLink.hover();
await expect(page.getByRole("tooltip")).toHaveText("Message deleted");
await deletedLink.click();
await expect(page.getByTestId("chat-title")).toHaveText("random");
await expect(page).toHaveURL(new RegExp(`#/channels/${channelId}$`));
});
test("cold-start entity links drain after the React listener mounts", async ({
page,
}) => {
const href = `buzz://repo?owner=${DEFAULT_MOCK_PUBKEY}&d=buzz&tab=prs`;
await installMockBridge(page, {
pendingEntityDeepLinks: [{ id: "cold-start-project", href }],
});
await page.goto("/", { waitUntil: "domcontentloaded" });
await expect(
page.getByRole("tab", { name: "Review", exact: true }),
).toHaveAttribute("aria-selected", "true");
await expect
.poll(() =>
page.evaluate(() =>
window.__TAURI_INTERNALS__?.invoke?.(
"take_pending_entity_deep_link",
{},
),
),
)
.toBeNull();
});