import { render, screen } from "@testing-library/react"; import { describe, expect, vi, beforeEach, it } from "vitest"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import userEvent from "@testing-library/user-event"; import { RepositorySelectionForm } from "../../../../src/components/features/home/repo-selection-form"; import OpenHands from "#/api/open-hands"; import { GitRepository } from "#/types/git"; // Create mock functions const mockUseUserRepositories = vi.fn(); const mockUseCreateConversation = vi.fn(); const mockUseIsCreatingConversation = vi.fn(); const mockUseTranslation = vi.fn(); const mockUseAuth = vi.fn(); // Setup default mock returns mockUseUserRepositories.mockReturnValue({ data: [], isLoading: false, isError: false, }); mockUseCreateConversation.mockReturnValue({ mutate: vi.fn(), isPending: false, isSuccess: false, }); mockUseIsCreatingConversation.mockReturnValue(false); mockUseTranslation.mockReturnValue({ t: (key: string) => key }); mockUseAuth.mockReturnValue({ isAuthenticated: true, isLoading: false, providersAreSet: true, user: { id: 1, login: "testuser", avatar_url: "https://example.com/avatar.png", name: "Test User", email: "test@example.com", company: "Test Company", }, login: vi.fn(), logout: vi.fn(), }); vi.mock("#/hooks/mutation/use-create-conversation", () => ({ useCreateConversation: () => mockUseCreateConversation(), })); vi.mock("#/hooks/use-is-creating-conversation", () => ({ useIsCreatingConversation: () => mockUseIsCreatingConversation(), })); vi.mock("react-i18next", () => ({ useTranslation: () => mockUseTranslation(), })); vi.mock("#/context/auth-context", () => ({ useAuth: () => mockUseAuth(), })); vi.mock("#/hooks/use-debounce", () => ({ useDebounce: (value: string) => value, })); const mockOnRepoSelection = vi.fn(); const renderForm = () => render(, { wrapper: ({ children }) => ( {children} ), }); describe("RepositorySelectionForm", () => { beforeEach(() => { vi.clearAllMocks(); }); it("shows loading indicator when repositories are being fetched", () => { const MOCK_REPOS: GitRepository[] = [ { id: 1, full_name: "user/repo1", git_provider: "github", is_public: true, }, { id: 2, full_name: "user/repo2", git_provider: "github", is_public: true, }, ]; const retrieveUserGitRepositoriesSpy = vi.spyOn( OpenHands, "retrieveUserGitRepositories", ); retrieveUserGitRepositoriesSpy.mockResolvedValue(MOCK_REPOS); renderForm(); // Check if loading indicator is displayed expect(screen.getByTestId("repo-dropdown-loading")).toBeInTheDocument(); expect(screen.getByText("HOME$LOADING_REPOSITORIES")).toBeInTheDocument(); }); it("shows dropdown when repositories are loaded", async () => { const MOCK_REPOS: GitRepository[] = [ { id: 1, full_name: "user/repo1", git_provider: "github", is_public: true, }, { id: 2, full_name: "user/repo2", git_provider: "github", is_public: true, }, ]; const retrieveUserGitRepositoriesSpy = vi.spyOn( OpenHands, "retrieveUserGitRepositories", ); retrieveUserGitRepositoriesSpy.mockResolvedValue(MOCK_REPOS); renderForm(); expect(await screen.findByTestId("repo-dropdown")).toBeInTheDocument(); }); it("shows error message when repository fetch fails", async () => { const retrieveUserGitRepositoriesSpy = vi.spyOn( OpenHands, "retrieveUserGitRepositories", ); retrieveUserGitRepositoriesSpy.mockRejectedValue( new Error("Failed to load"), ); renderForm(); expect( await screen.findByTestId("repo-dropdown-error"), ).toBeInTheDocument(); expect( screen.getByText("HOME$FAILED_TO_LOAD_REPOSITORIES"), ).toBeInTheDocument(); }); it("should call the search repos API when searching a URL", async () => { const MOCK_REPOS: GitRepository[] = [ { id: 1, full_name: "user/repo1", git_provider: "github", is_public: true, }, { id: 2, full_name: "user/repo2", git_provider: "github", is_public: true, }, ]; const MOCK_SEARCH_REPOS: GitRepository[] = [ { id: 3, full_name: "kubernetes/kubernetes", git_provider: "github", is_public: true, }, ]; const searchGitReposSpy = vi.spyOn(OpenHands, "searchGitRepositories"); const retrieveUserGitRepositoriesSpy = vi.spyOn( OpenHands, "retrieveUserGitRepositories", ); searchGitReposSpy.mockResolvedValue(MOCK_SEARCH_REPOS); retrieveUserGitRepositoriesSpy.mockResolvedValue(MOCK_REPOS); renderForm(); const input = await screen.findByTestId("repo-dropdown"); await userEvent.click(input); for (const repo of MOCK_REPOS) { expect(screen.getByText(repo.full_name)).toBeInTheDocument(); } expect( screen.queryByText(MOCK_SEARCH_REPOS[0].full_name), ).not.toBeInTheDocument(); expect(searchGitReposSpy).not.toHaveBeenCalled(); await userEvent.type(input, "https://github.com/kubernetes/kubernetes"); expect(searchGitReposSpy).toHaveBeenLastCalledWith( "kubernetes/kubernetes", 3, ); expect( screen.getByText(MOCK_SEARCH_REPOS[0].full_name), ).toBeInTheDocument(); for (const repo of MOCK_REPOS) { expect(screen.queryByText(repo.full_name)).not.toBeInTheDocument(); } }); it("should call onRepoSelection when a searched repository is selected", async () => { const MOCK_SEARCH_REPOS: GitRepository[] = [ { id: 3, full_name: "kubernetes/kubernetes", git_provider: "github", is_public: true, }, ]; const searchGitReposSpy = vi.spyOn(OpenHands, "searchGitRepositories"); searchGitReposSpy.mockResolvedValue(MOCK_SEARCH_REPOS); renderForm(); const input = await screen.findByTestId("repo-dropdown"); await userEvent.type(input, "https://github.com/kubernetes/kubernetes"); expect(searchGitReposSpy).toHaveBeenLastCalledWith( "kubernetes/kubernetes", 3, ); const searchedRepo = screen.getByText(MOCK_SEARCH_REPOS[0].full_name); expect(searchedRepo).toBeInTheDocument(); await userEvent.click(searchedRepo); expect(mockOnRepoSelection).toHaveBeenCalledWith( MOCK_SEARCH_REPOS[0].full_name, ); }); });