fix(chat): describe general chat and deep research on the chat home (#49531)

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
moe-berri 2026-10-31 16:39:33 -07:00 • committed by GitHub
parent 8c46b2c0d6
commit 28679bb9c3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 68 additions and 13 deletions

View file

@ -134,6 +134,47 @@ beforeEach(() => {
});
afterEach(() => vi.unstubAllGlobals());
it("should switch the composer placeholder with deep research on home and in a conversation", async () => {
const user = userEvent.setup();
const home = renderChat();
await screen.findByRole("button", { name: "research-chat" });
const homeEditor = screen.getByRole("textbox", { name: "Message" });
expect(homeEditor).toHaveAttribute("placeholder", "Ask a question, attach a file, or paste something to work on…");
const enableResearch = async () => {
await user.click(screen.getByRole("button", { name: "Connect tools" }));
await user.click(await screen.findByRole("button", { name: "Deep research", exact: true }));
};
await enableResearch();
expect(homeEditor).toHaveAttribute("placeholder", "What should I research on the web?");
await user.click(screen.getByRole("button", { name: "Turn off deep research" }));
expect(homeEditor).toHaveAttribute("placeholder", "Ask a question, attach a file, or paste something to work on…");
home.unmount();
const conversationId = "saved-chat";
const history: Conversation[] = [
{
id: conversationId,
title: "Saved chat",
model: "research-chat",
messages: [{ id: "saved-message", role: "user", content: "Previous question", timestamp: 1 }],
mcpServerNames: [],
createdAt: 1,
updatedAt: 1,
},
];
localStorage.setItem(storageKey, JSON.stringify(history));
renderChat(conversationId);
await screen.findByRole("button", { name: "research-chat" });
const conversationEditor = screen.getByRole("textbox", { name: "Message" });
expect(conversationEditor).toHaveAttribute("placeholder", "Send a message...");
await enableResearch();
expect(conversationEditor).toHaveAttribute("placeholder", "What should I research on the web?");
await user.click(screen.getByRole("button", { name: "Turn off deep research" }));
expect(conversationEditor).toHaveAttribute("placeholder", "Send a message...");
});
async function startResearch(withTools = true) {
const user = userEvent.setup();
await screen.findByRole("button", { name: "research-chat" });

View file

@ -72,7 +72,7 @@ const ON_TOTAL_LATENCY_INDEX = 24;
async function sendOneMessage(onUrlUpdate?: OnUrlUpdateFunction): Promise<void> {
renderWithProviders(<ChatConversationPage />, { onUrlUpdate });
expect(await screen.findByRole("button", { name: /gpt-5\.4-mini/ })).toBeInTheDocument();
fireEvent.change(screen.getByPlaceholderText("Describe a change, paste an error, or ask a question…"), {
fireEvent.change(screen.getByPlaceholderText("Ask a question, attach a file, or paste something to work on…"), {
target: { value: "How much did this cost?" },
});
fireEvent.click(screen.getByRole("button", { name: "Send" }));

View file

@ -505,9 +505,10 @@ export default function ChatConversationPage() {
const inputBar = (inConversation: boolean) => {
const hint = mode === "chat" || background.pending ? backgroundHint() : null;
const chatPlaceholder = inConversation
const defaultPlaceholder = inConversation
? "Send a message..."
: "Describe a change, paste an error, or ask a question…";
: "Ask a question, attach a file, or paste something to work on…";
const chatPlaceholder = research ? "What should I research on the web?" : defaultPlaceholder;
const preparing = isLoadingModels || isReadingAttachments;
const researchBlocked = research && !canResearch;
const submitDisabled =
@ -718,6 +719,7 @@ export default function ChatConversationPage() {
{showBlankState ? (
<MoyaiChatHome
imageMode={mode === "image"}
researchMode={research}
draft={inputText}
busy={isStreaming}
conversations={conversations}

View file

@ -66,7 +66,7 @@ describe("chat page URL state with ChatShellProvider", () => {
expect(await screen.findByRole("button", { name: /gpt-5\.4-mini/ })).toBeInTheDocument();
act(() => {
fireEvent.change(screen.getByPlaceholderText("Describe a change, paste an error, or ask a question…"), {
fireEvent.change(screen.getByPlaceholderText("Ask a question, attach a file, or paste something to work on…"), {
target: { value: "How much did this cost?" },
});
});
@ -87,9 +87,9 @@ describe("chat page URL state with ChatShellProvider", () => {
await waitFor(() => expect(window.location.search).toBe(""));
expect(
await screen.findByPlaceholderText("Describe a change, paste an error, or ask a question…"),
await screen.findByPlaceholderText("Ask a question, attach a file, or paste something to work on…"),
).toBeInTheDocument();
expect(screen.getByRole("heading", { level: 1, name: "What are we building?" })).toBeInTheDocument();
expect(screen.getByRole("heading", { level: 1, name: "What are we working on?" })).toBeInTheDocument();
expect(screen.queryByText("The estimated cost is one cent.")).not.toBeInTheDocument();
expect(screen.getByRole("button", { name: "How much did this cost?" })).toBeInTheDocument();
expect(window.location.pathname).toBe("/chat");

View file

@ -80,7 +80,7 @@ Show available providers in a single-select dropdown beside model search. “All
```tsx
// Page/greeting heading
<header className="moyai-chat-home-header">
<h1>What are we building?</h1>
<h1>What are we working on?</h1>
</header> // Moyai: 500 weight, clamp(32px, 3.4vw, 46px), 1.13 line-height
// Section heading (panel titles: "Your API Keys", "Your Usage")

View file

@ -9,7 +9,7 @@ function composerProps(overrides: Partial<ComponentProps<typeof MoyaiChatCompose
onChange: vi.fn(),
onSubmit: vi.fn(),
onCancel: vi.fn(),
placeholder: "Describe a change, paste an error, or ask a question…",
placeholder: "Ask a question, attach a file, or paste something to work on…",
isLoading: false,
submitDisabled: false,
submitLabel: "Send",

View file

@ -8,6 +8,7 @@ function homeProps(overrides: Partial<ComponentProps<typeof MoyaiChatHome>> = {}
return {
children: <span>Chat composer</span>,
imageMode: false,
researchMode: false,
draft: "",
busy: false,
conversations: [],
@ -24,12 +25,17 @@ function conversation(id: string, updatedAt: number): Conversation {
describe("MoyaiChatHome", () => {
it("should render the Moyai welcome and composer", () => {
render(<MoyaiChatHome {...homeProps()} />);
expect(screen.getByRole("heading", { level: 1, name: "What are we building?" })).toBeInTheDocument();
expect(screen.getByRole("heading", { level: 1, name: "What are we working on?" })).toBeInTheDocument();
expect(screen.getByText("Chat composer")).toBeInTheDocument();
expect(screen.getByRole("heading", { name: "Recent chats" })).toBeInTheDocument();
expect(screen.getByRole("status")).toHaveTextContent("No recent chats in this browser yet.");
});
it("should render the research welcome when research mode is enabled", () => {
render(<MoyaiChatHome {...homeProps({ researchMode: true })} />);
expect(screen.getByRole("heading", { level: 1, name: "What should we research?" })).toBeInTheDocument();
});
it("should show the three most recently updated chats and open the selected one", () => {
const conversations = [
conversation("first", 1),

View file

@ -23,6 +23,7 @@ const starters = [
export default function MoyaiChatHome({
children,
imageMode,
researchMode,
draft,
busy,
conversations,
@ -31,6 +32,7 @@ export default function MoyaiChatHome({
}: {
children: ReactNode;
imageMode: boolean;
researchMode: boolean;
draft: string;
busy: boolean;
conversations: Conversation[];
@ -44,11 +46,15 @@ export default function MoyaiChatHome({
<div className="moyai-chat-home-content">
<header className="moyai-chat-home-header">
<h1>
{imageMode ? (
"What are we creating?"
) : (
{imageMode && "What are we creating?"}
{!imageMode && researchMode && (
<>
What are we <span>building?</span>
What should we <span>research?</span>
</>
)}
{!imageMode && !researchMode && (
<>
What are we <span>working on?</span>
</>
)}
</h1>