135 lines
3.9 KiB
TypeScript
135 lines
3.9 KiB
TypeScript
/**
|
|
* Tests du hook useAutosave — FTD-21.
|
|
*
|
|
* Valide :
|
|
* - debounce 30 s (pas de save avant, save après)
|
|
* - flush immédiat sur `beforeunload`
|
|
* - cleanup du listener au unmount
|
|
* - enabled=false → aucun appel
|
|
*/
|
|
|
|
import { act, renderHook } from '@testing-library/react'
|
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
|
|
vi.mock('@/entities/production/api', () => ({
|
|
autosaveContenu: vi.fn().mockResolvedValue(undefined),
|
|
}))
|
|
|
|
import { autosaveContenu } from '@/entities/production/api'
|
|
import { useAutosave } from '../useAutosave'
|
|
|
|
const mocked = vi.mocked(autosaveContenu)
|
|
|
|
describe('useAutosave', () => {
|
|
beforeEach(() => {
|
|
vi.useFakeTimers()
|
|
mocked.mockClear()
|
|
mocked.mockResolvedValue(undefined)
|
|
})
|
|
|
|
afterEach(() => {
|
|
vi.useRealTimers()
|
|
})
|
|
|
|
it("debounce 30 s : pas d'appel avant, appel après", async () => {
|
|
const { rerender } = renderHook(
|
|
({ contenu }: { contenu: string }) => useAutosave('sim-1', contenu, true),
|
|
{ initialProps: { contenu: '' } },
|
|
)
|
|
|
|
rerender({ contenu: 'hello world' })
|
|
|
|
// Avant 30 s : aucun appel
|
|
await act(async () => {
|
|
await vi.advanceTimersByTimeAsync(29_000)
|
|
})
|
|
expect(mocked).not.toHaveBeenCalled()
|
|
|
|
// Après 30 s : save
|
|
await act(async () => {
|
|
await vi.advanceTimersByTimeAsync(2_000)
|
|
})
|
|
expect(mocked).toHaveBeenCalledTimes(1)
|
|
expect(mocked).toHaveBeenCalledWith('sim-1', 'hello world')
|
|
})
|
|
|
|
it('flush immédiat sur beforeunload', async () => {
|
|
const { rerender } = renderHook(
|
|
({ contenu }: { contenu: string }) => useAutosave('sim-1', contenu, true),
|
|
{ initialProps: { contenu: '' } },
|
|
)
|
|
|
|
rerender({ contenu: 'texte à sauvegarder' })
|
|
|
|
await act(async () => {
|
|
window.dispatchEvent(new Event('beforeunload'))
|
|
await Promise.resolve()
|
|
})
|
|
|
|
expect(mocked).toHaveBeenCalledTimes(1)
|
|
expect(mocked).toHaveBeenCalledWith('sim-1', 'texte à sauvegarder')
|
|
})
|
|
|
|
it('cleanup : unmount retire le listener beforeunload', async () => {
|
|
const { unmount, rerender } = renderHook(
|
|
({ contenu }: { contenu: string }) => useAutosave('sim-1', contenu, true),
|
|
{ initialProps: { contenu: '' } },
|
|
)
|
|
rerender({ contenu: 'texte' })
|
|
|
|
unmount()
|
|
|
|
await act(async () => {
|
|
window.dispatchEvent(new Event('beforeunload'))
|
|
await Promise.resolve()
|
|
})
|
|
|
|
expect(mocked).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('enabled=false : aucun appel, même après 30 s', async () => {
|
|
const { rerender } = renderHook(
|
|
({ contenu, enabled }: { contenu: string; enabled: boolean }) =>
|
|
useAutosave('sim-1', contenu, enabled),
|
|
{ initialProps: { contenu: 'texte', enabled: false } },
|
|
)
|
|
rerender({ contenu: 'texte modifié', enabled: false })
|
|
|
|
await act(async () => {
|
|
await vi.advanceTimersByTimeAsync(31_000)
|
|
})
|
|
|
|
expect(mocked).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('simulationId null : aucun appel', async () => {
|
|
renderHook(() => useAutosave(null, 'texte', true))
|
|
|
|
await act(async () => {
|
|
await vi.advanceTimersByTimeAsync(31_000)
|
|
})
|
|
|
|
expect(mocked).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it("dédoublonnage : pas de second appel si le contenu n'a pas changé", async () => {
|
|
const { rerender } = renderHook(
|
|
({ contenu }: { contenu: string }) => useAutosave('sim-1', contenu, true),
|
|
{ initialProps: { contenu: '' } },
|
|
)
|
|
|
|
rerender({ contenu: 'identique' })
|
|
await act(async () => {
|
|
await vi.advanceTimersByTimeAsync(31_000)
|
|
})
|
|
expect(mocked).toHaveBeenCalledTimes(1)
|
|
|
|
// Rerender avec même contenu → debounce ne repart pas (le contenu ref ne change pas côté React)
|
|
// Simule une édition puis retour au texte initial (déjà sauvegardé)
|
|
rerender({ contenu: 'identique' })
|
|
await act(async () => {
|
|
await vi.advanceTimersByTimeAsync(31_000)
|
|
})
|
|
expect(mocked).toHaveBeenCalledTimes(1)
|
|
})
|
|
})
|