/** * @file Tests for Settings → Git → Your git identity: a user with no key * makes one, a key shows only its public half and copies it, replacing asks * first, and a member (S3) adds an HTTPS login of their own while the host's * view leaves HTTPS to the shared Git credentials table. * @author Nguyễn Ngọc Trí Vĩ */ import { describe, it, expect, vi, beforeEach } from "vitest"; import { render, screen, waitFor } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { GitIdentitySettings } from "../GitIdentitySettings"; import { api, type GitIdentity } from "../../../lib/api"; import { confirmDialog } from "../../../lib/confirm"; import { copyText } from "../../../lib/clipboard"; vi.mock("../../../lib/api", () => ({ api: { gitIdentity: { get: vi.fn(), generateKey: vi.fn(), removeKey: vi.fn(), saveHttps: vi.fn(), saveAuthor: vi.fn(), removeHttps: vi.fn(), }, }, })); vi.mock("../../../lib/toast", () => ({ toast: { success: vi.fn(), error: vi.fn() } })); vi.mock("../../../lib/confirm", () => ({ confirmDialog: vi.fn(() => Promise.resolve(true)) })); vi.mock("../../../lib/clipboard", () => ({ copyText: vi.fn(() => Promise.resolve(true)) })); const KEY = { publicKey: "ssh-ed25519 AAAAC3Nz an@agenthub", fingerprint: "SHA256:abc" }; const view = (over: Partial = {}): GitIdentity => ({ account: "member", writable: true, ssh: null, https: [], author: { name: "An", email: "an@x.vn" }, ...over, }); beforeEach(() => { vi.clearAllMocks(); }); describe("GitIdentitySettings", () => { it("makes a key when there is none, without asking", async () => { vi.mocked(api.gitIdentity.get).mockResolvedValue(view()); vi.mocked(api.gitIdentity.generateKey).mockResolvedValue(view({ ssh: KEY })); render(); await userEvent.click(await screen.findByRole("button", { name: "Generate SSH key" })); expect(confirmDialog).not.toHaveBeenCalled(); expect(api.gitIdentity.generateKey).toHaveBeenCalledWith(false); expect(await screen.findByLabelText("Public key")).toHaveValue(KEY.publicKey); }); it("copies the public key and asks before replacing it", async () => { vi.mocked(api.gitIdentity.get).mockResolvedValue(view({ ssh: KEY })); vi.mocked(api.gitIdentity.generateKey).mockResolvedValue(view({ ssh: KEY })); render(); await userEvent.click(await screen.findByRole("button", { name: "Copy public key" })); expect(copyText).toHaveBeenCalledWith(KEY.publicKey); await userEvent.click(screen.getByRole("button", { name: "Replace key" })); expect(confirmDialog).toHaveBeenCalled(); await waitFor(() => expect(api.gitIdentity.generateKey).toHaveBeenCalledWith(true)); }); it("lets a member save an https login of their own", async () => { vi.mocked(api.gitIdentity.get).mockResolvedValue(view()); vi.mocked(api.gitIdentity.saveHttps).mockResolvedValue( view({ https: [{ protocol: "https", host: "github.com", username: "an", hasToken: true }] }) ); render(); await userEvent.click(await screen.findByRole("button", { name: "Add login" })); await userEvent.type(screen.getByLabelText("Server URL"), "https://github.com/o/r"); await userEvent.type(screen.getByLabelText("Username"), "an"); await userEvent.type(screen.getByLabelText("Password or access token"), "tok"); // The second Save: the first belongs to the commit-author form. await userEvent.click(screen.getAllByRole("button", { name: "Save" })[1]!); expect(api.gitIdentity.saveHttps).toHaveBeenCalledWith({ host: "github.com", username: "an", password: "tok", }); expect(await screen.findByText("github.com")).toBeInTheDocument(); }); it("warns while the commit author is unset and saves it", async () => { vi.mocked(api.gitIdentity.get).mockResolvedValue(view({ author: { name: null, email: null } })); vi.mocked(api.gitIdentity.saveAuthor).mockResolvedValue(view()); render(); expect( await screen.findByText(/commits from the dashboard and your agents fail/) ).toBeInTheDocument(); await userEvent.type(screen.getByLabelText("Name"), "An Nguyen"); await userEvent.type(screen.getByLabelText("Email"), "an@x.vn"); await userEvent.click(screen.getAllByRole("button", { name: "Save" })[0]!); expect(api.gitIdentity.saveAuthor).toHaveBeenCalledWith({ name: "An Nguyen", email: "an@x.vn", }); }); it("shows the host's key read-only and no https list", async () => { vi.mocked(api.gitIdentity.get).mockResolvedValue( view({ account: "host", writable: false, ssh: KEY }) ); render(); expect(await screen.findByLabelText("Public key")).toBeInTheDocument(); expect(screen.queryByRole("button", { name: "Replace key" })).toBeNull(); expect(screen.queryByRole("button", { name: "Add login" })).toBeNull(); }); });