amis2/packages/amis/__tests__/renderers/Form/transfer.test.tsx
2023-11-07 15:31:05 +08:00

1810 lines
43 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 组件名称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);
})