test: js => ts (#37392)

* test: js => ts

* test: add test

* fix: fix eslint error

* test: add test case

* fix: fix test error

* fix: fix eslint error

* fix: fix eslint error

* fix: eslint error fix

* fix: fallback eslint config & add test case

* test: add all test case

* fix: bugfix

* fix: bugFix

* fix: bugFix

* fix: bugFix

* fix: lint

* fix: add React.createRef

* fix: add breadcrumbName in Routes

* fix: any commit for restart ci/cd

* fix: remove type

* fix: test fix

* fix: test fix

* fix: add ts-ignore for id

* test: add Icon test case

* test: remove ts-ignore

* test: add Icon test case
This commit is contained in:
lijianan 2022-09-05 19:41:32 +08:00 committed by GitHub
parent f212b339c7
commit c34caad24c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
161 changed files with 1966 additions and 2007 deletions

View File

@ -8,7 +8,7 @@ describe('AutoComplete children could be focus', () => {
jest.useFakeTimers();
});
let container;
let container: HTMLDivElement;
beforeEach(() => {
container = document.createElement('div');
document.body.appendChild(container);
@ -24,10 +24,8 @@ describe('AutoComplete children could be focus', () => {
it('focus() and onFocus', () => {
const handleFocus = jest.fn();
const { container: wrapper } = render(<AutoComplete onFocus={handleFocus} />, {
attachTo: container,
});
wrapper.querySelector('input').focus();
const { container: wrapper } = render(<AutoComplete onFocus={handleFocus} />, { container });
wrapper.querySelector('input')?.focus();
act(() => {
jest.runAllTimers();
});
@ -36,14 +34,12 @@ describe('AutoComplete children could be focus', () => {
it('blur() and onBlur', () => {
const handleBlur = jest.fn();
const { container: wrapper } = render(<AutoComplete onBlur={handleBlur} />, {
attachTo: container,
});
wrapper.querySelector('input').focus();
const { container: wrapper } = render(<AutoComplete onBlur={handleBlur} />, { container });
wrapper.querySelector('input')?.focus();
act(() => {
jest.runAllTimers();
});
wrapper.querySelector('input').blur();
wrapper.querySelector('input')?.blur();
act(() => {
jest.runAllTimers();
});
@ -61,17 +57,13 @@ describe('AutoComplete children could be focus', () => {
});
it('child.ref instance should support be focused and blured', () => {
let inputRef;
const inputRef = React.createRef<HTMLInputElement>();
render(
<AutoComplete dataSource={[]}>
<input
ref={node => {
inputRef = node;
}}
/>
<input ref={inputRef} />
</AutoComplete>,
);
expect(typeof inputRef.focus).toBe('function');
expect(typeof inputRef.blur).toBe('function');
expect(typeof inputRef.current?.focus).toBe('function');
expect(typeof inputRef.current?.blur).toBe('function');
});
});

View File

@ -10,14 +10,14 @@ describe('BackTop', () => {
it('should scroll to top after click it', async () => {
const { container } = render(<BackTop visibilityHeight={-1} />);
const scrollToSpy = jest.spyOn(window, 'scrollTo').mockImplementation((x, y) => {
const scrollToSpy = jest.spyOn(window, 'scrollTo').mockImplementation((_, y) => {
window.scrollY = y;
window.pageYOffset = y;
document.documentElement.scrollTop = y;
});
window.scrollTo(0, 400);
expect(document.documentElement.scrollTop).toBe(400);
fireEvent.click(container.querySelector('.ant-back-top'));
fireEvent.click(container.querySelector('.ant-back-top')!);
await sleep(500);
expect(document.documentElement.scrollTop).toBe(0);
scrollToSpy.mockRestore();
@ -26,23 +26,21 @@ describe('BackTop', () => {
it('support onClick', async () => {
const onClick = jest.fn();
const { container } = render(<BackTop onClick={onClick} visibilityHeight={-1} />);
const scrollToSpy = jest.spyOn(window, 'scrollTo').mockImplementation((x, y) => {
const scrollToSpy = jest.spyOn(window, 'scrollTo').mockImplementation((_, y) => {
window.scrollY = y;
window.pageYOffset = y;
});
document.dispatchEvent(new Event('scroll'));
window.scrollTo(0, 400);
fireEvent.click(container.querySelector('.ant-back-top'));
fireEvent.click(container.querySelector('.ant-back-top')!);
expect(onClick).toHaveBeenCalled();
scrollToSpy.mockRestore();
});
it('invalid target', async () => {
const onClick = jest.fn();
const { container } = render(
<BackTop onClick={onClick} visible target={() => ({ documentElement: {} })} />,
);
fireEvent.click(container.querySelector('.ant-back-top'));
const { container } = render(<BackTop onClick={onClick} visible target={undefined} />);
fireEvent.click(container.querySelector('.ant-back-top')!);
expect(onClick).toHaveBeenCalled();
});
});

View File

@ -3,6 +3,7 @@ import accessibilityTest from '../../../tests/shared/accessibilityTest';
import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
import { render } from '../../../tests/utils';
import type { Route } from '../Breadcrumb';
import Breadcrumb from '../index';
describe('Breadcrumb', () => {
@ -22,7 +23,7 @@ describe('Breadcrumb', () => {
// https://github.com/airbnb/enzyme/issues/875
it('warns on non-Breadcrumb.Item and non-Breadcrumb.Separator children', () => {
const MyCom = () => <div>foo</div>;
const MyCom: React.FC = () => <div>foo</div>;
render(
<Breadcrumb>
<MyCom />
@ -74,7 +75,7 @@ describe('Breadcrumb', () => {
});
it('should render a menu', () => {
const routes = [
const routes: Route[] = [
{
path: 'index',
breadcrumbName: 'home',
@ -103,6 +104,7 @@ describe('Breadcrumb', () => {
},
{
path: 'third',
breadcrumbName: '',
},
];
const { asFragment } = render(<Breadcrumb routes={routes} />);
@ -142,7 +144,7 @@ describe('Breadcrumb', () => {
// https://github.com/ant-design/ant-design/issues/25975
it('should support Breadcrumb.Item default separator', () => {
const MockComponent = () => (
const MockComponent: React.FC = () => (
<span>
<Breadcrumb.Item>Mock Node</Breadcrumb.Item>
</span>

View File

@ -1,9 +1,10 @@
import React from 'react';
import React, { useMemo } from 'react';
import type { RouterProps } from 'react-router-dom';
import { Link, MemoryRouter, Route, Routes, useLocation, useNavigate } from 'react-router-dom';
import Breadcrumb from '../index';
import { render, fireEvent } from '../../../tests/utils';
const Apps = () => (
const Apps: React.FC = () => (
<ul className="app-list">
<li>
<Link to="/apps/1">Application1</Link><Link to="/apps/1/detail">Detail</Link>
@ -33,7 +34,7 @@ describe('react router', () => {
// https://github.com/airbnb/enzyme/issues/875
it('react router 6', () => {
const Home = () => {
const Home: React.FC = () => {
const location = useLocation();
const navigate = useNavigate();
const pathSnippets = location.pathname.split('/').filter(i => i);
@ -41,7 +42,7 @@ describe('react router', () => {
const url = `/${pathSnippets.slice(0, index + 1).join('/')}`;
return (
<Breadcrumb.Item key={url}>
<Link to={url}>{breadcrumbNameMap[url]}</Link>
<Link to={url}>{breadcrumbNameMap[url as keyof typeof breadcrumbNameMap]}</Link>
</Breadcrumb.Item>
);
});
@ -50,6 +51,14 @@ describe('react router', () => {
<Link to="/">Home</Link>
</Breadcrumb.Item>,
].concat(extraBreadcrumbItems);
const componentProps = useMemo<RouterProps>(
() => ({ component: Apps } as unknown as RouterProps),
[],
);
const renderProps = useMemo<RouterProps>(
() => ({ render: () => <span>Home Page</span> } as unknown as RouterProps),
[],
);
return (
<div className="demo">
<div className="demo-nav">
@ -57,8 +66,8 @@ describe('react router', () => {
<a onClick={() => navigate('/apps')}>Application List</a>
</div>
<Routes>
<Route path="/apps" component={Apps} />
<Route render={() => <span>Home Page</span>} />
<Route path="/apps" {...componentProps} />
<Route {...renderProps} />
</Routes>
<Breadcrumb>{breadcrumbItems}</Breadcrumb>
</div>

View File

@ -19,10 +19,10 @@ describe('Checkbox', () => {
<Checkbox onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave} />,
);
fireEvent.mouseEnter(container.querySelector('label'));
fireEvent.mouseEnter(container.querySelector('label')!);
expect(onMouseEnter).toHaveBeenCalled();
fireEvent.mouseLeave(container.querySelector('label'));
fireEvent.mouseLeave(container.querySelector('label')!);
expect(onMouseLeave).toHaveBeenCalled();
});

View File

@ -6,6 +6,8 @@ import Collapse from '../../collapse';
import Input from '../../input';
import Table from '../../table';
import Checkbox from '../index';
import type { CheckboxValueType } from '../Group';
import type { CheckboxGroupProps } from '../index';
describe('CheckboxGroup', () => {
mountTest(Checkbox.Group);
@ -60,9 +62,11 @@ describe('CheckboxGroup', () => {
it('all children should have a name property', () => {
const { container } = render(<Checkbox.Group name="checkboxgroup" options={['Yes', 'No']} />);
[...container.querySelectorAll('input[type="checkbox"]')].forEach(el => {
expect(el.getAttribute('name')).toEqual('checkboxgroup');
});
Array.from(container.querySelectorAll<HTMLInputElement>('input[type="checkbox"]')).forEach(
el => {
expect(el.getAttribute('name')).toEqual('checkboxgroup');
},
);
});
it('passes prefixCls down to checkbox', () => {
@ -81,10 +85,10 @@ describe('CheckboxGroup', () => {
{ label: 'Apple', value: 'Apple' },
{ label: 'Orange', value: 'Orange' },
];
const renderCheckbox = props => <Checkbox.Group {...props} />;
const renderCheckbox = (props: CheckboxGroupProps) => <Checkbox.Group {...props} />;
const { container, rerender } = render(renderCheckbox({ options }));
expect(container.querySelectorAll('.ant-checkbox-checked').length).toBe(0);
rerender(renderCheckbox({ options, value: 'Apple' }));
rerender(renderCheckbox({ options, value: 'Apple' as unknown as CheckboxValueType[] }));
expect(container.querySelectorAll('.ant-checkbox-checked').length).toBe(1);
});
@ -116,7 +120,7 @@ describe('CheckboxGroup', () => {
<Checkbox key={2} value={2} />
</Checkbox.Group>,
);
fireEvent.click(container.querySelector('.ant-checkbox-input'));
fireEvent.click(container.querySelector('.ant-checkbox-input')!);
expect(onChange).toHaveBeenCalledWith([2]);
});
@ -164,7 +168,7 @@ describe('CheckboxGroup', () => {
const { container } = render(
<Checkbox.Group>
<Collapse bordered={false}>
<Collapse.Panel header="test panel">
<Collapse.Panel key="test panel" header="test panel">
<div>
<Checkbox value="1">item</Checkbox>
</div>
@ -174,11 +178,11 @@ describe('CheckboxGroup', () => {
);
fireEvent.click(
container.querySelector('.ant-collapse-item').querySelector('.ant-collapse-header'),
container.querySelector('.ant-collapse-item')?.querySelector('.ant-collapse-header')!,
);
fireEvent.click(container.querySelector('.ant-checkbox-input'));
fireEvent.click(container.querySelector('.ant-checkbox-input')!);
expect(container.querySelectorAll('.ant-checkbox-checked').length).toBe(1);
fireEvent.click(container.querySelector('.ant-checkbox-input'));
fireEvent.click(container.querySelector('.ant-checkbox-input')!);
expect(container.querySelectorAll('.ant-checkbox-checked').length).toBe(0);
});
@ -210,12 +214,12 @@ describe('CheckboxGroup', () => {
});
it('should get div ref', () => {
const refCalls = [];
const refCalls: HTMLDivElement[] = [];
render(
<Checkbox.Group
options={['Apple', 'Pear', 'Orange']}
ref={node => {
refCalls.push(node);
refCalls.push(node!);
}}
/>,
);
@ -230,18 +234,18 @@ describe('CheckboxGroup', () => {
<Checkbox.Group options={[1, 'Pear', 'Orange']} onChange={onChange} />,
);
fireEvent.click(container.querySelector('.ant-checkbox-input'));
fireEvent.click(container.querySelector('.ant-checkbox-input')!);
expect(onChange).toHaveBeenCalledWith([1]);
});
it('should store latest checkbox value if changed', () => {
const onChange = jest.fn();
const Demo = () => {
const [v, setV] = useState('');
const Demo: React.FC = () => {
const [v, setV] = useState<string>('');
React.useEffect(() => {
setTimeout(setV('1'), 1000);
setTimeout(setV('1') as unknown as TimerHandler, 1000);
}, []);
return (
@ -257,12 +261,12 @@ describe('CheckboxGroup', () => {
};
const { container } = render(<Demo />);
fireEvent.click(container.querySelector('.ant-checkbox-input'));
fireEvent.click(container.querySelector('.ant-checkbox-input')!);
expect(onChange).toHaveBeenCalledWith([]);
fireEvent.click(container.querySelector('.ant-checkbox-input'));
fireEvent.click(container.querySelector('.ant-checkbox-input')!);
expect(onChange).toHaveBeenCalledWith(['length1']);
fireEvent.change(container.querySelector('.ant-input'), { target: { value: '' } });
fireEvent.click(container.querySelector('.ant-checkbox-input'));
fireEvent.change(container.querySelector('.ant-input')!, { target: { value: '' } });
fireEvent.click(container.querySelector('.ant-checkbox-input')!);
expect(onChange).toHaveBeenCalledWith(['A']);
});
});

View File

@ -1,12 +1,13 @@
import React from 'react';
import { act } from 'react-dom/test-utils';
import type { DrawerProps } from '..';
import Drawer from '..';
import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
import { fireEvent, render } from '../../../tests/utils';
import ConfigProvider from '../../config-provider';
const DrawerTest = ({ getContainer }) => (
const DrawerTest: React.FC<DrawerProps> = ({ getContainer }) => (
<div>
<Drawer open width={400} getContainer={getContainer}>
Here is content of Drawer
@ -59,35 +60,37 @@ describe('Drawer', () => {
});
it('getContainer return undefined', () => {
const { container: wrapper, rerender } = render(<DrawerTest getContainer={() => undefined} />);
const { container, rerender } = render(
<DrawerTest getContainer={() => undefined as unknown as HTMLElement} />,
);
triggerMotion();
expect(wrapper.firstChild).toMatchSnapshot();
expect(container.firstChild).toMatchSnapshot();
rerender(<DrawerTest getContainer={false} />);
triggerMotion();
expect(wrapper.firstChild).toMatchSnapshot();
expect(container.firstChild).toMatchSnapshot();
});
it('render top drawer', () => {
const { container: wrapper } = render(
<Drawer open height={400} placement="top" getContainer={false}>
const { container } = render(
<Drawer visible height={400} placement="top" getContainer={false}>
Here is content of Drawer
</Drawer>,
);
triggerMotion();
expect(wrapper.firstChild).toMatchSnapshot();
expect(container.firstChild).toMatchSnapshot();
});
it('have a title', () => {
const { container: wrapper } = render(
const { container } = render(
<Drawer open title="Test Title" getContainer={false}>
Here is content of Drawer
</Drawer>,
);
triggerMotion();
expect(wrapper.firstChild).toMatchSnapshot();
expect(container.firstChild).toMatchSnapshot();
});
it('closable is false', () => {

View File

@ -1,14 +1,15 @@
import React from 'react';
import type { DrawerProps } from '..';
import Drawer from '..';
import { act, fireEvent, render } from '../../../tests/utils';
describe('Drawer', () => {
const getDrawer = props => (
<Drawer open getContainer={false} {...props}>
Here is content of Drawer
</Drawer>
);
const DrawerTest: React.FC<DrawerProps> = props => (
<Drawer open getContainer={false} {...props}>
Here is content of Drawer
</Drawer>
);
describe('Drawer', () => {
beforeEach(() => {
jest.useFakeTimers();
});
@ -18,12 +19,12 @@ describe('Drawer', () => {
});
it('render correctly', () => {
const { container, asFragment, rerender } = render(getDrawer());
const { container, asFragment, rerender } = render(<DrawerTest />);
expect(container.querySelector('.ant-drawer-body')).toBeTruthy();
rerender(getDrawer({ open: false }));
rerender(<DrawerTest open={false} />);
expect(container.querySelector('.ant-drawer-body').textContent).toEqual(
expect(container.querySelector('.ant-drawer-body')?.textContent).toEqual(
'Here is content of Drawer',
);
@ -32,33 +33,33 @@ describe('Drawer', () => {
it('mask trigger onClose', () => {
const onClose = jest.fn();
const { container } = render(getDrawer({ onClose }));
const { container } = render(<DrawerTest onClose={onClose} />);
fireEvent.click(container.querySelector('.ant-drawer-mask'));
fireEvent.click(container.querySelector('.ant-drawer-mask')!);
expect(onClose).toHaveBeenCalled();
});
it('close button trigger onClose', () => {
const onClose = jest.fn();
const { container } = render(getDrawer({ onClose }));
const { container } = render(<DrawerTest onClose={onClose} />);
fireEvent.click(container.querySelector('.ant-drawer-close'));
fireEvent.click(container.querySelector('.ant-drawer-close')!);
expect(onClose).toHaveBeenCalled();
});
it('maskClosable no trigger onClose', () => {
const onClose = jest.fn();
const { container } = render(getDrawer({ onClose, maskClosable: false }));
const { container } = render(<DrawerTest onClose={onClose} maskClosable={false} />);
fireEvent.click(container.querySelector('.ant-drawer-mask'));
fireEvent.click(container.querySelector('.ant-drawer-mask')!);
expect(onClose).not.toHaveBeenCalled();
});
it('dom should be removed after close when destroyOnClose is true', () => {
const { container, rerender } = render(getDrawer({ destroyOnClose: true }));
const { container, rerender } = render(<DrawerTest destroyOnClose />);
expect(container.querySelector('.ant-drawer')).toBeTruthy();
rerender(getDrawer({ destroyOnClose: true, open: false }));
rerender(<DrawerTest destroyOnClose open={false} />);
act(() => {
jest.runAllTimers();
});
@ -67,53 +68,55 @@ describe('Drawer', () => {
});
it('dom should be existed after close when destroyOnClose is false', () => {
const { container, rerender } = render(getDrawer());
const { container, rerender } = render(<DrawerTest />);
expect(container.querySelector('.ant-drawer')).toBeTruthy();
rerender(getDrawer({ open: false }));
rerender(<DrawerTest open={false} />);
act(() => {
jest.runAllTimers();
});
fireEvent.animationEnd(container.querySelector('.ant-drawer-content'));
fireEvent.animationEnd(container.querySelector('.ant-drawer-content')!);
expect(container.querySelector('.ant-drawer')).toBeTruthy();
});
it('dom should be existed after close twice when getContainer is false', () => {
const { container, rerender } = render(getDrawer({ open: true, getContainer: false }));
const { container, rerender } = render(<DrawerTest open getContainer={false} />);
expect(container.querySelector('.ant-drawer-content')).toBeTruthy();
// Hide
rerender(getDrawer({ open: false, getContainer: false }));
rerender(<DrawerTest open={false} getContainer={false} />);
act(() => {
jest.runAllTimers();
});
fireEvent.animationEnd(container.querySelector('.ant-drawer-content-wrapper'));
fireEvent.animationEnd(container.querySelector('.ant-drawer-content-wrapper')!);
expect(container.querySelector('.ant-drawer-content-wrapper-hidden')).toBeTruthy();
// Show
rerender(getDrawer({ open: true, getContainer: false }));
rerender(<DrawerTest open getContainer={false} />);
expect(container.querySelector('.ant-drawer-content-wrapper')).toBeTruthy();
expect(container.querySelector('.ant-drawer-content-wrapper-hidden')).toBeFalsy();
// Hide
rerender(getDrawer({ open: false, getContainer: false }));
rerender(<DrawerTest open={false} getContainer={false} />);
act(() => {
jest.runAllTimers();
});
fireEvent.animationEnd(container.querySelector('.ant-drawer-content-wrapper'));
fireEvent.animationEnd(container.querySelector('.ant-drawer-content-wrapper')!);
expect(container.querySelector('.ant-drawer-content-wrapper-hidden')).toBeTruthy();
});
it('test afterVisibleChange', async () => {
const afterVisibleChange = jest.fn();
const { container, rerender } = render(getDrawer({ afterVisibleChange, open: true }));
rerender(getDrawer({ afterVisibleChange, open: false }));
const { container, rerender } = render(
<DrawerTest open afterVisibleChange={afterVisibleChange} />,
);
rerender(<DrawerTest open={false} afterVisibleChange={afterVisibleChange} />);
act(() => {
jest.runAllTimers();
});
fireEvent.animationEnd(container.querySelector('.ant-drawer-content-wrapper'));
fireEvent.animationEnd(container.querySelector('.ant-drawer-content-wrapper')!);
expect(afterVisibleChange).toHaveBeenCalledTimes(1);
});
@ -121,18 +124,18 @@ describe('Drawer', () => {
it('should support children ref', () => {
const fn = jest.fn();
const refCallback = ref => {
const refCallback = (ref: HTMLDivElement | null) => {
expect(typeof ref).toBe('object');
fn();
};
const RefDemo = () => {
const ref = React.useRef();
const RefDemo: React.FC = () => {
const ref = React.useRef<HTMLDivElement>(null);
const [open, setOpen] = React.useState(false);
React.useEffect(() => {
if (open) {
refCallback(ref.current);
refCallback(ref.current!);
}
}, [open]);
@ -146,7 +149,7 @@ describe('Drawer', () => {
);
};
const { container } = render(<RefDemo />);
fireEvent.click(container.querySelector('a'));
fireEvent.click(container.querySelector('a')!);
expect(fn).toHaveBeenCalled();
});
});

View File

@ -1,189 +0,0 @@
import React from 'react';
import Drawer from '..';
import { fireEvent, render } from '../../../tests/utils';
import Button from '../../button';
class MultiDrawer extends React.Component {
state = { open: false, childrenDrawer: false, hasChildren: true };
showDrawer = () => {
this.setState({
open: true,
hasChildren: true,
});
};
onClose = () => {
this.setState({
open: false,
});
};
showChildrenDrawer = () => {
this.setState({
childrenDrawer: true,
hasChildren: true,
});
};
onChildrenDrawerClose = () => {
this.setState({
childrenDrawer: false,
});
};
onRemoveChildDrawer = () => {
this.setState({
hasChildren: false,
});
};
render() {
const { childrenDrawer, open, hasChildren } = this.state;
const { placement, push } = this.props;
return (
<div>
<Button type="primary" id="open_drawer" onClick={this.showDrawer}>
Open drawer
</Button>
<Button type="primary" id="remove_drawer" onClick={this.onRemoveChildDrawer}>
rm child drawer
</Button>
<Drawer
title="Multi-level drawer"
className="test_drawer"
width={520}
onClose={this.onClose}
getContainer={false}
placement={placement}
open={open}
push={push}
>
<Button type="primary" id="open_two_drawer" onClick={this.showChildrenDrawer}>
Two-level drawer
</Button>
{hasChildren && (
<Drawer
title="Two-level Drawer"
width={320}
className="Two-level"
getContainer={false}
placement={placement}
onClose={this.onChildrenDrawerClose}
open={childrenDrawer}
>
<div id="two_drawer_text">This is two-level drawer</div>
</Drawer>
)}
<div
style={{
position: 'absolute',
bottom: 0,
width: '100%',
borderTop: '1px solid #e8e8e8',
padding: '10px 16px',
textAlign: 'right',
left: 0,
background: '#fff',
borderRadius: '0 0 4px 4px',
}}
>
<Button
style={{
marginRight: 8,
}}
onClick={this.onClose}
>
Cancel
</Button>
<Button onClick={this.onClose} type="primary">
Submit
</Button>
</div>
</Drawer>
<div className="childrenDrawer">{String(childrenDrawer)}</div>
</div>
);
}
}
describe('Drawer', () => {
it('render right MultiDrawer', () => {
const { container: wrapper } = render(<MultiDrawer placement="right" />);
fireEvent.click(wrapper.querySelector('button#open_drawer'));
fireEvent.click(wrapper.querySelector('button#open_two_drawer'));
expect(wrapper.querySelector('.ant-drawer-content-wrapper')).toHaveStyle({
transform: 'translateX(-180px)',
});
expect(wrapper.querySelectorAll('#two_drawer_text').length).toBe(1);
});
it('render left MultiDrawer', () => {
const { container: wrapper } = render(<MultiDrawer placement="left" />);
fireEvent.click(wrapper.querySelector('button#open_drawer'));
fireEvent.click(wrapper.querySelector('button#open_two_drawer'));
expect(wrapper.querySelector('.ant-drawer-content-wrapper')).toHaveStyle({
transform: 'translateX(180px)',
});
expect(wrapper.querySelectorAll('#two_drawer_text').length).toBe(1);
fireEvent.click(wrapper.querySelector('.Two-level .ant-drawer-close'));
expect(wrapper.querySelector('.childrenDrawer').innerHTML).toEqual('false');
});
it('render top MultiDrawer', () => {
const { container: wrapper } = render(<MultiDrawer placement="top" />);
fireEvent.click(wrapper.querySelector('button#open_drawer'));
fireEvent.click(wrapper.querySelector('button#open_two_drawer'));
expect(wrapper.querySelector('.ant-drawer-content-wrapper')).toHaveStyle({
transform: 'translateY(180px)',
});
expect(wrapper.querySelectorAll('#two_drawer_text').length).toBe(1);
});
it('render MultiDrawer is child in unmount', () => {
const { container: wrapper } = render(<MultiDrawer placement="top" mask={false} />);
fireEvent.click(wrapper.querySelector('button#open_drawer'));
fireEvent.click(wrapper.querySelector('button#open_two_drawer'));
fireEvent.click(wrapper.querySelector('button#remove_drawer'));
expect(wrapper.querySelector('.ant-drawer-content-wrapper')).toHaveStyle({
transform: '',
});
fireEvent.click(wrapper.querySelector('button#open_two_drawer'));
expect(wrapper.querySelector('.ant-drawer-content-wrapper')).toHaveStyle({
transform: 'translateY(180px)',
});
expect(wrapper.querySelectorAll('#two_drawer_text').length).toBe(1);
});
it('custom MultiDrawer push distance', () => {
const { container: wrapper } = render(<MultiDrawer push={{ distance: 256 }} />);
fireEvent.click(wrapper.querySelector('button#open_drawer'));
fireEvent.click(wrapper.querySelector('button#open_two_drawer'));
expect(wrapper.querySelector('.ant-drawer-content-wrapper')).toHaveStyle({
transform: 'translateX(-256px)',
});
});
it('custom MultiDrawer push with true', () => {
const { container: wrapper } = render(<MultiDrawer push />);
fireEvent.click(wrapper.querySelector('button#open_drawer'));
fireEvent.click(wrapper.querySelector('button#open_two_drawer'));
expect(wrapper.querySelector('.ant-drawer-content-wrapper')).toHaveStyle({
transform: 'translateX(-180px)',
});
});
it('custom MultiDrawer push with false', () => {
const { container: wrapper } = render(<MultiDrawer push={false} />);
fireEvent.click(wrapper.querySelector('button#open_drawer'));
fireEvent.click(wrapper.querySelector('button#open_two_drawer'));
expect(wrapper.querySelector('.ant-drawer-content-wrapper')).toHaveStyle({
transform: '',
});
});
});

View File

@ -0,0 +1,193 @@
import type { DrawerPopupProps } from 'rc-drawer/lib/DrawerPopup';
import React from 'react';
import Drawer from '..';
import { fireEvent, render } from '../../../tests/utils';
import Button from '../../button';
interface DrawerPropsType {
push?: DrawerPopupProps['push'];
placement?: DrawerPopupProps['placement'];
}
interface DrawerStateType {
open: boolean;
hasChildren: boolean;
childrenDrawer: boolean;
}
class MultiDrawer extends React.Component<DrawerPropsType, DrawerStateType> {
state = { open: false, childrenDrawer: false, hasChildren: true };
showDrawer = () => {
this.setState({
open: true,
hasChildren: true,
});
};
onClose = () => {
this.setState({
open: false,
});
};
showChildrenDrawer = () => {
this.setState({
childrenDrawer: true,
hasChildren: true,
});
};
onChildrenDrawerClose = () => {
this.setState({
childrenDrawer: false,
});
};
onRemoveChildDrawer = () => {
this.setState({
hasChildren: false,
});
};
render() {
const { childrenDrawer, open, hasChildren } = this.state;
const { placement, push } = this.props;
return (
<div>
<Button type="primary" id="open_drawer" onClick={this.showDrawer}>
Open drawer
</Button>
<Button type="primary" id="remove_drawer" onClick={this.onRemoveChildDrawer}>
rm child drawer
</Button>
<Drawer
title="Multi-level drawer"
className="test_drawer"
width={520}
onClose={this.onClose}
getContainer={false}
placement={placement}
open={open}
push={push}
>
<Button type="primary" id="open_two_drawer" onClick={this.showChildrenDrawer}>
Two-level drawer
</Button>
{hasChildren && (
<Drawer
title="Two-level Drawer"
width={320}
className="Two-level"
getContainer={false}
placement={placement}
onClose={this.onChildrenDrawerClose}
open={childrenDrawer}
>
<div id="two_drawer_text">This is two-level drawer</div>
</Drawer>
)}
<div
style={{
position: 'absolute',
bottom: 0,
width: '100%',
borderTop: '1px solid #e8e8e8',
padding: '10px 16px',
textAlign: 'right',
left: 0,
backgroundColor: '#fff',
borderRadius: '0 0 4px 4px',
}}
>
<Button style={{ marginRight: 8 }} onClick={this.onClose}>
Cancel
</Button>
<Button onClick={this.onClose} type="primary">
Submit
</Button>
</div>
</Drawer>
<div className="childrenDrawer">{String(childrenDrawer)}</div>
</div>
);
}
}
describe('Drawer', () => {
it('render right MultiDrawer', () => {
const { container } = render(<MultiDrawer placement="right" />);
fireEvent.click(container.querySelector('button#open_drawer')!);
fireEvent.click(container.querySelector('button#open_two_drawer')!);
expect(container.querySelector('.ant-drawer-content-wrapper')).toHaveStyle({
transform: 'translateX(-180px)',
});
expect(container.querySelectorAll('#two_drawer_text').length).toBe(1);
});
it('render left MultiDrawer', () => {
const { container } = render(<MultiDrawer placement="left" />);
fireEvent.click(container.querySelector('button#open_drawer')!);
fireEvent.click(container.querySelector('button#open_two_drawer')!);
expect(container.querySelector('.ant-drawer-content-wrapper')).toHaveStyle({
transform: 'translateX(180px)',
});
expect(container.querySelectorAll('#two_drawer_text').length).toBe(1);
fireEvent.click(container.querySelector('.Two-level .ant-drawer-close')!);
expect(container.querySelector('.childrenDrawer')?.innerHTML).toEqual('false');
});
it('render top MultiDrawer', () => {
const { container } = render(<MultiDrawer placement="top" />);
fireEvent.click(container.querySelector('button#open_drawer')!);
fireEvent.click(container.querySelector('button#open_two_drawer')!);
expect(container.querySelector('.ant-drawer-content-wrapper')).toHaveStyle({
transform: 'translateY(180px)',
});
expect(container.querySelectorAll('#two_drawer_text').length).toBe(1);
});
it('render MultiDrawer is child in unmount', () => {
const mask = { mask: false };
const { container } = render(<MultiDrawer placement="top" {...mask} />);
fireEvent.click(container.querySelector('button#open_drawer')!);
fireEvent.click(container.querySelector('button#open_two_drawer')!);
fireEvent.click(container.querySelector('button#remove_drawer')!);
expect(container.querySelector('.ant-drawer-content-wrapper')).toHaveStyle({ transform: '' });
fireEvent.click(container.querySelector('button#open_two_drawer')!);
expect(container.querySelector('.ant-drawer-content-wrapper')).toHaveStyle({
transform: 'translateY(180px)',
});
expect(container.querySelectorAll('#two_drawer_text').length).toBe(1);
});
it('custom MultiDrawer push distance', () => {
const { container } = render(<MultiDrawer push={{ distance: 256 }} />);
fireEvent.click(container.querySelector('button#open_drawer')!);
fireEvent.click(container.querySelector('button#open_two_drawer')!);
expect(container.querySelector('.ant-drawer-content-wrapper')).toHaveStyle({
transform: 'translateX(-256px)',
});
});
it('custom MultiDrawer push with true', () => {
const { container } = render(<MultiDrawer push />);
fireEvent.click(container.querySelector('button#open_drawer')!);
fireEvent.click(container.querySelector('button#open_two_drawer')!);
expect(container.querySelector('.ant-drawer-content-wrapper')).toHaveStyle({
transform: 'translateX(-180px)',
});
});
it('custom MultiDrawer push with false', () => {
const { container } = render(<MultiDrawer push={false} />);
fireEvent.click(container.querySelector('button#open_drawer')!);
fireEvent.click(container.querySelector('button#open_two_drawer')!);
expect(container.querySelector('.ant-drawer-content-wrapper')).toHaveStyle({ transform: '' });
});
});

View File

@ -11,7 +11,7 @@ describe('Empty', () => {
it('image size should change', () => {
const { container } = render(<Empty imageStyle={{ height: 20 }} />);
expect(container.querySelector('.ant-empty-image').style.height).toBe('20px');
expect(container.querySelector<HTMLDivElement>('.ant-empty-image')?.style.height).toBe('20px');
});
it('description can be false', () => {

View File

@ -0,0 +1,17 @@
import React from 'react';
import Icon from '..';
import { render } from '../../../tests/utils';
// v3 兼容性测试
describe('Icon', () => {
it('should render Icon', () => {
const { container } = render(<Icon />);
expect(container.firstChild).toBe(null);
});
it('should throw Error', () => {
const errSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
render(<Icon />);
expect(errSpy).toHaveBeenCalled();
});
});

View File

@ -1,6 +1,6 @@
import warning from '../_util/warning';
const Icon = () => {
const Icon: React.FC = () => {
warning(false, 'Icon', 'Empty Icon');
return null;
};

View File

@ -11,60 +11,58 @@ describe('Image', () => {
mountTest(Image);
rtlTest(Image);
it('Image preview props set false', () => {
const { container: wrapper } = render(<Image src={src} preview={false} />);
const { container } = render(<Image src={src} preview={false} />);
fireEvent.click(wrapper.querySelector('.ant-image'));
expect(wrapper.querySelector('.ant-image-preview-root')).toBe(null);
fireEvent.click(container.querySelector('.ant-image')!);
expect(container.querySelector('.ant-image-preview-root')).toBe(null);
});
it('Group preview props set false', () => {
const { container: wrapper } = render(
const { container } = render(
<Image.PreviewGroup preview={false}>
<Image src={src} />
</Image.PreviewGroup>,
);
fireEvent.click(wrapper.querySelector('.ant-image'));
fireEvent.click(container.querySelector('.ant-image')!);
expect(wrapper.querySelector('.ant-image-preview-root')).toBe(null);
expect(container.querySelector('.ant-image-preview-root')).toBe(null);
});
it('Default preview props', () => {
const { container: wrapper, baseElement } = render(
<Image src={src} preview={{ visible: true }} />,
);
const { container, baseElement } = render(<Image src={src} preview={{ visible: true }} />);
fireEvent.click(wrapper.querySelector('.ant-image'));
fireEvent.click(container.querySelector('.ant-image')!);
expect(baseElement.querySelector('.ant-image-preview-mask')).toHaveClass('ant-fade');
expect(baseElement.querySelector('.ant-image-preview')).toHaveClass('ant-zoom');
});
it('Default Group preview props', () => {
const { container: wrapper, baseElement } = render(
const { container, baseElement } = render(
<Image.PreviewGroup preview={{ visible: true }}>
<Image src={src} />
</Image.PreviewGroup>,
);
fireEvent.click(wrapper.querySelector('.ant-image'));
fireEvent.click(container.querySelector('.ant-image')!);
expect(baseElement.querySelector('.ant-image-preview-mask')).toHaveClass('ant-fade');
expect(baseElement.querySelector('.ant-image-preview')).toHaveClass('ant-zoom');
});
it('Customize preview props', () => {
const { container: wrapper, baseElement } = render(
const { container, baseElement } = render(
<Image
src={src}
preview={{ visible: true, transitionName: 'abc', maskTransitionName: 'def' }}
/>,
);
fireEvent.click(wrapper.querySelector('.ant-image'));
fireEvent.click(container.querySelector('.ant-image')!);
expect(baseElement.querySelector('.ant-image-preview')).toHaveClass('abc');
expect(baseElement.querySelector('.ant-image-preview-mask')).toHaveClass('def');
});
it('Customize Group preview props', () => {
const { container: wrapper, baseElement } = render(
const { container, baseElement } = render(
<Image.PreviewGroup
preview={{ visible: true, transitionName: 'abc', maskTransitionName: 'def' }}
>
@ -72,22 +70,21 @@ describe('Image', () => {
</Image.PreviewGroup>,
);
fireEvent.click(wrapper.querySelector('.ant-image'));
fireEvent.click(container.querySelector('.ant-image')!);
expect(baseElement.querySelector('.ant-image-preview')).toHaveClass('abc');
expect(baseElement.querySelector('.ant-image-preview-mask')).toHaveClass('def');
});
it('ConfigProvider getPopupContainer', () => {
const { container: wrapper, baseElement } = render(
const { container, baseElement } = render(
<>
<div className="container" />
<ConfigProvider getPopupContainer={() => document.querySelector('.container')}>
<ConfigProvider getPopupContainer={() => document.querySelector('.container')!}>
<Image src={src} />
</ConfigProvider>
</>,
);
fireEvent.click(wrapper.querySelector('.ant-image'));
const containerElement = baseElement.querySelector('.container');
expect(containerElement.children.length).not.toBe(0);
fireEvent.click(container.querySelector('.ant-image')!);
expect(baseElement.querySelector('.container')?.children.length).not.toBe(0);
});
});

View File

@ -15,18 +15,18 @@ describe('InputNumber', () => {
it('should return null when blur a empty input number', () => {
const onChange = jest.fn();
const { container } = render(<InputNumber defaultValue="1" onChange={onChange} />);
fireEvent.change(container.querySelector('input'), { target: { value: '' } });
fireEvent.change(container.querySelector('input')!, { target: { value: '' } });
expect(onChange).toHaveBeenLastCalledWith(null);
});
it('should call onStep when press up or down button', () => {
const onStep = jest.fn();
const { container } = render(<InputNumber defaultValue={1} onStep={onStep} />);
fireEvent.mouseDown(container.querySelector('.ant-input-number-handler-up'));
fireEvent.mouseDown(container.querySelector('.ant-input-number-handler-up')!);
expect(onStep).toHaveBeenCalledTimes(1);
expect(onStep).toHaveBeenLastCalledWith(2, { offset: 1, type: 'up' });
fireEvent.mouseDown(container.querySelector('.ant-input-number-handler-down'));
fireEvent.mouseDown(container.querySelector('.ant-input-number-handler-down')!);
expect(onStep).toHaveBeenCalledTimes(2);
expect(onStep).toHaveBeenLastCalledWith(1, { offset: 1, type: 'down' });
});

View File

@ -10,15 +10,15 @@ describe('prefix', () => {
);
it('should support className when has prefix', () => {
const { container } = render(<InputNumber prefix="suffix" className="my-class-name" />);
expect(container.firstChild?.className.includes('my-class-name')).toBe(true);
expect((container.firstChild as HTMLElement)?.className.includes('my-class-name')).toBe(true);
expect(container.querySelector('input')?.className.includes('my-class-name')).toBe(false);
});
it('should trigger focus when prefix is clicked', () => {
const { container } = render(<InputNumber prefix={<i>123</i>} />);
const mockFocus = jest.spyOn(container.querySelector('input'), 'focus');
fireEvent.mouseUp(container.querySelector('i'));
const mockFocus = jest.spyOn(container.querySelector('input')!, 'focus');
fireEvent.mouseUp(container.querySelector('i')!);
expect(mockFocus).toHaveBeenCalled();
});
});

View File

@ -1,5 +1,5 @@
import React from 'react';
// eslint-disable-next-line import/no-unresolved
import type { InputRef } from '..';
import Input from '..';
import focusTest from '../../../tests/shared/focusTest';
import mountTest from '../../../tests/shared/mountTest';
@ -13,12 +13,12 @@ describe('Input.Password', () => {
rtlTest(Input.Password);
it('should get input element from ref', () => {
const ref = React.createRef();
const ref = React.createRef<InputRef>();
const onSelect = jest.fn();
const { container } = render(<Input.Password onSelect={onSelect} ref={ref} />);
expect(ref.current.input instanceof HTMLInputElement).toBe(true);
fireEvent.select(container.querySelector('input'));
expect(ref.current?.input instanceof HTMLInputElement).toBe(true);
fireEvent.select(container.querySelector('input')!);
expect(onSelect).toHaveBeenCalled();
});
@ -30,13 +30,13 @@ describe('Input.Password', () => {
it('should change type when click', () => {
const { asFragment, container } = render(<Input.Password />);
fireEvent.change(container.querySelector('input'), { target: { value: '111' } });
fireEvent.change(container.querySelector('input')!, { target: { value: '111' } });
expect(asFragment().firstChild).toMatchSnapshot();
fireEvent.click(container.querySelector('.ant-input-password-icon'));
fireEvent.click(container.querySelector('.ant-input-password-icon')!);
expect(asFragment().firstChild).toMatchSnapshot();
fireEvent.click(container.querySelector('.ant-input-password-icon'));
fireEvent.click(container.querySelector('.ant-input-password-icon')!);
expect(asFragment().firstChild).toMatchSnapshot();
});
@ -50,7 +50,7 @@ describe('Input.Password', () => {
it('should not toggle visibility when disabled prop is true', () => {
const { container } = render(<Input.Password disabled />);
expect(container.querySelectorAll('.anticon-eye-invisible').length).toBe(1);
fireEvent.click(container.querySelector('.anticon-eye-invisible'));
fireEvent.click(container.querySelector('.anticon-eye-invisible')!);
expect(container.querySelectorAll('.anticon-eye').length).toBe(0);
});
@ -59,43 +59,43 @@ describe('Input.Password', () => {
container: document.body,
});
expect(document.activeElement).toBe(container.querySelector('input'));
document.activeElement.setSelectionRange(2, 2);
expect(document.activeElement.selectionStart).toBe(2);
fireEvent.mouseDown(container.querySelector('.ant-input-password-icon'));
fireEvent.mouseUp(container.querySelector('.ant-input-password-icon'));
fireEvent.click(container.querySelector('.ant-input-password-icon'));
(document?.activeElement as any)?.setSelectionRange(2, 2);
expect((document?.activeElement as any)?.selectionStart).toBe(2);
fireEvent.mouseDown(container.querySelector('.ant-input-password-icon')!);
fireEvent.mouseUp(container.querySelector('.ant-input-password-icon')!);
fireEvent.click(container.querySelector('.ant-input-password-icon')!);
expect(document.activeElement).toBe(container.querySelector('input'));
expect(document.activeElement.selectionStart).toBe(2);
expect((document?.activeElement as any).selectionStart).toBe(2);
unmount();
});
// https://github.com/ant-design/ant-design/issues/20541
it('should not show value attribute in input element', async () => {
const { container } = render(<Input.Password />);
fireEvent.change(container.querySelector('input'), { target: { value: 'value' } });
fireEvent.change(container.querySelector('input')!, { target: { value: 'value' } });
await sleep();
expect(container.querySelector('input').getAttribute('value')).toBeFalsy();
expect(container.querySelector('input')?.getAttribute('value')).toBeFalsy();
});
// https://github.com/ant-design/ant-design/issues/24526
it('should not show value attribute in input element after blur it', async () => {
const { container } = render(<Input.Password />);
fireEvent.change(container.querySelector('input'), { target: { value: 'value' } });
fireEvent.change(container.querySelector('input')!, { target: { value: 'value' } });
await sleep();
expect(container.querySelector('input').getAttribute('value')).toBeFalsy();
fireEvent.blur(container.querySelector('input'));
expect(container.querySelector('input')?.getAttribute('value')).toBeFalsy();
fireEvent.blur(container.querySelector('input')!);
await sleep();
expect(container.querySelector('input').getAttribute('value')).toBeFalsy();
fireEvent.focus(container.querySelector('input'));
expect(container.querySelector('input')?.getAttribute('value')).toBeFalsy();
fireEvent.focus(container.querySelector('input')!);
await sleep();
expect(container.querySelector('input').getAttribute('value')).toBeFalsy();
expect(container.querySelector('input')?.getAttribute('value')).toBeFalsy();
});
// https://github.com/ant-design/ant-design/issues/20541
it('could be unmount without errors', () => {
expect(() => {
const { container, unmount } = render(<Input.Password />);
fireEvent.change(container.querySelector('input'), { target: { value: 'value' } });
fireEvent.change(container.querySelector('input')!, { target: { value: 'value' } });
unmount();
}).not.toThrow();
});
@ -104,6 +104,6 @@ describe('Input.Password', () => {
it('should not contain value attribute in input element with defaultValue', async () => {
const { container } = render(<Input.Password defaultValue="value" />);
await sleep();
expect(container.querySelector('input').getAttribute('value')).toBeFalsy();
expect(container.querySelector('input')?.getAttribute('value')).toBeFalsy();
});
});

View File

@ -4,6 +4,7 @@ import focusTest from '../../../tests/shared/focusTest';
import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
import Button from '../../button';
import type { InputRef } from '../Input';
import Search from '../Search';
describe('Input.Search', () => {
@ -42,24 +43,16 @@ describe('Input.Search', () => {
const { container } = render(
<Search defaultValue="search text" onSearch={onSearch} disabled />,
);
fireEvent.click(container.querySelector('button'));
fireEvent.click(container.querySelector('button')!);
expect(onSearch).toHaveBeenCalledTimes(0);
});
it('should trigger onSearch when click search icon', () => {
const onSearch = jest.fn();
const { container } = render(<Search defaultValue="search text" onSearch={onSearch} />);
fireEvent.click(container.querySelector('button'));
fireEvent.click(container.querySelector('button')!);
expect(onSearch).toHaveBeenCalledTimes(1);
expect(onSearch).toHaveBeenCalledWith(
'search text',
expect.anything(),
// FIXME: should use following code
// expect.objectContaining({
// type: 'click',
// preventDefault: expect.any(Function),
// }),
);
expect(onSearch).toHaveBeenCalledWith('search text', expect.anything());
});
it('should trigger onSearch when click search button', () => {
@ -67,17 +60,9 @@ describe('Input.Search', () => {
const { container } = render(
<Search defaultValue="search text" enterButton onSearch={onSearch} />,
);
fireEvent.click(container.querySelector('button'));
fireEvent.click(container.querySelector('button')!);
expect(onSearch).toHaveBeenCalledTimes(1);
expect(onSearch).toHaveBeenCalledWith(
'search text',
expect.anything(),
// FIXME: should use following code
// expect.objectContaining({
// type: 'click',
// preventDefault: expect.any(Function),
// }),
);
expect(onSearch).toHaveBeenCalledWith('search text', expect.anything());
});
it('should trigger onSearch when click search button with text', () => {
@ -85,17 +70,9 @@ describe('Input.Search', () => {
const { container } = render(
<Search defaultValue="search text" enterButton="button text" onSearch={onSearch} />,
);
fireEvent.click(container.querySelector('button'));
fireEvent.click(container.querySelector('button')!);
expect(onSearch).toHaveBeenCalledTimes(1);
expect(onSearch).toHaveBeenCalledWith(
'search text',
expect.anything(),
// FIXME: should use following code
// expect.objectContaining({
// type: 'click',
// preventDefault: expect.any(Function),
// }),
);
expect(onSearch).toHaveBeenCalledWith('search text', expect.anything());
});
it('should trigger onSearch when click search button with customize button', () => {
@ -107,17 +84,9 @@ describe('Input.Search', () => {
onSearch={onSearch}
/>,
);
fireEvent.click(container.querySelector('button'));
fireEvent.click(container.querySelector('button')!);
expect(onSearch).toHaveBeenCalledTimes(1);
expect(onSearch).toHaveBeenCalledWith(
'search text',
expect.anything(),
// FIXME: should use following code
// expect.objectContaining({
// type: 'click',
// preventDefault: expect.any(Function),
// }),
);
expect(onSearch).toHaveBeenCalledWith('search text', expect.anything());
});
it('should trigger onSearch when click search button of native', () => {
@ -134,56 +103,32 @@ describe('Input.Search', () => {
onSearch={onSearch}
/>,
);
fireEvent.click(container.querySelector('button'));
fireEvent.click(container.querySelector('button')!);
expect(onSearch).toHaveBeenCalledTimes(1);
expect(onSearch).toHaveBeenCalledWith(
'search text',
expect.anything(),
// FIXME: should use following code
// expect.objectContaining({
// type: 'click',
// preventDefault: expect.any(Function),
// }),
);
expect(onSearch).toHaveBeenCalledWith('search text', expect.anything());
expect(onButtonClick).toHaveBeenCalledTimes(1);
});
it('should trigger onSearch when press enter', () => {
const onSearch = jest.fn();
const { container } = render(<Search defaultValue="search text" onSearch={onSearch} />);
fireEvent.keyDown(container.querySelector('input'), { key: 'Enter', keyCode: 13 });
fireEvent.keyDown(container.querySelector('input')!, { key: 'Enter', keyCode: 13 });
expect(onSearch).toHaveBeenCalledTimes(1);
expect(onSearch).toHaveBeenCalledWith(
'search text',
expect.anything(),
// FIXME: should use following code
// expect.objectContaining({
// type: 'keydown',
// preventDefault: expect.any(Function),
// }),
);
expect(onSearch).toHaveBeenCalledWith('search text', expect.anything());
});
// https://github.com/ant-design/ant-design/issues/34844
it('should not trigger onSearch when press enter using chinese inputting method', () => {
const onSearch = jest.fn();
const { container } = render(<Search defaultValue="search text" onSearch={onSearch} />);
fireEvent.compositionStart(container.querySelector('input'));
fireEvent.keyDown(container.querySelector('input'), { key: 'Enter', keyCode: 13 });
fireEvent.compositionStart(container.querySelector('input')!);
fireEvent.keyDown(container.querySelector('input')!, { key: 'Enter', keyCode: 13 });
expect(onSearch).not.toHaveBeenCalled();
fireEvent.compositionEnd(container.querySelector('input'));
fireEvent.keyDown(container.querySelector('input'), { key: 'Enter', keyCode: 13 });
fireEvent.compositionEnd(container.querySelector('input')!);
fireEvent.keyDown(container.querySelector('input')!, { key: 'Enter', keyCode: 13 });
expect(onSearch).toHaveBeenCalledTimes(1);
expect(onSearch).toHaveBeenCalledWith(
'search text',
expect.anything(),
// FIXME: should use following code
// expect.objectContaining({
// type: 'keydown',
// preventDefault: expect.any(Function),
// }),
);
expect(onSearch).toHaveBeenCalledWith('search text', expect.anything());
});
// https://github.com/ant-design/ant-design/issues/14785
@ -204,7 +149,7 @@ describe('Input.Search', () => {
const { container } = render(
<Search allowClear defaultValue="value" onSearch={onSearch} onChange={onChange} />,
);
fireEvent.click(container.querySelector('.ant-input-clear-icon'));
fireEvent.click(container.querySelector('.ant-input-clear-icon')!);
expect(onSearch).toHaveBeenLastCalledWith('', expect.anything());
expect(onChange).toHaveBeenCalled();
});
@ -236,13 +181,13 @@ describe('Input.Search', () => {
});
it('should prevent search button mousedown event', () => {
const ref = React.createRef();
const ref = React.createRef<InputRef>();
const { container } = render(<Search ref={ref} enterButton="button text" />, {
container: document.body,
});
ref.current.focus();
ref.current?.focus();
expect(document.activeElement).toBe(container.querySelector('input'));
fireEvent.mouseDown(container.querySelector('button'));
fireEvent.mouseDown(container.querySelector('button')!);
expect(document.activeElement).toBe(container.querySelector('input'));
});
@ -250,7 +195,7 @@ describe('Input.Search', () => {
const ref = jest.fn();
const { container } = render(<Search ref={ref} enterButton />);
expect(() => {
fireEvent.mouseDown(container.querySelector('button'));
fireEvent.mouseDown(container.querySelector('button')!);
}).not.toThrow();
});
@ -258,10 +203,10 @@ describe('Input.Search', () => {
it('Search with allowClear should have one className only', () => {
const { container } = render(<Search allowClear className="className" />);
expect(
container.querySelector('.ant-input-group-wrapper').classList.contains('className'),
container.querySelector('.ant-input-group-wrapper')?.classList.contains('className'),
).toBe(true);
expect(
container.querySelector('.ant-input-affix-wrapper').classList.contains('className'),
container.querySelector('.ant-input-affix-wrapper')?.classList.contains('className'),
).toBe(false);
});
});

View File

@ -1,8 +1,11 @@
import { spyElementPrototypes } from 'rc-util/lib/test/domHook';
import type { ChangeEventHandler, TextareaHTMLAttributes } from 'react';
import React, { useState } from 'react';
import Input from '..';
import focusTest from '../../../tests/shared/focusTest';
import type { RenderOptions } from '../../../tests/utils';
import { fireEvent, render, sleep, triggerResize } from '../../../tests/utils';
import type { TextAreaRef } from '../TextArea';
const { TextArea } = Input;
@ -12,27 +15,23 @@ describe('TextArea', () => {
const originalGetComputedStyle = window.getComputedStyle;
beforeAll(() => {
Object.defineProperty(window, 'getComputedStyle', {
value: node => ({
getPropertyValue: prop => {
if (prop === 'box-sizing') {
return originalGetComputedStyle(node)[prop] || 'border-box';
}
return originalGetComputedStyle(node)[prop];
},
value: (node: Element) => ({
getPropertyValue: (prop: PropertyKey) =>
prop === 'box-sizing'
? originalGetComputedStyle(node)[prop as unknown as number] || 'border-box'
: originalGetComputedStyle(node)[prop as unknown as number],
}),
});
});
afterAll(() => {
Object.defineProperty(window, 'getComputedStyle', {
value: originalGetComputedStyle,
});
Object.defineProperty(window, 'getComputedStyle', { value: originalGetComputedStyle });
});
it('should auto calculate height according to content length', async () => {
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
const ref = React.createRef();
const ref = React.createRef<TextAreaRef>();
const genTextArea = (props = {}) => (
<TextArea
@ -47,7 +46,7 @@ describe('TextArea', () => {
const { container, rerender } = render(genTextArea());
const mockFunc = jest.spyOn(ref.current.resizableTextArea, 'resizeTextarea');
const mockFunc = jest.spyOn(ref.current?.resizableTextArea!, 'resizeTextarea');
rerender(genTextArea({ value: '1111\n2222\n3333' }));
// wrapper.setProps({ value: '1111\n2222\n3333' });
@ -59,7 +58,7 @@ describe('TextArea', () => {
await sleep(0);
expect(mockFunc).toHaveBeenCalledTimes(2);
expect(container.querySelector('textarea').style.overflow).toBeFalsy();
expect(container.querySelector('textarea')?.style.overflow).toBeFalsy();
expect(errorSpy).not.toHaveBeenCalled();
errorSpy.mockRestore();
@ -72,12 +71,12 @@ describe('TextArea', () => {
<TextArea onKeyDown={fakeHandleKeyDown} onPressEnter={fakeHandlePressEnter} />,
);
/** KeyCode 65 is A */
fireEvent.keyDown(container.querySelector('textarea'), { keyCode: 65 });
fireEvent.keyDown(container.querySelector('textarea')!, { keyCode: 65 });
expect(fakeHandleKeyDown).toHaveBeenCalledTimes(1);
expect(fakeHandlePressEnter).toHaveBeenCalledTimes(0);
/** KeyCode 13 is Enter */
fireEvent.keyDown(container.querySelector('textarea'), { keyCode: 13 });
fireEvent.keyDown(container.querySelector('textarea')!, { keyCode: 13 });
expect(fakeHandleKeyDown).toHaveBeenCalledTimes(2);
expect(fakeHandlePressEnter).toHaveBeenCalledTimes(1);
});
@ -95,7 +94,7 @@ describe('TextArea', () => {
it('maxLength should not block control', () => {
const { container } = render(<TextArea maxLength={1} value="light" />);
expect(container.querySelector('textarea').value).toEqual('light');
expect(container.querySelector('textarea')?.value).toEqual('light');
});
it('should limit correctly when in control', () => {
@ -105,21 +104,21 @@ describe('TextArea', () => {
};
const { container } = render(<Demo />);
fireEvent.change(container.querySelector('textarea'), { target: { value: 'light' } });
fireEvent.change(container.querySelector('textarea')!, { target: { value: 'light' } });
expect(container.querySelector('textarea').value).toEqual('l');
expect(container.querySelector('textarea')?.value).toEqual('l');
});
it('should exceed maxLength when use IME', () => {
const onChange = jest.fn();
const { container } = render(<TextArea maxLength={1} onChange={onChange} />);
fireEvent.compositionStart(container.querySelector('textarea'));
fireEvent.change(container.querySelector('textarea'), { target: { value: 'zhu' } });
fireEvent.compositionEnd(container.querySelector('textarea'), {
fireEvent.compositionStart(container.querySelector('textarea')!);
fireEvent.change(container.querySelector('textarea')!, { target: { value: 'zhu' } });
fireEvent.compositionEnd(container.querySelector('textarea')!, {
currentTarget: { value: '竹' },
});
fireEvent.change(container.querySelector('textarea'), { target: { value: '竹' } });
fireEvent.change(container.querySelector('textarea')!, { target: { value: '竹' } });
expect(onChange).toHaveBeenLastCalledWith(
expect.objectContaining({ target: expect.objectContaining({ value: '竹' }) }),
@ -132,13 +131,13 @@ describe('TextArea', () => {
const { container } = render(
<TextArea maxLength={6} defaultValue="123456" onChange={onChange} />,
);
fireEvent.change(container.querySelector('textarea'), {
fireEvent.change(container.querySelector('textarea')!, {
target: { selectionStart: 1, value: 'w123456' },
});
fireEvent.change(container.querySelector('textarea'), {
fireEvent.change(container.querySelector('textarea')!, {
target: { selectionStart: 3, value: 'w123456' },
});
expect(container.querySelector('textarea').value).toBe('123456');
expect(container.querySelector('textarea')?.value).toBe('123456');
});
// 拼音输入
@ -148,21 +147,21 @@ describe('TextArea', () => {
const { container } = render(
<TextArea maxLength={6} defaultValue="1234" onChange={onChange} />,
);
fireEvent.change(container.querySelector('textarea'), {
fireEvent.change(container.querySelector('textarea')!, {
target: { selectionStart: 4, value: '1234' },
});
fireEvent.compositionStart(container.querySelector('textarea'));
fireEvent.compositionStart(container.querySelector('textarea')!);
fireEvent.change(container.querySelector('textarea'), {
fireEvent.change(container.querySelector('textarea')!, {
target: { selectionStart: 9, value: '1234z z z' },
});
fireEvent.change(container.querySelector('textarea'), {
fireEvent.change(container.querySelector('textarea')!, {
target: { selectionStart: 7, value: '1234组织者' },
});
fireEvent.compositionEnd(container.querySelector('textarea'));
fireEvent.compositionEnd(container.querySelector('textarea')!);
expect(container.querySelector('textarea').value).toBe('1234组织');
expect(container.querySelector('textarea')?.value).toBe('1234组织');
});
// 2. 光标位于中间或开头且当前字符数未达到6个若选中的字符 + 原字符的长度超过6个则显示原有字符
@ -171,29 +170,29 @@ describe('TextArea', () => {
const { container } = render(
<TextArea maxLength={6} defaultValue="1234" onChange={onChange} />,
);
fireEvent.change(container.querySelector('textarea'), {
fireEvent.change(container.querySelector('textarea')!, {
target: { selectionStart: 2, value: '1234' },
});
fireEvent.compositionStart(container.querySelector('textarea'));
fireEvent.compositionStart(container.querySelector('textarea')!);
fireEvent.change(container.querySelector('textarea'), {
fireEvent.change(container.querySelector('textarea')!, {
target: { selectionStart: 2, value: '12z z z34' },
});
fireEvent.change(container.querySelector('textarea'), {
fireEvent.change(container.querySelector('textarea')!, {
target: { selectionStart: 5, value: '12组织者34' },
});
fireEvent.compositionEnd(container.querySelector('textarea'));
fireEvent.compositionEnd(container.querySelector('textarea')!);
expect(container.querySelector('textarea').value).toBe('1234');
expect(container.querySelector('textarea')?.value).toBe('1234');
});
});
it('when prop value not in this.props, resizeTextarea should be called', async () => {
const ref = React.createRef();
const ref = React.createRef<TextAreaRef>();
const { container } = render(<TextArea aria-label="textarea" ref={ref} />);
const resizeTextarea = jest.spyOn(ref.current.resizableTextArea, 'resizeTextarea');
fireEvent.change(container.querySelector('textarea'), { target: { value: 'test' } });
const resizeTextarea = jest.spyOn(ref.current?.resizableTextArea!, 'resizeTextarea');
fireEvent.change(container.querySelector('textarea')!, { target: { value: 'test' } });
expect(resizeTextarea).toHaveBeenCalled();
});
@ -203,7 +202,7 @@ describe('TextArea', () => {
const { container } = render(
<TextArea onPressEnter={onPressEnter} onKeyDown={onKeyDown} aria-label="textarea" />,
);
fireEvent.keyDown(container.querySelector('textarea'), { keyCode: 13 });
fireEvent.keyDown(container.querySelector('textarea')!, { keyCode: 13 });
expect(onPressEnter).toHaveBeenCalled();
expect(onKeyDown).toHaveBeenCalled();
@ -211,18 +210,15 @@ describe('TextArea', () => {
it('should trigger onResize', async () => {
const onResize = jest.fn();
const ref = React.createRef();
const ref = React.createRef<TextAreaRef>();
render(<TextArea ref={ref} onResize={onResize} autoSize />);
await sleep(100);
const target = ref.current.resizableTextArea.textArea;
const target = ref.current?.resizableTextArea?.textArea!;
triggerResize(target);
await Promise.resolve();
expect(onResize).toHaveBeenCalledWith(
expect.objectContaining({
width: expect.any(Number),
height: expect.any(Number),
}),
expect.objectContaining({ width: expect.any(Number), height: expect.any(Number) }),
);
});
@ -233,24 +229,24 @@ describe('TextArea', () => {
);
inputRerender(<Input value={undefined} />);
textareaRerender(<TextArea value={undefined} />);
expect(textareaContainer.querySelector('textarea').value).toBe(
inputContainer.querySelector('input').value,
expect(textareaContainer.querySelector('textarea')?.value).toBe(
inputContainer.querySelector('input')?.value,
);
});
describe('should support showCount', () => {
it('maxLength', () => {
const { container } = render(<TextArea maxLength={5} showCount value="12345" />);
expect(container.querySelector('textarea').value).toBe('12345');
expect(container.querySelector('.ant-input-textarea').getAttribute('data-count')).toBe(
expect(container.querySelector('textarea')?.value).toBe('12345');
expect(container.querySelector('.ant-input-textarea')?.getAttribute('data-count')).toBe(
'5 / 5',
);
});
it('control exceed maxLength', () => {
const { container } = render(<TextArea maxLength={5} showCount value="12345678" />);
expect(container.querySelector('textarea').value).toBe('12345678');
expect(container.querySelector('.ant-input-textarea').getAttribute('data-count')).toBe(
expect(container.querySelector('textarea')?.value).toBe('12345678');
expect(container.querySelector('.ant-input-textarea')?.getAttribute('data-count')).toBe(
'8 / 5',
);
});
@ -258,29 +254,29 @@ describe('TextArea', () => {
describe('emoji', () => {
it('should minimize value between emoji length and maxLength', () => {
const { container } = render(<TextArea maxLength={1} showCount value="👀" />);
expect(container.querySelector('textarea').value).toBe('👀');
expect(container.querySelector('.ant-input-textarea').getAttribute('data-count')).toBe(
expect(container.querySelector('textarea')?.value).toBe('👀');
expect(container.querySelector('.ant-input-textarea')?.getAttribute('data-count')).toBe(
'1 / 1',
);
// fix: 当 maxLength 长度为 2 的时候,输入 emoji 之后 showCount 会显示 1/2但是不能再输入了
// zombieJ: 逻辑统一了emoji 现在也可以正确计数了
const { container: container1 } = render(<TextArea maxLength={2} showCount value="👀" />);
expect(container1.querySelector('.ant-input-textarea').getAttribute('data-count')).toBe(
expect(container1.querySelector('.ant-input-textarea')?.getAttribute('data-count')).toBe(
'1 / 2',
);
});
it('defaultValue should slice', () => {
const { container } = render(<TextArea maxLength={1} defaultValue="🧐cut" />);
expect(container.querySelector('textarea').value).toBe('🧐');
expect(container.querySelector('textarea')?.value).toBe('🧐');
});
// 修改TextArea value截取规则后新增单测
it('slice emoji', () => {
const { container } = render(<TextArea maxLength={5} showCount value="1234😂" />);
expect(container.querySelector('textarea').value).toBe('1234😂');
expect(container.querySelector('.ant-input-textarea').getAttribute('data-count')).toBe(
expect(container.querySelector('textarea')?.value).toBe('1234😂');
expect(container.querySelector('.ant-input-textarea')?.getAttribute('data-count')).toBe(
'5 / 5',
);
});
@ -292,12 +288,12 @@ describe('TextArea', () => {
);
// Outer
expect(container.querySelector('div').classList.contains('bamboo')).toBeTruthy();
expect(container.querySelector('div').style.background).toEqual('red');
expect(container.querySelector('div')?.classList.contains('bamboo')).toBeTruthy();
expect(container.querySelector('div')?.style.background).toEqual('red');
// Inner
expect(container.querySelector('.ant-input').classList.contains('bamboo')).toBeFalsy();
expect(container.querySelector('.ant-input').style.background).toBeFalsy();
expect(container.querySelector('.ant-input')?.classList.contains('bamboo')).toBeFalsy();
expect(container.querySelector<HTMLDivElement>('.ant-input')?.style.background).toBeFalsy();
});
it('count formatter', () => {
@ -310,8 +306,8 @@ describe('TextArea', () => {
value="12345"
/>,
);
expect(container.querySelector('textarea').value).toBe('12345');
expect(container.querySelector('.ant-input-textarea').getAttribute('data-count')).toBe(
expect(container.querySelector('textarea')?.value).toBe('12345');
expect(container.querySelector('.ant-input-textarea')?.getAttribute('data-count')).toBe(
'12345, 5, 5',
);
});
@ -319,36 +315,40 @@ describe('TextArea', () => {
it('should support size', async () => {
const { asFragment, container } = render(<TextArea size="large" />);
expect(container.querySelector('textarea').classList.contains('ant-input-lg')).toBe(true);
expect(container.querySelector('textarea')?.classList.contains('ant-input-lg')).toBe(true);
expect(asFragment().firstChild).toMatchSnapshot();
});
it('set mouse cursor position', () => {
const defaultValue = '11111';
const valLength = defaultValue.length;
const ref = React.createRef();
const ref = React.createRef<TextAreaRef>();
render(<TextArea autoFocus ref={ref} defaultValue={defaultValue} />);
ref.current.resizableTextArea.textArea.setSelectionRange(valLength, valLength);
expect(ref.current.resizableTextArea.textArea.selectionStart).toEqual(5);
expect(ref.current.resizableTextArea.textArea.selectionEnd).toEqual(5);
ref.current?.resizableTextArea?.textArea.setSelectionRange(valLength, valLength);
expect(ref.current?.resizableTextArea?.textArea.selectionStart).toEqual(5);
expect(ref.current?.resizableTextArea?.textArea.selectionEnd).toEqual(5);
});
});
describe('TextArea allowClear', () => {
it('should change type when click', () => {
const { asFragment, container } = render(<TextArea allowClear />);
fireEvent.change(container.querySelector('textarea'), { target: { value: '111' } });
expect(container.querySelector('textarea').value).toEqual('111');
fireEvent.change(container.querySelector('textarea')!, { target: { value: '111' } });
expect(container.querySelector('textarea')?.value).toEqual('111');
expect(asFragment().firstChild).toMatchSnapshot();
fireEvent.click(container.querySelector('.ant-input-clear-icon'));
fireEvent.click(container.querySelector('.ant-input-clear-icon')!);
expect(asFragment().firstChild).toMatchSnapshot();
expect(container.querySelector('textarea').value).toEqual('');
expect(container.querySelector('textarea')?.value).toEqual('');
});
it('should not show icon if value is undefined, null or empty string', () => {
const wrappers = [null, undefined, ''].map(val => render(<TextArea allowClear value={val} />));
const wrappers = [null, undefined, ''].map(val =>
render(
<TextArea allowClear value={val as TextareaHTMLAttributes<HTMLTextAreaElement>['value']} />,
),
);
wrappers.forEach(({ asFragment, container }) => {
expect(container.querySelector('textarea').value).toEqual('');
expect(container.querySelector('textarea')?.value).toEqual('');
expect(container.querySelector('.ant-input-clear-icon-hidden')).toBeTruthy();
expect(asFragment().firstChild).toMatchSnapshot();
});
@ -356,10 +356,15 @@ describe('TextArea allowClear', () => {
it('should not show icon if defaultValue is undefined, null or empty string', () => {
const wrappers = [null, undefined, ''].map(val =>
render(<TextArea allowClear defaultValue={val} />),
render(
<TextArea
allowClear
defaultValue={val as TextareaHTMLAttributes<HTMLTextAreaElement>['value']}
/>,
),
);
wrappers.forEach(({ asFragment, container }) => {
expect(container.querySelector('textarea').value).toEqual('');
expect(container.querySelector('textarea')?.value).toEqual('');
expect(container.querySelector('.ant-input-clear-icon-hidden')).toBeTruthy();
expect(asFragment().firstChild).toMatchSnapshot();
});
@ -368,36 +373,36 @@ describe('TextArea allowClear', () => {
it('should trigger event correctly', () => {
let argumentEventObjectType;
let argumentEventObjectValue;
const onChange = e => {
const onChange: ChangeEventHandler<HTMLTextAreaElement> = e => {
argumentEventObjectType = e.type;
argumentEventObjectValue = e.target.value;
};
const { container } = render(<TextArea allowClear defaultValue="111" onChange={onChange} />);
fireEvent.click(container.querySelector('.ant-input-clear-icon'));
fireEvent.click(container.querySelector('.ant-input-clear-icon')!);
expect(argumentEventObjectType).toBe('click');
expect(argumentEventObjectValue).toBe('');
expect(container.querySelector('textarea').value).toBe('');
expect(container.querySelector('textarea')?.value).toBe('');
});
it('should trigger event correctly on controlled mode', () => {
let argumentEventObjectType;
let argumentEventObjectValue;
const onChange = e => {
const onChange: ChangeEventHandler<HTMLTextAreaElement> = e => {
argumentEventObjectType = e.type;
argumentEventObjectValue = e.target.value;
};
const { container } = render(<TextArea allowClear value="111" onChange={onChange} />);
fireEvent.click(container.querySelector('.ant-input-clear-icon'));
fireEvent.click(container.querySelector('.ant-input-clear-icon')!);
expect(argumentEventObjectType).toBe('click');
expect(argumentEventObjectValue).toBe('');
expect(container.querySelector('textarea').value).toBe('111');
expect(container.querySelector('textarea')?.value).toBe('111');
});
it('should focus textarea after clear', () => {
const { container, unmount } = render(<TextArea allowClear defaultValue="111" />, {
container: document.body,
});
fireEvent.click(container.querySelector('.ant-input-clear-icon'));
fireEvent.click(container.querySelector('.ant-input-clear-icon')!);
expect(document.activeElement).toBe(container.querySelector('textarea'));
unmount();
});
@ -409,30 +414,30 @@ describe('TextArea allowClear', () => {
it('not block input when `value` is undefined', () => {
const { container, rerender } = render(<Input value={undefined} />);
fireEvent.change(container.querySelector('input'), { target: { value: 'Bamboo' } });
expect(container.querySelector('input').value).toEqual('Bamboo');
fireEvent.change(container.querySelector('input')!, { target: { value: 'Bamboo' } });
expect(container.querySelector('input')?.value).toEqual('Bamboo');
// Controlled
rerender(<Input value="Light" />);
fireEvent.change(container.querySelector('input'), { target: { value: 'Bamboo' } });
expect(container.querySelector('input').value).toEqual('Light');
fireEvent.change(container.querySelector('input')!, { target: { value: 'Bamboo' } });
expect(container.querySelector('input')?.value).toEqual('Light');
});
it('scroll to bottom when autoSize', async () => {
const ref = React.createRef();
const ref = React.createRef<TextAreaRef>();
const { container, unmount } = render(<Input.TextArea ref={ref} autoSize />, {
container: document.body,
legacyRoot: true,
});
fireEvent.focus(container.querySelector('textarea'));
container.querySelector('textarea').focus();
} as RenderOptions);
fireEvent.focus(container.querySelector('textarea')!);
container.querySelector('textarea')?.focus();
const setSelectionRangeFn = jest.spyOn(
container.querySelector('textarea'),
container.querySelector('textarea')!,
'setSelectionRange',
);
fireEvent.input(container.querySelector('textarea'), { target: { value: '\n1' } });
const target = ref.current.resizableTextArea.textArea;
fireEvent.input(container.querySelector('textarea')!, { target: { value: '\n1' } });
const target = ref.current?.resizableTextArea?.textArea!;
triggerResize(target);
await sleep(100);
expect(setSelectionRangeFn).toHaveBeenCalled();
@ -442,7 +447,7 @@ describe('TextArea allowClear', () => {
// https://github.com/ant-design/ant-design/issues/26308
it('should display defaultValue when value is undefined', () => {
const { container } = render(<Input.TextArea defaultValue="Light" value={undefined} />);
expect(container.querySelector('textarea').value).toBe('Light');
expect(container.querySelector('textarea')?.value).toBe('Light');
});
it('onChange event should return HTMLTextAreaElement', () => {
@ -451,25 +456,23 @@ describe('TextArea allowClear', () => {
function isNativeElement() {
expect(onChange).toHaveBeenCalledWith(
expect.objectContaining({
target: expect.any(HTMLTextAreaElement),
}),
expect.objectContaining({ target: expect.any(HTMLTextAreaElement) }),
);
onChange.mockReset();
}
// Change
fireEvent.change(container.querySelector('textarea'), { target: { value: 'bamboo' } });
fireEvent.change(container.querySelector('textarea')!, { target: { value: 'bamboo' } });
isNativeElement();
// Composition End
fireEvent.change(container.querySelector('textarea'), { target: { value: 'light' } });
fireEvent.compositionEnd(container.querySelector('textarea'));
fireEvent.change(container.querySelector('textarea')!, { target: { value: 'light' } });
fireEvent.compositionEnd(container.querySelector('textarea')!);
isNativeElement();
// Reset
fireEvent.click(container.querySelector('.ant-input-clear-icon'));
fireEvent.click(container.querySelector('.ant-input-clear-icon')!);
isNativeElement();
});
@ -489,12 +492,12 @@ describe('TextArea allowClear', () => {
};
const { container, unmount } = render(<App />);
container.querySelector('textarea').focus();
fireEvent.change(container.querySelector('textarea'), { target: { value: '111' } });
expect(container.querySelector('textarea').value).toEqual('111');
container.querySelector('textarea')?.focus();
fireEvent.change(container.querySelector('textarea')!, { target: { value: '111' } });
expect(container.querySelector('textarea')?.value).toEqual('111');
fireEvent.click(container.querySelector('.ant-input-clear-icon'));
expect(container.querySelector('textarea').value).toEqual('');
fireEvent.click(container.querySelector('.ant-input-clear-icon')!);
expect(container.querySelector('textarea')?.value).toEqual('');
unmount();
});
@ -508,12 +511,12 @@ describe('TextArea allowClear', () => {
container: document.body,
},
);
container.querySelector('textarea').focus();
fireEvent.mouseDown(container.querySelector('.ant-input-clear-icon'));
fireEvent.click(container.querySelector('.ant-input-clear-icon'));
fireEvent.mouseUp(container.querySelector('.ant-input-clear-icon'));
fireEvent.focus(container.querySelector('.ant-input-clear-icon'));
fireEvent.click(container.querySelector('.ant-input-clear-icon'));
container.querySelector('textarea')?.focus();
fireEvent.mouseDown(container.querySelector('.ant-input-clear-icon')!);
fireEvent.click(container.querySelector('.ant-input-clear-icon')!);
fireEvent.mouseUp(container.querySelector('.ant-input-clear-icon')!);
fireEvent.focus(container.querySelector('.ant-input-clear-icon')!);
fireEvent.click(container.querySelector('.ant-input-clear-icon')!);
expect(onBlur).not.toHaveBeenCalled();
unmount();
});
@ -522,16 +525,20 @@ describe('TextArea allowClear', () => {
const { container, unmount } = render(<TextArea allowClear defaultValue="111" />, {
container: document.body,
});
fireEvent.click(container.querySelector('.ant-input-clear-icon'));
fireEvent.click(container.querySelector('.ant-input-clear-icon')!);
expect(document.activeElement).toBe(container.querySelector('textarea'));
unmount();
});
it('should display boolean value as string', () => {
const { container, rerender } = render(<TextArea value />);
expect(container.querySelector('textarea').value).toBe('true');
rerender(<TextArea value={false} />);
expect(container.querySelector('textarea').value).toBe('false');
const { container, rerender } = render(
<TextArea value={true as unknown as TextareaHTMLAttributes<HTMLTextAreaElement>['value']} />,
);
expect(container.querySelector('textarea')?.value).toBe('true');
rerender(
<TextArea value={false as unknown as TextareaHTMLAttributes<HTMLTextAreaElement>['value']} />,
);
expect(container.querySelector('textarea')?.value).toBe('false');
});
it('should focus when clearBtn is clicked in controlled case', () => {
@ -541,18 +548,17 @@ describe('TextArea allowClear', () => {
focus: handleFocus,
});
const Demo = () => {
const Demo: React.FC = () => {
const [value, setValue] = React.useState('');
return <Input.TextArea allowClear value={value} onChange={e => setValue(e.target.value)} />;
};
const { container } = render(<Demo />);
fireEvent.change(container.querySelector('textarea'), { target: { value: 'test' } });
fireEvent.change(container.querySelector('textarea')!, { target: { value: 'test' } });
expect(container.querySelector('.ant-input-clear-icon')?.className).not.toContain(
'ant-input-clear-icon-hidden',
);
fireEvent.click(container.querySelector('.ant-input-clear-icon'));
fireEvent.click(container.querySelector('.ant-input-clear-icon')!);
expect(handleFocus).toHaveBeenCalledTimes(1);
textareaSpy.mockRestore();

View File

@ -1,9 +1,9 @@
import React, { useState } from 'react';
import { act } from 'react-dom/test-utils';
import { UserOutlined } from '@ant-design/icons';
import Layout from '..';
import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
import Icon from '../../icon';
import Menu from '../../menu';
import { fireEvent, render } from '../../../tests/utils';
@ -31,14 +31,14 @@ describe('Layout', () => {
<Content>Content</Content>
</Layout>,
);
expect(container.querySelector('.ant-layout').className.includes('ant-layout-has-sider')).toBe(
expect(container.querySelector('.ant-layout')?.className.includes('ant-layout-has-sider')).toBe(
true,
);
unmount();
});
it('umount from multiple siders', async () => {
const App = () => {
const App: React.FC = () => {
const [hide1, setHide1] = useState(false);
const [hide2, setHide2] = useState(false);
return (
@ -57,15 +57,15 @@ describe('Layout', () => {
);
};
const { container } = render(<App />);
expect(container.querySelector('.ant-layout').className.includes('ant-layout-has-sider')).toBe(
expect(container.querySelector('.ant-layout')?.className.includes('ant-layout-has-sider')).toBe(
true,
);
fireEvent.click(container.querySelectorAll('button')[0]);
expect(container.querySelector('.ant-layout').className.includes('ant-layout-has-sider')).toBe(
expect(container.querySelector('.ant-layout')?.className.includes('ant-layout-has-sider')).toBe(
true,
);
fireEvent.click(container.querySelectorAll('button')[1]);
expect(container.querySelector('.ant-layout').className.includes('ant-layout-has-sider')).toBe(
expect(container.querySelector('.ant-layout')?.className.includes('ant-layout-has-sider')).toBe(
false,
);
});
@ -79,7 +79,7 @@ describe('Layout', () => {
<Content>Content</Content>
</Layout>,
);
expect(container.querySelector('.ant-layout').className.includes('ant-layout-has-sider')).toBe(
expect(container.querySelector('.ant-layout')?.className.includes('ant-layout-has-sider')).toBe(
true,
);
});
@ -96,7 +96,7 @@ describe('Layout', () => {
expect(
container
.querySelector('.ant-layout-sider')
.className.includes('ant-layout-sider-has-trigger'),
?.className.includes('ant-layout-sider-has-trigger'),
).toBe(true);
});
@ -109,8 +109,8 @@ describe('Layout', () => {
<Content>Content</Content>
</Layout>,
);
expect(container.querySelector('.ant-layout-sider').style.width).toBe('50%');
expect(container.querySelector('.ant-layout-sider').style.flex).toBe('0 0 50%');
expect(container.querySelector<HTMLElement>('.ant-layout-sider')?.style.width).toBe('50%');
expect(container.querySelector<HTMLElement>('.ant-layout-sider')?.style.flex).toBe('0 0 50%');
});
describe('zeroWidth', () => {
@ -126,7 +126,7 @@ describe('Layout', () => {
expect(
container
.querySelector('.ant-layout-sider')
.className.includes('ant-layout-sider-zero-width'),
?.className.includes('ant-layout-sider-zero-width'),
).toBe(true);
});
@ -144,12 +144,12 @@ describe('Layout', () => {
);
onCollapse.mockReset();
fireEvent.click(container.querySelector('.ant-layout-sider-zero-width-trigger'));
fireEvent.click(container.querySelector('.ant-layout-sider-zero-width-trigger')!);
expect(onCollapse).toHaveBeenCalledTimes(1);
});
it('controlled', () => {
const Demo = () => {
const Demo: React.FC = () => {
const [collapsed, setCollapsed] = React.useState(true);
return (
@ -170,7 +170,7 @@ describe('Layout', () => {
const { container } = render(<Demo />);
expect(container.querySelector('.ant-layout-sider-collapsed')).toBeTruthy();
fireEvent.click(container.querySelector('.ant-layout-sider-zero-width-trigger'));
fireEvent.click(container.querySelector('.ant-layout-sider-zero-width-trigger')!);
expect(container.querySelector('.ant-layout-sider-collapsed')).toBeFalsy();
});
});
@ -179,14 +179,14 @@ describe('Layout', () => {
it('detect ant-layout-sider-dark as default theme', async () => {
const { container } = render(<Sider>Sider</Sider>);
expect(
container.querySelector('.ant-layout-sider').className.includes('ant-layout-sider-dark'),
container.querySelector('.ant-layout-sider')?.className.includes('ant-layout-sider-dark'),
).toBe(true);
});
it('detect ant-layout-sider-light when set light theme', async () => {
const { container } = render(<Sider theme="light">Sider</Sider>);
expect(
container.querySelector('.ant-layout-sider').className.includes('ant-layout-sider-light'),
container.querySelector('.ant-layout-sider')?.className.includes('ant-layout-sider-light'),
).toBe(true);
});
@ -208,7 +208,7 @@ describe('Layout', () => {
<Sider>Sider</Sider>
</Layout>,
);
expect(container.querySelector('.ant-layout').className.includes('ant-layout-has-sider')).toBe(
expect(container.querySelector('.ant-layout')?.className.includes('ant-layout-has-sider')).toBe(
false,
);
});
@ -219,30 +219,29 @@ describe('Layout', () => {
<Sider collapsible collapsed={false}>
<Menu mode="inline">
<Menu.Item key="1">
<Icon type="user" />
<UserOutlined />
<span>Light</span>
</Menu.Item>
</Menu>
</Sider>,
);
fireEvent.mouseEnter(container.querySelector('.ant-menu-item'));
fireEvent.mouseEnter(container.querySelector('.ant-menu-item')!);
act(() => {
jest.runAllTimers();
});
expect(container.querySelectorAll('.ant-tooltip-inner').length).toBeFalsy();
rerender(
<Sider collapsible collapsed>
<Menu mode="inline">
<Menu.Item key="1">
<Icon type="user" />
<UserOutlined />
<span>Light</span>
</Menu.Item>
</Menu>
</Sider>,
);
fireEvent.mouseEnter(container.querySelector('.ant-menu-item'));
fireEvent.mouseEnter(container.querySelector('.ant-menu-item')!);
act(() => {
jest.runAllTimers();
});
@ -290,14 +289,15 @@ describe('Sider', () => {
<Sider collapsedWidth={0} collapsible zeroWidthTriggerStyle={{ background: '#F96' }}>
<Menu theme="dark" mode="inline" defaultSelectedKeys={['1']}>
<Menu.Item key="1">
<Icon type="user" />
<UserOutlined />
<span>nav 1</span>
</Menu.Item>
</Menu>
</Sider>,
);
expect(
container.querySelector('.ant-layout-sider-zero-width-trigger').style.background,
container.querySelector<HTMLDivElement>('.ant-layout-sider-zero-width-trigger')?.style
.background,
).toEqual('rgb(255, 153, 102)');
});
@ -306,24 +306,23 @@ describe('Sider', () => {
<Sider collapsedWidth={0} collapsible trigger={<span className="my-trigger" />}>
<Menu theme="dark" mode="inline" defaultSelectedKeys={['1']}>
<Menu.Item key="1">
<Icon type="user" />
<UserOutlined />
<span>nav 1</span>
</Menu.Item>
</Menu>
</Sider>,
);
expect(
container.querySelector('.ant-layout-sider-zero-width-trigger').querySelector('.my-trigger'),
container.querySelector('.ant-layout-sider-zero-width-trigger')?.querySelector('.my-trigger'),
).toBeTruthy();
});
['Layout', 'Header', 'Footer', 'Sider'].forEach(tag => {
const ComponentMap = { Layout, Header, Footer, Sider };
it(`should get ${tag} element from ref`, () => {
const ref = React.createRef();
const ref = React.createRef<any>();
const onSelect = jest.fn();
const Component = ComponentMap[tag];
const Component = ComponentMap[tag as keyof typeof ComponentMap];
render(
<Component onSelect={onSelect} ref={ref}>
{tag}

View File

@ -49,7 +49,7 @@ describe('List Item Layout', () => {
/>,
);
expect(
wrapper.querySelector('.ant-list-item').classList.contains('ant-list-item-no-flex'),
wrapper.querySelector('.ant-list-item')?.classList.contains('ant-list-item-no-flex'),
).toBe(false);
});
@ -188,17 +188,17 @@ describe('List Item Layout', () => {
});
it('should ref', () => {
const ref = React.createRef();
const ref = React.createRef<HTMLElement>();
render(<List.Item ref={ref}>Item</List.Item>);
expect(ref.current).toHaveClass('ant-list-item');
});
it('should grid ref', () => {
const ref = React.createRef();
const ref = React.createRef<HTMLElement>();
render(
<List grid>
<List grid={{}}>
<List.Item ref={ref}>Item</List.Item>,
</List>,
);

View File

@ -1,4 +1,5 @@
import React from 'react';
import type { ListProps } from '..';
import List from '..';
import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
@ -13,12 +14,12 @@ describe('List', () => {
it('locale not passed to internal div', async () => {
const locale = { emptyText: 'Custom text' };
const renderItem = item => <List.Item>{item}</List.Item>;
const dataSource = [];
const renderItem: ListProps<any>['renderItem'] = item => <List.Item>{item}</List.Item>;
const dataSource: ListProps<any>['dataSource'] = [];
const { container } = render(
<List renderItem={renderItem} dataSource={dataSource} locale={locale} />,
);
expect(container.querySelector('div.ant-list').getAttribute('locale')).toBe(null);
expect(container.querySelector('div.ant-list')?.getAttribute('locale')).toBe(null);
});
});

View File

@ -6,9 +6,7 @@ import List from '..';
describe('List', () => {
it('renders empty loading', () => {
const loading = {
spinning: true,
};
const loading = { spinning: true };
const { container: wrapper } = render(
<List loading={loading} dataSource={[]} renderItem={() => <List.Item />} />,
);

View File

@ -1,10 +1,16 @@
import React from 'react';
import type { ListProps } from '..';
import List from '..';
import { fireEvent, render } from '../../../tests/utils';
import { noop } from '../../_util/warning';
interface DataSourceItem {
name: string;
key: React.Key;
}
describe('List.pagination', () => {
const data = [
const data: ListProps<DataSourceItem>['dataSource'] = [
{ key: 0, name: 'Jack' },
{ key: 1, name: 'Lucy' },
{ key: 2, name: 'Tom' },
@ -13,7 +19,7 @@ describe('List.pagination', () => {
const pagination = { className: 'my-page', pageSize: 2 };
function createList(props) {
function createList(props?: ListProps<DataSourceItem>) {
return (
<List
itemLayout="vertical"
@ -25,10 +31,10 @@ describe('List.pagination', () => {
);
}
function renderedNames(wrapper) {
function renderedNames(container: ReturnType<typeof render>['container']) {
return Array.prototype.map.call(
wrapper.querySelectorAll('.ant-list-item'),
row => row.textContent,
container.querySelectorAll('.ant-list-item'),
(row: HTMLDivElement) => row.textContent,
);
}
@ -39,12 +45,7 @@ describe('List.pagination', () => {
it('should not show pager if pagination.hideOnSinglePage is true and only 1 page', () => {
const { container: wrapper, rerender } = render(
createList({
pagination: {
pageSize: 3,
hideOnSinglePage: true,
},
}),
createList({ pagination: { pageSize: 3, hideOnSinglePage: true } }),
);
expect(wrapper.querySelectorAll('.ant-pagination')).toHaveLength(1);
rerender(createList({ pagination: { pageSize: 3, hideOnSinglePage: false } }));
@ -107,13 +108,13 @@ describe('List.pagination', () => {
expect(wrapper.querySelectorAll('.ant-pagination')).toHaveLength(1);
expect(wrapper.querySelectorAll('.ant-pagination-item')).toHaveLength(2);
fireEvent.click(wrapper.querySelector('.ant-pagination-item-2'));
fireEvent.click(wrapper.querySelector('.ant-pagination-item-2')!);
expect(renderedNames(wrapper)).toEqual(['Tom', 'Jerry']);
rerender(createList({ pagination: false }));
expect(wrapper.querySelectorAll('.ant-pagination')).toHaveLength(0);
rerender(createList({ pagination: true }));
rerender(createList({ pagination: true as ListProps<DataSourceItem>['pagination'] }));
expect(wrapper.querySelectorAll('.ant-pagination')).toHaveLength(1);
// Legacy code will make pageSize ping with 10, here we fixed to keep sync by current one
expect(wrapper.querySelectorAll('.ant-pagination-item')).toHaveLength(2);
@ -123,7 +124,7 @@ describe('List.pagination', () => {
// https://github.com/ant-design/ant-design/issues/5259
it('change to correct page when data source changes', () => {
const { container: wrapper, rerender } = render(createList({ pagination: { pageSize: 1 } }));
fireEvent.click(wrapper.querySelector('.ant-pagination-item-3'));
fireEvent.click(wrapper.querySelector('.ant-pagination-item-3')!);
rerender(createList({ dataSource: [data[0]] }));
expect(wrapper.querySelector('.ant-pagination-item-1')).toHaveClass(
'ant-pagination-item-active',
@ -134,20 +135,20 @@ describe('List.pagination', () => {
const { container: wrapper, rerender } = render(
createList({ pagination: { position: 'top' } }),
);
expect(wrapper.querySelector('.ant-list').querySelectorAll('.ant-pagination')).toHaveLength(1);
expect(wrapper.querySelector('.ant-list')?.querySelectorAll('.ant-pagination')).toHaveLength(1);
rerender(createList({ pagination: { position: 'bottom' } }));
expect(
wrapper.querySelector('.ant-list').lastElementChild.querySelectorAll('.ant-pagination'),
wrapper.querySelector('.ant-list')?.lastElementChild?.querySelectorAll('.ant-pagination'),
).toHaveLength(1);
rerender(createList({ pagination: { position: 'both' } }));
expect(wrapper.querySelectorAll('.ant-pagination')).toHaveLength(2);
expect(
wrapper.querySelector('.ant-list').firstElementChild.querySelectorAll('.ant-pagination'),
wrapper.querySelector('.ant-list')?.firstElementChild?.querySelectorAll('.ant-pagination'),
).toHaveLength(1);
expect(
wrapper.querySelector('.ant-list').lastElementChild.querySelectorAll('.ant-pagination'),
wrapper.querySelector('.ant-list')?.lastElementChild?.querySelectorAll('.ant-pagination'),
).toHaveLength(1);
});
@ -155,10 +156,10 @@ describe('List.pagination', () => {
const { container: wrapper } = render(createList({ pagination: { showSizeChanger: true } }));
expect(wrapper.querySelector('.ant-pagination')).toMatchSnapshot();
fireEvent.mouseDown(wrapper.querySelector('.ant-select-selector'));
fireEvent.mouseDown(wrapper.querySelector('.ant-select-selector')!);
fireEvent.click(wrapper.querySelectorAll('.ant-select-item-option')[2]);
fireEvent.mouseDown(wrapper.querySelector('.ant-select-selector'));
fireEvent.mouseDown(wrapper.querySelector('.ant-select-selector')!);
expect(wrapper.querySelector('.ant-pagination')).toMatchSnapshot();
});
@ -178,7 +179,7 @@ describe('List.pagination', () => {
}),
);
fireEvent.mouseDown(wrapper.querySelector('.ant-select-selector'));
fireEvent.mouseDown(wrapper.querySelector('.ant-select-selector')!);
fireEvent.click(wrapper.querySelectorAll('.ant-select-item-option')[1]);
expect(handlePaginationChange).toHaveBeenCalledWith(1, 10);
});
@ -199,10 +200,6 @@ describe('List.pagination', () => {
});
it('should not crash when pagination is null', () => {
render(
createList({
pagination: null,
}),
);
render(createList({ pagination: null as unknown as ListProps<DataSourceItem>['pagination'] }));
});
});

View File

@ -5,12 +5,14 @@ import KeyCode from 'rc-util/lib/KeyCode';
import { resetWarned } from 'rc-util/lib/warning';
import * as React from 'react';
import TestUtils from 'react-dom/test-utils';
import type { ModalFuncProps } from '..';
import Modal from '..';
import { sleep, act } from '../../../tests/utils';
import ConfigProvider from '../../config-provider';
import type { ModalFunc } from '../confirm';
import destroyFns from '../destroyFns';
globalThis.IS_REACT_ACT_ENVIRONMENT = true;
(globalThis as any).IS_REACT_ACT_ENVIRONMENT = true;
const { confirm } = Modal;
@ -20,7 +22,7 @@ describe('Modal.confirm triggers callbacks correctly', () => {
// Inject CSSMotion to replace with No transition support
const MockCSSMotion = genCSSMotion(false);
Object.keys(MockCSSMotion).forEach(key => {
CSSMotion[key] = MockCSSMotion[key];
(CSSMotion as any)[key] = (MockCSSMotion as any)[key];
});
// // Mock for rc-util raf
@ -67,11 +69,11 @@ describe('Modal.confirm triggers callbacks correctly', () => {
errorSpy.mockRestore();
});
function $$(className) {
return document.body.querySelectorAll(className);
function $$(className: string) {
return document.body.querySelectorAll<HTMLElement>(className);
}
function open(args) {
function open(args?: ModalFuncProps) {
jest.useFakeTimers();
confirm({
title: 'Want to delete these items?',
@ -242,23 +244,22 @@ describe('Modal.confirm triggers callbacks correctly', () => {
});
it('allows extra props on buttons', async () => {
open({ okButtonProps: { disabled: true }, cancelButtonProps: { 'data-test': 'baz' } });
open({
okButtonProps: { disabled: true },
cancelButtonProps: { 'data-test': 'baz' } as ModalFuncProps['cancelButtonProps'],
});
await sleep();
expect($$('.ant-btn')).toHaveLength(2);
expect($$('.ant-btn')[0].attributes['data-test'].value).toBe('baz');
expect($$('.ant-btn')[1].disabled).toBe(true);
expect(($$('.ant-btn')[0].attributes as any)['data-test'].value).toBe('baz');
expect(($$('.ant-btn')[1] as HTMLButtonElement).disabled).toBe(true);
});
describe('should close modals when click confirm button', () => {
['info', 'success', 'warning', 'error'].forEach(type => {
(['info', 'success', 'warning', 'error'] as const).forEach(type => {
it(type, async () => {
jest.useFakeTimers();
Modal[type]({
title: 'title',
content: 'content',
});
Modal[type]?.({ title: 'title', content: 'content' });
await act(async () => {
jest.runAllTimers();
await sleep();
@ -325,13 +326,13 @@ describe('Modal.confirm triggers callbacks correctly', () => {
});
describe('should not close modals when click confirm button when onOk has argument', () => {
['confirm', 'info', 'success', 'warning', 'error'].forEach(type => {
(['confirm', 'info', 'success', 'warning', 'error'] as const).forEach(type => {
it(type, async () => {
jest.useFakeTimers();
Modal[type]({
Modal[type]?.({
title: 'title',
content: 'content',
onOk: close => null, // eslint-disable-line no-unused-vars
onOk: _ => null, // eslint-disable-line no-unused-vars
});
await act(async () => {
jest.runAllTimers();
@ -351,10 +352,10 @@ describe('Modal.confirm triggers callbacks correctly', () => {
});
describe('could be update by new config', () => {
['info', 'success', 'warning', 'error'].forEach(type => {
(['info', 'success', 'warning', 'error'] as const).forEach(type => {
it(type, async () => {
jest.useFakeTimers();
const instance = Modal[type]({
const instance = Modal[type]?.({
title: 'title',
content: 'content',
});
@ -387,17 +388,12 @@ describe('Modal.confirm triggers callbacks correctly', () => {
});
describe('could be update by call function', () => {
['info', 'success', 'warning', 'error'].forEach(type => {
(['info', 'success', 'warning', 'error'] as const).forEach(type => {
it(type, () => {
jest.useFakeTimers();
const instance = Modal[type]({
const instance = Modal[type]?.({
title: 'title',
okButtonProps: {
loading: true,
style: {
color: 'red',
},
},
okButtonProps: { loading: true, style: { color: 'red' } },
});
act(() => {
jest.runAllTimers();
@ -434,10 +430,10 @@ describe('Modal.confirm triggers callbacks correctly', () => {
});
describe('could be destroy', () => {
['info', 'success', 'warning', 'error'].forEach(type => {
(['info', 'success', 'warning', 'error'] as const).forEach(type => {
jest.useFakeTimers();
it(type, async () => {
const instance = Modal[type]({
const instance = Modal[type]?.({
title: 'title',
content: 'content',
});
@ -462,8 +458,8 @@ describe('Modal.confirm triggers callbacks correctly', () => {
jest.useFakeTimers();
// Show
['info', 'success', 'warning', 'error'].forEach(type => {
Modal[type]({
(['info', 'success', 'warning', 'error'] as const).forEach(type => {
Modal[type]?.({
title: 'title',
content: 'content',
});
@ -515,9 +511,9 @@ describe('Modal.confirm triggers callbacks correctly', () => {
jest.runAllTimers();
});
const instances = [];
['info', 'success', 'warning', 'error'].forEach(type => {
const instance = Modal[type]({
const instances: ReturnType<ModalFunc>[] = [];
(['info', 'success', 'warning', 'error'] as const).forEach(type => {
const instance = Modal[type]?.({
title: 'title',
content: 'content',
});
@ -588,8 +584,9 @@ describe('Modal.confirm triggers callbacks correctly', () => {
it('ok button should trigger onOk multiple times when onOk has close argument', async () => {
const onOk = jest.fn();
open({
onOk: close => {
onOk(close?: any) {
onOk();
// @ts-ignore
(() => {})(close); // do nothing
},
});
@ -615,7 +612,7 @@ describe('Modal.confirm triggers callbacks correctly', () => {
expect(document.querySelectorAll('.my-btn').length).toBe(2);
expect(document.querySelectorAll('.bamboo-smile').length).toBe(1);
expect(document.querySelectorAll('.my-modal-confirm').length).toBe(1);
ConfigProvider.config({ prefixCls: 'ant', iconPrefixCls: null });
ConfigProvider.config({ prefixCls: 'ant', iconPrefixCls: undefined });
jest.useRealTimers();
});
@ -702,12 +699,12 @@ describe('Modal.confirm triggers callbacks correctly', () => {
});
describe('the callback close should be a method when onCancel has a close parameter', () => {
['confirm', 'info', 'success', 'warning', 'error'].forEach(type => {
(['confirm', 'info', 'success', 'warning', 'error'] as const).forEach(type => {
it(`click the close icon to trigger ${type} onCancel`, async () => {
jest.useFakeTimers();
const mock = jest.fn();
Modal[type]({
Modal[type]?.({
closable: true,
onCancel: close => mock(close),
});
@ -732,12 +729,12 @@ describe('Modal.confirm triggers callbacks correctly', () => {
});
});
['confirm', 'info', 'success', 'warning', 'error'].forEach(type => {
(['confirm', 'info', 'success', 'warning', 'error'] as const).forEach(type => {
it(`press ESC to trigger ${type} onCancel`, async () => {
jest.useFakeTimers();
const mock = jest.fn();
Modal[type]({
Modal[type]?.({
keyboard: true,
onCancel: close => mock(close),
});
@ -770,12 +767,12 @@ describe('Modal.confirm triggers callbacks correctly', () => {
});
});
['confirm', 'info', 'success', 'warning', 'error'].forEach(type => {
(['confirm', 'info', 'success', 'warning', 'error'] as const).forEach(type => {
it(`click the mask to trigger ${type} onCancel`, async () => {
jest.useFakeTimers();
const mock = jest.fn();
Modal[type]({
Modal[type]?.({
maskClosable: true,
onCancel: close => mock(close),
});

View File

@ -1,4 +1,6 @@
import React from 'react';
import type { OptionFC } from 'rc-select/lib/Option';
import type { PaginationProps } from '..';
import Pagination from '..';
import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
@ -11,7 +13,7 @@ describe('Pagination', () => {
rtlTest(Pagination);
it('should pass disabled to prev and next buttons', () => {
const itemRender = (current, type, originalElement) => {
const itemRender: PaginationProps['itemRender'] = (_, type, originalElement) => {
if (type === 'prev') {
return <button type="button">prev</button>;
}
@ -23,12 +25,12 @@ describe('Pagination', () => {
const { container } = render(
<Pagination defaultCurrent={1} total={50} itemRender={itemRender} />,
);
expect(container.querySelector('button').disabled).toBe(true);
expect(container.querySelector('button')?.disabled).toBe(true);
});
it('should autometically be small when size is not specified', async () => {
const { container } = render(<Pagination responsive />);
expect(container.querySelector('ul').className.includes('ant-pagination-mini')).toBe(true);
expect(container.querySelector('ul')?.className.includes('ant-pagination-mini')).toBe(true);
});
// https://github.com/ant-design/ant-design/issues/24913
@ -45,7 +47,7 @@ describe('Pagination', () => {
/>,
);
fireEvent.mouseDown(container.querySelector('.ant-select-selector'));
fireEvent.mouseDown(container.querySelector('.ant-select-selector')!);
expect(container.querySelectorAll('.ant-select-item-option').length).toBe(4);
fireEvent.click(container.querySelectorAll('.ant-select-item-option')[1]);
@ -53,9 +55,10 @@ describe('Pagination', () => {
});
it('should support custom selectComponentClass', () => {
const CustomSelect = ({ className, ...props }) => (
<Select className={`${className} custom-select`} {...props} />
);
const CustomSelect: React.FC<{ className?: string }> & { Option: OptionFC } = ({
className,
...props
}) => <Select className={`${className} custom-select`} {...props} />;
CustomSelect.Option = Select.Option;

View File

@ -49,7 +49,7 @@ export const sortGradient = (gradients: StringGradients) => {
* "100%": "#ffffff"
* }
*/
export const handleGradient = (strokeColor: ProgressGradient, directionConfig: DirectionType) => {
export const handleGradient = (strokeColor: ProgressGradient, directionConfig?: DirectionType) => {
const {
from = presetPrimaryColors.blue,
to = presetPrimaryColors.blue,

View File

@ -1,4 +1,5 @@
import React from 'react';
import type { ProgressProps } from '..';
import Progress from '..';
import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
@ -162,7 +163,9 @@ describe('Progress', () => {
// https://github.com/ant-design/ant-design/pull/15951#discussion_r273062969
it('should show success status when status is invalid', () => {
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
const { container: wrapper } = render(<Progress percent={100} status="invalid" />);
const { container: wrapper } = render(
<Progress percent={100} status={'invalid' as ProgressProps['status']} />,
);
expect(wrapper.querySelectorAll('.ant-progress-status-success')).toHaveLength(1);
errorSpy.mockRestore();
});
@ -183,21 +186,23 @@ describe('Progress', () => {
const { container: wrapper, rerender } = render(
<Progress steps={5} percent={60} strokeColor="#1890ff" />,
);
expect(wrapper.querySelectorAll('.ant-progress-steps-item')[0].style.backgroundColor).toBe(
'rgb(24, 144, 255)',
);
expect(
wrapper.querySelectorAll<HTMLDivElement>('.ant-progress-steps-item')[0].style.backgroundColor,
).toBe('rgb(24, 144, 255)');
rerender(<Progress steps={5} percent={40} strokeColor="#1890ff" />);
expect(wrapper.querySelectorAll('.ant-progress-steps-item')[2].style.backgroundColor).toBe('');
expect(wrapper.querySelectorAll('.ant-progress-steps-item')[1].style.backgroundColor).toBe(
'rgb(24, 144, 255)',
);
expect(
wrapper.querySelectorAll<HTMLDivElement>('.ant-progress-steps-item')[2].style.backgroundColor,
).toBe('');
expect(
wrapper.querySelectorAll<HTMLDivElement>('.ant-progress-steps-item')[1].style.backgroundColor,
).toBe('rgb(24, 144, 255)');
});
it('steps should support trailColor', () => {
const { container: wrapper } = render(<Progress steps={5} percent={20} trailColor="#1890ee" />);
expect(wrapper.querySelectorAll('.ant-progress-steps-item')[1].style.backgroundColor).toBe(
'rgb(24, 144, 238)',
);
expect(
wrapper.querySelectorAll<HTMLDivElement>('.ant-progress-steps-item')[1].style.backgroundColor,
).toBe('rgb(24, 144, 238)');
});
it('should display correct step', () => {
@ -210,8 +215,8 @@ describe('Progress', () => {
});
it('steps should have default percent 0', () => {
const { container: wrapper } = render(<ProgressSteps />);
expect(wrapper.firstChild).toMatchSnapshot();
const { container } = render(<ProgressSteps steps={0} />);
expect(container.firstChild).toMatchSnapshot();
});
it('should warnning if use `progress` in success', () => {

View File

@ -1,10 +1,11 @@
import React from 'react';
import type { RefAttributes } from 'react';
import type { RadioGroupProps } from '..';
import { render, fireEvent } from '../../../tests/utils';
import Radio from '..';
import { render, fireEvent } from '../../../tests/utils';
describe('Radio Group', () => {
function createRadioGroup(props) {
function createRadioGroup(props?: RadioGroupProps & RefAttributes<HTMLDivElement>) {
return (
<Radio.Group {...props}>
<Radio value="A">A</Radio>
@ -14,13 +15,12 @@ describe('Radio Group', () => {
);
}
function createRadioGroupByOption(props) {
function createRadioGroupByOption(props?: RadioGroupProps & RefAttributes<HTMLDivElement>) {
const options = [
{ label: 'A', value: 'A' },
{ label: 'B', value: 'B' },
{ label: 'C', value: 'C' },
];
return <Radio.Group {...props} options={options} />;
}
@ -34,10 +34,10 @@ describe('Radio Group', () => {
</Radio.Group>,
);
fireEvent.mouseEnter(container.querySelector('div'));
fireEvent.mouseEnter(container.querySelector('div')!);
expect(onMouseEnter).toHaveBeenCalled();
fireEvent.mouseLeave(container.querySelector('div'));
fireEvent.mouseLeave(container.querySelector('div')!);
expect(onMouseLeave).toHaveBeenCalled();
});
@ -66,7 +66,9 @@ describe('Radio Group', () => {
const onChange = jest.fn();
const onChangeRadioGroup = jest.fn();
const RadioGroup = props => (
const RadioGroup: React.FC<
RadioGroupProps & { onChangeRadioGroup: RadioGroupProps['onChange'] }
> = props => (
<Radio.Group onChange={props.onChangeRadioGroup}>
<Radio value="A" onChange={props.onChange}>
A
@ -95,7 +97,7 @@ describe('Radio Group', () => {
it('Trigger onChange when both of radioButton and radioGroup exists', () => {
const onChange = jest.fn();
const RadioGroup = props => (
const RadioGroup: React.FC<RadioGroupProps> = props => (
<Radio.Group {...props}>
<Radio.Button value="A">A</Radio.Button>
<Radio.Button value="B">B</Radio.Button>
@ -117,7 +119,7 @@ describe('Radio Group', () => {
const options = [{ label: 'Bamboo', value: 'Bamboo' }];
const { container } = render(<Radio.Group options={options} onChange={onChange} />);
fireEvent.click(container.querySelector('input'));
fireEvent.click(container.querySelector('input')!);
expect(onChange).toHaveBeenCalledTimes(1);
});
@ -153,7 +155,7 @@ describe('Radio Group', () => {
const GROUP_NAME = 'GROUP_NAME';
const { container } = render(createRadioGroup({ name: GROUP_NAME }));
container.querySelectorAll('input[type="radio"]').forEach(el => {
container.querySelectorAll<HTMLInputElement>('input[type="radio"]').forEach(el => {
expect(el.name).toEqual(GROUP_NAME);
});
});
@ -168,16 +170,16 @@ describe('Radio Group', () => {
});
it('should forward ref', () => {
let radioGroupRef;
let radioGroupRef: HTMLDivElement;
const { container } = render(
createRadioGroupByOption({
ref: ref => {
ref(ref: HTMLDivElement) {
radioGroupRef = ref;
},
}),
);
expect(radioGroupRef).toBe(container.querySelector('.ant-radio-group'));
expect(radioGroupRef!).toBe(container.querySelector<HTMLDivElement>('.ant-radio-group'));
});
it('should support data-* or aria-* props', () => {
@ -185,10 +187,14 @@ describe('Radio Group', () => {
createRadioGroup({
'data-radio-group-id': 'radio-group-id',
'aria-label': 'radio-group',
}),
} as RadioGroupProps),
);
expect((container.firstChild as HTMLDivElement)?.getAttribute('data-radio-group-id')).toBe(
'radio-group-id',
);
expect((container.firstChild as HTMLDivElement)?.getAttribute('aria-label')).toBe(
'radio-group',
);
expect(container.firstChild.getAttribute('data-radio-group-id')).toBe('radio-group-id');
expect(container.firstChild.getAttribute('aria-label')).toBe('radio-group');
});
it('Radio type should not be override', () => {
@ -241,9 +247,9 @@ describe('Radio Group', () => {
const { container } = render(
<Radio.Group options={['1', '2', '3']} onBlur={handleBlur} onFocus={handleFocus} />,
);
fireEvent.focus(container.firstChild);
fireEvent.focus(container.firstChild!);
expect(handleFocus).toHaveBeenCalledTimes(1);
fireEvent.blur(container.firstChild);
fireEvent.blur(container.firstChild!);
expect(handleBlur).toHaveBeenCalledTimes(1);
});
});

View File

@ -1,4 +1,6 @@
import type { RefAttributes } from 'react';
import React from 'react';
import type { RadioGroupProps } from '..';
import Radio, { Button } from '..';
import focusTest from '../../../tests/shared/focusTest';
import mountTest from '../../../tests/shared/mountTest';
@ -25,16 +27,16 @@ describe('Radio Button', () => {
<Button onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave} />,
);
fireEvent.mouseEnter(container.querySelector('label'));
fireEvent.mouseEnter(container.querySelector('label')!);
expect(onMouseEnter).toHaveBeenCalled();
fireEvent.mouseLeave(container.querySelector('label'));
fireEvent.mouseLeave(container.querySelector('label')!);
expect(onMouseLeave).toHaveBeenCalled();
});
});
describe('Radio Group', () => {
function createRadioGroup(props) {
function createRadioGroup(props?: RadioGroupProps & RefAttributes<HTMLDivElement>) {
return (
<Radio.Group {...props}>
<Button value="A">A</Button>
@ -128,7 +130,7 @@ describe('Radio Group', () => {
const options = [{ label: 'Bamboo', value: 'Bamboo' }];
const { container } = render(<Radio.Group options={options} onChange={onChange} />);
fireEvent.click(container.querySelector('input'));
fireEvent.click(container.querySelector('input')!);
expect(onChange).toHaveBeenCalledTimes(1);
});
@ -152,7 +154,7 @@ describe('Radio Group', () => {
const GROUP_NAME = 'GROUP_NAME';
const { container } = render(createRadioGroup({ name: GROUP_NAME }));
container.querySelectorAll('input[type="radio"]').forEach(el => {
container.querySelectorAll<HTMLInputElement>('input[type="radio"]').forEach(el => {
expect(el.name).toEqual(GROUP_NAME);
});
});
@ -167,16 +169,16 @@ describe('Radio Group', () => {
});
it('should forward ref', () => {
let radioGroupRef;
let radioGroupRef: HTMLDivElement;
const { container } = render(
createRadioGroup({
ref: ref => {
ref(ref: HTMLDivElement) {
radioGroupRef = ref;
},
}),
);
expect(radioGroupRef).toBe(container.querySelector('.ant-radio-group'));
expect(radioGroupRef!).toBe(container.querySelector('.ant-radio-group'));
});
it('should support data-* or aria-* props', () => {
@ -184,10 +186,14 @@ describe('Radio Group', () => {
createRadioGroup({
'data-radio-group-id': 'radio-group-id',
'aria-label': 'radio-group',
}),
} as RadioGroupProps),
);
expect((container.firstChild as HTMLDivElement)?.getAttribute('data-radio-group-id')).toBe(
'radio-group-id',
);
expect((container.firstChild as HTMLDivElement)?.getAttribute('aria-label')).toBe(
'radio-group',
);
expect(container.firstChild.getAttribute('data-radio-group-id')).toBe('radio-group-id');
expect(container.firstChild.getAttribute('aria-label')).toBe('radio-group');
});
it('Radio type should not be override', () => {

View File

@ -27,10 +27,10 @@ describe('Radio', () => {
const { container } = render(<Radio onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave} />);
fireEvent.mouseEnter(container.querySelector('label'));
fireEvent.mouseEnter(container.querySelector('label')!);
expect(onMouseEnter).toHaveBeenCalled();
fireEvent.mouseLeave(container.querySelector('label'));
fireEvent.mouseLeave(container.querySelector('label')!);
expect(onMouseLeave).toHaveBeenCalled();
});
});

View File

@ -63,7 +63,6 @@ exports[`Select Deprecated should ignore mode="combobox" 1`] = `
exports[`Select Select Custom Icons should support customized icons 1`] = `
<div
class="ant-select ant-select-single ant-select-show-arrow"
count="10"
>
<div
class="ant-select-selector"

View File

@ -1,10 +1,11 @@
import React from 'react';
import { CloseOutlined } from '@ant-design/icons';
import type { SelectProps } from '..';
import Select from '..';
import focusTest from '../../../tests/shared/focusTest';
import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
import { fireEvent, render, act } from '../../../tests/utils';
import Icon from '../../icon';
const { Option } = Select;
@ -13,8 +14,8 @@ describe('Select', () => {
mountTest(Select);
rtlTest(Select);
function toggleOpen(container) {
fireEvent.mouseDown(container.querySelector('.ant-select-selector'));
function toggleOpen(container: ReturnType<typeof render>['container']): void {
fireEvent.mouseDown(container.querySelector('.ant-select-selector')!);
act(() => {
jest.runAllTimers();
});
@ -42,14 +43,19 @@ describe('Select', () => {
});
it('should not have default notFoundContent when mode is combobox', () => {
const { container } = render(<Select mode={Select.SECRET_COMBOBOX_MODE_DO_NOT_USE} />);
const { container } = render(
<Select mode={Select.SECRET_COMBOBOX_MODE_DO_NOT_USE as SelectProps['mode']} />,
);
toggleOpen(container);
expect(container.querySelector('.ant-empty')).toBeFalsy();
});
it('should not have notFoundContent when mode is combobox and notFoundContent is set', () => {
const { container } = render(
<Select mode={Select.SECRET_COMBOBOX_MODE_DO_NOT_USE} notFoundContent="not at all" />,
<Select
mode={Select.SECRET_COMBOBOX_MODE_DO_NOT_USE as SelectProps['mode']}
notFoundContent="not at all"
/>,
);
toggleOpen(container);
expect(container.querySelector('.ant-select-item-option')).toBeFalsy();
@ -58,9 +64,9 @@ describe('Select', () => {
it('should be controlled by open prop', () => {
const onDropdownVisibleChange = jest.fn();
const TestComponent = () => {
const TestComponent: React.FC = () => {
const [open, setOpen] = React.useState(false);
const handleChange = value => {
const handleChange: SelectProps['onDropdownVisibleChange'] = value => {
onDropdownVisibleChange(value);
setOpen(value);
};
@ -100,24 +106,22 @@ describe('Select', () => {
errorSpy.mockRestore();
});
//
describe('Select Custom Icons', () => {
it('should support customized icons', () => {
const { rerender, asFragment } = render(
<Select
removeIcon={<Icon type="close" />}
clearIcon={<Icon type="close" />}
menuItemSelectedIcon={<Icon type="close" />}
removeIcon={<CloseOutlined />}
clearIcon={<CloseOutlined />}
menuItemSelectedIcon={<CloseOutlined />}
>
<Option value="1">1</Option>
</Select>,
);
rerender(
<Select
count={10}
removeIcon={<Icon type="close" />}
clearIcon={<Icon type="close" />}
menuItemSelectedIcon={<Icon type="close" />}
removeIcon={<CloseOutlined />}
clearIcon={<CloseOutlined />}
menuItemSelectedIcon={<CloseOutlined />}
>
<Option value="1">1</Option>
</Select>,
@ -132,7 +136,7 @@ describe('Select', () => {
describe('Deprecated', () => {
it('should ignore mode="combobox"', () => {
const { asFragment } = render(
<Select mode="combobox">
<Select mode={'combobox' as SelectProps['mode']}>
<Option value="1">1</Option>
</Select>,
);

Some files were not shown because too many files have changed in this diff Show More