Files
Triple-C/app/src/components/projects/ProjectCard.test.tsx
Josh Knapp 552aaebf16
Some checks failed
Build App / build-linux (push) Failing after 1m40s
Build App / build-windows (push) Failing after 1m43s
Simplify auth modes to Anthropic and Bedrock, fix Windows taskbar icon
Replace the three auth modes (Login, API Key, Bedrock) with two
(Anthropic, Bedrock). The Anthropic mode uses OAuth via `claude login`
inside the terminal, which generates and stores its own API key in the
persistent config volume. The separate API Key mode is removed because
Claude Code now requires interactive approval of externally-provided
keys, making the injected ANTHROPIC_API_KEY approach unreliable.

Old projects stored as "login" or "api_key" are automatically migrated
to "anthropic" via serde aliases.

Also fix the Windows taskbar icon showing as a black square by loading
icon.png instead of icon.ico for the runtime window icon.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 03:10:57 +00:00

129 lines
4.0 KiB
TypeScript

import { describe, it, expect, vi, beforeEach } from "vitest";
import { render, screen, fireEvent, act } from "@testing-library/react";
import ProjectCard from "./ProjectCard";
import type { Project } from "../../lib/types";
// Mock Tauri dialog plugin
vi.mock("@tauri-apps/plugin-dialog", () => ({
open: vi.fn(),
}));
// Mock hooks
const mockUpdate = vi.fn();
const mockStart = vi.fn();
const mockStop = vi.fn();
const mockRebuild = vi.fn();
const mockRemove = vi.fn();
vi.mock("../../hooks/useProjects", () => ({
useProjects: () => ({
start: mockStart,
stop: mockStop,
rebuild: mockRebuild,
remove: mockRemove,
update: mockUpdate,
}),
}));
vi.mock("../../hooks/useTerminal", () => ({
useTerminal: () => ({
open: vi.fn(),
}),
}));
let mockSelectedProjectId: string | null = null;
vi.mock("../../store/appState", () => ({
useAppState: vi.fn((selector) =>
selector({
selectedProjectId: mockSelectedProjectId,
setSelectedProject: vi.fn(),
})
),
}));
const mockProject: Project = {
id: "test-1",
name: "Test Project",
paths: [{ host_path: "/home/user/project", mount_name: "project" }],
container_id: null,
status: "stopped",
auth_mode: "anthropic",
bedrock_config: null,
allow_docker_access: false,
ssh_key_path: null,
git_token: null,
git_user_name: null,
git_user_email: null,
custom_env_vars: [],
claude_instructions: null,
created_at: "2026-01-01T00:00:00Z",
updated_at: "2026-01-01T00:00:00Z",
};
describe("ProjectCard", () => {
beforeEach(() => {
vi.clearAllMocks();
mockSelectedProjectId = null;
});
it("renders project name and path", () => {
render(<ProjectCard project={mockProject} />);
expect(screen.getByText("Test Project")).toBeInTheDocument();
expect(screen.getByText("/workspace/project")).toBeInTheDocument();
});
it("card root has min-w-0 and overflow-hidden to contain content", () => {
const { container } = render(<ProjectCard project={mockProject} />);
const card = container.firstElementChild;
expect(card).not.toBeNull();
expect(card!.className).toContain("min-w-0");
expect(card!.className).toContain("overflow-hidden");
});
describe("when selected and showing config", () => {
beforeEach(() => {
mockSelectedProjectId = "test-1";
});
it("expanded area has min-w-0 and overflow-hidden", () => {
const { container } = render(<ProjectCard project={mockProject} />);
// The expanded section (mt-2 ml-4) contains the auth/action/config controls
const expandedSection = container.querySelector(".ml-4.mt-2");
expect(expandedSection).not.toBeNull();
expect(expandedSection!.className).toContain("min-w-0");
expect(expandedSection!.className).toContain("overflow-hidden");
});
it("folder path inputs use min-w-0 to allow shrinking", async () => {
const { container } = render(<ProjectCard project={mockProject} />);
// Click Config button to show config panel
await act(async () => {
fireEvent.click(screen.getByText("Config"));
});
// After config is shown, check the folder host_path input has min-w-0
const hostPathInputs = container.querySelectorAll('input[placeholder="/path/to/folder"]');
expect(hostPathInputs.length).toBeGreaterThan(0);
expect(hostPathInputs[0].className).toContain("min-w-0");
});
it("config panel container has overflow-hidden", async () => {
const { container } = render(<ProjectCard project={mockProject} />);
// Click Config button
await act(async () => {
fireEvent.click(screen.getByText("Config"));
});
// The config panel has border-t and overflow containment classes
const allDivs = container.querySelectorAll("div");
const configPanel = Array.from(allDivs).find(
(div) => div.className.includes("border-t") && div.className.includes("min-w-0")
);
expect(configPanel).toBeDefined();
expect(configPanel!.className).toContain("overflow-hidden");
});
});
});