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>
This commit is contained in:
@@ -0,0 +1,189 @@
|
||||
/**
|
||||
* InitiateConnectionDialog Component Tests
|
||||
* Following TDD - write tests first!
|
||||
*/
|
||||
|
||||
import { describe, it, expect, vi } from "vitest";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { InitiateConnectionDialog } from "./InitiateConnectionDialog";
|
||||
|
||||
describe("InitiateConnectionDialog", (): void => {
|
||||
const mockOnInitiate = vi.fn();
|
||||
const mockOnCancel = vi.fn();
|
||||
|
||||
it("should render when open is true", (): void => {
|
||||
render(
|
||||
<InitiateConnectionDialog open={true} onInitiate={mockOnInitiate} onCancel={mockOnCancel} />
|
||||
);
|
||||
expect(screen.getByText(/connect to remote instance/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should not render when open is false", (): void => {
|
||||
const { container } = render(
|
||||
<InitiateConnectionDialog open={false} onInitiate={mockOnInitiate} onCancel={mockOnCancel} />
|
||||
);
|
||||
expect(container.firstChild).toBeNull();
|
||||
});
|
||||
|
||||
it("should render PDA-friendly title", (): void => {
|
||||
render(
|
||||
<InitiateConnectionDialog open={true} onInitiate={mockOnInitiate} onCancel={mockOnCancel} />
|
||||
);
|
||||
expect(screen.getByText(/connect to remote instance/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should render PDA-friendly description", (): void => {
|
||||
render(
|
||||
<InitiateConnectionDialog open={true} onInitiate={mockOnInitiate} onCancel={mockOnCancel} />
|
||||
);
|
||||
expect(screen.getByText(/enter the url/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should render URL input field", (): void => {
|
||||
render(
|
||||
<InitiateConnectionDialog open={true} onInitiate={mockOnInitiate} onCancel={mockOnCancel} />
|
||||
);
|
||||
expect(screen.getByLabelText(/instance url/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should render Connect button", (): void => {
|
||||
render(
|
||||
<InitiateConnectionDialog open={true} onInitiate={mockOnInitiate} onCancel={mockOnCancel} />
|
||||
);
|
||||
expect(screen.getByRole("button", { name: /connect/i })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should render Cancel button", (): void => {
|
||||
render(
|
||||
<InitiateConnectionDialog open={true} onInitiate={mockOnInitiate} onCancel={mockOnCancel} />
|
||||
);
|
||||
expect(screen.getByRole("button", { name: /cancel/i })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should call onCancel when cancel button clicked", async (): Promise<void> => {
|
||||
const user = userEvent.setup();
|
||||
render(
|
||||
<InitiateConnectionDialog open={true} onInitiate={mockOnInitiate} onCancel={mockOnCancel} />
|
||||
);
|
||||
|
||||
const cancelButton = screen.getByRole("button", { name: /cancel/i });
|
||||
await user.click(cancelButton);
|
||||
|
||||
expect(mockOnCancel).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("should call onInitiate with URL when connect button clicked", async (): Promise<void> => {
|
||||
const user = userEvent.setup();
|
||||
render(
|
||||
<InitiateConnectionDialog open={true} onInitiate={mockOnInitiate} onCancel={mockOnCancel} />
|
||||
);
|
||||
|
||||
const urlInput = screen.getByLabelText(/instance url/i);
|
||||
await user.type(urlInput, "https://mosaic.example.com");
|
||||
|
||||
const connectButton = screen.getByRole("button", { name: /connect/i });
|
||||
await user.click(connectButton);
|
||||
|
||||
expect(mockOnInitiate).toHaveBeenCalledWith("https://mosaic.example.com");
|
||||
});
|
||||
|
||||
it("should disable connect button when URL is empty", (): void => {
|
||||
render(
|
||||
<InitiateConnectionDialog open={true} onInitiate={mockOnInitiate} onCancel={mockOnCancel} />
|
||||
);
|
||||
const connectButton = screen.getByRole("button", { name: /connect/i });
|
||||
expect(connectButton).toBeDisabled();
|
||||
});
|
||||
|
||||
it("should enable connect button when URL is entered", async (): Promise<void> => {
|
||||
const user = userEvent.setup();
|
||||
render(
|
||||
<InitiateConnectionDialog open={true} onInitiate={mockOnInitiate} onCancel={mockOnCancel} />
|
||||
);
|
||||
|
||||
const urlInput = screen.getByLabelText(/instance url/i);
|
||||
await user.type(urlInput, "https://mosaic.example.com");
|
||||
|
||||
const connectButton = screen.getByRole("button", { name: /connect/i });
|
||||
expect(connectButton).not.toBeDisabled();
|
||||
});
|
||||
|
||||
it("should show validation error for invalid URL", async (): Promise<void> => {
|
||||
const user = userEvent.setup();
|
||||
render(
|
||||
<InitiateConnectionDialog open={true} onInitiate={mockOnInitiate} onCancel={mockOnCancel} />
|
||||
);
|
||||
|
||||
const urlInput = screen.getByLabelText(/instance url/i);
|
||||
await user.type(urlInput, "not-a-valid-url");
|
||||
|
||||
const connectButton = screen.getByRole("button", { name: /connect/i });
|
||||
await user.click(connectButton);
|
||||
|
||||
expect(screen.getByText(/please enter a valid url/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should clear input when dialog is closed", async (): Promise<void> => {
|
||||
const user = userEvent.setup();
|
||||
const { rerender } = render(
|
||||
<InitiateConnectionDialog open={true} onInitiate={mockOnInitiate} onCancel={mockOnCancel} />
|
||||
);
|
||||
|
||||
const urlInput = screen.getByLabelText(/instance url/i);
|
||||
await user.type(urlInput, "https://mosaic.example.com");
|
||||
|
||||
// Close dialog
|
||||
rerender(
|
||||
<InitiateConnectionDialog open={false} onInitiate={mockOnInitiate} onCancel={mockOnCancel} />
|
||||
);
|
||||
|
||||
// Reopen dialog
|
||||
rerender(
|
||||
<InitiateConnectionDialog open={true} onInitiate={mockOnInitiate} onCancel={mockOnCancel} />
|
||||
);
|
||||
|
||||
const newUrlInput = screen.getByLabelText(/instance url/i);
|
||||
expect(newUrlInput).toHaveValue("");
|
||||
});
|
||||
|
||||
it("should show loading state when isLoading is true", (): void => {
|
||||
render(
|
||||
<InitiateConnectionDialog
|
||||
open={true}
|
||||
onInitiate={mockOnInitiate}
|
||||
onCancel={mockOnCancel}
|
||||
isLoading={true}
|
||||
/>
|
||||
);
|
||||
expect(screen.getByText(/connecting/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should disable buttons when isLoading is true", (): void => {
|
||||
render(
|
||||
<InitiateConnectionDialog
|
||||
open={true}
|
||||
onInitiate={mockOnInitiate}
|
||||
onCancel={mockOnCancel}
|
||||
isLoading={true}
|
||||
/>
|
||||
);
|
||||
const connectButton = screen.getByRole("button", { name: /connecting/i });
|
||||
const cancelButton = screen.getByRole("button", { name: /cancel/i });
|
||||
|
||||
expect(connectButton).toBeDisabled();
|
||||
expect(cancelButton).toBeDisabled();
|
||||
});
|
||||
|
||||
it("should display error message when error prop is provided", (): void => {
|
||||
render(
|
||||
<InitiateConnectionDialog
|
||||
open={true}
|
||||
onInitiate={mockOnInitiate}
|
||||
onCancel={mockOnCancel}
|
||||
error="Unable to connect to remote instance"
|
||||
/>
|
||||
);
|
||||
expect(screen.getByText("Unable to connect to remote instance")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user