/** * FE-AI-HOME-001 to FE-AI-HOME-006 * * The home chat's shared hooks: its key and name, the way into one trip's own * chat with the question waiting there (a question already typed there * stays), the way in from the pages outside a trip, and the questions the * empty home chat offers, and the journal on screen as a chat. */ import type { AiStatusResponse } from '@trek/shared'; import { afterEach, describe, expect, it, vi } from 'vitest'; import { fireEvent, render, screen } from '../../../tests/helpers/render'; import { chatOf, useAssistantStore } from '../../store/assistantStore'; import { useJourneyStore } from '../../store/journeyStore'; import { HOME_SUGGESTIONS } from './assistantModel'; import { chatPagePath, useAssistantHome, useAssistantHomeLauncher, useRouteJournalChat } from './useAssistantHome'; import { useAssistantSuggestions } from './useAssistantSuggestions'; const INITIAL = useAssistantStore.getState(); afterEach(() => { useAssistantStore.setState(INITIAL, true); }); function Home({ switchTo }: { switchTo: (trip: { id: number; title: string }) => void }) { const home = useAssistantHome(switchTo); return (

chat {home.chat.id} {home.chat.title}

); } function Launcher() { const { available, open } = useAssistantHomeLauncher(); return available ? ( ) : null; } function Suggestions() { const { suggestions, canEdit } = useAssistantSuggestions(0); return ( ); } describe('useAssistantHome', () => { it('FE-AI-HOME-001: names the home chat and keeps it under its own key', () => { render(); expect(screen.getByText('chat 0 Your travels')).toBeInTheDocument(); }); it("FE-AI-HOME-002: goes on in a trip's chat with the question waiting, and never over one typed there", () => { const switchTo = vi.fn(); render(); fireEvent.click(screen.getByRole('button', { name: 'ask' })); expect(switchTo).toHaveBeenCalledWith({ id: 9, title: 'Lisbon' }); expect(chatOf(useAssistantStore.getState(), 9).draft).toBe('What is missing?'); useAssistantStore.getState().setDraft(9, 'My own words'); fireEvent.click(screen.getByRole('button', { name: 'ask' })); expect(chatOf(useAssistantStore.getState(), 9).draft).toBe('My own words'); fireEvent.click(screen.getByRole('button', { name: 'go' })); expect(switchTo).toHaveBeenCalledTimes(3); }); }); describe('useAssistantHomeLauncher', () => { it('FE-AI-HOME-003: offers the way in only while the assistant can run, and opens the window', () => { const { rerender } = render(); expect(screen.queryByRole('button', { name: 'open' })).not.toBeInTheDocument(); useAssistantStore.setState({ status: { available: true } as AiStatusResponse }); rerender(); fireEvent.click(screen.getByRole('button', { name: 'open' })); expect(useAssistantStore.getState().window).toEqual({ open: true, minimized: false }); }); }); describe('the empty home chat', () => { it('FE-AI-HOME-004: offers questions across the trips, all of them, and holds nothing back', () => { render(); const items = screen.getAllByRole('listitem').map((item) => item.textContent); expect(items).toHaveLength(HOME_SUGGESTIONS.length); expect(items).toContain('Which trip is next, and what is still open?'); expect(items).toContain('How many countries have I visited?'); expect(screen.getByRole('list')).toHaveAttribute('data-can-edit', 'true'); }); }); function RouteJournal() { const chat = useRouteJournalChat(); return

{chat ? `journal ${chat.id} ${chat.title}` : 'no journal'}

; } describe('the journal on screen', () => { it("FE-AI-HOME-005: a journey's page is its journal's chat, under the journey's key, but not in its Studio", () => { useJourneyStore.setState({ current: { id: 12, title: 'Japan 2026' } as never }); render(, { initialEntries: ['/journey/12'] }); expect(screen.getByText('journal -12 Japan 2026')).toBeInTheDocument(); render(, { initialEntries: ['/journey/12/studio'] }); expect(screen.getAllByText('no journal')).toHaveLength(1); render(, { initialEntries: ['/journey'] }); expect(screen.getAllByText('no journal')).toHaveLength(2); // Another journey loaded: the key is the route's, the title waits for its own. render(, { initialEntries: ['/journey/13'] }); expect(screen.getByText(/^journal -13\s*$/)).toBeInTheDocument(); useJourneyStore.setState({ current: null }); }); it('FE-AI-HOME-006: a chat switched to from home opens on its own page', () => { expect(chatPagePath({ id: 5, title: 'Kyoto' })).toBe('/trips/5'); expect(chatPagePath({ id: -12, title: 'Japan 2026' })).toBe('/journey/12'); }); });