Files
notes/src/components/NoteEditor.test.tsx
T

202 lines
7.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { fireEvent, render, screen } from '@testing-library/react'
import { describe, expect, it, vi } from 'vitest'
import NoteEditor from './NoteEditor'
import type { Note } from '../types/note'
const note: Note = { id: 42, title: 'Courses', content: 'Lait\nOeufs' }
const renderEdit = (overrides: Partial<Note> = {}) => {
const onUpdate = vi.fn()
const onDelete = vi.fn()
render(
<NoteEditor
mode="edit"
note={{ ...note, ...overrides }}
onUpdate={onUpdate}
onDelete={onDelete}
/>,
)
return { onUpdate, onDelete }
}
const renderCreate = () => {
const onCreate = vi.fn()
const onCancelCreate = vi.fn()
render(<NoteEditor mode="create" onCreate={onCreate} onCancelCreate={onCancelCreate} />)
return { onCreate, onCancelCreate }
}
describe('NoteEditor in edit mode', () => {
it('displays the note title and content in read mode', () => {
renderEdit()
expect(screen.getByText('Courses')).toBeInTheDocument()
expect(screen.getByText('Lait Oeufs')).toBeInTheDocument()
expect(screen.getByRole('button', { name: 'Éditer' })).toBeInTheDocument()
})
it('shows placeholders for a note without title or content', () => {
renderEdit({ title: '', content: '' })
expect(screen.getByText('Sans titre')).toBeInTheDocument()
expect(screen.getByText('Note vide')).toBeInTheDocument()
})
it('switches to edit mode with prefilled fields', () => {
renderEdit()
fireEvent.click(screen.getByRole('button', { name: 'Éditer' }))
expect(screen.getByLabelText('Titre')).toHaveValue('Courses')
expect(screen.getByLabelText('Contenu')).toHaveValue('Lait\nOeufs')
expect(screen.getByRole('button', { name: 'Enregistrer' })).toBeInTheDocument()
expect(screen.getByRole('button', { name: 'Annuler' })).toBeInTheDocument()
})
it('saves edits and calls onUpdate with the updated values', () => {
const { onUpdate } = renderEdit()
fireEvent.click(screen.getByRole('button', { name: 'Éditer' }))
fireEvent.change(screen.getByLabelText('Titre'), { target: { value: 'Liste de courses' } })
fireEvent.change(screen.getByLabelText('Contenu'), { target: { value: 'Pain' } })
fireEvent.click(screen.getByRole('button', { name: 'Enregistrer' }))
expect(onUpdate).toHaveBeenCalledWith(42, { title: 'Liste de courses', content: 'Pain' })
expect(screen.getByRole('button', { name: 'Éditer' })).toBeInTheDocument()
})
it('saves when the form is submitted', () => {
const { onUpdate } = renderEdit()
fireEvent.click(screen.getByRole('button', { name: 'Éditer' }))
const form = screen.getByLabelText('Titre').closest('form')!
fireEvent.submit(form)
expect(onUpdate).toHaveBeenCalledWith(42, { title: 'Courses', content: 'Lait\nOeufs' })
})
it('trims the title on save', () => {
const { onUpdate } = renderEdit()
fireEvent.click(screen.getByRole('button', { name: 'Éditer' }))
fireEvent.change(screen.getByLabelText('Titre'), { target: { value: ' Courses ' } })
fireEvent.click(screen.getByRole('button', { name: 'Enregistrer' }))
expect(onUpdate).toHaveBeenCalledWith(42, { title: 'Courses', content: 'Lait\nOeufs' })
})
it('refuses saving when the title is empty', () => {
const { onUpdate } = renderEdit()
fireEvent.click(screen.getByRole('button', { name: 'Éditer' }))
fireEvent.change(screen.getByLabelText('Titre'), { target: { value: '' } })
fireEvent.click(screen.getByRole('button', { name: 'Enregistrer' }))
expect(onUpdate).not.toHaveBeenCalled()
expect(screen.getByText('Le titre est obligatoire.')).toBeInTheDocument()
expect(screen.getByLabelText('Titre')).toHaveAttribute('aria-invalid', 'true')
})
it('clears the title error once a title is typed', () => {
renderEdit()
fireEvent.click(screen.getByRole('button', { name: 'Éditer' }))
fireEvent.change(screen.getByLabelText('Titre'), { target: { value: '' } })
fireEvent.click(screen.getByRole('button', { name: 'Enregistrer' }))
expect(screen.getByText('Le titre est obligatoire.')).toBeInTheDocument()
fireEvent.change(screen.getByLabelText('Titre'), { target: { value: 'Rempli' } })
expect(screen.queryByText('Le titre est obligatoire.')).not.toBeInTheDocument()
})
it('cancels editing without calling onUpdate', () => {
const { onUpdate } = renderEdit()
fireEvent.click(screen.getByRole('button', { name: 'Éditer' }))
fireEvent.change(screen.getByLabelText('Titre'), { target: { value: 'Non sauvegardé' } })
fireEvent.click(screen.getByRole('button', { name: 'Annuler' }))
expect(onUpdate).not.toHaveBeenCalled()
expect(screen.getByText('Courses')).toBeInTheDocument()
})
it('opens the delete confirmation when the × button is clicked', () => {
renderEdit()
expect(screen.queryByRole('dialog')).not.toBeInTheDocument()
fireEvent.click(screen.getByRole('button', { name: 'Supprimer la note' }))
expect(screen.getByRole('dialog', { name: 'Supprimer la note ?' })).toBeInTheDocument()
})
it('deletes the note after confirmation', () => {
const { onDelete } = renderEdit()
fireEvent.click(screen.getByRole('button', { name: 'Supprimer la note' }))
fireEvent.click(screen.getByRole('button', { name: 'Supprimer' }))
expect(onDelete).toHaveBeenCalledWith(42)
expect(screen.queryByRole('dialog')).not.toBeInTheDocument()
})
it('does not delete when the confirmation is cancelled', () => {
const { onDelete } = renderEdit()
fireEvent.click(screen.getByRole('button', { name: 'Supprimer la note' }))
fireEvent.click(screen.getByRole('button', { name: 'Annuler' }))
expect(onDelete).not.toHaveBeenCalled()
expect(screen.queryByRole('dialog')).not.toBeInTheDocument()
})
})
describe('NoteEditor in create mode', () => {
it('renders an empty form without read view or delete button', () => {
renderCreate()
expect(screen.getByLabelText('Titre')).toHaveValue('')
expect(screen.getByLabelText('Contenu')).toHaveValue('')
expect(screen.getByRole('button', { name: 'Enregistrer' })).toBeInTheDocument()
expect(screen.queryByRole('button', { name: 'Éditer' })).not.toBeInTheDocument()
expect(screen.queryByRole('button', { name: 'Supprimer la note' })).not.toBeInTheDocument()
})
it('creates the note with title and content on save', () => {
const { onCreate } = renderCreate()
fireEvent.change(screen.getByLabelText('Titre'), { target: { value: 'Courses' } })
fireEvent.change(screen.getByLabelText('Contenu'), { target: { value: 'Lait' } })
fireEvent.click(screen.getByRole('button', { name: 'Enregistrer' }))
expect(onCreate).toHaveBeenCalledWith('Courses', 'Lait')
})
it('trims the title on create', () => {
const { onCreate } = renderCreate()
fireEvent.change(screen.getByLabelText('Titre'), { target: { value: ' Courses ' } })
fireEvent.click(screen.getByRole('button', { name: 'Enregistrer' }))
expect(onCreate).toHaveBeenCalledWith('Courses', '')
})
it('refuses creation when the title is empty', () => {
const { onCreate } = renderCreate()
fireEvent.click(screen.getByRole('button', { name: 'Enregistrer' }))
expect(onCreate).not.toHaveBeenCalled()
expect(screen.getByText('Le titre est obligatoire.')).toBeInTheDocument()
})
it('cancels creation without creating the note', () => {
const { onCreate, onCancelCreate } = renderCreate()
fireEvent.click(screen.getByRole('button', { name: 'Annuler' }))
expect(onCreate).not.toHaveBeenCalled()
expect(onCancelCreate).toHaveBeenCalled()
})
})