import { render, screen, fireEvent } from '@testing-library/react'; import { describe, it, expect } from 'vitest'; import { createColumnHelper, createTable, getCoreRowModel, } from '@tanstack/react-table'; import { Datatable, defaultGlobalFilterFn, Props } from './Datatable'; import { BasicTableSettings, createPersistedStore, refreshableSettings, RefreshableTableSettings, } from './types'; import { useTableState } from './useTableState'; // Mock data and dependencies type MockData = { id: string; name: string; age: number }; const mockData = [ { id: '1', name: 'John Doe', age: 30 }, { id: '2', name: 'Jane Smith', age: 25 }, { id: '3', name: 'Bob Johnson', age: 35 }, ]; const mockColumns = [ { accessorKey: 'name', header: 'Name' }, { accessorKey: 'age', header: 'Age' }, ]; // mock table settings / state export interface TableSettings extends BasicTableSettings, RefreshableTableSettings {} function createStore(storageKey: string) { return createPersistedStore(storageKey, 'name', (set) => ({ ...refreshableSettings(set), })); } const storageKey = 'test-table'; const settingsStore = createStore(storageKey); const mockSettingsManager = { pageSize: 10, search: '', sortBy: undefined, setSearch: vitest.fn(), setSortBy: vitest.fn(), setPageSize: vitest.fn(), }; function DatatableWithStore(props: Omit, 'settingsManager'>) { const tableState = useTableState(settingsStore, storageKey); return ( ); } describe('Datatable', () => { it('renders the table with correct data', () => { render( ); expect(screen.getByText('John Doe')).toBeInTheDocument(); expect(screen.getByText('Jane Smith')).toBeInTheDocument(); expect(screen.getByText('Bob Johnson')).toBeInTheDocument(); }); it('renders the table with a title', () => { render( ); expect(screen.getByText('Test Table')).toBeInTheDocument(); }); it('handles row selection when not disabled', () => { render( ); const checkboxes = screen.getAllByRole('checkbox'); fireEvent.click(checkboxes[1]); // Select the first row // Check if the row is selected (you might need to adapt this based on your implementation) expect(checkboxes[1]).toBeChecked(); }); it('disables row selection when disableSelect is true', () => { render( ); const checkboxes = screen.queryAllByRole('checkbox'); expect(checkboxes.length).toBe(0); }); it('handles sorting', () => { render( ); const nameHeader = screen.getByText('Name'); fireEvent.click(nameHeader); // Check if setSortBy was called with the correct arguments expect(mockSettingsManager.setSortBy).toHaveBeenCalledWith('name', true); }); it('renders loading state', () => { render( ); expect(screen.getByText('Loading...')).toBeInTheDocument(); }); it('renders empty state', () => { render( ); expect(screen.getByText('No data available')).toBeInTheDocument(); }); }); // Test the defaultGlobalFilterFn used in searches type Person = { id: string; name: string; age: number; isEmployed: boolean; tags?: string[]; city?: string; family?: { sister: string; uncles?: string[] }; }; const data: Person[] = [ { // searching primitives should be supported id: '1', name: 'Alice', age: 30, isEmployed: true, // supporting arrays of primitives should be supported tags: ['music', 'likes-pixar'], // supporting objects of primitives should be supported (values only). // but shouldn't be support nested objects / arrays family: { sister: 'sophie', uncles: ['john', 'david'] }, }, ]; const columnHelper = createColumnHelper(); const columns = [ columnHelper.accessor('name', { id: 'name', }), columnHelper.accessor('isEmployed', { id: 'isEmployed', }), columnHelper.accessor('age', { id: 'age', }), columnHelper.accessor('tags', { id: 'tags', }), columnHelper.accessor('family', { id: 'family', }), ]; const mockTable = createTable({ columns, data, getCoreRowModel: getCoreRowModel(), state: {}, onStateChange() {}, renderFallbackValue: undefined, getRowId: (row) => row.id, }); const mockRow = mockTable.getRow('1'); describe('defaultGlobalFilterFn', () => { it('should return true when filterValue is null', () => { const result = defaultGlobalFilterFn(mockRow, 'Name', null); expect(result).toBe(true); }); it('should return true when filterValue.search is empty', () => { const result = defaultGlobalFilterFn(mockRow, 'Name', { search: '', }); expect(result).toBe(true); }); it('should filter string values correctly', () => { expect( defaultGlobalFilterFn(mockRow, 'name', { search: 'hello', }) ).toBe(false); expect( defaultGlobalFilterFn(mockRow, 'name', { search: 'ALICE', }) ).toBe(true); expect( defaultGlobalFilterFn(mockRow, 'name', { search: 'Alice', }) ).toBe(true); }); it('should filter number values correctly', () => { expect(defaultGlobalFilterFn(mockRow, 'age', { search: '123' })).toBe( false ); expect(defaultGlobalFilterFn(mockRow, 'age', { search: '30' })).toBe(true); expect(defaultGlobalFilterFn(mockRow, 'age', { search: '67' })).toBe(false); }); it('should filter boolean values correctly', () => { expect( defaultGlobalFilterFn(mockRow, 'isEmployed', { search: 'true' }) ).toBe(true); expect( defaultGlobalFilterFn(mockRow, 'isEmployed', { search: 'false' }) ).toBe(false); }); it('should filter object values correctly', () => { expect(defaultGlobalFilterFn(mockRow, 'family', { search: 'sophie' })).toBe( true ); expect(defaultGlobalFilterFn(mockRow, 'family', { search: '30' })).toBe( false ); }); it('should filter array values correctly', () => { expect(defaultGlobalFilterFn(mockRow, 'tags', { search: 'music' })).toBe( true ); expect( defaultGlobalFilterFn(mockRow, 'tags', { search: 'Likes-Pixar' }) ).toBe(true); expect(defaultGlobalFilterFn(mockRow, 'tags', { search: 'grape' })).toBe( false ); expect(defaultGlobalFilterFn(mockRow, 'tags', { search: 'likes' })).toBe( true ); }); it('should handle complex nested structures', () => { expect(defaultGlobalFilterFn(mockRow, 'family', { search: 'sophie' })).toBe( true ); expect(defaultGlobalFilterFn(mockRow, 'family', { search: 'mason' })).toBe( false ); }); it('should not filter non-primitive values within objects and arrays', () => { expect(defaultGlobalFilterFn(mockRow, 'family', { search: 'john' })).toBe( false ); expect(defaultGlobalFilterFn(mockRow, 'family', { search: 'david' })).toBe( false ); }); });