feat: add SSH URL validation, inline radio buttons in repo creation dialog

RepositoryCreateDialog (already unified, used everywhere):
- Add isSshUrl() helper to detect git@ and ssh:// URLs
- Require SSH key selection when URL is SSH; show error otherwise
- Inline existing/new radio buttons with smaller styling (.repo-mode-radios)

Tests:
- Update repositories-settings-tab.test.tsx to select SSH key for owner/repo mode
- Mock listSSHKeys in tests

Quality gates: tsc --noEmit pass, npm run build pass, 80/80 tests pass
This commit is contained in:
Developer
2026-06-09 09:54:24 +00:00
parent 349066bcfa
commit 152f87a254
3 changed files with 55 additions and 7 deletions
@@ -3,6 +3,7 @@ import { afterEach, describe, expect, it, vi } from "vitest";
import { RepositoriesSettingsTab } from "./repositories-settings-tab";
import * as gitRepositoriesApi from "../../../api/git-repositories";
import * as sshKeysApi from "../../../api/ssh-keys";
const mockRepositories = [
{
@@ -35,6 +36,9 @@ 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]);
vi.spyOn(sshKeysApi, "listSSHKeys").mockResolvedValue([
{ id: "key-1", name: "My Key", public_key: "ssh-ed25519 AAA...", created_at: "2024-01-01" },
]);
render(<RepositoriesSettingsTab />);
@@ -52,12 +56,17 @@ describe("RepositoriesSettingsTab", () => {
fireEvent.change(screen.getByPlaceholderText(/repo-name/i), {
target: { value: "demo" },
});
const sshSelect = screen.getByRole("combobox", { name: /ssh key/i });
await waitFor(() => expect(sshSelect.querySelector('option[value="key-1"]')).toBeTruthy());
(sshSelect as HTMLSelectElement).value = "key-1";
fireEvent.change(sshSelect);
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",
ssh_key_id: "key-1",
});
});
expect(listMock).toHaveBeenCalledTimes(2);
@@ -66,6 +75,7 @@ describe("RepositoriesSettingsTab", () => {
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]);
vi.spyOn(sshKeysApi, "listSSHKeys").mockResolvedValue([]);
render(<RepositoriesSettingsTab />);