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 = {}) => { const onUpdate = vi.fn() const onDelete = vi.fn() render( , ) return { onUpdate, onDelete } } const renderCreate = () => { const onCreate = vi.fn() const onCancelCreate = vi.fn() render() 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() }) })