import { cleanup, fireEvent, render, screen, waitFor } from "@testing-library/react"; import { afterEach, describe, expect, it, vi } from "vitest"; import { RepositoriesSettingsTab } from "./repositories-settings-tab"; import * as gitRepositoriesApi from "../api/git_repositories"; const mockRepositories = [ { id: "repo-1", name: "Main Repo", path: "/repos/main", project_id: "proj-1", owner_id: "user-1", is_mirror: false, remote_url: null, last_push: null, created_at: null, }, ]; vi.mock("react-router-dom", async () => { const actual = await vi.importActual("react-router-dom"); return { ...actual, useParams: () => ({ projectId: "proj-1" }), }; }); afterEach(() => { cleanup(); vi.restoreAllMocks(); }); describe("RepositoriesSettingsTab", () => { it("opens create dialog and clones an existing repository", async () => { const listMock = vi.spyOn(gitRepositoriesApi, "listRepositories").mockResolvedValue(mockRepositories); const createMock = vi.spyOn(gitRepositoriesApi, "createRepository").mockResolvedValue(mockRepositories[0]); render(); await waitFor(() => { expect(screen.getByText("Main Repo")).toBeInTheDocument(); }); fireEvent.click(screen.getByRole("button", { name: /add repository/i })); fireEvent.change(screen.getByPlaceholderText(/repository-name/i), { target: { value: "New Repo" }, }); fireEvent.change(screen.getByPlaceholderText(/owner/i), { target: { value: "alice" }, }); fireEvent.change(screen.getByPlaceholderText(/repo-name/i), { target: { value: "demo" }, }); fireEvent.click(screen.getByRole("button", { name: /clone repository/i })); await waitFor(() => { expect(createMock).toHaveBeenCalledWith("proj-1", { name: "New Repo", remote_url: "git@git.commumedia.org:alice/demo.git", }); }); expect(listMock).toHaveBeenCalledTimes(2); }); it("uses advanced url fallback when requested", async () => { const listMock = vi.spyOn(gitRepositoriesApi, "listRepositories").mockResolvedValue(mockRepositories); const createMock = vi.spyOn(gitRepositoriesApi, "createRepository").mockResolvedValue(mockRepositories[0]); render(); await waitFor(() => { expect(screen.getByText("Main Repo")).toBeInTheDocument(); }); fireEvent.click(screen.getByRole("button", { name: /add repository/i })); fireEvent.change(screen.getByPlaceholderText(/repository-name/i), { target: { value: "New Repo" }, }); fireEvent.click(screen.getByRole("button", { name: /use full url instead/i })); fireEvent.change(screen.getByPlaceholderText(/https:\/\/github.com\/user\/repo.git/i), { target: { value: "https://github.com/user/repo.git" }, }); fireEvent.click(screen.getByRole("button", { name: /clone repository/i })); await waitFor(() => { expect(createMock).toHaveBeenCalledWith("proj-1", { name: "New Repo", remote_url: "https://github.com/user/repo.git", }); }); expect(listMock).toHaveBeenCalledTimes(2); }); it("shows validation when cloning without a remote url", async () => { vi.spyOn(gitRepositoriesApi, "listRepositories").mockResolvedValue(mockRepositories); render(); await waitFor(() => { expect(screen.getByText("Main Repo")).toBeInTheDocument(); }); fireEvent.click(screen.getByRole("button", { name: /add repository/i })); fireEvent.change(screen.getByPlaceholderText(/repository-name/i), { target: { value: "New Repo" }, }); fireEvent.change(screen.getByPlaceholderText(/owner/i), { target: { value: "" }, }); fireEvent.click(screen.getByRole("button", { name: /clone repository/i })); expect(screen.getByText(/owner and repository name are required/i)).toBeInTheDocument(); }); });