Admin AI assistant screen (mock 1o) (#82)
CI / ci (push) Successful in 1m56s

Co-authored-by: dev_agent <dev_agent@cubecraftlabs.com>
This commit was merged in pull request #82.
This commit is contained in:
2026-10-06 10:22:07 -04:00
committed by approver_agent
parent de4f539aea
commit e9a35a7f57
15 changed files with 1589 additions and 39 deletions
+23 -7
View File
@@ -1,12 +1,12 @@
import { act, render, screen } from '@testing-library/react'
import { MemoryRouter } from 'react-router'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { createApiClient, createVerifiedSession } from './api/index.ts'
import { createNetworkStatus } from './pwa/networkStatus.ts'
import { mockViewport } from './test/matchMedia.ts'
import { errorResponse, fakeLocation, jsonResponse, ME, request, stubFetch, stubFetchByPath } from './test/fetchStub.ts'
import App, { routes } from './App.tsx'
import { SETTINGS_API } from './test/ApiProviders.tsx'
import { AI_SETTINGS, SETTINGS_API } from './test/ApiProviders.tsx'
import { InDataRouter } from './test/dataRouter.tsx'
afterEach(() => {
Reflect.deleteProperty(window, 'matchMedia')
@@ -20,12 +20,15 @@ function renderAt(path: string, location = fakeLocation(path)) {
function renderAppAt(path: string, location = fakeLocation(path)) {
return render(
<MemoryRouter initialEntries={[path]}>
<InDataRouter path={path}>
<App api={createApiClient(location, createNetworkStatus(window), createVerifiedSession(localStorage))} serviceWorker={undefined} />
</MemoryRouter>,
</InDataRouter>,
)
}
// The routes that render a built screen rather than a placeholder page.
const SCREENS = { '/settings': true, '/settings/ai': true }
describe('App routes', () => {
it('registers every handoff route plus the extra settings pages', () => {
expect(routes.map((route) => route.path)).toEqual([
@@ -47,7 +50,7 @@ describe('App routes', () => {
])
})
it.each(routes.filter(({ path }) => path !== '/settings').map(({ path, title }) => [path.replace(':id', 'abc123'), title]))(
it.each(routes.filter(({ path }) => !Object.hasOwn(SCREENS, path)).map(({ path, title }) => [path.replace(':id', 'abc123'), title]))(
'%s renders its placeholder page inside the shell',
async (path, title) => {
mockViewport(390)
@@ -70,6 +73,19 @@ describe('App routes', () => {
expect(screen.getByRole('main')).toContainElement(heading)
expect(await screen.findByText('Anthropic · Claude Sonnet')).toBeInTheDocument()
})
it('/settings/ai renders the AI assistant screen inside the shell', async () => {
mockViewport(390)
stubFetchByPath({
'/api/v1/me': () => jsonResponse(200, ME),
'/api/v1/settings/ai': () => jsonResponse(200, AI_SETTINGS),
'/api/v1/settings/ai/models': () => jsonResponse(200, { models: [] }),
})
renderAt('/settings/ai')
const heading = await screen.findByRole('heading', { level: 1, name: 'AI assistant' })
expect(screen.getByRole('main')).toContainElement(heading)
expect(await screen.findByText('••••••••3f9a')).toBeInTheDocument()
})
})
describe('App bootstrap', () => {
@@ -190,9 +206,9 @@ describe('App offline bootstrap', () => {
goOffline()
const api = createApiClient(fakeLocation('/plan'), createNetworkStatus(window), createVerifiedSession(localStorage))
render(
<MemoryRouter initialEntries={['/plan']}>
<InDataRouter path="/plan">
<App api={api} serviceWorker={undefined} />
</MemoryRouter>,
</InDataRouter>,
)
expect(await screen.findByRole('heading', { level: 1, name: 'Plan' })).toBeInTheDocument()
await expect(api.apiFetch('/api/v1/plans', { method: 'POST', body: {} })).rejects.toThrow(/no CSRF token/)