brainHero.test.tsx5.8 KBView on GitHub /**
* The /brain hero — the Brain's landing screen, built on the same machinery as the /agent home
* hero (see tests/modules/home/agentHomeHero.test.tsx).
*
* Two things are pinned here:
*
* 1. THE TWO DESTINATIONS — Playbook and Knowledge base, each pointing at the route that
* actually serves it, with the AGENTS rendered in place rather than behind a third card. A card whose
* href drifts is a dead end that still looks like a working button; a card that only reveals
* a grid is a click charged for nothing.
* 2. THE RAIL IS RECENTS — where /agent puts configurable widgets, /brain puts the files you
* last touched, and clicking one opens it in the Knowledge explorer rather than nowhere.
*/
import { fireEvent, render, screen } from '@testing-library/react';
import type { ReactNode } from 'react';
const mockNavigate = jest.fn();
jest.mock('motion/react', () => {
const MotionDiv = ({ children, className }: { children?: ReactNode; className?: string }) => (
<div className={className}>{children}</div>
);
return { motion: new Proxy({}, { get: () => MotionDiv }) };
});
jest.mock('react-router', () => ({ useNavigate: () => mockNavigate }));
jest.mock('@/modules/cedar-os/src/cedar-os-components/chatComponents/welcomeShortcuts', () => ({
useAllActionsModal: () => [false, jest.fn()],
}));
// The agent-template modal is only mounted once its pill is clicked; the pill row here is about
// which pills exist, not about the catalogue behind one of them.
jest.mock('@/modules/cedar-os/src/cedar-os-components/chatComponents/AgentLibrary', () => ({
AgentLibraryDialog: () => null,
}));
// The agent grid pulls the whole agent stack (avatars, the create mutation, tRPC). It has its
// own test; here it stands in as a sentinel, so the hero's ORDER can be asserted without it.
jest.mock('@/modules/agents/components/AgentsGrid', () => ({
AgentsGrid: () => <div data-testid="agents-grid">AGENTS</div>,
}));
// Named `mock*` so jest's hoisting of the factory above the imports still resolves it.
const mockUseQuery = jest.fn();
jest.mock('@tanstack/react-query', () => ({ useQuery: () => mockUseQuery() }));
jest.mock('@/providers/query-provider', () => ({
useTRPC: () => ({
files: { searchForLink: { queryOptions: () => ({}) } },
aop: { listAopsForUser: { queryOptions: () => ({}) } },
}),
}));
// The administered-user picker and the Deals-default AOP fetch are a self-contained sentinel
// here (both have their own tests) — this suite is about the hero's own composition, not
// about who the picker lets you become or which conversation type it defaults to.
jest.mock('@/modules/administeredUser', () => ({
AdministeredUserBar: () => null,
useTargetUserId: () => undefined,
}));
import {
BrainFilesRail,
BrainHeroBelowChat,
BrainHeroGreeting,
} from '@/modules/brain/components/BrainHero';
beforeEach(() => {
mockNavigate.mockClear();
mockUseQuery.mockReturnValue({ data: undefined, isLoading: false });
});
describe('the Brain hero', () => {
it('greets you by the surface, not by the time of day', () => {
render(<BrainHeroGreeting />);
expect(screen.getByText('Welcome to your Brain')).toBeInTheDocument();
});
it.each([
['Playbook', '/brain/playbooks'],
['Knowledge base', '/brain/knowledge'],
])('the %s card goes to %s', (title, href) => {
render(<BrainHeroBelowChat onPrompt={jest.fn()} />);
fireEvent.click(screen.getByText(title).closest('button')!);
expect(mockNavigate).toHaveBeenCalledWith(href);
});
it('offers exactly two destinations, and renders the agents themselves in place', () => {
render(<BrainHeroBelowChat onPrompt={jest.fn()} />);
expect(screen.getAllByTestId('brain-destination-card')).toHaveLength(2);
// Agents are the body of the screen, not a third card pointing at one.
expect(screen.getByTestId('agents-grid')).toBeInTheDocument();
expect(screen.queryByText('Agents')).not.toBeInTheDocument();
});
it('puts the agents LAST — the one block with no ceiling on its height', () => {
const { container } = render(<BrainHeroBelowChat onPrompt={jest.fn()} />);
// The agents section now also carries the administered-user bar and the AOP filter
// ABOVE the grid, so the direct child is that section (which contains the grid), not
// the grid itself.
const stack = Array.from(container.firstElementChild?.children ?? []);
const agentsSection = screen.getByTestId('agents-section');
expect(stack.at(-1)).toBe(agentsSection);
expect(agentsSection).toContainElement(screen.getByTestId('agents-grid'));
});
it('loads a pill prompt into the composer instead of sending it', () => {
const onPrompt = jest.fn();
render(<BrainHeroBelowChat onPrompt={onPrompt} />);
fireEvent.click(screen.getByText('Improve playbook'));
expect(onPrompt).toHaveBeenCalledWith(expect.stringContaining('playbook'));
});
});
describe('the Brain rail', () => {
it('lists recent files and opens one in the Knowledge explorer', () => {
mockUseQuery.mockReturnValue({
data: [
{
id: 'doc_1',
label: 'ICP notes',
path: 'user/brain/icp',
documentType: 'custom',
lastOpened: '2026-08-30T10:00:00.000Z',
updatedAt: '2026-08-29T10:00:00.000Z',
},
],
isLoading: false,
});
render(<BrainFilesRail />);
fireEvent.click(screen.getByText('ICP notes'));
expect(mockNavigate).toHaveBeenCalledWith('/brain/knowledge?documentId=doc_1');
});
it('says so when there is nothing recent, rather than showing an empty box', () => {
mockUseQuery.mockReturnValue({ data: [], isLoading: false });
render(<BrainFilesRail />);
expect(screen.getByText(/Nothing opened yet/)).toBeInTheDocument();
expect(screen.queryAllByTestId('brain-recent-file')).toHaveLength(0);
});
});