Files
stack/apps/web/src/components/federation/ConnectionList.test.tsx
Jason Woltje 5cf02e824b feat(#91): implement Connection Manager UI for federation
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>
2026-02-03 14:03:44 -06:00

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();
});
});