HomeAgenda.test.tsx4.8 KBView on GitHub
/**
 * "Daily agenda" on the home hero — today's TASKS, in one always-expanded card.
 *
 * Three things are pinned here:
 *
 *  1. TASKS ONLY. The meetings that used to share this card live in the home widget rail
 *     now, so a meetings column reappearing here is a regression.
 *  2. NO FOLD AND NO CLAMP. The card folded back when it competed for the single column
 *     with the meetings and the agent list; both have moved out, so a clamp now only hides
 *     the tasks the screen exists for.
 *  3. THE TITLE IS THE LAYOUT'S, not the editor's — the document's own date heading renders
 *     empty on this surface.
 */

import { fireEvent, render, screen } from '@testing-library/react';
import type { ReactNode } from 'react';

jest.mock('@/modules/auth/utils/auth-client', () => ({
  useSession: () => ({ data: { user: { id: 'user-1', name: 'Jesse Li' } }, isPending: false }),
}));

// The agenda's own fetch rides on its MOUNT, so the spy fires from a mount-only
// effect rather than from the render body — a re-render is free, a remount is the bug.
const mockAgendaDocumentMounted = jest.fn();
jest.mock('@/modules/agentCanvas/components/AgendaDocument', () => {
  const { useEffect } = require('react');
  return {
    AgendaDocument: () => {
      useEffect(() => {
        mockAgendaDocumentMounted();
      }, []);
      return <div data-testid="agenda-document">AGENDA</div>;
    },
  };
});
// Left UNMOCKED deliberately: if the agenda ever reaches for the meetings column again, this
// sentinel is not here to catch it — the import itself would pull the whole calendar stack in
// and the suite would fail loudly rather than quietly rendering a stub.
jest.mock('@/modules/agentCanvas/context/MultiDayEditorRegistry', () => ({
  MultiDayEditorRegistryProvider: ({ children }: { children: ReactNode }) => <>{children}</>,
}));
jest.mock('@/modules/agentCanvas/context/CrossEditorDragBus', () => ({
  CrossEditorDragBusProvider: ({ children }: { children: ReactNode }) => <>{children}</>,
}));
const mockNavigate = jest.fn();
jest.mock('react-router', () => ({ useNavigate: () => mockNavigate }));

jest.mock('@/providers/query-provider', () => ({
  useTRPC: () => ({
    aopAgents: { getAgendaInstructions: { queryOptions: () => ({ queryKey: ['agendaInstructions'] }) } },
  }),
}));

jest.mock('@tanstack/react-query', () => ({
  useQuery: () => ({ data: { agentId: 'agent-1' } }),
}));

import { HomeAgenda } from '@/modules/home/components/HomeAgenda';

const body = () => screen.getByTestId('home-agenda-body');
const card = () => screen.getByTestId('home-agenda');

beforeEach(() => {
  mockAgendaDocumentMounted.mockClear();
});

describe('HomeAgenda', () => {
  it('holds the tasks document and NOT the meetings', () => {
    render(<HomeAgenda />);

    expect(body()).toContainElement(screen.getByTestId('agenda-document'));
    // The meetings moved to the home widget rail. A two-column agenda card is the old shape.
    expect(screen.queryByTestId('meetings')).not.toBeInTheDocument();
    expect(body().className).not.toContain('grid-cols');
  });

  it('carries the "Daily agenda" title INSIDE the container, above the document', () => {
    render(<HomeAgenda />);
    const title = screen.getByRole('heading', { name: 'Daily agenda' });

    // Inside the card it controls, but outside the editor: the document's own date
    // heading renders empty on this surface (see DateHeadingNode's `surface` option).
    expect(card()).toContainElement(title);
    expect(body()).not.toContainElement(title);
    expect(title.compareDocumentPosition(body()) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy();
  });

  it('is always expanded — no toggle, no clamp, no fade', () => {
    render(<HomeAgenda />);

    // The fold existed to keep the agent list below reachable; that list is gone from the
    // hero, so a clamp here would only hide the tasks the screen exists for.
    expect(screen.queryByRole('button', { expanded: false })).not.toBeInTheDocument();
    expect(screen.queryByRole('button', { expanded: true })).not.toBeInTheDocument();
    expect(body().className).not.toContain('max-h-');
    expect(body().className).not.toContain('overflow-hidden');
    expect(body()).toBeVisible();
  });

  it('mounts the agenda document, so its query and Y.Doc sync are live', () => {
    render(<HomeAgenda />);
    expect(mockAgendaDocumentMounted).toHaveBeenCalled();
    expect(screen.getByTestId('agenda-document')).toBeInTheDocument();
  });

  it('sends the instructions button to the agent\'s own config page', () => {
    // The instructions ARE the agent's config, so a dialog showing the prose on
    // its own left the reader one click short of every question it raised.
    render(<HomeAgenda />);
    fireEvent.click(screen.getByRole('button', { name: /Agenda Agent Instructions/i }));
    expect(mockNavigate).toHaveBeenCalledWith('/agents/agent-1?tab=config');
  });
});