amis2/packages/amis/__tests__/renderers/Nav.test.tsx
2023-10-27 12:46:47 +08:00

939 lines
23 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.

/**
* 组件名称Nav 导航
* 单测内容:
* 1. 基本使用
* 2. 多层级
* 3. 横向摆放
* 4. 响应式收纳
* 5. 动态导航
* 6. 懒加载
* 7. 更多操作
*/
import {render, cleanup, fireEvent, waitFor} from '@testing-library/react';
import '../../src';
import {render as amisRender} from '../../src';
import {makeEnv, wait} from '../helper';
afterEach(cleanup);
// 1. 基本使用
test('Renderer:Nav', () => {
const {container} = render(
amisRender(
{
type: 'nav',
stacked: true,
className: 'w-md',
itemBadge: {
mode: 'ribbon',
text: '${customText}',
position: 'top-left',
visibleOn: 'this.customText',
level: '${customLevel}'
},
links: [
{
__id: 0,
label: 'Nav 1',
to: '/docs/index',
icon: 'https://suda.cdn.bcebos.com/images%2F2021-01%2Fdiamond.svg',
active: true
},
{
__id: 1,
label: 'Nav 2',
to: '/docs/api',
customText: 'HOT',
customLevel: 'danger'
},
{
__id: 2,
label: 'Nav 3',
to: '/docs/renderers',
customText: 'SUC',
customLevel: 'success'
},
{
__id: 3,
label: '外部地址',
to: 'http://www.baidu.com/',
target: '_blank'
}
]
},
{},
makeEnv({})
)
);
expect(container).toMatchSnapshot();
const items = container.querySelectorAll('.cxd-Nav-Menu-item')!;
expect(items.length).toBe(4);
expect(items[0].querySelector('img')).toHaveAttribute(
'src',
'https://suda.cdn.bcebos.com/images%2F2021-01%2Fdiamond.svg'
);
expect(items[1].firstElementChild?.firstElementChild).toHaveClass(
'cxd-Badge'
);
expect(items[2].querySelector('.cxd-Badge--success')).toHaveTextContent(
'SUC'
);
});
// 2. 多层级
test('Renderer:Nav with multi-level', () => {
const {container} = render(
amisRender(
{
type: 'nav',
stacked: true,
className: 'w-md',
links: [
{
__id: 1,
label: 'Nav 1',
to: '/docs/index',
icon: 'fa fa-user',
active: true
},
{
__id: 2,
label: 'Nav 2',
unfolded: true,
children: [
{
__id: 2.1,
label: 'Nav 2-1',
children: [
{
__id: 2.11,
label: 'Nav 2-1-1',
to: '/docs/api-2-1-1'
}
]
},
{
__id: 2.2,
label: 'Nav 2-2',
to: '/docs/api-2-2'
}
]
},
{
__id: 3,
label: 'Nav 3',
to: '/docs/renderers'
}
]
},
{},
makeEnv({})
)
);
expect(container).toMatchSnapshot();
expect(container.querySelector('.cxd-Nav-Menu-submenu')!).toBeInTheDocument();
const menuWrapper = container.querySelector(
'.cxd-Nav-Menu > .cxd-Nav-Menu-submenu > .cxd-Nav-Menu'
);
const children = menuWrapper?.children;
expect(children?.length).toBe(2);
});
// 3. 横向摆放
test('Renderer:Nav with stacked', () => {
const {container} = render(
amisRender(
{
type: 'nav',
stacked: false,
links: [
{
__id: 1,
label: 'Nav 1',
to: '/docs/index',
icon: 'fa fa-user'
},
{
__id: 2,
label: 'Nav 2',
to: '/docs/api'
},
{
__id: 3,
label: 'Nav 3',
to: '/docs/renderers'
}
]
},
{},
makeEnv({})
)
);
expect(container).toMatchSnapshot();
expect(
container.querySelector('.cxd-Nav-Menu-horizontal')!
).toBeInTheDocument();
});
// 4. 响应式收纳
test('Renderer:Nav with overflow', async () => {
const {container} = render(
amisRender(
{
type: 'page',
body: {
type: 'nav',
stacked: false,
overflow: {
__id: 'overflowid',
enable: true,
overflowLabel: '点击展开',
overflowIndicator: 'fas fa-angle-double-down',
maxVisibleCount: 2,
wrapperComponent: 'section',
style: {
color: 'red'
},
overflowClassName: 'thisisoverflowClassName',
overflowPopoverClassName: 'thisisoverflowPopoverClassName',
overflowListClassName: 'thisisoverflowListClassName'
},
links: [
{
__id: 1,
label: 'Nav 1',
to: '?to=nav1'
},
{
__id: 2,
label: 'Nav 2',
to: '?to=nav1'
},
{
__id: 3,
label: 'Nav 3',
to: '?to=nav3'
},
{
__id: 4,
label: 'Nav 4',
to: '?to=nav4'
},
{
__id: 5,
label: 'Nav 5',
to: '?to=nav5'
},
{
__id: 6,
label: 'Nav 6',
to: '?to=nav6'
},
{
__id: 7,
label: 'Nav 7',
to: '?to=nav7'
},
{
__id: 8,
label: 'Nav 8',
to: '?to=nav8'
}
]
}
},
{},
makeEnv({})
)
);
const wrap = container.querySelector(
'section.cxd-Nav-Menu.cxd-Nav-Menu-horizontal'
)!;
expect(wrap).toBeInTheDocument();
expect(wrap).toHaveStyle({
color: 'red'
});
const btn = container.querySelector('.cxd-Nav-Menu-overflow-item-rest');
expect(btn).toBeInTheDocument();
expect(btn).toHaveTextContent('点击展开');
expect(btn?.querySelector('.fa-angle-double-down')!).toBeInTheDocument();
expect(btn?.querySelector('.cxd-Nav-Menu-overflowedIcon')!).toHaveClass(
'thisisoverflowClassName'
);
expect(
container.querySelectorAll(
'.cxd-Nav-Menu-item:not(.cxd-Nav-Menu-overflow-item-rest)'
)!.length
).toEqual(2);
fireEvent.click(btn!);
await waitFor(() =>
expect(container.querySelector('.cxd-Spinner')).not.toBeInTheDocument()
);
expect(container).toMatchSnapshot();
});
// 5. 动态导航
test('Renderer:Nav with source', async () => {
const {container} = render(
amisRender(
{
type: 'page',
data: {
nav: [
{
__id: 1,
label: 'Nav 1',
to: '/docs/index',
icon: 'fa fa-user'
},
{
__id: 2,
label: 'Nav 2',
to: '/docs/api'
},
{
__id: 3,
label: 'Nav 3',
to: '/docs/renderers'
}
]
},
body: {
type: 'nav',
stacked: true,
source: '${nav}'
}
},
{},
makeEnv({})
)
);
expect(container.querySelectorAll('.cxd-Nav-Menu-item').length).toBe(3);
expect(container).toMatchSnapshot();
});
// 6. 懒加载
test('Renderer:Nav with defer', async () => {
const fetcher = jest.fn().mockImplementation(
({query}) =>
new Promise((resolve, reject) => {
if (query?.parentId) {
resolve({
status: 0,
msg: '',
data: [
{label: 'Nav 3-1', to: '?cat=3-1', value: '3-1', __id: 3.1},
{label: 'Nav 3-2', to: '?cat=3-2', value: '3-2', __id: 3.2}
]
});
return;
}
resolve({
status: 0,
msg: '',
data: {
links: [
{
label: 'Nav 1',
to: '?cat=1',
value: '1',
icon: 'fa fa-user',
__id: 1
},
{
label: 'Nav 2',
__id: 2,
unfolded: true,
children: [
{
__id: 2.1,
label: 'Nav 2-1',
children: [
{
label: 'Nav 2-1-1',
to: '?cat=2-1',
value: '2-1',
__id: 2.11
}
]
},
{label: 'Nav 2-2', to: '?cat=2-2', value: '2-2', __id: 2.2}
]
},
{label: 'Nav 3', to: '?cat=3', value: '3', defer: true, __id: 3}
],
value: '?cat=1'
}
});
})
);
const {container, getByTitle, getByText} = render(
amisRender(
{
type: 'page',
body: {
type: 'nav',
stacked: true,
source: '/api/options/nav?parentId=${value}'
}
},
{},
makeEnv({
fetcher,
session: 'test-nav-1'
})
)
);
await wait(200);
expect(fetcher).toBeCalled();
const menu = container.querySelector('.cxd-Nav-Menu');
expect(menu?.children.length).toBe(3);
// const navThreeHeader = getByTitle('Nav 3');
// expect(navThreeHeader).toBeInTheDocument();
// fireEvent.click(
// navThreeHeader?.querySelector('.cxd-Nav-Menu-submenu-arrow')!
// );
// await wait(200);
// expect(fetcher).toBeCalledTimes(1);
// expect(fetcher.mock.calls[0][0]).toEqual({
// config: {},
// method: 'get',
// query: {parentId: '3'},
// url: '/api/options/nav?parentId=3'
// });
// await wait(200);
// const navThree = container.querySelector(
// '.cxd-Menu > .cxd-Menu-submenu:last-of-type'
// );
// expect(navThree!.querySelector('.cxd-Menu-sub')?.children.length).toBe(2);
// expect(getByText('Nav 3-2')).not.toBeNull();
// expect(container).toMatchSnapshot();
});
// 7. 更多操作
test('Renderer:Nav with itemActions', async () => {
const {container, getByTitle, getByText} = render(
amisRender(
{
type: 'page',
body: {
type: 'nav',
stacked: true,
className: 'w-md',
draggable: true,
saveOrderApi: '/api/options/nav',
source: '/api/options/nav?parentId=${value}',
itemActions: [
{
type: 'icon',
icon: 'cloud',
visibleOn: "this.to === '?cat=1'"
},
{
type: 'dropdown-button',
level: 'link',
icon: 'fa fa-ellipsis-h',
hideCaret: true,
buttons: [
{
type: 'button',
label: '编辑'
},
{
type: 'button',
label: '删除'
}
]
}
],
links: [
{
label: 'Nav 1',
to: '?cat=1',
value: '1',
icon: 'fa fa-user',
__id: 1
},
{
label: 'Nav 2',
__id: 2,
unfolded: true,
children: [
{
__id: 2.1,
label: 'Nav 2-1',
children: [
{
label: 'Nav 2-1-1',
to: '?cat=2-1',
value: '2-1',
__id: 2.11
}
]
},
{label: 'Nav 2-2', to: '?cat=2-2', value: '2-2', __id: 2.2}
]
},
{label: 'Nav 3', to: '?cat=3', value: '3', defer: true, __id: 3}
]
}
},
{},
makeEnv({})
)
);
expect(container).toMatchSnapshot();
expect(container.querySelectorAll('.fa-cloud').length).toBe(1);
fireEvent.click(
container.querySelector('.cxd-Nav-Menu-item-extra .cxd-Button')!
);
await waitFor(() =>
expect(container.querySelector('.cxd-Spinner')).not.toBeInTheDocument()
);
expect(container).toMatchSnapshot();
expect(getByText('编辑')).toBeInTheDocument();
});
// 8.各种图标展示
test('Renderer:Nav with icons', async () => {
const {container} = render(
amisRender(
{
type: 'page',
body: {
type: 'nav',
stacked: true,
links: [
{
label: 'Nav 1',
to: '?cat=1',
value: '1',
icon: 'fa fa-user',
__id: 1
},
{
label: 'Nav 2',
__id: 2,
unfolded: true,
children: [
{
__id: 2.1,
label: 'Nav 2-1',
icon: [
{
icon: 'star',
position: 'before'
},
{
icon: 'search',
position: 'before'
},
{
icon: 'https://suda.cdn.bcebos.com/images%2F2021-01%2Fdiamond.svg',
position: 'after'
}
],
children: [
{
label: 'Nav 2-1-1',
to: '?cat=2-1',
value: '2-1',
__id: 2.11
}
]
}
]
}
]
}
},
{},
makeEnv({})
)
);
expect(container).toMatchSnapshot();
expect(container.querySelectorAll('.fa-user').length).toBe(1);
expect(container.querySelectorAll('[icon=search]').length).toBe(1);
expect(container.querySelectorAll('img').length).toBe(1);
});
// 9.Nav在Dialog里
test('Renderer:Nav with Dialog', async () => {
const {container, getByText} = render(
amisRender(
{
type: 'page',
body: {
type: 'button',
label: '点击弹框',
actionType: 'dialog',
dialog: {
title: '弹框',
body: [
{
type: 'nav',
stacked: true,
className: 'w-md',
draggable: true,
saveOrderApi: '/api/options/nav',
source: '/api/options/nav?parentId=${value}',
itemActions: [
{
type: 'icon',
icon: 'cloud',
visibleOn: "this.to === '?cat=1'"
},
{
type: 'dropdown-button',
level: 'link',
icon: 'fa fa-ellipsis-h',
hideCaret: true,
buttons: [
{
type: 'button',
label: '编辑'
},
{
type: 'button',
label: '删除'
}
]
}
],
links: [
{
label: 'Nav 1',
to: '?cat=1',
value: '1',
icon: 'fa fa-user',
__id: 1
},
{
label: 'Nav 2',
__id: 2,
unfolded: true,
children: [
{
__id: 2.1,
label: 'Nav 2-1',
children: [
{
label: 'Nav 2-1-1',
to: '?cat=2-1',
value: '2-1',
__id: 2.11
}
]
},
{
label: 'Nav 2-2',
to: '?cat=2-2',
value: '2-2',
__id: 2.2
}
]
},
{
label: 'Nav 3',
to: '?cat=3',
value: '3',
defer: true,
__id: 3
}
]
}
]
}
}
},
{},
makeEnv({
getModalContainer: () => container
})
)
);
expect(container).toMatchSnapshot();
fireEvent.click(getByText('点击弹框'));
await waitFor(() => {
expect(container.querySelector('[role="dialog"]')).toBeInTheDocument();
});
fireEvent.click(
container.querySelector(
'[role="dialog"] .cxd-Nav-Menu-item-extra .cxd-Button'
)!
);
await waitFor(() => {
expect(
container.querySelector('[role="dialog"] .cxd-PopOver')
).toBeInTheDocument();
});
});
// 10.Nav配置reload动作
test('Renderer:Nav with reload1', async () => {
const fetcher = jest.fn().mockImplementation(() =>
Promise.resolve({
data: {
status: 0,
msg: 'ok',
data: ''
}
})
);
const {container}: any = render(
amisRender(
{
type: 'page',
data: {
nav: [
{
label: 'Nav 1',
to: '/docs/index',
icon: 'fa fa-user'
},
{
label: 'Nav 2',
to: '/docs/api'
},
{
label: 'Nav 3',
to: '/docs/renderers'
}
]
},
body: [
{
type: 'nav',
stacked: true,
id: 'xxNav',
source: '${nav}'
},
{
type: 'button',
level: 'primary',
className: 'mr-4',
label: 'reload',
onEvent: {
click: {
actions: [
{
componentId: 'xxNav',
actionType: 'reload'
}
]
}
}
}
]
},
{},
makeEnv({
session: 'test-case-action-no',
fetcher
})
)
);
fireEvent.click(container.querySelector('.cxd-Button'));
await wait(500);
expect(fetcher).not.toBeCalled();
});
// 11.Nav配置reload动作
test('Renderer:Nav with reload2', async () => {
const fetcher = jest.fn().mockImplementation(() =>
Promise.resolve({
data: {
status: 0,
msg: 'ok',
data: {
links: [
{
label: 'Nav 1',
to: '?cat=1',
value: '1',
icon: 'fa fa-user'
},
{
label: 'Nav 2',
unfolded: true,
children: [
{
label: 'Nav 2-1',
children: [
{
label: 'Nav 2-1-1',
to: '?cat=2-1',
value: '2-1'
}
]
},
{
label: 'Nav 2-2',
to: '?cat=2-2',
value: '2-2'
}
]
},
{
label: 'Nav 3',
to: '?cat=3',
value: '3',
defer: true
}
],
value: '?cat=1'
}
}
})
);
const {container}: any = render(
amisRender(
{
type: 'page',
data: {
url: '/api/options/nav'
},
body: [
{
type: 'nav',
stacked: true,
id: 'xxNav',
source: '${url}'
},
{
type: 'button',
level: 'primary',
className: 'mr-4',
label: 'reload',
onEvent: {
click: {
actions: [
{
componentId: 'xxNav',
actionType: 'reload'
}
]
}
}
}
]
},
{},
makeEnv({
session: 'test-case-action-no',
fetcher
})
)
);
await wait(500);
const navItem = container.querySelector('.cxd-Nav-Menu-item');
expect(navItem).toBeInTheDocument();
fireEvent.click(container.querySelector('.cxd-Button'));
await wait(500);
expect(fetcher).toBeCalled();
});
test('Renderer:Nav with searchable', async () => {
const {container} = render(amisRender({
"type": "nav",
"stacked": true,
"searchable": true,
"searchConfig": {
"matchFunc": "return link.searchKey === keyword;"
},
"links": [
{
"label": "Nav 1",
"to": "?to=nav1",
"searchKey": "1"
},
{
"label": "Nav 2",
"to": "?to=nav2",
"searchKey": "2",
"children": [
{
"label": "Nav 2-1",
"to": "?to=nav2-1",
"searchKey": "2-1",
"children": [
{
"label": "Nav 2-1-1",
"to": "?to=nav2-1-1",
"searchKey": "2-1-1"
}
]
}
]
},
{
"label": "Nav 3",
"to": "?to=nav3",
"searchKey": "3",
"children": [
{
"label": "Nav 3-1",
"to": "?to=nav3-1",
"searchKey": "3-1"
}
]
},
{
"label": "Nav 4",
"to": "?to=nav4",
"searchKey": "4"
},
{
"label": "Nav 5",
"to": "?to=nav5",
"searchKey": "5"
}
]
}, {}, makeEnv({})));
const nav = container.querySelector('.cxd-Nav')!;
const searchbox = container.querySelector('.cxd-Nav-SearchBox input')!;
const searchboxBtn = container.querySelector('.cxd-SearchBox-searchBtn')!;
expect(nav).toBeInTheDocument();
fireEvent.change(searchbox, {target: {value: '2-1-1'}});
await wait(200);
fireEvent.click(searchboxBtn);
await wait(200);
expect(document.querySelectorAll('[role=menuitem]')?.length).toEqual(3);
fireEvent.change(searchbox, {target: {value: '3'}});
await wait(200);
fireEvent.click(searchboxBtn);
await wait(200);
expect(document.querySelectorAll('[role=menuitem]')?.length).toEqual(2);
});