Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions apps/web/src/domains/chat/components/busy-indicator.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/**
* Tests for `BusyIndicator`.
*
* The dot is a bare `<span>`, whose default `display: inline` makes the
* CSS box model ignore `width`/`height`. Inside a flex parent the dot is
* blockified and sizes correctly, but standalone (e.g. the avatar
* `ProgressBadge`) it would collapse to 0×0 and never paint. The
* `inline-block` class guarantees the dot lays out at its declared size
* regardless of its parent's layout. Uses happy-dom via the bun:test
* preload configured in `web/bunfig.toml`.
*/

import { afterEach, describe, expect, test } from "bun:test";

import { cleanup, render } from "@testing-library/react";

import { BusyIndicator } from "@/domains/chat/components/busy-indicator";

afterEach(() => {
cleanup();
});

describe("BusyIndicator", () => {
test("is block-level so its size applies outside a flex parent", () => {
/**
* Tests that the dot is block-level and carries its declared size,
* so it paints even when its parent is not a flex container.
*/

// GIVEN a 6px busy indicator rendered with no flex parent
const { container } = render(<BusyIndicator size={6} />);

// WHEN we read the rendered dot element
const dot = container.firstElementChild as HTMLElement;

// THEN it is block-level so width/height are not ignored
expect(dot.className).toContain("inline-block");

// AND it lays out at the requested size
expect(dot.style.width).toBe("6px");
expect(dot.style.height).toBe("6px");
});

test("uses the shared busy-pulse keyframe", () => {
/**
* Tests that the dot keeps the shared pulse class so its animation
* matches every other busy affordance in the app.
*/

// GIVEN a default busy indicator
const { container } = render(<BusyIndicator size={8} />);

// WHEN we read the rendered dot element
const dot = container.firstElementChild as HTMLElement;

// THEN it carries the shared busy-indicator pulse class
expect(dot.className).toContain("busy-indicator");
});
});
2 changes: 1 addition & 1 deletion apps/web/src/domains/chat/components/busy-indicator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export function BusyIndicator({ size = 8 }: { size?: number }) {
return (
<span
aria-hidden="true"
className="busy-indicator shrink-0 rounded-full"
className="busy-indicator inline-block shrink-0 rounded-full"
style={{
width: size,
height: size,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1170,6 +1170,7 @@ export function ChatRouteContent({
customImageUrl={avatarImageUrl}
size={40}
interactive
isProcessing={activeConversationIsProcessing}
/>
) : null,
greeting: editingApp ? buildEditAppGreeting(editingApp) : emptyStateGreeting,
Expand Down