/** * 组件名称:Transfer 穿梭器 * 单测内容: * 1. 基础模式 * 2. 树形模式 * 3. 分组模式 * 4. 表格模式 * 5. 级联选择模式 * 6. 关联选择模式 * 7. 结果面板跟随模式 * 8. 结果搜索功能 * 9. 分组模式虚拟滚动 * 10. 表格模式虚拟滚动 * 11. 级联模式虚拟滚动 * 12. 关联模式虚拟滚动 */ import {fireEvent, render, waitFor, screen} from '@testing-library/react'; import '../../../src'; import {render as amisRender} from '../../../src'; import {makeEnv, formatStyleObject, wait} from '../../helper'; const setup = async (items: any = {}, formOptions: any = {}) => { const utils = render( amisRender( { type: 'form', api: '/api/mock2/form/saveForm', body: items, ...formOptions }, {}, makeEnv() ) ); return utils; }; test('Renderer:transfer', () => { const {container} = render( amisRender( { type: 'page', body: { type: 'form', api: '/api/mock2/form/saveForm', body: [ { label: '默认', type: 'transfer', name: 'transfer', options: [ { label: '诸葛亮', value: 'zhugeliang' }, { label: '曹操', value: 'caocao' }, { label: '钟无艳', value: 'zhongwuyan' }, { label: '李白', value: 'libai' }, { label: '韩信', value: 'hanxin' }, { label: '云中君', value: 'yunzhongjun' } ] } ] } }, {}, makeEnv({}) ) ); expect(container).toMatchSnapshot(); }); test('Renderer:transfer tree', () => { const {container} = render( amisRender( { type: 'page', body: { type: 'form', api: '/api/mock2/form/saveForm', body: [ { label: '树型展示', type: 'transfer', name: 'transfer4', selectMode: 'tree', options: [ { label: '法师', children: [ { label: '诸葛亮', value: 'zhugeliang' } ] }, { label: '战士', children: [ { label: '曹操', value: 'caocao' }, { label: '钟无艳', value: 'zhongwuyan' } ] }, { label: '打野', children: [ { label: '李白', value: 'libai' }, { label: '韩信', value: 'hanxin' }, { label: '云中君', value: 'yunzhongjun' } ] } ] } ] } }, {}, makeEnv({}) ) ); expect(container).toMatchSnapshot(); }); test('Renderer:transfer group options', async () => { const {container, findByText} = render( amisRender( { type: 'page', body: { type: 'form', api: '/api/mock2/form/saveForm', body: [ { label: '分组', type: 'transfer', name: 'transfer', options: [ { label: '法师', children: [ { label: '诸葛亮', value: 'zhugeliang' } ] }, { label: '战士', children: [ { label: '曹操', value: 'caocao' }, { label: '钟无艳', value: 'zhongwuyan' } ] }, { label: '打野', children: [ { label: '李白', value: 'libai' }, { label: '韩信', value: 'hanxin' }, { label: '云中君', value: 'yunzhongjun' } ] } ] } ] } }, {}, makeEnv({}) ) ); await findByText('诸葛亮'); // TODO: 莫名其妙 github 上运行就会报错 // expect(container).toMatchSnapshot(); }); test('Renderer:transfer table', async () => { const {container, findByText} = render( amisRender( { type: 'page', body: { type: 'form', api: '/api/mock2/form/saveForm', body: [ { label: '表格形式', type: 'transfer', name: 'transfer', selectMode: 'table', columns: [ { name: 'label', label: '英雄' }, { name: 'position', label: '位置' } ], options: [ { label: '诸葛亮', value: 'zhugeliang', position: '中单' }, { label: '曹操', value: 'caocao', position: '上单' }, { label: '钟无艳', value: 'zhongwuyan', position: '上单' }, { label: '李白', value: 'libai', position: '打野' }, { label: '韩信', value: 'hanxin', position: '打野' }, { label: '云中君', value: 'yunzhongjun', position: '打野' } ] } ] } }, {}, makeEnv({}) ) ); await findByText('诸葛亮'); expect(container).toMatchSnapshot(); }); test('Renderer:transfer chained', async () => { const {container, findByText} = render( amisRender( { type: 'page', body: { type: 'form', api: '/api/mock2/form/saveForm', body: [ { label: '级联选择', type: 'transfer', name: 'transfer5', selectMode: 'chained', searchable: true, sortable: true, options: [ { label: '法师', children: [ { label: '诸葛亮', value: 'zhugeliang' } ] }, { label: '战士', children: [ { label: '曹操', value: 'caocao' }, { label: '钟无艳', value: 'zhongwuyan' } ] }, { label: '打野', children: [ { label: '李白', value: 'libai' }, { label: '韩信', value: 'hanxin' }, { label: '云中君', value: 'yunzhongjun' } ] } ] } ] } }, {}, makeEnv({}) ) ); await findByText('法师'); expect(container).toMatchSnapshot(); }); test('Renderer:transfer left tree', async () => { const {container, findByText} = render( amisRender( { type: 'page', body: { type: 'form', api: '/api/mock2/form/saveForm', body: [ { label: '关联选择模式', type: 'transfer', name: 'b', sortable: true, searchable: true, deferApi: '/api/mock2/form/deferOptions?label=${label}', selectMode: 'associated', leftMode: 'tree', leftOptions: [ { label: '法师', children: [ { label: '诸葛亮', value: 'zhugeliang' } ] }, { label: '战士', children: [ { label: '曹操', value: 'caocao' }, { label: '钟无艳', value: 'zhongwuyan' } ] }, { label: '打野', children: [ { label: '李白', value: 'libai' }, { label: '韩信', value: 'hanxin' }, { label: '云中君', value: 'yunzhongjun' } ] } ], options: [ { ref: 'zhugeliang', children: [ { label: 'A', value: 'a' } ] }, { ref: 'caocao', children: [ { label: 'B', value: 'b' }, { label: 'C', value: 'c' } ] }, { ref: 'zhongwuyan', children: [ { label: 'D', value: 'd' }, { label: 'E', value: 'e' } ] }, { ref: 'libai', defer: true }, { ref: 'hanxin', defer: true }, { ref: 'yunzhongjun', defer: true } ] } ] } }, {}, makeEnv({}) ) ); await findByText('诸葛亮'); expect(container).toMatchSnapshot(); }); // 跟随模式 test('Renderer:transfer follow left mode', async () => { const {container, findByText} = render( amisRender( { type: 'form', api: '/api/mock2/form/saveForm', body: [ { label: '树型展示', type: 'transfer', name: 'transfer4', selectMode: 'tree', searchable: true, resultListModeFollowSelect: true, options: [ { label: '法师', children: [ { label: '诸葛亮', value: 'zhugeliang' } ] }, { label: '战士', children: [ { label: '曹操', value: 'caocao' }, { label: '钟无艳', value: 'zhongwuyan' } ] }, { label: '打野', children: [ { label: '李白', value: 'libai' }, { label: '韩信', value: 'hanxin' }, { label: '云中君', value: 'yunzhongjun' } ] } ] } ] }, {}, makeEnv({}) ) ); const zhugeliang = await findByText('诸葛亮'); fireEvent.click(zhugeliang); await wait(200); expect(container).toMatchSnapshot(); }); // 结果前端搜索 test('Renderer:transfer follow left mode', async () => { const {container, findByText} = render( amisRender( { type: 'form', api: '/api/mock2/form/saveForm', body: [ { label: '树型展示', type: 'transfer', name: 'transfer4', selectMode: 'tree', searchable: true, resultListModeFollowSelect: true, resultSearchable: true, options: [ { label: '法师', children: [ { label: '诸葛亮', value: 'zhugeliang' } ] }, { label: '战士', children: [ { label: '曹操', value: 'caocao' }, { label: '钟无艳', value: 'zhongwuyan' } ] }, { label: '打野', children: [ { label: '李白', value: 'libai' }, { label: '韩信', value: 'hanxin' }, { label: '云中君', value: 'yunzhongjun' } ] } ] } ] }, {}, makeEnv({}) ) ); const zhugeliang = await findByText('诸葛亮'); fireEvent.click(zhugeliang); await wait(200); const caocao = await findByText('曹操'); fireEvent.click(caocao); await wait(200); const input = container.querySelectorAll('input[type=text]')[1]; expect(input).not.toBeNull(); fireEvent.change(input, { target: {value: 'caocao'} }); // 300 毫秒才行 await wait(300); const dom = container.querySelector( '.cxd-ResultTreeList .cxd-Tree-item .cxd-Tree-itemText' ); expect(dom).not.toBeNull(); expect(dom?.getAttribute('title')).toEqual('战士'); expect(container).toMatchSnapshot(); }); test('should call the custom filterOption if it is provided', async () => { const filterOption = jest.fn().mockImplementation(options => options); const options = [ { label: '法师', children: [ { label: '诸葛亮', value: 'zhugeliang', weapon: '翡翠仙扇' } ] } ]; render( amisRender( { label: '树型展示', type: 'transfer', name: 'transfer4', selectMode: 'tree', searchable: true, filterOption: filterOption, options }, {}, makeEnv({}) ) ); const input = screen.getByPlaceholderText('请输入关键字'); fireEvent.change(input, { target: {value: '翡翠仙扇'} }); await wait(300); expect(filterOption).toBeCalledTimes(1); expect(filterOption).toBeCalledWith(options, '翡翠仙扇', { keys: ['label', 'value'] }); }); test('should call the string style custom filterOption if it is provided', async () => { const options = [ { label: '法师', children: [ { label: '诸葛亮', value: 'zhugeliang', weapon: '翡翠仙扇' } ] } ]; render( amisRender( { label: '树型展示', type: 'transfer', name: 'transfer4', selectMode: 'tree', searchable: true, filterOption: 'return [];', options }, {}, makeEnv({}) ) ); const input = screen.getByPlaceholderText('请输入关键字'); expect(screen.getByText('诸葛亮')).toBeInTheDocument(); fireEvent.change(input, { target: {value: '翡翠仙扇'} }); await wait(300); expect(screen.queryByText('诸葛亮')).not.toBeInTheDocument(); }); test('should call the notify function with error message if the filterOption is not a valid function', async () => { const options = [ { label: '法师', children: [ { label: '诸葛亮', value: 'zhugeliang', weapon: '翡翠仙扇' } ] } ]; const mockNotify = jest.fn().mockImplementation(options => options); render( amisRender( { label: '树型展示', type: 'transfer', name: 'transfer4', selectMode: 'tree', searchable: true, filterOption: 10086, options }, {}, {notify: mockNotify} ) ); const input = screen.getByPlaceholderText('请输入关键字'); expect(screen.getByText('诸葛亮')).toBeInTheDocument(); fireEvent.change(input, { target: {value: '翡翠仙扇'} }); await wait(300); expect(mockNotify).toBeCalledTimes(1); expect(mockNotify).toBeCalledWith('error', '自定义检索函数不符合要求'); mockNotify.mockClear(); }); test('Renderer:transfer group mode with virtual', async () => { const options = [...Array(20)].map((_, i) => ({ label: `group-${i + 1}`, children: [...Array(10)].map((_, j) => ({ label: `option-${i * 10 + j + 1}`, value: `value-${i * 10 + j + 1}` })) })); const {container, getByText, queryByText} = await setup([ { label: '分组', type: 'transfer', name: 'transfer', options: options, virtualThreshold: 100, itemHeight: 40 } ]); const optionsOrLabel = container.querySelectorAll( '.cxd-GroupedSelection.cxd-Transfer-selection > div > div > div > div.cxd-GroupedSelection-group' ); expect(optionsOrLabel.length > 0).toBeTruthy(); const firstStyle = formatStyleObject(optionsOrLabel[0].getAttribute('style')); expect(firstStyle.top).toBe(0); expect(firstStyle.height).toBe(40); await waitFor(() => expect(optionsOrLabel.length > 1).toBeTruthy()); expect(formatStyleObject(optionsOrLabel[1].getAttribute('style')).top).toBe( 40 ); expect(getByText('option-1')).toBeInTheDocument(); expect(await queryByText('option-100')).toBeNull(); const scrollContainer = container.querySelector( '.cxd-GroupedSelection.cxd-Transfer-selection > div > div' )!; // 滚动到底部 fireEvent.scroll(scrollContainer, { target: { scrollTop: 9999 } }); await wait(300); // 最后一项 const lastOne = container.querySelector( '.cxd-GroupedSelection.cxd-Transfer-selection > div > div > div > div.cxd-GroupedSelection-group:last-of-type' ); expect(formatStyleObject(lastOne!.getAttribute('style')).top).toBe(8760); expect(await queryByText('option-1')).toBeNull(); expect(container).toMatchSnapshot(); }); test('Renderer:transfer table mode with virtual', async () => { const options = [...Array(200)].map((_, i) => ({ label: `label-${i + 1}`, value: i + 1 })); const value = [...Array(10)].map((_, i) => i * 2 + 1).join(','); const {container, getByText} = await setup([ { label: '分组', type: 'transfer', name: 'transfer', options: options, value: value, virtualThreshold: 10, selectMode: 'table', columns: [ { name: 'label', label: '名称' }, { name: 'value', label: '值' } ] } ]); const virtualTable = container.querySelector( '.cxd-Transfer-select .cxd-Table-content.is-virtual .cxd-Table-content-virtual table.cxd-Table-table' )!; expect(virtualTable).toBeInTheDocument(); const resultList = container.querySelector( '.cxd-Transfer-result .cxd-Selections.cxd-Transfer-value .cxd-Selections-items' )!; expect(resultList.firstChild).toHaveClass('cxd-Selections-item'); expect(container).toMatchSnapshot('result not virtual'); // 点击后,value 变为 11 项,右侧列表变成虚拟列表 fireEvent.click(getByText('label-2')); await wait(300); const firstVirtualItem = resultList.querySelector( 'div > div:first-of-type > div > .cxd-Selections-item' ); expect(firstVirtualItem).toBeInTheDocument(); expect(formatStyleObject(firstVirtualItem!.getAttribute('style'))!.top).toBe( 0 ); expect(container).toMatchSnapshot('result virtual'); }); test('Renderer:transfer chained mode with virtual', async () => { const options = [...Array(10)].map((_, i) => ({ label: `group-${i + 1}`, children: [...Array(200)].map((_, j) => ({ label: `group-${i + 1}-option-${j + 1}`, value: `${i + 1}-${j + 1}` })) })); const {container, getByText, queryByText} = await setup([ { label: '分组', type: 'transfer', name: 'transfer', options: options, virtualThreshold: 100, selectMode: 'chained' } ]); const cols = container.querySelectorAll('.cxd-ChainedSelection-col'); expect(cols.length).toBe(2); expect(cols[0].children.length).toBe(10); expect(cols[1].firstChild).toHaveClass('cxd-ChainedSelection-placeholder'); fireEvent.click(getByText('group-2')); await wait(300); expect(cols[1].firstChild).not.toHaveClass( 'cxd-ChainedSelection-placeholder' ); expect(getByText('group-2-option-1')).toBeInTheDocument(); expect(await queryByText('group-2-option-200')).toBeNull(); expect(container).toMatchSnapshot(); }); test('Renderer:transfer associated mode with virtual', async () => { const options = [...Array(200)].map((_, i) => ({ label: `label-${i + 1}`, value: i + 1 })); const {container, getByText, queryByText} = await setup([ { label: '关联选择模式', type: 'transfer', name: 'b', sortable: true, searchable: true, selectMode: 'associated', leftMode: 'tree', leftOptions: [ { label: '法师', children: [ { label: '诸葛亮', value: 'zhugeliang' } ] }, { label: '战士', children: [ { label: '曹操', value: 'caocao' }, { label: '钟无艳', value: 'zhongwuyan' } ] } ], options: [ { ref: 'zhugeliang', children: [ { label: 'A', value: 'a' } ] }, { ref: 'caocao', children: [ { label: 'B', value: 'b' }, { label: 'C', value: 'c' } ] }, { ref: 'zhongwuyan', children: options } ] } ]); const rightCol = container.querySelector('.cxd-AssociatedSelection-right'); expect(rightCol).toBeInTheDocument(); fireEvent.click(getByText('诸葛亮')); await wait(200); expect( rightCol!.querySelector( '.cxd-GroupedSelection .cxd-GroupedSelection-item .cxd-GroupedSelection-itemLabel span' )!.innerHTML ).toBe('A'); fireEvent.click(getByText('钟无艳')); await wait(200); expect( rightCol!.querySelectorAll('.cxd-GroupedSelection-item').length < 200 ).toBeTruthy(); expect(getByText('label-1')).toBeInTheDocument(); expect(await queryByText('label-100')).toBeNull(); expect(container).toMatchSnapshot(); }); test('Renderer:transfer with showInvalidMatch & unmatched do not add', async () => { const schema = { label: '默认', type: 'transfer', name: 'transfer', value: 'v,w,x,y,z', selectMode: 'tree', options: [ { label: '诸葛亮', value: 'zhugeliang' }, { label: '曹操', value: 'caocao' }, { label: '钟无艳', value: 'zhongwuyan' } ] }; const {container, getByText, queryByText, rerender} = await setup({ ...schema, deferApi: 'xxx' }); function leftItems() { return container.querySelectorAll('.cxd-Transfer-select .cxd-Tree-item'); } function rightItems() { return container.querySelectorAll( '.cxd-Transfer-result .cxd-Selections-item' ); } expect(leftItems()!.length).toBe(3); expect(rightItems()!.length).toBe(5); expect( rightItems()[0]!.querySelector('.cxd-Selections-label')! ).not.toHaveClass('is-invalid'); fireEvent.click(rightItems()[0]!.querySelector('.cxd-Selections-delBtn')!); await wait(500); expect(leftItems()!.length).toBe(3); expect(rightItems()!.length).toBe(4); rerender(amisRender({...schema, showInvalidMatch: true})); await wait(500); expect(rightItems()[0]!.querySelector('.cxd-Selections-label')!).toHaveClass( 'is-invalid' ); fireEvent.click(rightItems()[0]!.querySelector('.cxd-Selections-delBtn')!); await wait(500); expect(container).toMatchSnapshot(); expect(leftItems()!.length).toBe(3); expect(rightItems()!.length).toBe(4); }); test('Renderer:transfer with searchApi', async () => { const onSubmit = jest.fn(); const fetcher = jest.fn().mockImplementationOnce(() => Promise.resolve({ data: { status: 0, msg: 'ok', data: { options: [ { "label": "法师", "children": [ { "label": "诸葛亮", "value": "zhugeliang" } ] }, { "label": "战士", "children": [ { "label": "钟无艳", "value": "zhongwuyan" } ] } ] } } }) ); const {getByText, container} = render( amisRender( { "type": "form", "api": "/api/mock2/form/saveForm", "body": [ { "label": "树型展示", "type": "transfer", "name": "transfer", "selectMode": "tree", "searchable": true, "searchApi": "/api/transfer/search?name=${term}", "options": [ { "label": "法师", "children": [ { "label": "诸葛亮", "value": "zhugeliang" } ] }, { "label": "战士", "value": "zhanshi", "children": [ { "label": "曹操", "value": "caocao" }, { "label": "钟无艳", "value": "zhongwuyan" } ] }, { "label": "打野", "children": [ { "label": "李白", "value": "libai" }, { "label": "韩信", "value": "hanxin" }, { "label": "云中君", "value": "yunzhongjun" } ] } ] } ] }, {onSubmit}, makeEnv({ fetcher }) ) ); const zhangshi = container.querySelector('span[title=战士]'); expect(zhangshi).not.toBeNull(); zhangshi && fireEvent.click(zhangshi); fireEvent.click(getByText('提交')); await wait(100); expect(onSubmit).toBeCalledTimes(1); expect(onSubmit.mock.calls[0][0]).toEqual({ transfer: "caocao,zhongwuyan" }); const input = container.querySelector('input[placeholder=请输入关键字]'); expect(input).not.toBeNull(); input && fireEvent.change(input, { target: {value: 'a'} }); await wait(300); const caocao = container.querySelector('span[title=李白]'); expect(caocao).toBeNull(); }); test('Renderer:transfer tree onlyChildren true', async () => { const onSubmit = jest.fn(); const schema = { "type": "form", "api": "/api/mock2/form/saveForm", "debug": true, "body": [ { "label": "默认", "type": "transfer", "name": "transfer", "value": "libai", "selectMode": "tree", "searchable": true, "onlyChildren": true, "options": [ { "label": "法师", "children": [ { "label": "诸葛亮", "value": "zhugeliang" } ] }, { "label": "战士", "value": "战士", "children": [ { "label": "曹操", "disabled": true, "value": "caocao" }, { "label": "钟无艳", "value": "zhongwuyan" } ] }, { "label": "打野", "children": [ { "label": "李白", "value": "libai" }, { "label": "韩信", "value": "hanxin" }, { "label": "云中君", "value": "yunzhongjun" } ] } ] } ] }; const {getByText, container} = render( amisRender(schema, {onSubmit}, makeEnv({})) ); const checkbox = container.querySelector('.cxd-Checkbox'); expect(checkbox).not.toBeNull(); checkbox && fireEvent.click(checkbox); fireEvent.click(getByText('提交')); await wait(100); expect(onSubmit).toBeCalledTimes(1); expect(onSubmit.mock.calls[0][0]).toEqual({ transfer: "zhugeliang,zhongwuyan,libai,hanxin,yunzhongjun" }); }); test('Renderer:transfer search highlight', async () => { const onSubmit = jest.fn(); const {container} = render( amisRender( { "type": "form", "api": "/api/mock2/form/saveForm", "body": [ { "label": "默认", "type": "transfer", "name": "transfer", "selectMode": "tree", "searchable": true, "options": [ { "label": "法师", "children": [ { "label": "诸葛亮", "value": "zhugeliang" } ] }, { "label": "战士", "children": [ { "label": "曹操", "disabled": true, "value": "caocao" }, { "label": "钟无艳", "value": "zhongwuyan" } ] }, { "label": "打野", "children": [ { "label": "李白", "value": "libai" }, { "label": "韩信", "value": "hanxin" }, { "label": "云中君", "value": "yunzhongjun" } ] } ] } ] }, {onSubmit}, makeEnv({}) ) ) const input = container.querySelectorAll('input[type=text]')[0]; expect(input).not.toBeNull(); fireEvent.change(input, { target: {value: '战士'} }); await wait(500); const isMatchDom = container.querySelector('.is-matched'); expect(isMatchDom).not.toBeNull(); }); test('Renderer:transfer tree search', async () => { const onSubmit = jest.fn(); const {container, findByText, getByText} = render( amisRender( { "type": "form", "api": "/api/mock2/form/saveForm", "body": [ { "label": "默认", "type": "transfer", "name": "transfer", "selectMode": "tree", "searchable": true, "options": [ { "label": "法师", "children": [ { "label": "诸葛亮", "value": "zhugeliang" } ] }, { "label": "战士", "children": [ { "label": "曹操", "value": "caocao" }, { "label": "钟无艳", "value": "zhongwuyan" } ] }, { "label": "打野", "children": [ { "label": "李白", "value": "libai" }, { "label": "韩信", "value": "hanxin" }, { "label": "云中君", "value": "yunzhongjun" } ] } ] } ] }, {onSubmit}, makeEnv({}) ) ) const input = container.querySelectorAll('input[type=text]')[0]; expect(input).not.toBeNull(); fireEvent.change(input, { target: { value: '战士' } }); await(300); const caocao = getByText('曹操'); expect(caocao).not.toBeNull(); fireEvent.click(caocao); fireEvent.change(input, { target: { value: '' } }); await(300); fireEvent.change(input, { target: { value: '打野' } }); await(300); const libai = getByText('李白'); expect(libai).not.toBeNull(); fireEvent.click(libai); await wait(500); fireEvent.click(getByText('提交')); await wait(300); expect(onSubmit).toBeCalledTimes(1); expect(onSubmit.mock.calls[0][0]).toEqual({ transfer: "caocao,libai" }); }); test('Renderer:Transfer with pagination', async () => { const mockData = [ { "label": "Laura Lewis", "value": "1" }, { "label": "David Gonzalez", "value": "2" }, { "label": "Christopher Rodriguez", "value": "3" }, { "label": "Sarah Young", "value": "4" }, { "label": "James Jones", "value": "5" }, { "label": "Larry Robinson", "value": "6" }, { "label": "Christopher Perez", "value": "7" }, { "label": "Sharon Davis", "value": "8" }, { "label": "Kenneth Anderson", "value": "9" }, { "label": "Deborah Lewis", "value": "10" }, { "label": "Jennifer Lewis", "value": "11" }, { "label": "Laura Miller", "value": "12" }, { "label": "Larry Harris", "value": "13" }, { "label": "Patricia Robinson", "value": "14" }, { "label": "Mark Davis", "value": "15" }, { "label": "Jessica Harris", "value": "16" }, { "label": "Anna Brown", "value": "17" }, { "label": "Lisa Young", "value": "18" }, { "label": "Donna Williams", "value": "19" }, { "label": "Shirley Davis", "value": "20" } ]; const fetcher = jest.fn().mockImplementation((api) => { const perPage = 10; /** 锁死10个方便测试 */ const page = Number(api.query.page || 1); return Promise.resolve({ data: { status: 0, msg: 'ok', data: { count: mockData.length, page: page, items: mockData.concat().splice((page - 1) * perPage, perPage) } } }); }); const {container} = render( amisRender( { "type": "form", "debug": true, "body": [ { "label": "默认", "type": "transfer", "name": "transfer", "joinValues": false, "extractValue": false, "source": "/api/mock2/options/transfer?page=${page}&perPage=${perPage}", "pagination": { "enable": true, "layout": ["pager", "perpage", "total"], "popOverContainerSelector": ".cxd-Panel--form" }, "value": [ {"label": "Laura Lewis", "value": "1", id: 1}, {"label": "Christopher Rodriguez", "value": "3", id: 3}, {"label": "Laura Miller", "value": "12", id: 12}, {"label": "Patricia Robinson", "value": "14", id: 14} ] } ] }, {}, makeEnv({fetcher}))); await wait(500); expect(container.querySelector('.cxd-Transfer-footer-pagination')).toBeInTheDocument(); const checkboxes = container.querySelectorAll('input[type=checkbox]')!; expect(checkboxes.length).toEqual(11); /** 包括顶部全选 */ expect((checkboxes[1] as HTMLInputElement)?.checked).toEqual(true); expect((checkboxes[2] as HTMLInputElement)?.checked).toEqual(false); expect((checkboxes[3] as HTMLInputElement)?.checked).toEqual(true); expect((checkboxes[4] as HTMLInputElement)?.checked).toEqual(false); const nextBtn = container.querySelector('.cxd-Pagination-next')!; fireEvent.click(nextBtn); await wait(500); const checkboxes2 = container.querySelectorAll('input[type=checkbox]')!; expect(checkboxes2.length).toEqual(11); expect((checkboxes2[1] as HTMLInputElement)?.checked).toEqual(false); expect((checkboxes2[2] as HTMLInputElement)?.checked).toEqual(true); expect((checkboxes2[3] as HTMLInputElement)?.checked).toEqual(false); expect((checkboxes2[4] as HTMLInputElement)?.checked).toEqual(true); }) test.only('Renderer:Transfer with pagination and data source from data scope', async () => { const mockData = [ { "label": "Laura Lewis", "value": "1" }, { "label": "David Gonzalez", "value": "2" }, { "label": "Christopher Rodriguez", "value": "3" }, { "label": "Sarah Young", "value": "4" }, { "label": "James Jones", "value": "5" }, { "label": "Larry Robinson", "value": "6" }, { "label": "Christopher Perez", "value": "7" }, { "label": "Sharon Davis", "value": "8" }, { "label": "Kenneth Anderson", "value": "9" }, { "label": "Deborah Lewis", "value": "10" }, { "label": "Jennifer Lewis", "value": "11" }, { "label": "Laura Miller", "value": "12" }, { "label": "Larry Harris", "value": "13" }, { "label": "Patricia Robinson", "value": "14" }, { "label": "Mark Davis", "value": "15" }, { "label": "Jessica Harris", "value": "16" }, { "label": "Anna Brown", "value": "17" }, { "label": "Lisa Young", "value": "18" }, { "label": "Donna Williams", "value": "19" }, { "label": "Shirley Davis", "value": "20" } ]; const fetcher = jest.fn().mockImplementation((api) => { return Promise.resolve({ data: { status: 0, msg: 'ok', data: { count: mockData.length, items: mockData } } }); }); const {container} = render( amisRender( { "type": "form", "debug": true, "body": [ { "type": "service", "api": { "url": "/api/mock2/options/loadDataOnce", "method": "get", "responseData": { "transferOptions": "${items}" } }, body: [ { "label": "默认", "type": "transfer", "name": "transfer", "joinValues": false, "extractValue": false, "source": "${transferOptions}", "pagination": { "enable": true, "layout": ["pager", "perpage", "total"], "popOverContainerSelector": ".cxd-Panel--form" }, "value": [ {"label": "Laura Lewis", "value": "1", id: 1}, {"label": "Christopher Rodriguez", "value": "3", id: 3}, {"label": "Laura Miller", "value": "12", id: 12}, {"label": "Patricia Robinson", "value": "14", id: 14} ] } ] } ] }, {}, makeEnv({fetcher}))); await wait(500); expect(container.querySelector('.cxd-Transfer-footer-pagination')).toBeInTheDocument(); const checkboxes = container.querySelectorAll('input[type=checkbox]')!; expect(checkboxes.length).toEqual(11); /** 包括顶部全选 */ expect((checkboxes[1] as HTMLInputElement)?.checked).toEqual(true); expect((checkboxes[2] as HTMLInputElement)?.checked).toEqual(false); expect((checkboxes[3] as HTMLInputElement)?.checked).toEqual(true); expect((checkboxes[4] as HTMLInputElement)?.checked).toEqual(false); const nextBtn = container.querySelector('.cxd-Pagination-next')!; fireEvent.click(nextBtn); await wait(500); const checkboxes2 = container.querySelectorAll('input[type=checkbox]')!; expect(checkboxes2.length).toEqual(11); expect((checkboxes2[1] as HTMLInputElement)?.checked).toEqual(false); expect((checkboxes2[2] as HTMLInputElement)?.checked).toEqual(true); expect((checkboxes2[3] as HTMLInputElement)?.checked).toEqual(false); expect((checkboxes2[4] as HTMLInputElement)?.checked).toEqual(true); })