Skip to content

Commit 5d1ea82

Browse files
committed
fix: add play functions to ensure Chromatic captures scrolled state
- Add waitForChatScroll play function that waits for messages to load and scroll to complete before Chromatic takes screenshots - Add data-testid="message-window" to scroll container in AIView - Add data-testid="chat-message" to message wrappers for test queries - Apply play function to ActiveWorkspaceWithChat and MarkdownTables stories - Fix ProviderIcon lint error: remove redundant "mux-gateway" union type (already included in ProviderName)
1 parent 939ad6a commit 5d1ea82

File tree

3 files changed

+42
-1
lines changed

3 files changed

+42
-1
lines changed

src/browser/App.stories.tsx

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,49 @@
11
import type { Meta, StoryObj } from "@storybook/react-vite";
22
import { useMemo } from "react";
3+
import { waitFor, within } from "@storybook/test";
34
import { AppLoader } from "./components/AppLoader";
45
import type { ProjectConfig } from "@/node/config";
56
import type { FrontendWorkspaceMetadata } from "@/common/types/workspace";
67
import type { WorkspaceChatMessage } from "@/common/orpc/types";
78
import { DEFAULT_RUNTIME_CONFIG } from "@/common/constants/workspace";
89
import { createMockORPCClient, type MockORPCClientOptions } from "../../.storybook/mocks/orpc";
910

11+
/**
12+
* Play function that waits for chat messages to load and scroll to complete.
13+
* Used by Chromatic to ensure screenshots capture the final scrolled state.
14+
*/
15+
async function waitForChatScroll({ canvasElement }: { canvasElement: HTMLElement }) {
16+
const canvas = within(canvasElement);
17+
18+
// Wait for messages to appear (they load via setTimeout in mock)
19+
await waitFor(
20+
() => {
21+
const messages = canvas.queryAllByTestId("chat-message");
22+
if (messages.length === 0) {
23+
throw new Error("No messages yet");
24+
}
25+
},
26+
{ timeout: 2000 }
27+
);
28+
29+
// Wait for scroll to be at or near bottom
30+
await waitFor(
31+
() => {
32+
const messageWindow = canvasElement.querySelector('[data-testid="message-window"]');
33+
if (!messageWindow) {
34+
throw new Error("Message window not found");
35+
}
36+
const { scrollTop, scrollHeight, clientHeight } = messageWindow;
37+
const distanceFromBottom = scrollHeight - scrollTop - clientHeight;
38+
// Allow 50px tolerance for "at bottom"
39+
if (distanceFromBottom > 50) {
40+
throw new Error(`Not scrolled to bottom yet (${distanceFromBottom}px from bottom)`);
41+
}
42+
},
43+
{ timeout: 2000 }
44+
);
45+
}
46+
1047
// Stable timestamp for testing active states (use fixed time minus small offsets)
1148
// This ensures workspaces don't show as "Older than 1 day" and keeps stories deterministic
1249
const NOW = 1700000000000; // Fixed timestamp: Nov 14, 2023
@@ -1093,6 +1130,7 @@ main
10931130
/>
10941131
);
10951132
},
1133+
play: waitForChatScroll,
10961134
};
10971135

10981136
/**
@@ -1234,4 +1272,5 @@ These tables should render cleanly without any disruptive copy or download actio
12341272

12351273
return <AppWithMocks projects={projects} workspaces={workspaces} onChat={onChat} />;
12361274
},
1275+
play: waitForChatScroll,
12371276
};

src/browser/components/AIView.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -437,6 +437,7 @@ const AIViewInner: React.FC<AIViewProps> = ({
437437
aria-busy={canInterrupt}
438438
aria-label="Conversation transcript"
439439
tabIndex={0}
440+
data-testid="message-window"
440441
className="h-full overflow-y-auto p-[15px] leading-[1.5] break-words whitespace-pre-wrap"
441442
>
442443
<div className={cn("max-w-4xl mx-auto", mergedMessages.length === 0 && "h-full")}>
@@ -466,6 +467,7 @@ const AIViewInner: React.FC<AIViewProps> = ({
466467
return (
467468
<React.Fragment key={msg.id}>
468469
<div
470+
data-testid="chat-message"
469471
data-message-id={
470472
msg.type !== "history-hidden" && msg.type !== "workspace-init"
471473
? msg.historyId

src/browser/components/ProviderIcon.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import MuxIcon from "@/browser/assets/icons/mux.svg?react";
88
import { PROVIDER_DISPLAY_NAMES, type ProviderName } from "@/common/constants/providers";
99
import { cn } from "@/common/lib/utils";
1010

11-
const PROVIDER_ICONS: Partial<Record<ProviderName | "mux-gateway", React.FC>> = {
11+
const PROVIDER_ICONS: Partial<Record<ProviderName, React.FC>> = {
1212
anthropic: AnthropicIcon,
1313
openai: OpenAIIcon,
1414
google: GoogleIcon,

0 commit comments

Comments
 (0)