import BaseTable from '../Table'; import Table from '../index'; import Column from '../Column'; import {Tag, Tooltip} from '../../index'; import { getColumns, getData, getGroupColumns, getGroupData, getJSXColumns, getNestColumns, getNestData, getRandomNumber, getTreeData, mount, sleep, } from '../../_test_/utils'; import {BASE_CLASS_PREFIX} from '../../../semi-foundation/base/constants'; import {IconStar} from '@douyinfe/semi-icons'; /** * Table 需要测试的使用场景 * ✅ JSX 写法 * ✅ 行选择 * ✅ 分页 * ✅ 固定列 * ✅ 排序 * ✅ 筛选 * ✅ 展开 * ✅ 树形展示 * ✅ 自定义行或单元格 * ✅ 分组 * ✅ 表头合并 * ✅ 更新数据 * ✅ 完全自定义渲染 * ✅ 行列合并 * ❌ 可伸缩列(不好测试) * ❌ 拖拽排序(不好测试) * ❌ 虚拟化 */ const dataTotalSize = 46; const data = getData(dataTotalSize); const columns = getColumns(); function testAppearance(demo, params) { const {onRow, myCls, myClsIndex} = params; // check table wrapper expect(demo.find(`.${BASE_CLASS_PREFIX}-table-wrapper`).length).toBe(1); // check if has header expect(demo.find(`.${BASE_CLASS_PREFIX}-table-title`).length).toBe(1); // check if has footer expect(demo.find(`.${BASE_CLASS_PREFIX}-table-footer`).length).toBe(1); // check onRow expect(onRow.called).toBe(true); expect( demo .find(`.${BASE_CLASS_PREFIX}-table-tbody .${BASE_CLASS_PREFIX}-table-row`) .at(0) .hasClass(myCls) ).toBe(false); expect( demo.find(`.${BASE_CLASS_PREFIX}-table-tbody .${BASE_CLASS_PREFIX}-table-row`).reduce((prev, item, index) => { if (item.hasClass(myCls)) { return index; } else { return prev; } }, -1) ).toBe(myClsIndex); // check pagination expect(demo.find(`.${BASE_CLASS_PREFIX}-table-pagination-outer`).length).toBe(1); // click page 2 const curPage = 2; demo.find(`.${BASE_CLASS_PREFIX}-page li`) .at(curPage) .simulate('click'); expect(demo.find(BaseTable).state('pagination')).toHaveProperty('currentPage', curPage); } describe(`Table`, () => { it(`test jsx columns appearance`, async () => { const myCls = `my-tr-class`; const myClsIndex = 2; const onRow = sinon.spy((record, index) => ({ className: index === myClsIndex ? myCls : '', })); const columns = getJSXColumns(); const demo = mount( 'Table Title'} footer={() => 'Table Footer'} children={columns} /> ); testAppearance(demo, { onRow, myCls, myClsIndex, }); const newColumns = getJSXColumns(); demo.setProps({ children: newColumns, }); testAppearance(demo, { onRow, myCls, myClsIndex, }); demo.unmount(); }); it(`test object columns appearance`, async () => { const myCls = `my-tr-class`; const myClsIndex = 2; const onRow = sinon.spy((record, index) => ({ className: index === myClsIndex ? myCls : '', })); const columns = getColumns(); const demo = mount(
'Table Title'} footer={() => 'Table Footer'} columns={columns} /> ); testAppearance(demo, { onRow, myCls, myClsIndex, }); const newColumns = getColumns(); demo.setProps({ columns: newColumns, }); testAppearance(demo, { onRow, myCls, myClsIndex, }); }); it(`test dataSource change appearance`, async () => { const myCls = `my-tr-class`; const myClsIndex = 2; const onRow = sinon.spy((record, index) => ({ className: index === myClsIndex ? myCls : '', })); const columns = getColumns(); const data = getData(25); const demo = mount(
'Table Title'} footer={() => 'Table Footer'} columns={columns} /> ); testAppearance(demo, { onRow, myCls, myClsIndex, }); const dataNum = getRandomNumber(100, 40); const newData = getData(dataNum); demo.setProps({ dataSource: newData, }); testAppearance(demo, { onRow, myCls, myClsIndex, }); }); it(`test selection`, async () => { const columns = getColumns(); const selectedRowKeys = ['0']; const rowKey = 'key'; const disabledKeys = ['4']; let rowSelection = { onChange: sinon.spy((selectedRowKeys, selectedRows) => { rowSelection = {...rowSelection, selectedRowKeys: [...selectedRowKeys]}; demo.setProps({ rowSelection, }); }), onSelectAll: sinon.spy((selected, selectedRows, changedRows) => { rowSelection = {...rowSelection, selectedRowKeys: selected ? [...selectedRowKeys] : []}; demo.setProps({ rowSelection, }); }), getCheckboxProps: sinon.spy(record => ({ disabled: disabledKeys.includes(record[rowKey]), // Column configuration not to be checked name: record.name, })), onSelect: sinon.spy((record, selected) => { }), selectedRowKeys: ['3'], defaultSelectedRowKeys: ['0', '1'], }; const demo = mount(
); expect(demo.find(`.${BASE_CLASS_PREFIX}-checkbox`).length).toBeGreaterThan(0); expect(demo.find(`.${BASE_CLASS_PREFIX}-table-row .${BASE_CLASS_PREFIX}-checkbox-inner-checked`).length).toBe( rowSelection.selectedRowKeys.length ); expect(demo.find(`.${BASE_CLASS_PREFIX}-table-thead .${BASE_CLASS_PREFIX}-checkbox-indeterminate`).length).toBe( 1 ); expect( demo .find(`.${BASE_CLASS_PREFIX}-table-tbody .${BASE_CLASS_PREFIX}-checkbox`) .map(el => el) .every((el, index) => disabledKeys.includes(index.toString()) ? el.hasClass(`${BASE_CLASS_PREFIX}-checkbox-disabled`) : true ) ).toBeTruthy(); // click disabled checkbox demo.find(`.${BASE_CLASS_PREFIX}-table-row .${BASE_CLASS_PREFIX}-checkbox-disabled`).simulate('click'); expect(rowSelection.onSelect.notCalled).toBeTruthy(); // click first row checkbox demo.find(`.${BASE_CLASS_PREFIX}-table-tbody .${BASE_CLASS_PREFIX}-table-row .${BASE_CLASS_PREFIX}-checkbox`) .at(0) .simulate('click'); expect(rowSelection.onSelect.calledOnce).toBeTruthy(); // click table header checkbox and select all demo.find( `.${BASE_CLASS_PREFIX}-table-thead .${BASE_CLASS_PREFIX}-table-column-selection .${BASE_CLASS_PREFIX}-checkbox` ).simulate('click'); expect(rowSelection.onSelectAll.calledOnce).toBeTruthy(); expect(demo.find(BaseTable).state('rowSelection')).toHaveProperty( 'selectedRowKeys', rowSelection.selectedRowKeys ); expect(demo.find(`.${BASE_CLASS_PREFIX}-table-thead .${BASE_CLASS_PREFIX}-checkbox-inner-checked`).length).toBe( 1 ); // click table header checkbox and deselect all demo.find( `.${BASE_CLASS_PREFIX}-table-thead .${BASE_CLASS_PREFIX}-table-column-selection .${BASE_CLASS_PREFIX}-checkbox` ).simulate('click'); expect(rowSelection.onSelectAll.calledTwice).toBeTruthy(); expect(demo.find(BaseTable).state('rowSelection')).toHaveProperty('selectedRowKeys', []); expect(demo.find(`.${BASE_CLASS_PREFIX}-table-thead .${BASE_CLASS_PREFIX}-checkbox-inner-checked`).length).toBe( 0 ); expect(demo.find(`.${BASE_CLASS_PREFIX}-table-thead .${BASE_CLASS_PREFIX}-checkbox-indeterminate`).length).toBe( 0 ); }); it(`test selection after data change`, async () => { const columns = getColumns(); const selectedRowKeys = ['0']; const rowKey = 'key'; const disabledKeys = ['4']; let rowSelection = { onSelectAll: sinon.spy((selected, selectedRows, changedRows) => { rowSelection = {...rowSelection, selectedRowKeys: selected ? [...selectedRowKeys] : []}; demo.setProps({ rowSelection, }); }), getCheckboxProps: sinon.spy(record => ({ disabled: disabledKeys.includes(record[rowKey]), // Column configuration not to be checked name: record.name, })), onSelect: sinon.spy((record, selected) => { }), }; const demo = mount(
); const newData = getData(50); demo.setProps({ dataSource: newData, }); // click disabled checkbox demo.find(`.${BASE_CLASS_PREFIX}-table-row .${BASE_CLASS_PREFIX}-checkbox-disabled`).simulate('click'); expect(rowSelection.onSelect.notCalled).toBeTruthy(); // click first row checkbox demo.find(`.${BASE_CLASS_PREFIX}-table-tbody .${BASE_CLASS_PREFIX}-table-row .${BASE_CLASS_PREFIX}-checkbox`) .at(0) .simulate('click'); expect(rowSelection.onSelect.calledOnce).toBeTruthy(); // click table header checkbox and select all demo.find( `.${BASE_CLASS_PREFIX}-table-thead .${BASE_CLASS_PREFIX}-table-column-selection .${BASE_CLASS_PREFIX}-checkbox` ).simulate('click'); expect(rowSelection.onSelectAll.calledOnce).toBeTruthy(); // click table header checkbox and deselect all demo.find( `.${BASE_CLASS_PREFIX}-table-thead .${BASE_CLASS_PREFIX}-table-column-selection .${BASE_CLASS_PREFIX}-checkbox` ).simulate('click'); expect(rowSelection.onSelectAll.calledTwice).toBeTruthy(); }); it(`test expandedRowRender and fixed`, async () => { const columns = getColumns(); const rowKey = 'key'; const expandedRowRender = (record, index, expanded) => (

{record.description}

); const unexpandableKey = '2'; const demo = mount(
record[rowKey] !== unexpandableKey} /> ); const leftFixedCells = demo.find(`.${BASE_CLASS_PREFIX}-table-cell-fixed-left`); const rightFixedCells = demo.find(`.${BASE_CLASS_PREFIX}-table-cell-fixed-right`); expect(leftFixedCells.length).toBeGreaterThan(0); expect(rightFixedCells.length).toBeGreaterThan(0); expect( demo.find(`.${BASE_CLASS_PREFIX}-table-cell-fixed-left.${BASE_CLASS_PREFIX}-table-column-expand`).length ).toBeGreaterThan(0); const newData = getData(50); demo.setProps({ dataSource: newData, }); expect( demo.find(`.${BASE_CLASS_PREFIX}-table-cell-fixed-left.${BASE_CLASS_PREFIX}-table-column-expand`).length ).toBeGreaterThan(0); }); it(`test tree data`, async () => { const rowKey = 'key'; const childrenRecordName = 'children'; const childrenData = getTreeData(); let expandedRowKeys = [1]; const onExpand = sinon.spy((expanded, record) => { const keySet = new Set([...expandedRowKeys]); if (expanded) { keySet.add(record[rowKey]); } else { keySet.delete(record[rowKey]); } expandedRowKeys = Array.from(keySet); demo.setProps({ expandedRowKeys, }); }); const demo = mount(
); const table = demo.find(BaseTable); expect(demo.find(`.${BASE_CLASS_PREFIX}-table-tbody .${BASE_CLASS_PREFIX}-table-row`).length).toBe( childrenData[0].children.length + childrenData.length ); // click 3rd row to expand row table .find( `.${BASE_CLASS_PREFIX}-table-body .${BASE_CLASS_PREFIX}-table-row .${BASE_CLASS_PREFIX}-table-expand-icon` ) .at(1) .simulate('click'); expect(onExpand.calledOnce).toBeTruthy(); expect(table.state(`expandedRowKeys`)).toEqual(expandedRowKeys); // click first expand button to collapse row table .find( `.${BASE_CLASS_PREFIX}-table-body .${BASE_CLASS_PREFIX}-table-row .${BASE_CLASS_PREFIX}-table-expand-icon` ) .at(0) .simulate('click'); expect(table.state('expandedRowKeys')).toEqual(expandedRowKeys); }); it(`test grouped data`, async () => { const rowKey = record => `${record.city && record.city.toLowerCase()}-${record.job && record.job.toLowerCase()}`; const data = [ { city: 'Beijing', job: 'FE', department: 'IES', }, { city: 'Beijing', job: 'BE', department: 'IES', }, { city: 'Shanghai', job: 'Android', department: 'IES', }, { city: 'Tokyo', job: 'Android', department: 'IES', }, { city: 'Shanghai', job: 'IOS', department: 'EE', }, { city: 'LA', job: 'SE', department: 'EE', }, { city: 'Beijing', job: 'Android', department: 'EE', }, { city: 'Tokyo', job: 'IOS', department: 'EE', }, { city: 'Tokyo', job: 'SE', department: 'DATA', }, { city: 'Shanghai', job: 'BE', department: 'DATA', }, { city: 'LA', job: 'Android', department: 'DATA', }, { city: 'LA', job: 'IOS', department: 'DATA', }, ]; const columns = [ { dataIndex: 'city', title: 'City', width: 400, sorter: (a, b) => (a.city > b.city ? 1 : -1), }, { dataIndex: 'job', title: 'Job', width: 200, filters: [ { text: 'IOS', value: 'IOS', }, { text: 'Android', value: 'Android', }, ], onFilter: (value, record) => record.job && record.job.indexOf(value) === 0, }, { dataIndex: 'department', title: 'Department', }, ]; const groupedRowClick = sinon.spy(); const demo = mount(
Jobs in {groupKey}:} onGroupedRow={(group, index) => ({ onClick: groupedRowClick, })} clickGroupedRowToExpand scroll={{ y: 480, }} /> ); expect(demo.find(`.${BASE_CLASS_PREFIX}-table-row-section`).length).toBe(4); expect(demo.find(`.${BASE_CLASS_PREFIX}-table-body .${BASE_CLASS_PREFIX}-table-row`).length).toBe(0); demo.find(`.${BASE_CLASS_PREFIX}-table-row-section`) .at(0) .simulate('click'); expect(demo.find(`.${BASE_CLASS_PREFIX}-table-row`).length).toBeGreaterThan(0); }); it(`test events: page, filter, sort`, async () => { const columns = [ { title: 'Name', dataIndex: 'name', width: 150, filters: [ { text: '名字包含"1"', value: '1', }, { text: '名字包含"2"', value: '2', }, ], onFilter: (value, record) => record.name.indexOf(value) > -1, }, { title: 'Age', dataIndex: 'age', width: 150, sorter: (a, b) => (a.age - b.age > 0 ? 1 : -1), }, { title: 'Address', dataIndex: 'address', }, { render: (text, record) => ( Show Info ), width: 150, }, ]; let onChangeCalledCount = 0; const onChange = sinon.spy(); const demo = mount(
); demo.find(`.${BASE_CLASS_PREFIX}-table-thead .${BASE_CLASS_PREFIX}-table-column-filter`).simulate('click'); expect(demo.find(`.${BASE_CLASS_PREFIX}-dropdown`).length).toBe(1); expect(onChange.callCount).toBe(onChangeCalledCount); // click first filter demo.find(`.${BASE_CLASS_PREFIX}-dropdown .${BASE_CLASS_PREFIX}-dropdown-item`) .at(0) .find(`.${BASE_CLASS_PREFIX}-checkbox`) .simulate('click', { nativeEvent: null, }); expect(onChange.callCount).toBe(++onChangeCalledCount); // click first filter again demo.find(`.${BASE_CLASS_PREFIX}-dropdown .${BASE_CLASS_PREFIX}-dropdown-item`) .at(0) .find(`.${BASE_CLASS_PREFIX}-checkbox`) .simulate('click', { nativeEvent: null, }); expect(onChange.callCount).toBe(++onChangeCalledCount); // to page 2 demo.find(`.${BASE_CLASS_PREFIX}-page .${BASE_CLASS_PREFIX}-page-item`) .at(2) .simulate('click', { nativeEvent: null, }); expect(onChange.callCount).toBe(++onChangeCalledCount); // to page 1 demo.find(`.${BASE_CLASS_PREFIX}-page .${BASE_CLASS_PREFIX}-page-item`) .at(1) .simulate('click', { nativeEvent: null, }); expect(onChange.callCount).toBe(++onChangeCalledCount); // sort demo.find(`.${BASE_CLASS_PREFIX}-table-thead .${BASE_CLASS_PREFIX}-table-column-sorter`).simulate('click', { nativeEvent: null, }); expect(onChange.callCount).toBe(++onChangeCalledCount); }); it(`test filter, and update dataSource with exist filter`, () => { const columns = [ { className: 'name-col', title: 'Name', dataIndex: 'name', width: 150, filters: [ { text: '名字包含"1"', value: '1', }, { text: '名字包含"2"', value: '2', }, ], onFilter: (value, record) => record.name.indexOf(value) > -1, }, { title: 'Age', dataIndex: 'age', width: 150, sorter: (a, b) => (a.age - b.age > 0 ? 1 : -1), }, { title: 'Address', dataIndex: 'address', }, ]; const demo = mount(
); demo.find(`.${BASE_CLASS_PREFIX}-table-thead .${BASE_CLASS_PREFIX}-table-column-filter`).simulate('click'); // click first filter demo.find(`.${BASE_CLASS_PREFIX}-dropdown .${BASE_CLASS_PREFIX}-dropdown-item`) .at(0) .find(`.${BASE_CLASS_PREFIX}-checkbox`) .simulate('click', { nativeEvent: null, }); const nameColList = demo.find('.semi-table-tbody .name-col'); const filteredData = data.filter(item => item.name.indexOf('1') > -1); expect(nameColList.length).toBe(filteredData.length); // update dataSource with exist filter const newData = getData(10); const newFilteredData = newData.filter(item => item.name.indexOf('1') > -1); demo.setProps({ dataSource: newData, }); demo.update(); const newNameColList = demo.find('.semi-table-tbody .name-col'); expect(newNameColList.length).toBe(newFilteredData.length); }); it(`test controlled dataSource and columns`, async () => { let pagination = { currentPage: 1, total: data.length, pageSize: 10, }; const getPageData = () => data.slice( (pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize ); let currentData = getPageData(); let columns = [ { title: 'Name', dataIndex: 'name', width: 150, filters: [ { text: '名字包含"1"', value: '1', }, { text: '名字包含"2"', value: '2', }, ], onFilter: (value, record) => record.name.indexOf(value) > -1, }, { title: 'Age', dataIndex: 'age', width: 150, sorter: true, }, { title: 'Address', dataIndex: 'address', }, { render: (text, record) => ( Show Info ), width: 150, }, ]; const onChange = sinon.spy(({pagination: tablePagination, filters: tableFilters, sorters: tableSorters}) => { if (tablePagination && tablePagination.currentPage !== pagination.currentPage) { pagination.currentPage = tablePagination.currentPage; currentData = getPageData(); columns = [...columns]; demo.setProps({ dataSource: currentData, columns, pagination, }); } }); const demo = mount(
); const table = demo.find(BaseTable); demo.find(`.${BASE_CLASS_PREFIX}-page .${BASE_CLASS_PREFIX}-page-item`) .at(2) .simulate('click', { nativeEvent: null, }); expect(onChange.called).toBe(true); expect( demo .find(`.${BASE_CLASS_PREFIX}-page .${BASE_CLASS_PREFIX}-page-item`) .at(2) .hasClass(`${BASE_CLASS_PREFIX}-page-item-active`) ).toBe(true); }); it(`test resizable table`, async () => { let columns = [ { title: 'Name', dataIndex: 'name', width: 150, filters: [ { text: '名字包含"1"', value: '1', }, { text: '名字包含"2"', value: '2', }, ], onFilter: (value, record) => record.name.indexOf(value) > -1, }, { title: 'Age', dataIndex: 'age', width: 150, sorter: true, }, { title: 'Address', dataIndex: 'address', width: 300, }, { render: (text, record) => ( Show Info ), }, ]; const expandedRowRender = (record, index, expanded) => (

{record.description}

); const demo = mount(
); expect(demo.find(`.${BASE_CLASS_PREFIX}-table-thead .react-resizable-handle`).length).toBe( columns.reduce((count, col) => (typeof col.width === 'number' ? count + 1 : count), 0) ); }); it('test table sortOrder descend', async () => { const sortColumns = [ { title: 'Name', dataIndex: 'name', sorter: (a, b) => a.name.length - b.name.length, sortOrder: 'descend', }, { title: 'Age', dataIndex: 'age', }, { title: 'Address', dataIndex: 'address', }, ]; const sortData = [ { key: '1', name: 'John Brown', age: 32, address: 'New York No. 1 Lake Park', }, { key: '2', name: 'Jim Green', age: 42, address: 'London No. 1 Lake Park', }, { key: '3', name: 'Joe Black', age: 32, address: 'Sidney No. 1 Lake Park', }, { key: '4', name: 'Jim Red', age: 32, address: 'London No. 2 Lake Park', }, ]; const targetData = sortData.reduce((a, b) => (a.name.length > b.name.length ? a : b)); const {name} = targetData; const sortTable = mount(
); const firstCell = sortTable.find('.semi-table-tbody .semi-table-row .semi-table-row-cell').at(0); // console.log(sortTable.debug({ ignoreProps: true })); expect(firstCell.text()).toBe(name); }); it('test table sortOrder ascend', async () => { const sortColumns = [ { title: 'Name', dataIndex: 'name', sorter: (a, b) => a.name.length - b.name.length, sortOrder: 'ascend', }, { title: 'Age', dataIndex: 'age', }, { title: 'Address', dataIndex: 'address', }, ]; const sortData = [ { key: '1', name: 'John Brown', age: 32, address: 'New York No. 1 Lake Park', }, { key: '2', name: 'Jim Green', age: 42, address: 'London No. 1 Lake Park', }, { key: '3', name: 'Joe Black', age: 32, address: 'Sidney No. 1 Lake Park', }, { key: '4', name: 'Jim Red', age: 32, address: 'London No. 2 Lake Park', }, ]; const targetData = sortData.reduce((a, b) => (a.name.length < b.name.length ? a : b)); const {name} = targetData; const sortTable = mount(
); const firstCell = sortTable.find('.semi-table-tbody .semi-table-row .semi-table-row-cell').at(0); expect(firstCell.text()).toBe(name); }); it('test controlled sortOrder', async () => { const sortColumns = [ { title: 'Name', dataIndex: 'name', sorter: (a, b) => a.name.length - b.name.length, sortOrder: 'descend', }, { title: 'Age', dataIndex: 'age', }, { title: 'Address', dataIndex: 'address', }, ]; const sortData = [ { key: '1', name: 'long name', age: 32, address: 'New York No. 1 Lake Park', }, { key: '2', name: 'longest name', age: 42, address: 'London No. 1 Lake Park', }, { key: '3', name: 'longer name', age: 32, address: 'Sidney No. 1 Lake Park', }, { key: '4', name: 'short', age: 32, address: 'London No. 2 Lake Park', }, ]; const targetData = sortData.reduce((a, b) => (a.name.length > b.name.length ? a : b)); const {name} = targetData; const sortTable = mount(
); const firstCell = sortTable.find('.semi-table-tbody .semi-table-row .semi-table-row-cell').at(0); expect(firstCell.text()).toBe(name); const ascendOrderColumns = [ { title: 'Name', dataIndex: 'name', sorter: (a, b) => a.name.length - b.name.length, sortOrder: 'ascend', }, { title: 'Age', dataIndex: 'age', }, { title: 'Address', dataIndex: 'address', }, ]; // test sortOrder: ascend sortTable.setProps({ columns: ascendOrderColumns, }); sortTable.update(); const ascendTargetData = sortData.reduce((a, b) => (a.name.length < b.name.length ? a : b)); const {name: ascendTargetName} = ascendTargetData; const ascendFirstCell = sortTable.find('.semi-table-tbody .semi-table-row .semi-table-row-cell').at(0); expect(ascendFirstCell.text()).toBe(ascendTargetName); // test sortOrder: false, expect order as given data const defaultOrderColumns = [ { title: 'Name', dataIndex: 'name', sorter: (a, b) => a.name.length - b.name.length, sortOrder: false, }, { title: 'Age', dataIndex: 'age', }, { title: 'Address', dataIndex: 'address', }, ]; sortTable.setProps({ columns: defaultOrderColumns, }); sortTable.update(); const defaultTargetData = sortData[0]; const {name: defaultTargetName} = defaultTargetData; const defaultFirstCell = sortTable.find('.semi-table-tbody .semi-table-row .semi-table-row-cell').at(0); expect(defaultFirstCell.text()).toBe(defaultTargetName); }); it('test table select all when disabled all rows', async () => { const columns = [ { title: 'Name', dataIndex: 'name', width: 150, }, { title: 'Age', dataIndex: 'age', width: 150, }, { title: 'Address', dataIndex: 'address', }, ]; const rowSelection = { onSelectAll: sinon.spy(() => { }), getCheckboxProps: sinon.spy(record => ({ disabled: true, // disabled all name: record.name, })), }; const demo = mount(
); demo.find( `.${BASE_CLASS_PREFIX}-table-thead .${BASE_CLASS_PREFIX}-table-column-selection .${BASE_CLASS_PREFIX}-checkbox` ).simulate('click'); // should select 0 rows expect(rowSelection.onSelectAll.getCall(0).args[1].length).toBe(0); }); it('test jsx async data', async () => { const asyncData = [ { key: '1', name: 'John Brown', age: 32, address: 'New York No. 1 Lake Park, New York No. 1 Lake Park', }, { key: '2', name: 'Jim Green', age: 42, address: 'London No. 1 Lake Park', }, { key: '3', name: 'Joe Black', age: 32, address: 'Sidney No. 1 Lake Park', }, { key: '4', name: 'Michael James', age: 99, address: 'Sidney No. 1 Lake Park', }, ]; const demo = mount(
{text}}/>
); setTimeout(() => { demo.setProps({ dataSource: asyncData, }); }, 2000); await sleep(3000); const baseTable = demo.find(BaseTable); expect(baseTable.state('cachedColumns').length).toEqual(3); expect(baseTable.state('dataSource').length).toEqual(4); }); it('test cell align', async () => { const columns = getColumns(); const alignRightColumns = columns.map(column => { column.align = 'right'; return column; }); const demo = mount(); const titleAlignCells = demo.find(`.${BASE_CLASS_PREFIX}-table-align-right`); expect(titleAlignCells.length).toBeGreaterThan(0); const bodyAlignCells = document.querySelectorAll(`.${BASE_CLASS_PREFIX}-table-row-cell`); bodyAlignCells.forEach(cell => { expect(cell.style.textAlign).toEqual('right'); }); }); it('test column className', async () => { const columns = getColumns(); const alignRightColumns = columns.map(column => { column.className = 'test'; return column; }); const demo = mount(
); const titleAlignCells = demo.find(`.${BASE_CLASS_PREFIX}-table-row-head`); const bodyAlignCells = demo.find(`.${BASE_CLASS_PREFIX}-table-row-cell`); titleAlignCells.forEach(cell => { expect(cell.hasClass('test')).toBeTruthy(); }); bodyAlignCells.forEach(cell => { expect(cell.hasClass('test')).toBeTruthy(); }); }); it('test header appearance', async () => { // object column const columns = getColumns(); const demo = mount(
); expect(demo.find('.semi-table-thead').length).toEqual(1); demo.setProps({ showHeader: false, }); expect(demo.find('.semi-table-thead').length).toEqual(0); // jsx column const jsxColumns = getJSXColumns(); const demo1 = mount(
{jsxColumns}
); expect(demo1.find('.semi-table-thead').length).toEqual(1); demo1.setProps({ showHeader: false, }); expect(demo1.find('.semi-table-thead').length).toEqual(0); }); it('test ref getCurrentPageData', async () => { // object column const pageSize = 10; const refObj = { current: null, }; const columns = getColumns(); const demo = mount( ); const {dataSource} = refObj.current.getCurrentPageData(); expect(dataSource.length).toEqual(pageSize); // jsx column const jsxColumns = getJSXColumns(); const jsxRefObj = { current: null, }; const jsxDemo = mount(
{jsxColumns}
); const {dataSource: jsxData} = jsxRefObj.current.getCurrentPageData(); expect(jsxData.length).toEqual(pageSize); }); it('test render expandIcon', async () => { const expandedRowRender = () =>
Semi Design
; const columns = getColumns(); const demo = mount( } /> ); expect(demo.find('.semi-icon-star').length).toBeGreaterThan(0); const demo2 = mount(
{expanded && }
} /> ); expect(demo2.find('.semi-icon-star').length).toBeGreaterThan(0); }); it(`test onRow/onCell`, async () => { const onRowClick = sinon.spy(() => { }); const onHeaderRowClick = sinon.spy(() => { }); const onCellClick = sinon.spy(() => { }); const onHeaderCellClick = sinon.spy(() => { }); const onRow = sinon.spy((record, index) => ({ className: 'test-row', onClick: onRowClick, })); const onHeaderRow = sinon.spy((record, index) => ({ className: 'test-row', onClick: onHeaderRowClick, })); const columns = getColumns(); const onCellColumns = columns.map((column, index) => { const style = index === 0 ? { width: 200, height: 60, } : {}; column.onCell = () => ({ onClick: onCellClick, onCellClick, style, }); column.onHeaderCell = () => ({ onClick: onHeaderCellClick, style, }); return column; }); const demo = mount(
); const tableCells = demo.find('.semi-table-body .semi-table-row-cell'); const tableHeaderCells = demo.find('.semi-table-thead .semi-table-row-head'); // cell style expect(tableCells.at(0).instance().style.width).toEqual('200px'); expect(tableCells.at(0).instance().style.height).toEqual('60px'); expect(tableHeaderCells.at(0).instance().style.width).toEqual('200px'); expect(tableHeaderCells.at(0).instance().style.height).toEqual('60px'); // body click tableCells.at(0).simulate('click'); expect(onCellClick.called).toBeTruthy(); const tableRows = demo.find('.semi-table-body .semi-table-row'); tableRows.at(0).simulate('click'); expect(onRowClick.called).toBeTruthy(); // header click tableHeaderCells.at(0).simulate('click'); expect(onHeaderCellClick.called).toBeTruthy(); const tableHeaderRows = demo.find('.semi-table-thead .semi-table-row'); tableHeaderRows.at(0).simulate('click'); expect(onHeaderRowClick.called).toBeTruthy(); }); it('test header merge', async () => { // 测试头部合并 function testHeaderMerge(demo, params) { const rows = demo.find('.semi-table-thead .semi-table-row'); expect(rows.length).toEqual(2); const firstRow = rows.at(0); const secondRow = rows.at(1); expect(firstRow.childAt(0).instance().colSpan).toEqual(2); expect(firstRow.childAt(1).instance().colSpan).toEqual(2); expect(firstRow.childAt(2).instance().colSpan).toEqual(1); expect(firstRow.childAt(2).instance().rowSpan).toEqual(2); expect(secondRow.childAt(0).instance().colSpan).toEqual(1); expect(secondRow.childAt(1).instance().colSpan).toEqual(1); expect(secondRow.childAt(2).instance().colSpan).toEqual(1); expect(secondRow.childAt(3).instance().colSpan).toEqual(1); } const nestData = getNestData(); const filters = [ { text: 'Code 45', value: '45', }, { text: 'King 4', value: 'King 4', }, ]; const jsxDemo = mount(
record.name.includes(value)} /> (a.age - b.age > 0 ? 1 : -1)} />
); const columns = getNestColumns(); const demo = mount(); testHeaderMerge(jsxDemo); testHeaderMerge(demo); }); it('test without columns', async () => { const jsxDemo = mount(
); const demo = mount(); expect(jsxDemo.find('.semi-table-row-head').length).toBe(0); expect(jsxDemo.find('.semi-table-row-cell').length).toBe(0); expect(demo.find('.semi-table-row-head').length).toBe(0); expect(demo.find('.semi-table-row-cell').length).toBe(0); }); it('test useFullRender', async () => { const columns = [ { dataIndex: 'name', width: 250, filters: [ { text: '名字包含"1"', value: '1', }, { text: '名字包含"2"', value: '2', }, ], onFilter: (value, record) => record.name.indexOf(value) > -1, sorter: (a, b) => a.name.length - b.name.length, // 此处将useFullRender设置为true开启完全自定义渲染 useFullRender: true, // 此处从render的第四个形参中解构出 展开按钮、选择按钮、文本等内容 render: (text, record, index, {expandIcon, selection, indentText}) => { return ( {indentText} {expandIcon} {selection} {text} ); }, title: ({sorter, filter, selection}) => ( {selection} Name {sorter} {filter} ), }, { title: 'Age', dataIndex: 'age', }, { title: 'Address', dataIndex: 'address', }, ]; const rowSelection = { hidden: true, fixed: 'left', }; const demo = mount(
{record.description}
} /> ); const node = demo.find('.custom-render').at(0); expect(node.children().length).toEqual(3); expect(node.childAt(0).exists('.semi-table-expand-icon')).toBeTruthy(); expect(node.childAt(1).exists('.semi-table-selection-wrap')).toBeTruthy(); const title = demo.find('.custom-title').at(0); expect(title.children().length).toEqual(4); expect(title.childAt(0).exists('.semi-table-selection-wrap')).toBeTruthy(); expect(title.childAt(1).text()).toEqual('Name'); expect(title.childAt(2).exists('.semi-table-column-sorter')).toBeTruthy(); expect(title.childAt(3).exists('.semi-table-column-filter')).toBeTruthy(); }); it('test defaultExpandedRowKeys changed', async () => { const expandedRowRender = (record, index) =>
{`Semi Design ${index}`}
; const pageSize = 20; const defaultExpandedRowKeys = Array.from({ length: getRandomNumber(pageSize), }).map((_, i) => String(i)); const newDefaultExpandedRowKeys = Array.from({ length: getRandomNumber(pageSize), }).map((_, i) => String(i)); const columns = getColumns(); const demo = mount(
); expect(demo.find(BaseTable).state('expandedRowKeys').length).toEqual(defaultExpandedRowKeys.length); demo.setProps({ defaultExpandedRowKeys: newDefaultExpandedRowKeys, }); // 2.x defaultExpandedRowKeys 不再响应变化 expect(demo.find(BaseTable).state('expandedRowKeys').length).toEqual(defaultExpandedRowKeys.length); }); it('test getCheckboxProps changed', async () => { const defaultGetCheckboxProps = record => ({ disabled: record.key === '1', }); const newGetCheckboxProps = record => ({ disabled: ['0', '1'].includes(record.key), }); const columns = getColumns(); const demo = mount(
); expect(demo.find(BaseTable).state('disabledRowKeys').length).toEqual(1); demo.setProps({ rowSelection: { getCheckboxProps: newGetCheckboxProps, }, }); expect(demo.find(BaseTable).state('disabledRowKeys').length).toEqual(2); }); /** * 分页受控场景,更新数据后查看分页器是否保持当前页 */ it('test controlled pagination reset when dataSource change', async () => { const total = 100; const pagination = { pageSize: 10, currentPage: 2, }; const columns = getColumns(); const demo = mount(
); const dataNum = getRandomNumber(100, 40); const newData = getData(dataNum); demo.setProps({ dataSource: newData, }); await sleep(2000); expect( demo .find(`.${BASE_CLASS_PREFIX}-page .${BASE_CLASS_PREFIX}-page-item-active`) .getDOMNode().innerHTML ).toBe('2'); }); /** * 分页非受控场景,更新数据后查看分页器是否重置 */ it('test uncontrolled pagination reset when dataSource change', async () => { const total = 100; const columns = getColumns(); const demo = mount(
); demo.find(`.${BASE_CLASS_PREFIX}-page .${BASE_CLASS_PREFIX}-page-item`) .at(2) .simulate('click'); const dataNum = getRandomNumber(100, 40); const newData = getData(dataNum); demo.setProps({ dataSource: newData, }); await sleep(2000); demo.update(); expect(demo.find(`.semi-page .semi-page-item-active`).getDOMNode().innerHTML).toBe('1'); expect(demo.find(BaseTable).state('pagination')).toHaveProperty('currentPage', 1); }); it('test pagination changed', async () => { const total = 100; const pagination = { pageSize: 10, currentPage: 2, }; const newPagination = { pageSize: 5, currentPage: 1, }; const columns = getColumns(); const demo = mount(
); const paginationProps = demo.find(BaseTable).state('pagination'); expect(paginationProps.total).toEqual(total); expect(paginationProps.pageSize).toEqual(pagination.pageSize); expect(paginationProps.currentPage).toEqual(pagination.currentPage); demo.setProps({ pagination: newPagination, }); await sleep(2000); const newPaginationProps = demo.find(BaseTable).state('pagination'); expect(newPaginationProps.pageSize).toEqual(newPagination.pageSize); expect(newPaginationProps.currentPage).toEqual(newPagination.currentPage); }); it(`test grouped data change dataSource`, async () => { const data = getGroupData(); const columns = getGroupColumns(); const groupedRowClick = sinon.spy(); const rowKey = record => `${record.city && record.city.toLowerCase()}-${record.job && record.job.toLowerCase()}`; const demo = mount(
Jobs in {groupKey}:} onGroupedRow={(group, index) => ({ onClick: groupedRowClick, })} clickGroupedRowToExpand scroll={{ y: 480, }} /> ); const newData = [ { city: 'Tianjin', job: 'FE', department: 'IES', }, { city: 'Tianjin', job: 'Android', department: 'IES', }, { city: 'Wuhan', job: 'Android', department: 'IES', }, { city: 'Wuhan', job: 'SE', department: 'EE', }, { city: 'Chengdu', job: 'Android', department: 'EE', }, { city: 'Chengdu', job: 'IOS', department: 'EE', }, { city: 'Xiamen', job: 'SE', department: 'DATA', }, { city: 'Xiamen', job: 'IOS', department: 'DATA', }, ]; demo.setProps({ dataSource: newData, }); expect(demo.find(`.${BASE_CLASS_PREFIX}-table-row-section`).length).toBe(4); expect(demo.find(`.${BASE_CLASS_PREFIX}-table-body .${BASE_CLASS_PREFIX}-table-row`).length).toBe(0); demo.find(`.${BASE_CLASS_PREFIX}-table-row-section`) .at(0) .simulate('click'); expect(demo.find(`.${BASE_CLASS_PREFIX}-table-row`).length).toBeGreaterThan(0); }); it(`test given object columns and children`, async () => { const demo = mount(
); const test = demo.find(BaseTable); // 同时传columns与children时,columns优先 expect(test.state('cachedColumns')).toEqual(columns); expect(test.state('queries').length).toEqual(columns.length); }); it('test header colSpan', async () => { const columns = [ { title: 'Name', dataIndex: 'name', }, { title: 'Age', dataIndex: 'age', }, { title: 'Home phone', colSpan: 2, dataIndex: 'tel', }, { title: 'Phone', colSpan: 0, dataIndex: 'phone', }, { title: 'Address', dataIndex: 'address', }, ]; const data = [ { key: '1', name: 'ZhangSan', age: 50, tel: '010-20000000', phone: 10010, address: 'BeiJing No.1 High School', }, { key: '2', name: 'LiSi', tel: '010-30000000', phone: 10086, age: 40, address: 'ShangHai No. 1 High School', }, { key: '3', name: 'WangWu', age: 60, tel: '010-40000000', phone: 10011, address: 'NaiJing No.1 High School', }, { key: '4', name: 'XiaoMing', age: 20, tel: '010-50000000', phone: 12580, address: 'ShiJiaZhuang No.1 High School', }, { key: '5', name: 'XiaoHong', age: 40, tel: '010-60000000', phone: 12530, address: 'TaiBei No.2 High School', }, ]; const demo = mount(); expect(demo.find('.semi-table-row .semi-table-row-head').length).toBe(columns.length - 1); }); it('test expandAllRows', async () => { const expandedRowRender = () =>
Semi Design
const initData = []; const columns = getColumns(); const demo = mount(
); const newData = getData(20); demo.setProps({dataSource: newData}); demo.update(); const expandedRows = demo.find(`.${BASE_CLASS_PREFIX}-table-tbody .${BASE_CLASS_PREFIX}-table-row-expanded`) expect(expandedRows.length).toEqual(newData.length); // 动态切换 expandAllRows demo.setProps({expandAllRows: false}); demo.update(); const newExpandedRows = demo.find(`.${BASE_CLASS_PREFIX}-table-tbody .${BASE_CLASS_PREFIX}-table-row-expanded`) expect(newExpandedRows.length).toEqual(0); }); it(`test defaultExpandAllGroupRows`, async () => { const data = getGroupData(); const columns = getGroupColumns(); const rowKey = record => `${record.city && record.city.toLowerCase()}-${record.job && record.job.toLowerCase()}`; const groupSize = new Set(data.map(item => item.city)).size; const demo = mount(
Jobs in {groupKey}:} scroll={{y: 480}} defaultExpandAllGroupRows /> ); expect(demo.find(`.${BASE_CLASS_PREFIX}-table-tbody .${BASE_CLASS_PREFIX}-table-row-section.on`).length).toBe(groupSize); }); it(`test expandAllGroupRows`, async () => { const data = getGroupData(); const groupSize = new Set(data.map(item => item.city)).size; const columns = getGroupColumns(); const rowKey = record => `${record.city && record.city.toLowerCase()}-${record.job && record.job.toLowerCase()}`; const demo = mount(
Jobs in {groupKey}:} scroll={{y: 480}} expandAllGroupRows={true} /> ); demo.setProps({dataSource: data}); demo.update(); expect(demo.find(`.${BASE_CLASS_PREFIX}-table-tbody .${BASE_CLASS_PREFIX}-table-row-section.on`).length).toBe(groupSize); demo.setProps({expandAllGroupRows: false}); demo.update(); expect(demo.find(`.${BASE_CLASS_PREFIX}-table-tbody .${BASE_CLASS_PREFIX}-table-row-section.on`).length).toBe(0); }); it('test defaultSortOrder', async () => { const sortColumns = [ { title: 'Name', dataIndex: 'name', sorter: (a, b) => a.name.length - b.name.length, defaultSortOrder: 'descend', }, { title: 'Age', dataIndex: 'age', }, { title: 'Address', dataIndex: 'address', } ]; const sortData = [ { key: '1', name: 'long name', age: 32, address: 'New York No. 1 Lake Park', }, { key: '2', name: 'longest name', age: 42, address: 'London No. 1 Lake Park', }, { key: '3', name: 'longer name', age: 32, address: 'Sidney No. 1 Lake Park', }, { key: '4', name: 'short', age: 32, address: 'London No. 2 Lake Park', }, ]; const onChange = sinon.spy(() => { }); // test default descend const targetData = sortData.reduce((a, b) => a.name.length > b.name.length ? a : b); const {name} = targetData; const sortTable = mount(
); const firstCell = sortTable.find('.semi-table-tbody .semi-table-row .semi-table-row-cell').at(0); expect(firstCell.text()).toBe(name); // test default ascend const ascendOrderColumns = [ { title: 'Name', dataIndex: 'name', sorter: (a, b) => a.name.length - b.name.length, defaultSortOrder: 'ascend', }, { title: 'Age', dataIndex: 'age', }, { title: 'Address', dataIndex: 'address', } ]; sortTable.setProps({columns: ascendOrderColumns}) sortTable.update() const ascendTargetData = sortData.reduce((a, b) => a.name.length < b.name.length ? a : b); const {name: ascendTargetName} = ascendTargetData; const ascendFirstCell = sortTable.find('.semi-table-tbody .semi-table-row .semi-table-row-cell').at(0); expect(ascendFirstCell.text()).toBe(ascendTargetName); // test default false const defaultOrderColumns = [ { title: 'Name', dataIndex: 'name', sorter: (a, b) => a.name.length - b.name.length, defaultSortOrder: false, }, { title: 'Age', dataIndex: 'age', }, { title: 'Address', dataIndex: 'address', } ] sortTable.setProps({columns: defaultOrderColumns}) sortTable.update() const defaultTargetData = sortData[0] const {name: defaultTargetName} = defaultTargetData; const defaultFirstCell = sortTable.find('.semi-table-tbody .semi-table-row .semi-table-row-cell').at(0); expect(defaultFirstCell.text()).toBe(defaultTargetName); // test click sorter sortTable.find(`.${BASE_CLASS_PREFIX}-table-thead .${BASE_CLASS_PREFIX}-table-column-sorter`).at(0).simulate('click', {nativeEvent: null}); expect(onChange.callCount).toBe(1); expect(onChange.getCall(0).args[0].sorter.sortOrder).toBe('ascend'); sortTable.find(`.${BASE_CLASS_PREFIX}-table-thead .${BASE_CLASS_PREFIX}-table-column-sorter`).at(0).simulate('click', {nativeEvent: null}); expect(onChange.callCount).toBe(2); expect(onChange.getCall(1).args[0].sorter.sortOrder).toBe('descend'); // test change data const newData = [ { key: '1', name: 'longest name', age: 18, address: 'New York No. 1 Lake Park', }, { key: '2', name: 'long name', age: 20, address: 'London No. 1 Lake Park', }, { key: '3', name: 'longer name', age: 16, address: 'Sidney No. 1 Lake Park', }, { key: '4', name: 'short', age: 33, address: 'London No. 2 Lake Park', }, ]; sortTable.setProps({dataSource: newData}); sortTable.update() const newDescendTargetData = newData.reduce((a, b) => a.name.length > b.name.length ? a : b); const {name: newDescendTargetName} = newDescendTargetData; const newDataFirstCell = sortTable.find('.semi-table-tbody .semi-table-row .semi-table-row-cell').at(0); expect(newDataFirstCell.text()).toBe(newDescendTargetName); }); it(`test expandRowByClick`, async () => { const onExpand = sinon.spy(); const onExpandedRowsChange = sinon.spy(); const demo = mount(
Semi Design
} expandRowByClick /> ); const table = demo.find(BaseTable); const rows = demo.find(`.${BASE_CLASS_PREFIX}-table-tbody .${BASE_CLASS_PREFIX}-table-row`); rows.at(0).simulate('click'); rows.at(1).simulate('click'); expect(onExpand.calledTwice).toBeTruthy(); expect(onExpandedRowsChange.calledTwice).toBeTruthy(); expect(onExpand.getCall(1).args[0]).toEqual(true); expect(onExpand.getCall(1).args[1]).toEqual(data[1]); expect(onExpand.getCall(1).args[2].constructor.name).toBe('SyntheticEvent'); expect(onExpandedRowsChange.getCall(1).args[0].length).toEqual(2); expect(table.state(`expandedRowKeys`)).toEqual(['0', '1']); const expandedRows = demo.find(`.${BASE_CLASS_PREFIX}-table-tbody .${BASE_CLASS_PREFIX}-table-row-expanded`); expandedRows.at(0).simulate('click'); // 查看点击第二次是否折叠 const newExpandedRows = demo.find(`.${BASE_CLASS_PREFIX}-table-tbody .${BASE_CLASS_PREFIX}-table-row-expanded`) expect(newExpandedRows.length).toEqual(1); expect(table.state(`expandedRowKeys`)).toEqual(['1']); }); it(`test expanded row re-render`, () => { const expandedRowRender = sinon.spy(() =>
Semi Design
); const demo = mount(
); const table = demo.find(BaseTable); const expandIcons = demo.find(`.semi-table-tbody .semi-table-row .semi-table-expand-icon`); expandIcons.at(0).simulate('click'); expandIcons.at(1).simulate('click'); expect(expandedRowRender.calledTwice).toBeTruthy(); }); });