// @vitest-environment jsdom import '@testing-library/jest-dom/vitest'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { cleanup, render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { HttpResponse, http } from 'msw'; import { setupServer } from 'msw/node'; import { MemoryRouter } from 'react-router-dom'; import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it } from 'vitest'; import { LearningPage } from '../../apps/dashboard/src/features/learning/learning-page.js'; const TRAJECTORY_ID = 'traj-1'; let detailCalls: string[] = []; function summary() { return { total: 1, provisional: 0, eligible: 1, excluded: 0, invalidated: 0, needsReview: 0, humanCreated: 1, humanImproved: 0, humanCompletedAi: 0, autonomous: 0, recipientInitiated: 0, uncertain: 0, exact: 1, fingerprint: 0, inferred: 0, }; } function trajectory() { return { id: TRAJECTORY_ID, accountId: '@t2_learner:reddit.com', conversationId: '!room:reddit.com', sentMessageId: 7, subreddit: 'cats', sourceType: 'chat' as const, policyMode: 'r4r-media' as const, firstHumanEventId: '$human1', outcomeEventId: '$image1', outcomeType: 'image-received' as const, creditClass: 'human-created-success' as const, attributionConfidence: 'exact' as const, status: 'eligible' as const, maturityAt: 1_700_000_100_000, invalidatedAt: null, invalidationReason: null, reviewedAt: null, createdAt: 1_700_000_000_000, updatedAt: 1_700_000_050_000, }; } function decision() { return { id: 'dec-1', trajectoryId: TRAJECTORY_ID, accountId: '@t2_learner:reddit.com', decisionEventId: '$human1', stage: 'media-invitation' as const, context: [{ eventId: '$in1', timestamp: 1_700_000_010_000, role: 'recipient' as const, actor: 'recipient' as const, kind: 'inbound-text' as const, text: 'hey how was your weekend', }], chosenResponse: 'no pressure, only if you want to', turnsUntilOutcome: 2, isDirectMediaInvitation: true, qualityStatus: 'eligible' as const, createdAt: 1_700_000_000_000, updatedAt: 1_700_000_050_000, }; } const server = setupServer( http.get('/api/learning', () => HttpResponse.json({ summary: summary(), trajectories: [trajectory()], captureEnabled: true, generationEnabled: false, shadowMode: true, policyVersion: 'baseline-v1', })), http.get('/api/learning/trajectories/:id', ({ params }) => { detailCalls.push(String(params.id)); return HttpResponse.json({ trajectory: trajectory(), decisions: [decision()] }); }), ); function renderLearning() { const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false }, mutations: { retry: false } }, }); return render( , ); } beforeAll(() => server.listen({ onUnhandledRequest: 'error' })); afterEach(() => { server.resetHandlers(); cleanup(); }); afterAll(() => server.close()); beforeEach(() => { detailCalls = []; }); describe('learning page trajectory detail', () => { it('does not fetch decisions until a row is expanded', async () => { renderLearning(); await screen.findByText(/human created success/i); expect(detailCalls).toEqual([]); }); it('reveals the redacted conversation evidence when expanded', async () => { renderLearning(); await screen.findByText(/human created success/i); await userEvent.click(screen.getByRole('button', { name: /show conversation evidence/i })); expect(await screen.findByText('no pressure, only if you want to')).toBeInTheDocument(); expect(screen.getByText('hey how was your weekend')).toBeInTheDocument(); expect(screen.getByText('!room:reddit.com')).toBeInTheDocument(); expect(screen.getByText(/direct media ask/i)).toBeInTheDocument(); await waitFor(() => expect(detailCalls).toEqual([TRAJECTORY_ID])); }); it('links the evidence to the owning account\'s conversation', async () => { renderLearning(); await screen.findByText(/human created success/i); await userEvent.click(screen.getByRole('button', { name: /show conversation evidence/i })); const link = await screen.findByRole('link', { name: '!room:reddit.com' }); // The account id has to survive in the owned id or the route 404s. expect(link).toHaveAttribute( 'href', `/conversations/${encodeURIComponent('%40t2_learner%3Areddit.com~!room%3Areddit.com')}`, ); }); it('collapses the evidence again on a second click', async () => { renderLearning(); await screen.findByText(/human created success/i); const toggle = screen.getByRole('button', { name: /show conversation evidence/i }); await userEvent.click(toggle); await screen.findByText('no pressure, only if you want to'); await userEvent.click(toggle); await waitFor(() => { expect(screen.queryByText('no pressure, only if you want to')).not.toBeInTheDocument(); }); expect(toggle).toHaveAttribute('aria-expanded', 'false'); }); });