feat: add create and edit book form

Claude-Session: https://claude.ai/code/session_01M9MLit5Ko3X4s7rzC5Kv7X
This commit is contained in:
2026-10-02 14:26:30 -05:00
parent da6c5c2de5
commit 18bc74f8f9
3 changed files with 221 additions and 0 deletions
+65
View File
@@ -0,0 +1,65 @@
import { screen } from '@testing-library/react'
import { describe, expect, it } from 'vitest'
import { dune, renderApp, signedIn } from '../test/helpers'
const fresh = { ...dune, current_page: 0, status: 'not_started' as const }
describe('BookForm', () => {
it('adds a book and opens it', async () => {
const calls = signedIn({
'POST /api/books': () => [201, fresh],
'GET /api/books/7': () => [200, fresh],
})
const user = renderApp('/books/new')
await screen.findByRole('option', { name: 'Science Fiction' })
await user.type(screen.getByLabelText('Title'), 'Dune')
await user.type(screen.getByLabelText('Author'), 'Frank Herbert')
await user.selectOptions(screen.getByLabelText('Genre'), 'Science Fiction')
await user.type(screen.getByLabelText('Total pages'), '380')
await user.click(screen.getByRole('button', { name: 'Add book' }))
expect(await screen.findByRole('heading', { name: 'Dune', level: 1 })).toBeInTheDocument()
expect(calls).toContainEqual({
method: 'POST',
path: '/api/books',
body: { title: 'Dune', author: 'Frank Herbert', genre_id: 5, total_pages: 380, current_page: 0 },
})
})
it('shows a server validation error against the field', async () => {
signedIn({ 'POST /api/books': () => [400, { error: 'Unknown genre_id; see GET /api/genres', field: 'genre_id' }] })
const user = renderApp('/books/new')
await screen.findByRole('option', { name: 'Science Fiction' })
await user.type(screen.getByLabelText('Title'), 'Dune')
await user.type(screen.getByLabelText('Author'), 'Frank Herbert')
await user.selectOptions(screen.getByLabelText('Genre'), 'Science Fiction')
await user.type(screen.getByLabelText('Total pages'), '380')
await user.click(screen.getByRole('button', { name: 'Add book' }))
expect(await screen.findByRole('alert')).toHaveTextContent('Unknown genre_id')
expect(screen.getByLabelText('Genre')).toHaveAttribute('aria-invalid', 'true')
})
it('edits an existing book', async () => {
let saved = dune
const calls = signedIn({
'GET /api/books/7': () => [200, saved],
'PATCH /api/books/7': ({ body }) => {
saved = { ...saved, ...(body as object) }
return [200, saved]
},
})
const user = renderApp('/books/7/edit')
const title = await screen.findByDisplayValue('Dune')
await user.clear(title)
await user.type(title, 'Dune Messiah')
await user.click(screen.getByRole('button', { name: 'Save changes' }))
expect(await screen.findByRole('heading', { name: 'Dune Messiah', level: 1 })).toBeInTheDocument()
expect(calls).toContainEqual({
method: 'PATCH',
path: '/api/books/7',
body: { title: 'Dune Messiah', author: 'Frank Herbert', genre_id: 5, total_pages: 380, current_page: 142 },
})
})
})