refactor(front) : extrait debounce dans shared/utils + tests Vitest
T-013 — sort la fonction debounce inline de audit-log.vue vers frontend/shared/utils/debounce.ts (auto-importe par Nuxt) et ajoute 3 tests Vitest (delay coalesce, derniere invocation gagne, plusieurs executions espacees). Pret pour reutilisation sur les prochaines pages avec recherche/filtres.
This commit is contained in:
@@ -301,16 +301,9 @@ async function loadEntries(): Promise<void> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Debounce utilitaire pour le champ texte performedBy : evite un refetch a
|
// Debounce auto-importe depuis `frontend/shared/utils/debounce.ts` : evite
|
||||||
// chaque frappe (reseau + SQL) et laisse l'utilisateur finir sa saisie.
|
// un refetch a chaque frappe sur le champ texte performedBy (reseau + SQL)
|
||||||
function debounce<T extends (...args: never[]) => void>(fn: T, delay: number): T {
|
// et laisse l'utilisateur finir sa saisie avant de lancer la requete.
|
||||||
let timer: ReturnType<typeof setTimeout> | null = null
|
|
||||||
return ((...args: Parameters<T>) => {
|
|
||||||
if (null !== timer) clearTimeout(timer)
|
|
||||||
timer = setTimeout(() => fn(...args), delay)
|
|
||||||
}) as T
|
|
||||||
}
|
|
||||||
|
|
||||||
const debouncedReload = debounce(() => loadEntries(), 300)
|
const debouncedReload = debounce(() => loadEntries(), 300)
|
||||||
|
|
||||||
function toIso(localDateTime: string): string {
|
function toIso(localDateTime: string): string {
|
||||||
|
|||||||
52
frontend/shared/utils/__tests__/debounce.test.ts
Normal file
52
frontend/shared/utils/__tests__/debounce.test.ts
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
import { describe, it, expect, vi } from 'vitest'
|
||||||
|
import { debounce } from '../debounce'
|
||||||
|
|
||||||
|
describe('debounce', () => {
|
||||||
|
it('attend delay ms avant d\'appeler fn une seule fois apres plusieurs invocations rapides', () => {
|
||||||
|
vi.useFakeTimers()
|
||||||
|
const fn = vi.fn()
|
||||||
|
const debounced = debounce(fn, 100)
|
||||||
|
|
||||||
|
debounced()
|
||||||
|
debounced()
|
||||||
|
debounced()
|
||||||
|
expect(fn).not.toHaveBeenCalled()
|
||||||
|
|
||||||
|
vi.advanceTimersByTime(100)
|
||||||
|
expect(fn).toHaveBeenCalledTimes(1)
|
||||||
|
|
||||||
|
vi.useRealTimers()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('passe les arguments du dernier appel a fn', () => {
|
||||||
|
vi.useFakeTimers()
|
||||||
|
const fn = vi.fn<(a: string, b: number) => void>()
|
||||||
|
const debounced = debounce(fn, 50)
|
||||||
|
|
||||||
|
debounced('first', 1)
|
||||||
|
debounced('second', 2)
|
||||||
|
debounced('third', 3)
|
||||||
|
vi.advanceTimersByTime(50)
|
||||||
|
|
||||||
|
expect(fn).toHaveBeenCalledTimes(1)
|
||||||
|
expect(fn).toHaveBeenCalledWith('third', 3)
|
||||||
|
|
||||||
|
vi.useRealTimers()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('autorise plusieurs executions espacees dans le temps', () => {
|
||||||
|
vi.useFakeTimers()
|
||||||
|
const fn = vi.fn()
|
||||||
|
const debounced = debounce(fn, 50)
|
||||||
|
|
||||||
|
debounced()
|
||||||
|
vi.advanceTimersByTime(50)
|
||||||
|
expect(fn).toHaveBeenCalledTimes(1)
|
||||||
|
|
||||||
|
debounced()
|
||||||
|
vi.advanceTimersByTime(50)
|
||||||
|
expect(fn).toHaveBeenCalledTimes(2)
|
||||||
|
|
||||||
|
vi.useRealTimers()
|
||||||
|
})
|
||||||
|
})
|
||||||
15
frontend/shared/utils/debounce.ts
Normal file
15
frontend/shared/utils/debounce.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
/**
|
||||||
|
* Utilitaire de debounce partage.
|
||||||
|
*
|
||||||
|
* Retarde l'execution d'une fonction : chaque appel reset un timer et
|
||||||
|
* l'execution reelle n'a lieu qu'apres `delay` ms sans nouvelle invocation.
|
||||||
|
* Utile pour eviter un spam d'appels reseau sur un champ de recherche
|
||||||
|
* (une requete par touche -> une seule requete apres la derniere frappe).
|
||||||
|
*/
|
||||||
|
export function debounce<T extends (...args: never[]) => void>(fn: T, delay: number): T {
|
||||||
|
let timer: ReturnType<typeof setTimeout> | null = null
|
||||||
|
return ((...args: Parameters<T>) => {
|
||||||
|
if (null !== timer) clearTimeout(timer)
|
||||||
|
timer = setTimeout(() => fn(...args), delay)
|
||||||
|
}) as T
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user