test(datatable) : champ de saut de page (debounce, Entrée, clamp)

This commit is contained in:
2026-06-09 15:10:48 +02:00
parent f797c1c8a0
commit c6bca756f1
@@ -1,4 +1,4 @@
import { describe, expect, it } from 'vitest'
import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest'
import { h } from 'vue'
import { mount } from '@vue/test-utils'
import type { DefineComponent } from 'vue'
@@ -189,24 +189,6 @@ describe('MalioDataTable', () => {
expect(wrapper.find('[data-test="pagination"]').exists()).toBe(true)
})
it('renders all pages when totalPages <= 5', () => {
const wrapper = mountComponent({ totalItems: 50, perPage: 10 })
for (let i = 1; i <= 5; i++) {
expect(wrapper.find(`[data-test="page-${i}"]`).exists()).toBe(true)
}
})
it('highlights current page', () => {
const wrapper = mountComponent({ totalItems: 50, perPage: 10, page: 3 })
expect(wrapper.find('[data-test="page-3"]').attributes('aria-current')).toBe('page')
})
it('emits update:page on page button click', async () => {
const wrapper = mountComponent({ totalItems: 50, perPage: 10, page: 1 })
await wrapper.find('[data-test="page-3"]').trigger('click')
expect(wrapper.emitted('update:page')?.[0]).toEqual([3])
})
it('Prev button is disabled on page 1', () => {
const wrapper = mountComponent({ totalItems: 50, perPage: 10, page: 1 })
expect(wrapper.find('[data-test="prev-button"]').attributes('disabled')).toBeDefined()
@@ -229,26 +211,6 @@ describe('MalioDataTable', () => {
expect(wrapper.emitted('update:page')?.[0]).toEqual([4])
})
it('shows ellipsis for truncated pages (> 5 pages)', () => {
const wrapper = mountComponent({ totalItems: 200, perPage: 10, page: 10 })
const ellipsis = wrapper.findAll('[aria-hidden="true"]')
expect(ellipsis.length).toBeGreaterThan(0)
expect(ellipsis[0].text()).toBe('…')
})
it('always shows first and last page when > 5 pages', () => {
const wrapper = mountComponent({ totalItems: 200, perPage: 10, page: 10 })
expect(wrapper.find('[data-test="page-1"]').exists()).toBe(true)
expect(wrapper.find('[data-test="page-20"]').exists()).toBe(true)
})
it('shows 1 neighbor on each side of current page', () => {
const wrapper = mountComponent({ totalItems: 200, perPage: 10, page: 10 })
expect(wrapper.find('[data-test="page-9"]').exists()).toBe(true)
expect(wrapper.find('[data-test="page-10"]').exists()).toBe(true)
expect(wrapper.find('[data-test="page-11"]').exists()).toBe(true)
})
it('pagination nav has aria-label', () => {
const wrapper = mountComponent({ totalItems: 30 })
expect(wrapper.find('[data-test="pagination-nav"]').attributes('aria-label')).toBe('Pagination')
@@ -265,6 +227,80 @@ describe('MalioDataTable', () => {
})
})
describe('Pagination — saut de page (champ)', () => {
beforeEach(() => { vi.useFakeTimers() })
afterEach(() => { vi.runOnlyPendingTimers(); vi.useRealTimers() })
it('affiche la page courante et le total dans le champ', () => {
const wrapper = mountComponent({ totalItems: 310, perPage: 10, page: 16 })
expect((wrapper.find('[data-test="page-input"]').element as HTMLInputElement).value).toBe('16')
expect(wrapper.find('[data-test="total-pages"]').text()).toBe('31')
})
it('émet update:page après le debounce pour une valeur valide', async () => {
const wrapper = mountComponent({ totalItems: 310, perPage: 10, page: 1 })
const input = wrapper.find('[data-test="page-input"]')
await input.setValue('16')
expect(wrapper.emitted('update:page')).toBeUndefined()
vi.advanceTimersByTime(400)
expect(wrapper.emitted('update:page')?.at(-1)).toEqual([16])
})
it('n\'émet pas avant la fin du debounce', async () => {
const wrapper = mountComponent({ totalItems: 310, perPage: 10, page: 1 })
await wrapper.find('[data-test="page-input"]').setValue('16')
vi.advanceTimersByTime(399)
expect(wrapper.emitted('update:page')).toBeUndefined()
})
it('Entrée applique immédiatement', async () => {
const wrapper = mountComponent({ totalItems: 310, perPage: 10, page: 1 })
const input = wrapper.find('[data-test="page-input"]')
await input.setValue('16')
await input.trigger('keydown.enter')
expect(wrapper.emitted('update:page')?.at(-1)).toEqual([16])
})
it('clampe une valeur > N à la dernière page (Entrée)', async () => {
const wrapper = mountComponent({ totalItems: 310, perPage: 10, page: 1 })
const input = wrapper.find('[data-test="page-input"]')
await input.setValue('50')
await input.trigger('keydown.enter')
expect(wrapper.emitted('update:page')?.at(-1)).toEqual([31])
})
it('restaure la page courante quand le champ est vidé au blur', async () => {
const wrapper = mountComponent({ totalItems: 310, perPage: 10, page: 5 })
const input = wrapper.find('[data-test="page-input"]')
await input.setValue('')
await input.trigger('blur')
expect(wrapper.emitted('update:page')).toBeUndefined()
expect((input.element as HTMLInputElement).value).toBe('5')
})
it('n\'émet pas pour 0 et restaure la page courante (Entrée)', async () => {
const wrapper = mountComponent({ totalItems: 310, perPage: 10, page: 5 })
const input = wrapper.find('[data-test="page-input"]')
await input.setValue('0')
await input.trigger('keydown.enter')
expect(wrapper.emitted('update:page')).toBeUndefined()
expect((input.element as HTMLInputElement).value).toBe('5')
})
it('retire les caractères non numériques à la frappe', async () => {
const wrapper = mountComponent({ totalItems: 310, perPage: 10, page: 1 })
const input = wrapper.find('[data-test="page-input"]')
await input.setValue('1a2b')
expect((input.element as HTMLInputElement).value).toBe('12')
})
it('resynchronise le champ quand la prop page change', async () => {
const wrapper = mountComponent({ totalItems: 310, perPage: 10, page: 1 })
await wrapper.setProps({ page: 7 })
expect((wrapper.find('[data-test="page-input"]').element as HTMLInputElement).value).toBe('7')
})
})
describe('Per-page selector', () => {
it('emits update:per-page and reset page to 1 on change', async () => {
const wrapper = mountComponent({ totalItems: 100, perPage: 10, page: 5 })