77 lines
3 KiB
TypeScript
77 lines
3 KiB
TypeScript
|
|
import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest'
|
||
|
|
import { render, screen, waitFor, fireEvent, cleanup } from '@testing-library/react'
|
||
|
|
import App from '../App'
|
||
|
|
|
||
|
|
const tasksResponse = {
|
||
|
|
tasks: [
|
||
|
|
{ id: 't_1', title: 'Build thing', status: 'ready', assignee: 'developer', priority: 5, age: '1m' },
|
||
|
|
],
|
||
|
|
groups: {
|
||
|
|
triage: [],
|
||
|
|
todo: [],
|
||
|
|
ready: [{ id: 't_1', title: 'Build thing', status: 'ready', assignee: 'developer', priority: 5 }],
|
||
|
|
running: [],
|
||
|
|
blocked: [],
|
||
|
|
done: [],
|
||
|
|
archived: [],
|
||
|
|
},
|
||
|
|
}
|
||
|
|
|
||
|
|
beforeEach(() => {
|
||
|
|
vi.stubGlobal('fetch', vi.fn(async (url: string, init?: RequestInit) => {
|
||
|
|
if (String(url).includes('/api/tasks/t_1/events')) return new Response(JSON.stringify({ events: [{ kind: 'created' }] }))
|
||
|
|
if (String(url).includes('/api/tasks/t_1/runs')) return new Response(JSON.stringify({ runs: [{ id: 1, status: 'completed' }] }))
|
||
|
|
if (String(url).includes('/api/tasks/t_1/log')) return new Response(JSON.stringify({ log: 'tail', missing: false }))
|
||
|
|
if (String(url).endsWith('/api/tasks/t_1')) return new Response(JSON.stringify({ task: { ...tasksResponse.tasks[0], body: 'Full body' } }))
|
||
|
|
if (String(url).includes('/api/tasks') && init?.method === 'POST') return new Response(JSON.stringify({ task: { id: 't_new', title: 'Created', status: 'todo' } }))
|
||
|
|
if (String(url).includes('/api/tasks')) return new Response(JSON.stringify(tasksResponse))
|
||
|
|
return new Response(JSON.stringify({ status: 'ok' }))
|
||
|
|
}))
|
||
|
|
})
|
||
|
|
|
||
|
|
afterEach(() => {
|
||
|
|
cleanup()
|
||
|
|
vi.unstubAllGlobals()
|
||
|
|
})
|
||
|
|
|
||
|
|
describe('App', () => {
|
||
|
|
it('renders board columns', async () => {
|
||
|
|
render(<App />)
|
||
|
|
expect(await screen.findByText('ready')).toBeInTheDocument()
|
||
|
|
expect(screen.getByText('blocked')).toBeInTheDocument()
|
||
|
|
})
|
||
|
|
|
||
|
|
it('renders task cards with title status and assignee', async () => {
|
||
|
|
render(<App />)
|
||
|
|
expect(await screen.findByText('Build thing')).toBeInTheDocument()
|
||
|
|
expect(screen.getByText('developer')).toBeInTheDocument()
|
||
|
|
})
|
||
|
|
|
||
|
|
it('opens detail drawer and shows body events and log sections', async () => {
|
||
|
|
render(<App />)
|
||
|
|
fireEvent.click(await screen.findByText('Build thing'))
|
||
|
|
expect(await screen.findByText('Full body')).toBeInTheDocument()
|
||
|
|
expect(screen.getByText('Events')).toBeInTheDocument()
|
||
|
|
expect(screen.getByText('Log')).toBeInTheDocument()
|
||
|
|
})
|
||
|
|
|
||
|
|
it('create form validates title', async () => {
|
||
|
|
render(<App />)
|
||
|
|
fireEvent.click(await screen.findByText('Create task'))
|
||
|
|
fireEvent.click(screen.getByText('Create'))
|
||
|
|
expect(await screen.findByText('Title is required')).toBeInTheDocument()
|
||
|
|
})
|
||
|
|
|
||
|
|
it('shows API error state', async () => {
|
||
|
|
vi.stubGlobal('fetch', vi.fn(async () => new Response(JSON.stringify({ detail: { message: 'boom' } }), { status: 500 })))
|
||
|
|
render(<App />)
|
||
|
|
expect(await screen.findByText(/boom/)).toBeInTheDocument()
|
||
|
|
})
|
||
|
|
|
||
|
|
it('does not crash when Telegram WebApp is absent', async () => {
|
||
|
|
vi.stubGlobal('Telegram', undefined)
|
||
|
|
render(<App />)
|
||
|
|
await waitFor(() => expect(screen.getByText('Hermes Kanban')).toBeInTheDocument())
|
||
|
|
})
|
||
|
|
})
|