Implemented comprehensive UI for managing federation connections: Features: - View existing federation connections grouped by status - Initiate new connections to remote instances - Accept/reject pending connection requests - Disconnect active connections - Display connection status, metadata, and capabilities - PDA-friendly design throughout (no demanding language) Components: - ConnectionCard: Display individual connections with actions - ConnectionList: Grouped list view with status sections - InitiateConnectionDialog: Modal for connecting to new instances - Connections page: Main management interface Implementation: - Full test coverage (42 tests, 100% passing) - TypeScript strict mode compliance - ESLint passing with no warnings - Mock data for development (ready for backend integration) - Proper error handling and loading states - PDA-friendly language (calm, supportive, stress-free) Status indicators: - 🟢 Active (soft green) - 🔵 Pending (soft blue) - ⏸️ Disconnected (soft yellow) - ⚪ Rejected (light gray) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
121 lines
4.3 KiB
TypeScript
121 lines
4.3 KiB
TypeScript
/**
|
|
* ConnectionList Component Tests
|
|
* Following TDD - write tests first!
|
|
*/
|
|
|
|
import { describe, it, expect, vi } from "vitest";
|
|
import { render, screen } from "@testing-library/react";
|
|
import { ConnectionList } from "./ConnectionList";
|
|
import { FederationConnectionStatus, type ConnectionDetails } from "@/lib/api/federation";
|
|
|
|
describe("ConnectionList", (): void => {
|
|
const mockConnections: ConnectionDetails[] = [
|
|
{
|
|
id: "conn-1",
|
|
workspaceId: "workspace-1",
|
|
remoteInstanceId: "instance-work-001",
|
|
remoteUrl: "https://mosaic.work.example.com",
|
|
remotePublicKey: "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----",
|
|
remoteCapabilities: {
|
|
supportsQuery: true,
|
|
supportsCommand: true,
|
|
supportsEvent: true,
|
|
supportsAgentSpawn: true,
|
|
protocolVersion: "1.0",
|
|
},
|
|
status: FederationConnectionStatus.ACTIVE,
|
|
metadata: {
|
|
name: "Work Instance",
|
|
description: "Corporate Mosaic instance",
|
|
},
|
|
createdAt: new Date("2026-02-01").toISOString(),
|
|
updatedAt: new Date("2026-02-01").toISOString(),
|
|
connectedAt: new Date("2026-02-01").toISOString(),
|
|
disconnectedAt: null,
|
|
},
|
|
{
|
|
id: "conn-2",
|
|
workspaceId: "workspace-1",
|
|
remoteInstanceId: "instance-partner-001",
|
|
remoteUrl: "https://mosaic.partner.example.com",
|
|
remotePublicKey: "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----",
|
|
remoteCapabilities: {
|
|
supportsQuery: true,
|
|
supportsCommand: false,
|
|
supportsEvent: true,
|
|
supportsAgentSpawn: false,
|
|
protocolVersion: "1.0",
|
|
},
|
|
status: FederationConnectionStatus.PENDING,
|
|
metadata: {
|
|
name: "Partner Instance",
|
|
description: "Awaiting acceptance",
|
|
},
|
|
createdAt: new Date("2026-02-02").toISOString(),
|
|
updatedAt: new Date("2026-02-02").toISOString(),
|
|
connectedAt: null,
|
|
disconnectedAt: null,
|
|
},
|
|
];
|
|
|
|
const mockOnAccept = vi.fn();
|
|
const mockOnReject = vi.fn();
|
|
const mockOnDisconnect = vi.fn();
|
|
|
|
it("should render loading state", (): void => {
|
|
render(<ConnectionList connections={[]} isLoading={true} />);
|
|
expect(screen.getByText(/loading/i)).toBeInTheDocument();
|
|
});
|
|
|
|
it("should render empty state when no connections", (): void => {
|
|
render(<ConnectionList connections={[]} isLoading={false} />);
|
|
expect(screen.getByText(/no federation connections/i)).toBeInTheDocument();
|
|
});
|
|
|
|
it("should render PDA-friendly empty state message", (): void => {
|
|
render(<ConnectionList connections={[]} isLoading={false} />);
|
|
expect(screen.getByText(/ready to connect/i)).toBeInTheDocument();
|
|
});
|
|
|
|
it("should render all connections", (): void => {
|
|
render(<ConnectionList connections={mockConnections} isLoading={false} />);
|
|
expect(screen.getByText("Work Instance")).toBeInTheDocument();
|
|
expect(screen.getByText("Partner Instance")).toBeInTheDocument();
|
|
});
|
|
|
|
it("should group connections by status", (): void => {
|
|
render(<ConnectionList connections={mockConnections} isLoading={false} />);
|
|
expect(screen.getByRole("heading", { name: "Active" })).toBeInTheDocument();
|
|
expect(screen.getByRole("heading", { name: "Pending" })).toBeInTheDocument();
|
|
});
|
|
|
|
it("should pass handlers to connection cards", (): void => {
|
|
render(
|
|
<ConnectionList
|
|
connections={mockConnections}
|
|
isLoading={false}
|
|
onAccept={mockOnAccept}
|
|
onReject={mockOnReject}
|
|
onDisconnect={mockOnDisconnect}
|
|
/>
|
|
);
|
|
|
|
const acceptButtons = screen.getAllByRole("button", { name: /accept/i });
|
|
const disconnectButtons = screen.getAllByRole("button", { name: /disconnect/i });
|
|
|
|
expect(acceptButtons.length).toBeGreaterThan(0);
|
|
expect(disconnectButtons.length).toBeGreaterThan(0);
|
|
});
|
|
|
|
it("should handle null connections array", (): void => {
|
|
render(<ConnectionList connections={null} isLoading={false} />);
|
|
expect(screen.getByText(/no federation connections/i)).toBeInTheDocument();
|
|
});
|
|
|
|
it("should render with compact layout when compact prop is true", (): void => {
|
|
render(<ConnectionList connections={mockConnections} isLoading={false} compact={true} />);
|
|
// Verify connections are rendered
|
|
expect(screen.getByText("Work Instance")).toBeInTheDocument();
|
|
});
|
|
});
|