import { describe, it, expect, beforeEach, vi } from 'vitest'
import { render, screen, act } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { MemoryRouter } from 'react-router-dom'
import { BuildFlash } from './BuildFlash'
import { useSession } from '@/store/session'
import type { WsEvent } from '@/types'
// Capture the live-activity callback so tests can push node events.
const sendPrompt = vi.fn()
let liveOnEvent: ((e: WsEvent) => void) | null = null
const closeSpy = vi.fn()
vi.mock('@/lib/api', () => ({
AGENT: 'default',
sendPrompt: (...args: unknown[]) => sendPrompt(...args),
openTeamActivity: (_teamId: string, onEvent: (e: WsEvent) => void) => {
liveOnEvent = onEvent
return closeSpy
},
// OpenYourNode (rendered here) reads the runtime mode.
getMode: () => Promise.resolve({ localMode: false }),
}))
describe('BuildFlash', () => {
beforeEach(() => {
useSession.getState().reset()
sessionStorage.clear()
sendPrompt.mockReset()
sendPrompt.mockResolvedValue(undefined)
closeSpy.mockReset()
liveOnEvent = null
})
it('disables Send until there is a prompt', () => {
render()
expect(screen.getByRole('button', { name: /send/i })).toBeDisabled()
})
it('shows the Live board badge', () => {
render()
expect(screen.getByText(/live board/i)).toBeInTheDocument()
})
it('sends the prompt to the single agent and renders streamed board activity', async () => {
const user = userEvent.setup()
render()
await user.type(screen.getByLabelText(/prompt your board/i), 'scroll HELLO')
await user.click(screen.getByRole('button', { name: /working|send/i }))
expect(sendPrompt).toHaveBeenCalledWith(useSession.getState().teamId, 'scroll HELLO', 'default')
// a flash event streams in over the (mocked) SSE feed
act(() => {
liveOnEvent?.({ type: 'node:activity', teamId: 't', kind: 'flash', label: 'Flashed to 0x80F0000', ts: 'T' })
})
expect(screen.getByTestId('activity-log')).toHaveTextContent(/flashed to 0x80F0000/i)
// terminal event re-enables Send
expect(screen.getByRole('button', { name: /send/i })).toBeEnabled()
})
it('closes the activity stream on unmount', () => {
const { unmount } = render()
unmount()
expect(closeSpy).toHaveBeenCalled()
})
})