import React from 'react'; import { render, screen, fireEvent, within } from '@testing-library/react'; jest.mock('lucide-react', () => new Proxy( {}, { get: (_t, prop) => prop === '__esModule' ? true : (props) => , } ) ); import Agents from '@/pages/doormile/agents/Agents'; /** * The Agents page. * * It renders a hand-maintained table (lib/agentNetwork.js) rather than a live * feed, so the risk here is not stale data — agentNetwork.test.js guards the * table — it is that the page silently drops something. A detail panel that * renders an empty string, or a card whose status has no colour, looks like a * quiet page rather than a broken one. */ describe('the Agents page', () => { it('lists all nine agents', () => { render(); ['JARVIS', 'Dispatch', 'Exception', 'Express Dispatch', 'Order', 'Route Optimizer', 'Hub', 'Fleet', 'Customer'].forEach( (name) => { expect(screen.getAllByText(name).length).toBeGreaterThan(0); } ); }); it('opens on Dispatch rather than an empty panel', () => { // It is the only agent with anything to show. Landing on a blank detail // panel reads as a broken page. render(); expect(screen.getByText('DISPATCH_AGENT')).toBeInTheDocument(); expect(screen.getByText(/only agent doing real work/i)).toBeInTheDocument(); }); it('swaps the whole detail panel when another agent is picked', () => { render(); fireEvent.click(screen.getByRole('button', { name: /Exception/ })); expect(screen.getByText('EXCEPTION_AGENT')).toBeInTheDocument(); expect(screen.getByText(/17,188 of the 18,146 lines/)).toBeInTheDocument(); // and the previous agent's record is gone, not merely scrolled away expect(screen.queryByText('DISPATCH_AGENT')).not.toBeInTheDocument(); }); it('marks the selected card for a screen reader, not just with a colour', () => { render(); const exception = screen.getByRole('button', { name: /Exception/ }); expect(exception).toHaveAttribute('aria-pressed', 'false'); fireEvent.click(exception); expect(exception).toHaveAttribute('aria-pressed', 'true'); }); it('says a task-driven agent listens to nothing, rather than leaving a blank', () => { render(); fireEvent.click(screen.getByRole('button', { name: /Fleet/ })); expect(screen.getByText(/runs only when JARVIS hands it a task/i)).toBeInTheDocument(); }); it('shows the pipeline, both halves of it', () => { render(); expect(screen.getByText('doormile_backend')).toBeInTheDocument(); expect(screen.getByText('NATS JetStream')).toBeInTheDocument(); expect(screen.getByText('Event-driven')).toBeInTheDocument(); expect(screen.getByText('Task-driven')).toBeInTheDocument(); expect(screen.getByText('Alert, never act')).toBeInTheDocument(); }); it('states that nothing is switched on, in the KPI row', () => { // The page's central claim: the swarm observes and alerts, and does not // write to the system. render(); expect(screen.getByText(/Gates on/)).toBeInTheDocument(); expect(screen.getByText(/All autonomy gates OFF/)).toBeInTheDocument(); }); it('admits the figures are a snapshot, not a feed', () => { // A status board that looks live and is four days old is worse than one // that says how old it is. render(); expect(screen.getAllByText(/Last run · 16–20 Sep 2026/).length).toBeGreaterThan(0); }); it('shows the repeated-error count on the log rows', () => { render(); const errs = screen.getAllByText('×8,594'); expect(errs).toHaveLength(2); }); it('every card carries a real trigger label, not undefined', () => { const { container } = render(); const cards = container.querySelectorAll('button[aria-pressed]'); expect(cards).toHaveLength(9); cards.forEach((card) => { const text = within(card).getByText(/NATS event|NATS pull|JARVIS task|In-process/); expect(text).toBeInTheDocument(); }); }); });