import { describe, it, expect, beforeEach, vi } from 'vitest' import { render, screen, act, waitFor } from '@testing-library/react' import userEvent from '@testing-library/user-event' import { MemoryRouter } from 'react-router-dom' import { Module2 } from './Module2' import { useSession } from '@/store/session' import type { WsEvent } from '@/types' let emit: (e: WsEvent) => void = () => {} vi.mock('@/lib/api', async (orig) => ({ ...(await orig()), openTeamActivity: (_teamId: string, on: (e: WsEvent) => void) => { emit = on return () => {} }, sendPrompt: vi.fn().mockResolvedValue(undefined), })) function renderPage() { return render( , ) } /** Click a canned prompt and let the agent reach a terminal (success) step. */ async function completePrompt(user: ReturnType, id: string) { await user.click(screen.getByTestId(`prompt-${id}`)) act(() => emit({ type: 'node:activity', teamId: 'x', kind: 'response', label: 'Agent finished', ts: '' })) await waitFor(() => expect(screen.getByTestId(`prompt-${id}`)).toHaveAttribute('data-state', 'done')) } describe('Module2', () => { beforeEach(() => { useSession.getState().reset() sessionStorage.clear() }) it('renders the heading', () => { renderPage() expect(screen.getByRole('heading', { name: /skills.*policies/i })).toBeInTheDocument() }) it('is a chat with the three canned prompts — no live feed / build & flash', () => { renderPage() expect(screen.getByTestId('agent-chat')).toBeInTheDocument() expect(screen.getByTestId('prompt-i2c')).toBeInTheDocument() expect(screen.getByTestId('prompt-count')).toBeInTheDocument() expect(screen.getByTestId('prompt-scroll')).toBeInTheDocument() expect(screen.queryByTestId('live-board-feed')).toBeNull() expect(screen.queryByTestId('activity-log')).toBeNull() }) it('reveals "what\'s next" (the ADD layers) only after all three prompts succeed', async () => { const user = userEvent.setup() renderPage() expect(screen.queryByTestId('whats-next')).toBeNull() expect(screen.queryByLabelText(/layer 2/i)).toBeNull() await completePrompt(user, 'i2c') await completePrompt(user, 'count') expect(screen.queryByTestId('whats-next')).toBeNull() // still one to go await completePrompt(user, 'scroll') expect(screen.getByTestId('whats-next')).toBeInTheDocument() expect(screen.getByLabelText(/layer 2/i)).toBeInTheDocument() }) it('gates Proceed until all prompts ran AND L2 + L3 are filled', async () => { const user = userEvent.setup() useSession.getState().setAddLayer('L2', 'escalate on critical') useSession.getState().setAddLayer('L3', 'drive damper on critical') renderPage() await completePrompt(user, 'i2c') await completePrompt(user, 'count') await completePrompt(user, 'scroll') const proceed = screen.getByRole('button', { name: /proceed/i }) expect(proceed).toBeEnabled() }) it('marks m2 complete on Proceed', async () => { const user = userEvent.setup() renderPage() await completePrompt(user, 'i2c') await completePrompt(user, 'count') await completePrompt(user, 'scroll') await user.type(screen.getByLabelText(/layer 2/i), 'L2 text') await user.type(screen.getByLabelText(/layer 3/i), 'L3 text') await user.click(screen.getByRole('button', { name: /proceed/i })) expect(useSession.getState().phases.m2).toBe(true) }) })