openAgentAndFileContext.test.tsx3.9 KBView on GitHub
import React from 'react';
import { render, screen } from '@testing-library/react';

import { ContextBadgeRow } from '@/modules/cedar-os/src/cedar-os-components/chatInput/ContextBadgeRow';
import { useCedarStore } from '@/modules/cedar-os/src/store/CedarStore';

/**
 * The two things the chat had no way of knowing: WHICH agent workspace is on screen, and
 * WHICH file is open inside it.
 *
 * Before this, opening a file from a file browser (the agent's Output tab, a deal's Files
 * tab, the Brain explorer) published nothing at all, and an agent workspace published
 * nothing anywhere — so a question asked while looking at both arrived with neither.
 */

function seed(over: Record<string, unknown> = {}) {
  useCedarStore.setState((state) => ({
    ...state,
    activeConversationId: null,
    conversationSelection: [],
    conversations: {},
    isThreadOpen: false,
    isConversationOpen: false,
    selectedThreadId: null,
    threadData: {},
    mainThreadId: 'main',
    threadMap: { main: { id: 'main', name: 'Main', messages: [] } },
    additionalContext: {},
    chatContextVisibility: {},
    excludedManualContextKeys: new Set<string>(),
    docSelectedText: null,
    openChannelThread: null,
    teachModeAgentContext: null,
    activeCanvasId: null,
    currentConversationList: [],
    openAgent: { agentId: 'agent_1', name: 'Meeting prep', avatar: null },
    activeDoc: {
      path: 'user/agent-agent_1/overview',
      documentId: 'doc_1',
      title: 'Overview',
      documentType: 'document',
    },
    ...over,
  }));
}

const wire = () =>
  useCedarStore.getState().buildMergedContextForMailSendMessage() as unknown as Record<
    string,
    { data: unknown } | undefined
  >;

describe('the open agent and the open file, as context', () => {
  it('puts both on the wire as pointers', () => {
    seed();
    const ctx = wire();

    expect(ctx.openAgentId).toEqual({ data: 'agent_1' });
    expect(ctx.openAgentName).toEqual({ data: 'Meeting prep' });
    expect(ctx.activeDocPath).toEqual({ data: 'user/agent-agent_1/overview' });
    // The id is what `manage-context` attaches a file by — a path is not an id.
    expect(ctx.activeDocId).toEqual({ data: 'doc_1' });
    expect(ctx.activeDocTitle).toEqual({ data: 'Overview' });
    expect(ctx.activeDocDocumentType).toEqual({ data: 'document' });
  });

  it('draws a badge for each, the agent ahead of the file it contains', () => {
    seed();
    render(<ContextBadgeRow />);

    const agent = screen.getByLabelText('Selected agent Meeting prep');
    const file = screen.getByLabelText('Selected document Overview');
    // Reading order matches the nesting on screen: the file is inside the agent.
    expect(agent.compareDocumentPosition(file) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy();
  });

  it('falls back to the path when the surface knows no title', () => {
    seed({ activeDoc: { path: 'user/files/q3-pipeline.md' } });
    render(<ContextBadgeRow />);

    expect(screen.getByLabelText('Selected document Q3 Pipeline')).toBeInTheDocument();
  });

  it('dismissing a badge takes its context off the wire too', () => {
    seed();
    useCedarStore.getState().setBadgeVisible('agent', false);
    useCedarStore.getState().setBadgeVisible('document', false);

    const ctx = wire();
    expect(ctx.openAgentId).toBeUndefined();
    expect(ctx.activeDocPath).toBeUndefined();
  });

  it('stands the file badge down once the file is committed to the thread', () => {
    seed({
      threadMap: {
        main: {
          id: 'main',
          name: 'Main',
          messages: [],
          chatContext: { items: [{ kind: 'file', id: 'doc_1', label: 'Overview' }] },
        },
      },
    });
    render(<ContextBadgeRow />);

    // ChatContextRow already draws it, hydrated in full — one file, one chip.
    expect(screen.queryByLabelText('Selected document Overview')).toBeNull();
    expect(screen.getByLabelText('Selected agent Meeting prep')).toBeInTheDocument();
  });
});