llm browser
This commit is contained in:
1 parent
afed023152
commit
a4dbb75195
39 files changed
+3709
-58
No files matched your search
@@ -0,0 +1,160 @@
|
||||
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
|
||||
import { SWRConfig } from 'swr'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { AgentViewPage } from '../src/pages/AgentViewPage'
|
||||
import { browse } from '../src/api/browse'
|
||||
import type { Snapshot } from '../src/api/browse'
|
||||
|
||||
/* Page-level wiring: the api module is mocked, everything else (SWR, the
|
||||
markdown renderer, tabs, the ?url= loop) is real. */
|
||||
vi.mock('../src/api/browse', () => ({
|
||||
browse: vi.fn(),
|
||||
}))
|
||||
|
||||
const browseMock = vi.mocked(browse)
|
||||
|
||||
function snapshot(overrides: Partial<Snapshot> = {}): Snapshot {
|
||||
return {
|
||||
requestedUrl: 'https://example.test',
|
||||
finalUrl: 'https://example.test/',
|
||||
status: 200,
|
||||
contentType: 'text/html; charset=utf-8',
|
||||
durationMs: 210,
|
||||
fetchedAt: '2026-09-16T10:00:00Z',
|
||||
page: {
|
||||
title: 'Example Site',
|
||||
description: 'An example.',
|
||||
canonical: '',
|
||||
robotsMeta: '',
|
||||
lang: 'en',
|
||||
h1: 'Welcome',
|
||||
linkCount: 1,
|
||||
deadLinks: 0,
|
||||
scriptCount: 1,
|
||||
imageCount: 0,
|
||||
wordCount: 12,
|
||||
textLength: 80,
|
||||
},
|
||||
markdown: '# Welcome\n\nRead the [docs](https://example.test/docs).',
|
||||
markdownTruncated: false,
|
||||
files: {
|
||||
llmsTxt: {
|
||||
url: 'https://example.test/llms.txt',
|
||||
status: 200,
|
||||
contentType: 'text/markdown',
|
||||
content: '# Example Site\n\n## Docs\n\n- [Guide](https://example.test/guide): the guide\n',
|
||||
truncated: false,
|
||||
llmsDoc: {
|
||||
title: 'Example Site',
|
||||
description: '',
|
||||
sections: [
|
||||
{
|
||||
heading: 'Docs',
|
||||
description: '',
|
||||
links: [{ title: 'Guide', url: 'https://example.test/guide', description: 'the guide' }],
|
||||
},
|
||||
],
|
||||
linkCount: 1,
|
||||
},
|
||||
},
|
||||
llmsFullTxt: null,
|
||||
wellKnownLlmsTxt: null,
|
||||
robotsTxt: {
|
||||
url: 'https://example.test/robots.txt',
|
||||
status: 200,
|
||||
contentType: 'text/plain',
|
||||
content: 'User-agent: *\nAllow: /\n',
|
||||
truncated: false,
|
||||
},
|
||||
},
|
||||
checks: [
|
||||
{ id: 'page.status', label: 'Page reachable', severity: 'ok', detail: '200 OK' },
|
||||
{ id: 'llms.txt', label: '/llms.txt', severity: 'ok', detail: 'found' },
|
||||
{ id: 'page.description', label: 'Meta description', severity: 'warn', detail: 'thin' },
|
||||
],
|
||||
...overrides,
|
||||
}
|
||||
}
|
||||
|
||||
function renderPage() {
|
||||
return render(
|
||||
<SWRConfig value={{ provider: () => new Map(), dedupingInterval: 0 }}>
|
||||
<AgentViewPage />
|
||||
</SWRConfig>,
|
||||
)
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
window.history.replaceState(null, '', '/')
|
||||
browseMock.mockReset()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
describe('AgentViewPage', () => {
|
||||
it('stays on the explainer until a domain is submitted', () => {
|
||||
browseMock.mockResolvedValue(snapshot())
|
||||
renderPage()
|
||||
expect(screen.getByText(/What does your site look like to an LLM/i)).toBeInTheDocument()
|
||||
expect(browseMock).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('fetches the domain and renders the markdown with live links', async () => {
|
||||
browseMock.mockResolvedValue(snapshot())
|
||||
renderPage()
|
||||
|
||||
fireEvent.change(screen.getByLabelText(/domain or url/i), { target: { value: 'example.test' } })
|
||||
fireEvent.click(screen.getByRole('button', { name: /view as agent/i }))
|
||||
|
||||
await waitFor(() => expect(browseMock).toHaveBeenCalledWith('example.test'))
|
||||
expect(await screen.findByRole('heading', { level: 1, name: 'Welcome' })).toBeInTheDocument()
|
||||
|
||||
const link = screen.getByRole('link', { name: 'docs' })
|
||||
expect(link).toHaveAttribute('href', 'https://example.test/docs')
|
||||
|
||||
expect(screen.getByText(/210 ms/i)).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('re-enters the viewer when a markdown link is clicked', async () => {
|
||||
browseMock.mockResolvedValue(snapshot())
|
||||
renderPage()
|
||||
|
||||
fireEvent.change(screen.getByLabelText(/domain or url/i), { target: { value: 'example.test' } })
|
||||
fireEvent.click(screen.getByRole('button', { name: /view as agent/i }))
|
||||
fireEvent.click(await screen.findByRole('link', { name: 'docs' }))
|
||||
|
||||
await waitFor(() => expect(browseMock).toHaveBeenCalledWith('https://example.test/docs'))
|
||||
})
|
||||
|
||||
it('shows the parsed llms.txt and the checks', async () => {
|
||||
browseMock.mockResolvedValue(snapshot())
|
||||
renderPage()
|
||||
|
||||
fireEvent.change(screen.getByLabelText(/domain or url/i), { target: { value: 'example.test' } })
|
||||
fireEvent.click(screen.getByRole('button', { name: /view as agent/i }))
|
||||
|
||||
fireEvent.click(await screen.findByRole('tab', { name: /llms\.txt/i }))
|
||||
expect((await screen.findAllByText('Example Site')).length).toBeGreaterThan(0)
|
||||
expect(screen.getByText('Guide')).toBeInTheDocument()
|
||||
|
||||
fireEvent.click(screen.getByRole('tab', { name: /checks/i }))
|
||||
expect(await screen.findByText('Page reachable')).toBeInTheDocument()
|
||||
expect(screen.getByText(/warnings/)).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('surfaces fetch failures with a retry', async () => {
|
||||
browseMock.mockRejectedValue(
|
||||
new (await import('../src/api/client')).ApiError(502, 'unavailable', 'could not fetch https://down.test/'),
|
||||
)
|
||||
renderPage()
|
||||
|
||||
fireEvent.change(screen.getByLabelText(/domain or url/i), { target: { value: 'down.test' } })
|
||||
fireEvent.click(screen.getByRole('button', { name: /view as agent/i }))
|
||||
|
||||
expect(await screen.findByText(/the site did not answer/i)).toBeInTheDocument()
|
||||
fireEvent.click(screen.getByRole('button', { name: /try again/i }))
|
||||
await waitFor(() => expect(browseMock).toHaveBeenCalledTimes(2))
|
||||
})
|
||||
})
|
||||
Reference in new issue
Block a user