refactor: rename frontend pages to PascalCase with Page suffix

Renamed 18 page files:
- dashboard.tsx → DashboardPage.tsx
- projects.tsx → ProjectsPage.tsx
- sessions.tsx → SessionsPage.tsx
- settings.tsx → SettingsPage.tsx
- ssh-keys.tsx → SshKeysPage.tsx
- terminal.tsx → TerminalPage.tsx
- tool-workshop.tsx → ToolWorkshopPage.tsx
- config-profiles.tsx → ConfigProfilesPage.tsx
- git-repositories.tsx → GitRepositoriesPage.tsx
- repo-workspace.tsx → RepoWorkspacePage.tsx
- workspaces.tsx → WorkspacesPage.tsx
- workspace-detail.tsx → WorkspaceDetailPage.tsx
- profile.tsx → ProfilePage.tsx
- project-settings.tsx → ProjectSettingsPage.tsx
- git-history.tsx → GitHistoryPage.tsx
- placeholder.tsx → PlaceholderPage.tsx

Updated router.tsx imports.

Quality gates: verified no remaining old imports.
This commit is contained in:
2026-06-04 12:34:43 +02:00
parent 020f832eed
commit 7224afafd1
19 changed files with 15 additions and 15 deletions
+196
View File
@@ -0,0 +1,196 @@
import { cleanup, fireEvent, render, screen, waitFor, within } from "@testing-library/react";
import { MemoryRouter } from "react-router-dom";
import { afterEach, describe, expect, it, vi } from "vitest";
import { ProjectsPage } from "./projects";
import * as projectsApi from "../api/projects";
const mockProjects = [
{
id: "proj-1",
name: "Alpha Project",
description: "First project",
owner_id: "user-1",
default_ssh_key_id: null,
},
{
id: "proj-2",
name: "Beta Project",
description: null,
owner_id: "user-1",
default_ssh_key_id: null,
},
];
afterEach(() => {
cleanup();
vi.restoreAllMocks();
});
describe("ProjectsPage", () => {
it("renders loading state initially", () => {
vi.spyOn(projectsApi, "listProjects").mockImplementation(() => new Promise(() => {}));
render(
<MemoryRouter>
<ProjectsPage />
</MemoryRouter>
);
expect(screen.getByText(/loading projects/i)).toBeInTheDocument();
});
it("renders project list after loading", async () => {
vi.spyOn(projectsApi, "listProjects").mockResolvedValue(mockProjects);
render(
<MemoryRouter>
<ProjectsPage />
</MemoryRouter>
);
await waitFor(() => {
expect(screen.getByText("Alpha Project")).toBeInTheDocument();
});
expect(screen.getByText("Beta Project")).toBeInTheDocument();
expect(screen.getByText("First project")).toBeInTheDocument();
});
it("renders empty state when no projects", async () => {
vi.spyOn(projectsApi, "listProjects").mockResolvedValue([]);
render(
<MemoryRouter>
<ProjectsPage />
</MemoryRouter>
);
await waitFor(() => {
expect(screen.getByText(/no projects yet/i)).toBeInTheDocument();
});
});
it("renders error state with retry button", async () => {
vi.spyOn(projectsApi, "listProjects").mockRejectedValue(new Error("fail"));
render(
<MemoryRouter>
<ProjectsPage />
</MemoryRouter>
);
await waitFor(() => {
expect(screen.getByText(/failed to load projects/i)).toBeInTheDocument();
});
expect(screen.getByRole("button", { name: /retry/i })).toBeInTheDocument();
});
it("opens create dialog and submits new project", async () => {
const listMock = vi.spyOn(projectsApi, "listProjects").mockResolvedValue([]);
const createMock = vi.spyOn(projectsApi, "createProject").mockResolvedValue(mockProjects[0]);
render(
<MemoryRouter>
<ProjectsPage />
</MemoryRouter>
);
await waitFor(() => {
expect(screen.getByText(/no projects yet/i)).toBeInTheDocument();
});
fireEvent.click(screen.getByRole("button", { name: /new project/i }));
expect(screen.getByRole("dialog")).toBeInTheDocument();
fireEvent.change(screen.getByPlaceholderText(/project name/i), {
target: { value: "Gamma Project" },
});
fireEvent.change(screen.getByPlaceholderText(/optional description/i), {
target: { value: "A new project" },
});
fireEvent.click(screen.getByRole("button", { name: /create/i }));
await waitFor(() => {
expect(createMock).toHaveBeenCalledWith({
name: "Gamma Project",
description: "A new project",
});
});
expect(listMock).toHaveBeenCalledTimes(2);
});
it("shows validation error when name is empty", async () => {
vi.spyOn(projectsApi, "listProjects").mockResolvedValue([]);
render(
<MemoryRouter>
<ProjectsPage />
</MemoryRouter>
);
await waitFor(() => {
expect(screen.getByText(/no projects yet/i)).toBeInTheDocument();
});
fireEvent.click(screen.getByRole("button", { name: /new project/i }));
fireEvent.click(screen.getByRole("button", { name: /create/i }));
expect(screen.getByText(/project name is required/i)).toBeInTheDocument();
});
it("opens edit dialog and saves changes", async () => {
const listMock = vi.spyOn(projectsApi, "listProjects").mockResolvedValue(mockProjects);
const updateMock = vi.spyOn(projectsApi, "updateProject").mockResolvedValue(mockProjects[0]);
render(
<MemoryRouter>
<ProjectsPage />
</MemoryRouter>
);
await waitFor(() => {
expect(screen.getByText("Alpha Project")).toBeInTheDocument();
});
const alphaCard = screen.getByText("Alpha Project").closest(".project-card") as HTMLElement | null;
if (!alphaCard) throw new Error("Card not found");
fireEvent.click(within(alphaCard).getByRole("button", { name: /edit/i }));
expect(screen.getByRole("dialog")).toBeInTheDocument();
const nameInput = screen.getByDisplayValue("Alpha Project");
fireEvent.change(nameInput, { target: { value: "Alpha Updated" } });
fireEvent.click(screen.getByRole("button", { name: /save/i }));
await waitFor(() => {
expect(updateMock).toHaveBeenCalledWith("proj-1", {
name: "Alpha Updated",
description: "First project",
});
});
expect(listMock).toHaveBeenCalledTimes(2);
});
it("shows delete confirmation and deletes project", async () => {
const listMock = vi.spyOn(projectsApi, "listProjects").mockResolvedValue(mockProjects);
const deleteMock = vi.spyOn(projectsApi, "deleteProject").mockResolvedValue(undefined);
render(
<MemoryRouter>
<ProjectsPage />
</MemoryRouter>
);
await waitFor(() => {
expect(screen.getByText("Alpha Project")).toBeInTheDocument();
});
const alphaCard = screen.getByText("Alpha Project").closest(".project-card") as HTMLElement | null;
if (!alphaCard) throw new Error("Card not found");
fireEvent.click(within(alphaCard).getByRole("button", { name: /delete/i }));
expect(within(alphaCard).getByText(/are you sure/i)).toBeInTheDocument();
fireEvent.click(within(alphaCard).getByRole("button", { name: /delete/i }));
await waitFor(() => {
expect(deleteMock).toHaveBeenCalledWith("proj-1");
});
expect(listMock).toHaveBeenCalledTimes(2);
});
});