Compare commits

...

1 Commits

Author SHA1 Message Date
openhands d6935e325b Fix #7621: Allow empty query when using 'start from scratch' button 2025-04-01 13:08:49 +00:00
4 changed files with 135 additions and 2 deletions
@@ -0,0 +1,50 @@
import { render, screen, fireEvent } from "@testing-library/react";
import { CodeNotInGitHubLink } from "#/components/features/github/code-not-in-github-link";
import { useDispatch } from "react-redux";
import { useCreateConversation } from "#/hooks/mutation/use-create-conversation";
import { setInitialPrompt } from "#/state/initial-query-slice";
import { describe, it, expect, vi, beforeEach } from "vitest";
// Mock dependencies
vi.mock("react-redux", () => ({
useDispatch: vi.fn(),
}));
vi.mock("#/hooks/mutation/use-create-conversation", () => ({
useCreateConversation: vi.fn(),
}));
vi.mock("#/state/initial-query-slice", () => ({
setInitialPrompt: vi.fn(),
}));
describe("CodeNotInGitHubLink", () => {
const mockDispatch = vi.fn();
const mockCreateConversation = vi.fn();
beforeEach(() => {
vi.clearAllMocks();
(useDispatch as any).mockReturnValue(mockDispatch);
(useCreateConversation as any).mockReturnValue({
mutate: mockCreateConversation,
});
});
it("renders correctly", () => {
render(<CodeNotInGitHubLink />);
expect(screen.getByText(/Code not in GitHub\?/)).toBeInTheDocument();
expect(screen.getByText("Start from scratch")).toBeInTheDocument();
});
it("calls createConversation with allowEmptyQuery when clicked", () => {
render(<CodeNotInGitHubLink />);
fireEvent.click(screen.getByText("Start from scratch"));
expect(mockDispatch).toHaveBeenCalledWith(setInitialPrompt(""));
expect(mockCreateConversation).toHaveBeenCalledWith({
q: "",
allowEmptyQuery: true,
});
});
});
@@ -0,0 +1,79 @@
import { renderHook, act } from "@testing-library/react";
import { useCreateConversation } from "#/hooks/mutation/use-create-conversation";
import OpenHands from "#/api/open-hands";
import { useNavigate } from "react-router";
import { useDispatch, useSelector } from "react-redux";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { describe, it, expect, vi, beforeEach } from "vitest";
// Mock dependencies
vi.mock("react-router", () => ({
useNavigate: vi.fn(),
}));
vi.mock("react-redux", () => ({
useDispatch: vi.fn(),
useSelector: vi.fn(),
}));
vi.mock("#/api/open-hands", () => ({
default: {
createConversation: vi.fn(),
},
}));
vi.mock("posthog-js", () => ({
default: {
capture: vi.fn(),
},
}));
describe("useCreateConversation", () => {
const mockNavigate = vi.fn();
const mockDispatch = vi.fn();
const mockQueryClient = new QueryClient();
beforeEach(() => {
vi.clearAllMocks();
(useNavigate as any).mockReturnValue(mockNavigate);
(useDispatch as any).mockReturnValue(mockDispatch);
(useSelector as any).mockReturnValue({
selectedRepository: null,
files: [],
replayJson: null,
});
(OpenHands.createConversation as any).mockResolvedValue({
conversation_id: "test-id",
});
});
const wrapper = ({ children }: { children: React.ReactNode }) => (
<QueryClientProvider client={mockQueryClient}>{children}</QueryClientProvider>
);
it("should throw an error when no query, repository, files, or replayJson is provided", async () => {
const { result } = renderHook(() => useCreateConversation(), { wrapper });
await act(async () => {
await expect(result.current.mutateAsync({ q: "" })).rejects.toThrow(
"No query provided"
);
});
});
it("should allow empty query when allowEmptyQuery is true", async () => {
const { result } = renderHook(() => useCreateConversation(), { wrapper });
await act(async () => {
await result.current.mutateAsync({ q: "", allowEmptyQuery: true });
});
expect(OpenHands.createConversation).toHaveBeenCalledWith(
undefined,
"",
[],
undefined
);
expect(mockNavigate).toHaveBeenCalledWith("/conversations/test-id");
});
});
@@ -12,7 +12,7 @@ export function CodeNotInGitHubLink() {
const handleStartFromScratch = () => {
// Set the initial prompt and create a new conversation
dispatch(setInitialPrompt(INITIAL_PROMPT));
createConversation({ q: INITIAL_PROMPT });
createConversation({ q: INITIAL_PROMPT, allowEmptyQuery: true });
};
return (
@@ -16,8 +16,12 @@ export const useCreateConversation = () => {
);
return useMutation({
mutationFn: async (variables: { q?: string }) => {
mutationFn: async (variables: {
q?: string;
allowEmptyQuery?: boolean;
}) => {
if (
!variables.allowEmptyQuery &&
!variables.q?.trim() &&
!selectedRepository &&
files.length === 0 &&