mirror of
https://github.com/block/buzz.git
synced 2026-11-05 17:01:35 +00:00
fix(messages): paint owner avatar fallback and harden its e2e assertions
Radix AvatarFallback's delayMs exists to avoid an initials flash while an avatar image loads. With no image src there is nothing to wait for, yet the 200ms delay still applied — so avatar-less owners painted a blank 16px box on cold renders (caught in live-build review). Only apply the delay when an image src is present. Tighten the e2e so it can no longer pass on an empty wrapper: seed one owner with a hermetic data-URL avatar and one without, assert a fully loaded <img> (naturalWidth > 0) for the former and visible initials text for the latter, and give the second-level owner-profile fetch generous timeouts to kill the cold-run race. 18 consecutive runs green including cold server starts. Co-authored-by: Taylor Ho <taylorkmho@gmail.com> Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
This commit is contained in:
committed by
npub14vtk7pvazqrq9639qu7e560wnqtl0d53ca4gjuvq6jzf3k2el23qqlwa7f
parent
3488713763
commit
602e5d3478
@@ -447,6 +447,7 @@ export const MessageRow = React.memo(
|
||||
truncatePubkey(message.agentOwner.pubkey)
|
||||
}
|
||||
size="2xs"
|
||||
testId="message-agent-owner-avatar"
|
||||
/>
|
||||
</span>
|
||||
</TooltipTrigger>
|
||||
|
||||
@@ -68,7 +68,10 @@ export function UserAvatar({
|
||||
: "bg-secondary text-secondary-foreground",
|
||||
)}
|
||||
data-testid={testId ? `${testId}-fallback` : undefined}
|
||||
delayMs={200}
|
||||
// The delay exists to avoid an initials flash while an image loads.
|
||||
// With no image source there is nothing to wait for — render the
|
||||
// initials immediately or the avatar paints as an empty box.
|
||||
delayMs={src ? 200 : undefined}
|
||||
>
|
||||
{initials}
|
||||
</AvatarFallback>
|
||||
|
||||
@@ -14,6 +14,12 @@ const SCOUT_B_PUBKEY =
|
||||
const SOLO_PUBKEY =
|
||||
"aaaa000000000000000000000000000000000000000000000000000000000003";
|
||||
|
||||
// 8x8 solid teal PNG. A data URL keeps the avatar-image path hermetic — no
|
||||
// network fetch, no relay proxy — while still exercising Radix's real
|
||||
// image-load lifecycle (AvatarImage only renders once the img has loaded).
|
||||
const ALICE_AVATAR_DATA_URL =
|
||||
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAIAAABLbSncAAAAFElEQVR4nGMUWTCNARtgwio6aCUABdEBWlX6zykAAAAASUVORK5CYII=";
|
||||
|
||||
async function emitMockMessage(page: Page, content: string, pubkey: string) {
|
||||
const event = await page.evaluate(
|
||||
({ msg, pk }) => {
|
||||
@@ -87,7 +93,8 @@ test("duplicate-named agents get an inline owner avatar; unique agents do not",
|
||||
},
|
||||
],
|
||||
// Seeded after managed agents, so these override the default
|
||||
// owner (the mock viewer) with two distinct humans.
|
||||
// owner (the mock viewer) with two distinct humans. Alice carries an
|
||||
// avatar image; bob has none, exercising the initials fallback.
|
||||
searchProfiles: [
|
||||
{
|
||||
pubkey: SCOUT_A_PUBKEY,
|
||||
@@ -101,6 +108,15 @@ test("duplicate-named agents get an inline owner avatar; unique agents do not",
|
||||
ownerPubkey: TEST_IDENTITIES.bob.pubkey,
|
||||
isAgent: true,
|
||||
},
|
||||
{
|
||||
pubkey: TEST_IDENTITIES.alice.pubkey,
|
||||
displayName: "alice",
|
||||
avatarUrl: ALICE_AVATAR_DATA_URL,
|
||||
},
|
||||
{
|
||||
pubkey: TEST_IDENTITIES.bob.pubkey,
|
||||
displayName: "bob",
|
||||
},
|
||||
],
|
||||
});
|
||||
await page.goto("/");
|
||||
@@ -123,13 +139,33 @@ test("duplicate-named agents get an inline owner avatar; unique agents do not",
|
||||
.getByTestId("message-row")
|
||||
.filter({ hasText: "Ranger here, no twin." });
|
||||
|
||||
// Both same-named agents carry the inline owner avatar…
|
||||
await expect(
|
||||
scoutARow.getByTestId("message-agent-owner-avatar"),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
scoutBRow.getByTestId("message-agent-owner-avatar"),
|
||||
).toBeVisible();
|
||||
// Both same-named agents carry the inline owner avatar. The wrapper being
|
||||
// visible is not enough — assert actual painted content. Owner profiles
|
||||
// arrive via a second-level batch fetch, so give the cold first render
|
||||
// time to settle instead of racing it.
|
||||
const scoutABadge = scoutARow.getByTestId("message-agent-owner-avatar");
|
||||
const scoutBBadge = scoutBRow.getByTestId("message-agent-owner-avatar");
|
||||
await expect(scoutABadge).toBeVisible({ timeout: 15_000 });
|
||||
await expect(scoutBBadge).toBeVisible({ timeout: 15_000 });
|
||||
|
||||
// Alice has an avatar image — a real, fully loaded <img> must render.
|
||||
const aliceImage = scoutABadge.getByTestId(
|
||||
"message-agent-owner-avatar-image",
|
||||
);
|
||||
await expect(aliceImage).toBeVisible({ timeout: 15_000 });
|
||||
expect(
|
||||
await aliceImage.evaluate(
|
||||
(img: HTMLImageElement) => img.complete && img.naturalWidth > 0,
|
||||
),
|
||||
).toBe(true);
|
||||
|
||||
// Bob has no avatar URL — the initials fallback must paint visible text.
|
||||
const bobFallback = scoutBBadge.getByTestId(
|
||||
"message-agent-owner-avatar-fallback",
|
||||
);
|
||||
await expect(bobFallback).toBeVisible({ timeout: 15_000 });
|
||||
await expect(bobFallback).toHaveText("B");
|
||||
|
||||
// …the uniquely named agent does not.
|
||||
await expect(rangerRow.getByTestId("message-agent-owner-avatar")).toHaveCount(
|
||||
0,
|
||||
|
||||
Reference in New Issue
Block a user