import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest' import { act, fireEvent, render, screen } from '@testing-library/react' import { Provider } from '../provider/widget-provider' import { clearAllWidgetStores, getWidgetStore } from '../stores' import { Table } from './table' import type { TableColumn, TableWidgetState } from './types' beforeEach(() => clearAllWidgetStores()) afterEach(() => clearAllWidgetStores()) const COLUMNS: TableColumn[] = [ { id: 'name', label: 'Name', sortable: true }, { id: 'score', label: 'Score', sortable: true }, ] const DATA = Array.from({ length: 23 }, (_, i) => ({ id: i + 1, name: `Row ${String(i + 1).padStart(2, '0')}`, score: ((i * 7) % 11) + 1, })) describe('', () => { it('paginates rows from the store, defaulting to page 0', () => { render(
, ) expect(screen.getByText('Row 01')).toBeTruthy() expect(screen.getByText('Row 05')).toBeTruthy() expect(screen.queryByText('Row 06')).toBeNull() }) it('writes sort to the store and resets page=0', () => { render(
, ) // Move to page 2 first. getWidgetStore('t2').setState({ page: 2 } as Partial) fireEvent.click(screen.getByText('Score')) const state = getWidgetStore('t2').getState() as TableWidgetState expect(state.sort).toEqual({ columnId: 'score', direction: 'asc' }) expect(state.page).toBe(0) }) it('honors columnOrder from the store', () => { render(
, ) act(() => { getWidgetStore('t3').setState({ columnOrder: ['score', 'name'], } as Partial) }) const headers = screen.getAllByRole('columnheader') expect(headers[0]?.textContent).toContain('Score') expect(headers[1]?.textContent).toContain('Name') }) it('in remote mode renders data as-is and uses external `total` for the footer', () => { // Only 3 rows worth of "current page" data, but the server says // there are 87 total. The bridge must not re-sort / re-slice. const PAGE = DATA.slice(0, 3) render(
, ) expect(screen.getByText('Row 01')).toBeTruthy() expect(screen.getByText('Row 03')).toBeTruthy() expect(screen.queryByText('Row 04')).toBeNull() // Pagination footer surfaces the remote total, not data.length. expect(screen.getByText(/of 87/)).toBeTruthy() }) it('fires onSortChange / onPageChange / onPageSizeChange after store writes', () => { const onSortChange = vi.fn() const onPageChange = vi.fn() const onPageSizeChange = vi.fn() render(
, ) fireEvent.click(screen.getByText('Score')) expect(onSortChange).toHaveBeenCalledWith({ columnId: 'score', direction: 'asc', }) }) it('selection prop drives the row checkbox state and onSelectionChange fires', () => { const onSelectionChange = vi.fn() render(
, ) const cb = screen.getByLabelText('Select row 2') expect((cb as HTMLInputElement).checked).toBe(true) fireEvent.click(screen.getByLabelText('Select row 1')) expect(onSelectionChange).toHaveBeenCalledWith([2, 1]) }) })