fix: Notification & message throw createRoot warning in React 18 (#35030)

* chore: bump notification version

* test: notification test case

* test: more test case

* test: part message test

* test: rest message test

* test: notification config test
This commit is contained in:
二货机器人 2022-04-14 18:09:19 +08:00 committed by GitHub
parent 9ed31930e7
commit 67ccf39bd3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 444 additions and 202 deletions

View File

@ -3,7 +3,6 @@ import ReactDOM from 'react-dom';
import { act } from 'react-dom/test-utils'; import { act } from 'react-dom/test-utils';
import { mount } from 'enzyme'; import { mount } from 'enzyme';
import { render } from '@testing-library/react'; import { render } from '@testing-library/react';
import '@testing-library/jest-dom';
import Avatar from '..'; import Avatar from '..';
import mountTest from '../../../tests/shared/mountTest'; import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest'; import rtlTest from '../../../tests/shared/rtlTest';

View File

@ -1,7 +1,6 @@
import React from 'react'; import React from 'react';
import { mount } from 'enzyme'; import { mount } from 'enzyme';
import { render } from '@testing-library/react'; import { render } from '@testing-library/react';
import '@testing-library/jest-dom';
import Breadcrumb from '../index'; import Breadcrumb from '../index';
import mountTest from '../../../tests/shared/mountTest'; import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest'; import rtlTest from '../../../tests/shared/rtlTest';

View File

@ -1,7 +1,6 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import { mount } from 'enzyme'; import { mount } from 'enzyme';
import { render } from '@testing-library/react'; import { render } from '@testing-library/react';
import '@testing-library/jest-dom';
import { act } from 'react-dom/test-utils'; import { act } from 'react-dom/test-utils';
import { SearchOutlined } from '@ant-design/icons'; import { SearchOutlined } from '@ant-design/icons';
import { resetWarned } from 'rc-util/lib/warning'; import { resetWarned } from 'rc-util/lib/warning';

View File

@ -1,7 +1,6 @@
import React from 'react'; import React from 'react';
import { mount } from 'enzyme'; import { mount } from 'enzyme';
import { render } from '@testing-library/react'; import { render } from '@testing-library/react';
import '@testing-library/jest-dom';
import { act } from 'react-dom/test-utils'; import { act } from 'react-dom/test-utils';
import ConfigProvider from '..'; import ConfigProvider from '..';
import zhCN from '../../locale/zh_CN'; import zhCN from '../../locale/zh_CN';

View File

@ -1,7 +1,6 @@
import React from 'react'; import React from 'react';
import { mount } from 'enzyme'; import { mount } from 'enzyme';
import { render } from '@testing-library/react'; import { render } from '@testing-library/react';
import '@testing-library/jest-dom';
import Drawer from '..'; import Drawer from '..';
import ConfigProvider from '../../config-provider'; import ConfigProvider from '../../config-provider';
import mountTest from '../../../tests/shared/mountTest'; import mountTest from '../../../tests/shared/mountTest';

View File

@ -1,7 +1,6 @@
import React, { Component, useState } from 'react'; import React, { Component, useState } from 'react';
import { mount } from 'enzyme'; import { mount } from 'enzyme';
import { render, fireEvent } from '@testing-library/react'; import { render, fireEvent } from '@testing-library/react';
import '@testing-library/jest-dom';
import { act } from 'react-dom/test-utils'; import { act } from 'react-dom/test-utils';
import scrollIntoView from 'scroll-into-view-if-needed'; import scrollIntoView from 'scroll-into-view-if-needed';
import Form from '..'; import Form from '..';

View File

@ -2,7 +2,6 @@ import React from 'react';
import { mount } from 'enzyme'; import { mount } from 'enzyme';
import { act } from 'react-dom/test-utils'; import { act } from 'react-dom/test-utils';
import { render, fireEvent } from '@testing-library/react'; import { render, fireEvent } from '@testing-library/react';
import '@testing-library/jest-dom';
import Form from '..'; import Form from '..';
import Input from '../../input'; import Input from '../../input';
import Button from '../../button'; import Button from '../../button';

View File

@ -1,7 +1,6 @@
import React from 'react'; import React from 'react';
import ReactDOMServer from 'react-dom/server'; import ReactDOMServer from 'react-dom/server';
import { render } from '@testing-library/react'; import { render } from '@testing-library/react';
import '@testing-library/jest-dom';
import { mount } from 'enzyme'; import { mount } from 'enzyme';
import { Col, Row } from '..'; import { Col, Row } from '..';
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars

View File

@ -1,7 +1,6 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { mount } from 'enzyme'; import { mount } from 'enzyme';
import { render, fireEvent } from '@testing-library/react'; import { render, fireEvent } from '@testing-library/react';
import '@testing-library/jest-dom';
// eslint-disable-next-line import/no-unresolved // eslint-disable-next-line import/no-unresolved
import Form from '../../form'; import Form from '../../form';
import Input, { InputProps, InputRef } from '..'; import Input, { InputProps, InputRef } from '..';

View File

@ -1,7 +1,6 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { mount } from 'enzyme'; import { mount } from 'enzyme';
import { render } from '@testing-library/react'; import { render } from '@testing-library/react';
import '@testing-library/jest-dom';
import RcTextArea from 'rc-textarea'; import RcTextArea from 'rc-textarea';
import Input from '..'; import Input from '..';
import focusTest from '../../../tests/shared/focusTest'; import focusTest from '../../../tests/shared/focusTest';

View File

@ -8,7 +8,6 @@ import {
UserOutlined, UserOutlined,
} from '@ant-design/icons'; } from '@ant-design/icons';
import { render, fireEvent } from '@testing-library/react'; import { render, fireEvent } from '@testing-library/react';
import '@testing-library/jest-dom';
import { act } from 'react-dom/test-utils'; import { act } from 'react-dom/test-utils';
import Menu from '..'; import Menu from '..';
import Layout from '../../layout'; import Layout from '../../layout';

View File

@ -1,3 +1,4 @@
import { act } from 'react-dom/test-utils';
import { sleep } from '../../../tests/utils'; import { sleep } from '../../../tests/utils';
import message, { getInstance } from '..'; import message, { getInstance } from '..';
import ConfigProvider from '../../config-provider'; import ConfigProvider from '../../config-provider';
@ -11,18 +12,26 @@ describe('message.config', () => {
beforeEach(() => { beforeEach(() => {
jest.useFakeTimers(); jest.useFakeTimers();
jest.clearAllTimers();
}); });
afterEach(() => { afterEach(() => {
message.destroy();
jest.useRealTimers(); jest.useRealTimers();
act(() => {
message.destroy();
});
}); });
it('should be able to config top', () => { it('should be able to config top', () => {
message.config({ message.config({
top: 100, top: 100,
}); });
message.info('whatever');
act(() => {
message.info('whatever');
});
expect(document.querySelectorAll('.ant-message')[0].style.top).toBe('100px'); expect(document.querySelectorAll('.ant-message')[0].style.top).toBe('100px');
}); });
@ -30,7 +39,11 @@ describe('message.config', () => {
message.config({ message.config({
rtl: true, rtl: true,
}); });
message.info('whatever');
act(() => {
message.info('whatever');
});
expect(document.querySelectorAll('.ant-message-rtl').length).toBe(1); expect(document.querySelectorAll('.ant-message-rtl').length).toBe(1);
}); });
@ -43,7 +56,10 @@ describe('message.config', () => {
return div; return div;
}, },
}); });
message.info('whatever');
act(() => {
message.info('whatever');
});
expect(document.querySelectorAll('.custom-container').length).toBe(1); expect(document.querySelectorAll('.custom-container').length).toBe(1);
}); });
@ -52,13 +68,21 @@ describe('message.config', () => {
maxCount: 5, maxCount: 5,
}); });
for (let i = 0; i < 10; i += 1) { for (let i = 0; i < 10; i += 1) {
message.info('test'); act(() => {
message.info('test');
});
} }
message.info('last'); act(() => {
message.info('last');
});
expect(document.querySelectorAll('.ant-message-notice').length).toBe(5); expect(document.querySelectorAll('.ant-message-notice').length).toBe(5);
expect(document.querySelectorAll('.ant-message-notice')[4].textContent).toBe('last'); expect(document.querySelectorAll('.ant-message-notice')[4].textContent).toBe('last');
jest.runAllTimers();
act(() => {
jest.runAllTimers();
});
expect(getInstance().component.state.notices).toHaveLength(0); expect(getInstance().component.state.notices).toHaveLength(0);
}); });
@ -67,7 +91,10 @@ describe('message.config', () => {
message.config({ message.config({
duration: 0.5, duration: 0.5,
}); });
message.info('last');
act(() => {
message.info('last');
});
expect(getInstance().component.state.notices).toHaveLength(1); expect(getInstance().component.state.notices).toHaveLength(1);
await sleep(1000); await sleep(1000);
@ -82,7 +109,9 @@ describe('message.config', () => {
prefixCls: 'light-message', prefixCls: 'light-message',
}); });
message.info('bamboo'); act(() => {
message.info('bamboo');
});
expect(getInstance().config).toEqual( expect(getInstance().config).toEqual(
expect.objectContaining({ expect.objectContaining({
@ -97,7 +126,11 @@ describe('message.config', () => {
it('should be able to global config rootPrefixCls', () => { it('should be able to global config rootPrefixCls', () => {
ConfigProvider.config({ prefixCls: 'prefix-test', iconPrefixCls: 'bamboo' }); ConfigProvider.config({ prefixCls: 'prefix-test', iconPrefixCls: 'bamboo' });
message.info('last');
act(() => {
message.info('last');
});
expect(document.querySelectorAll('.ant-message-notice')).toHaveLength(0); expect(document.querySelectorAll('.ant-message-notice')).toHaveLength(0);
expect(document.querySelectorAll('.prefix-test-message-notice')).toHaveLength(1); expect(document.querySelectorAll('.prefix-test-message-notice')).toHaveLength(1);
expect(document.querySelectorAll('.bamboo-info-circle')).toHaveLength(1); expect(document.querySelectorAll('.bamboo-info-circle')).toHaveLength(1);
@ -107,7 +140,11 @@ describe('message.config', () => {
message.config({ message.config({
prefixCls: 'prefix-test', prefixCls: 'prefix-test',
}); });
message.info('last');
act(() => {
message.info('last');
});
expect(document.querySelectorAll('.ant-message-notice')).toHaveLength(0); expect(document.querySelectorAll('.ant-message-notice')).toHaveLength(0);
expect(document.querySelectorAll('.prefix-test-notice')).toHaveLength(1); expect(document.querySelectorAll('.prefix-test-notice')).toHaveLength(1);
message.config({ message.config({
@ -119,7 +156,11 @@ describe('message.config', () => {
message.config({ message.config({
transitionName: '', transitionName: '',
}); });
message.info('last');
act(() => {
message.info('last');
});
expect(document.querySelectorAll('.ant-move-up-enter')).toHaveLength(0); expect(document.querySelectorAll('.ant-move-up-enter')).toHaveLength(0);
message.config({ message.config({
transitionName: 'ant-move-up', transitionName: 'ant-move-up',
@ -145,13 +186,20 @@ describe('message.config', () => {
getContainer: () => container1, getContainer: () => container1,
}); });
const messageText1 = 'mounted in container1'; const messageText1 = 'mounted in container1';
message.info(messageText1);
act(() => {
message.info(messageText1);
});
expect(container1.querySelector('.ant-message-notice').textContent).toEqual(messageText1); expect(container1.querySelector('.ant-message-notice').textContent).toEqual(messageText1);
message.config({ message.config({
getContainer: () => container2, getContainer: () => container2,
}); });
const messageText2 = 'mounted in container2'; const messageText2 = 'mounted in container2';
message.info(messageText2);
act(() => {
message.info(messageText2);
});
expect(container2.querySelector('.ant-message-notice').textContent).toEqual(messageText2); expect(container2.querySelector('.ant-message-notice').textContent).toEqual(messageText2);
removeContainer1(); removeContainer1();
removeContainer2(); removeContainer2();

View File

@ -1,6 +1,7 @@
/* eslint-disable jsx-a11y/control-has-associated-label */ /* eslint-disable jsx-a11y/control-has-associated-label */
import React from 'react'; import React from 'react';
import { mount } from 'enzyme'; import { mount } from 'enzyme';
import { act } from 'react-dom/test-utils';
import message, { getInstance } from '..'; import message, { getInstance } from '..';
import ConfigProvider from '../../config-provider'; import ConfigProvider from '../../config-provider';
@ -167,10 +168,16 @@ describe('message.hooks', () => {
const wrapper = mount(<Demo />); const wrapper = mount(<Demo />);
wrapper.find('button').simulate('click'); wrapper.find('button').simulate('click');
jest.runAllTimers();
act(() => {
jest.runAllTimers();
});
expect(document.querySelectorAll('.my-test-message-notice').length).toBe(1); expect(document.querySelectorAll('.my-test-message-notice').length).toBe(1);
hide();
jest.runAllTimers(); act(() => {
hide();
jest.runAllTimers();
});
expect(getInstance().component.state.notices).toHaveLength(0); expect(getInstance().component.state.notices).toHaveLength(0);
}); });

View File

@ -1,50 +1,91 @@
import React from 'react'; import React from 'react';
import { mount } from 'enzyme'; import { mount } from 'enzyme';
import { act } from 'react-dom/test-utils';
import { SmileOutlined } from '@ant-design/icons'; import { SmileOutlined } from '@ant-design/icons';
import message, { getInstance } from '..'; import message, { getInstance } from '..';
globalThis.IS_REACT_ACT_ENVIRONMENT = true;
describe('message', () => { describe('message', () => {
beforeEach(() => { beforeEach(() => {
jest.useFakeTimers(); jest.useFakeTimers();
}); });
afterEach(() => { afterEach(() => {
message.destroy();
jest.useRealTimers(); jest.useRealTimers();
act(() => {
message.destroy();
});
}); });
it('should be able to hide manually', () => { it('should be able to hide manually', async () => {
const hide1 = message.info('whatever', 0); let hide1;
const hide2 = message.info('whatever', 0); let hide2;
act(() => {
hide1 = message.info('whatever', 0);
});
act(() => {
hide2 = message.info('whatever', 0);
});
expect(document.querySelectorAll('.ant-message-notice').length).toBe(2); expect(document.querySelectorAll('.ant-message-notice').length).toBe(2);
hide1(); hide1();
jest.runAllTimers(); act(() => {
jest.runAllTimers();
});
expect(getInstance().component.state.notices).toHaveLength(1); expect(getInstance().component.state.notices).toHaveLength(1);
hide2(); hide2();
jest.runAllTimers(); act(() => {
jest.runAllTimers();
});
expect(getInstance().component.state.notices).toHaveLength(0); expect(getInstance().component.state.notices).toHaveLength(0);
}); });
it('should be able to remove manually with a unique key', () => { it('should be able to remove manually with a unique key', () => {
const key1 = 'key1'; const key1 = 'key1';
const key2 = 'key2'; const key2 = 'key2';
message.info({ content: 'Message1', key: 'key1', duration: 0 });
message.info({ content: 'Message2', key: 'key2', duration: 0 }); act(() => {
message.info({ content: 'Message1', key: 'key1', duration: 0 });
});
act(() => {
message.info({ content: 'Message2', key: 'key2', duration: 0 });
});
expect(document.querySelectorAll('.ant-message-notice').length).toBe(2); expect(document.querySelectorAll('.ant-message-notice').length).toBe(2);
message.destroy(key1);
jest.runAllTimers(); act(() => {
message.destroy(key1);
jest.runAllTimers();
});
expect(getInstance().component.state.notices).toHaveLength(1); expect(getInstance().component.state.notices).toHaveLength(1);
message.destroy(key2);
jest.runAllTimers(); act(() => {
message.destroy(key2);
jest.runAllTimers();
});
expect(getInstance().component.state.notices).toHaveLength(0); expect(getInstance().component.state.notices).toHaveLength(0);
}); });
it('should be able to destroy globally', () => { it('should be able to destroy globally', () => {
message.info('whatever', 0); act(() => {
message.info('whatever', 0); message.info('whatever', 0);
});
act(() => {
message.info('whatever', 0);
});
expect(document.querySelectorAll('.ant-message').length).toBe(1); expect(document.querySelectorAll('.ant-message').length).toBe(1);
expect(document.querySelectorAll('.ant-message-notice').length).toBe(2); expect(document.querySelectorAll('.ant-message-notice').length).toBe(2);
message.destroy();
act(() => {
message.destroy();
jest.runAllTimers();
});
expect(document.querySelectorAll('.ant-message').length).toBe(0); expect(document.querySelectorAll('.ant-message').length).toBe(0);
expect(document.querySelectorAll('.ant-message-notice').length).toBe(0); expect(document.querySelectorAll('.ant-message-notice').length).toBe(0);
}); });
@ -103,7 +144,9 @@ describe('message', () => {
let hide; let hide;
class Test extends React.Component { class Test extends React.Component {
componentDidMount() { componentDidMount() {
hide = message.loading('Action in progress..', 0); act(() => {
hide = message.loading('Action in progress..', 0);
});
} }
render() { render() {
@ -112,13 +155,18 @@ describe('message', () => {
} }
mount(<Test />); mount(<Test />);
expect(document.querySelectorAll('.ant-message-notice').length).toBe(1); expect(document.querySelectorAll('.ant-message-notice').length).toBe(1);
hide();
jest.runAllTimers(); act(() => {
hide();
jest.runAllTimers();
});
expect(getInstance().component.state.notices).toHaveLength(0); expect(getInstance().component.state.notices).toHaveLength(0);
}); });
it('should allow custom icon', () => { it('should allow custom icon', () => {
message.open({ content: 'Message', icon: <SmileOutlined /> }); act(() => {
message.open({ content: 'Message', icon: <SmileOutlined /> });
});
expect(document.querySelectorAll('.anticon-smile').length).toBe(1); expect(document.querySelectorAll('.anticon-smile').length).toBe(1);
}); });
@ -177,9 +225,16 @@ describe('message', () => {
const key = 'updatable'; const key = 'updatable';
class Test extends React.Component { class Test extends React.Component {
componentDidMount() { componentDidMount() {
const hideLoading = message.loading({ content: 'Loading...', key, duration: 0 }); let hideLoading;
act(() => {
hideLoading = message.loading({ content: 'Loading...', key, duration: 0 });
});
// Testing that content of the message should be cancel manually. // Testing that content of the message should be cancel manually.
setTimeout(hideLoading, 1000); setTimeout(() => {
act(() => {
hideLoading();
});
}, 1000);
} }
render() { render() {
@ -189,6 +244,7 @@ describe('message', () => {
mount(<Test />); mount(<Test />);
expect(document.querySelectorAll('.ant-message-notice').length).toBe(1); expect(document.querySelectorAll('.ant-message-notice').length).toBe(1);
jest.advanceTimersByTime(1500); jest.advanceTimersByTime(1500);
expect(getInstance().component.state.notices).toHaveLength(0); expect(getInstance().component.state.notices).toHaveLength(0);
}); });

View File

@ -1,3 +1,4 @@
import { act } from 'react-dom/test-utils';
import notification, { getInstance } from '..'; import notification, { getInstance } from '..';
import { sleep } from '../../../tests/utils'; import { sleep } from '../../../tests/utils';
@ -17,26 +18,37 @@ describe('notification.config', () => {
}); });
for (let i = 0; i < 10; i += 1) { for (let i = 0; i < 10; i += 1) {
notification.open({ act(() => {
message: 'Notification message', notification.open({
key: i, message: 'Notification message',
key: i,
});
}); });
} }
notification.open({ act(() => {
message: 'Notification last', notification.open({
key: '11', message: 'Notification last',
key: '11',
});
}); });
await Promise.resolve(); await act(async () => {
await Promise.resolve();
});
expect(document.querySelectorAll('.ant-notification-notice').length).toBe(5); expect(document.querySelectorAll('.ant-notification-notice').length).toBe(5);
expect(document.querySelectorAll('.ant-notification-notice')[4].textContent).toBe( expect(document.querySelectorAll('.ant-notification-notice')[4].textContent).toBe(
'Notification last', 'Notification last',
); );
jest.runAllTimers(); act(() => {
await sleep(500); jest.runAllTimers();
});
await act(async () => {
await sleep(500);
});
expect((await getInstance('ant-notification-topRight')).component.state.notices).toHaveLength( expect((await getInstance('ant-notification-topRight')).component.state.notices).toHaveLength(
0, 0,
); );

View File

@ -1,109 +1,171 @@
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import { act } from 'react-dom/test-utils';
import { UserOutlined } from '@ant-design/icons'; import { UserOutlined } from '@ant-design/icons';
import notification, { getInstance } from '..'; import notification, { getInstance } from '..';
import ConfigProvider from '../../config-provider'; import ConfigProvider from '../../config-provider';
import { sleep } from '../../../tests/utils';
globalThis.IS_REACT_ACT_ENVIRONMENT = true;
describe('notification', () => { describe('notification', () => {
beforeEach(() => { beforeEach(() => {
jest.useFakeTimers(); jest.useFakeTimers();
jest.clearAllTimers();
}); });
afterEach(() => { afterEach(() => {
jest.runAllTimers();
jest.useRealTimers(); jest.useRealTimers();
notification.destroy();
act(() => {
notification.destroy();
});
}); });
it('not duplicate create holder', () => { it('not duplicate create holder', async () => {
const originRender = ReactDOM.render;
const argsList = [];
const spyRender = jest.spyOn(ReactDOM, 'render').mockImplementation((...args) => {
argsList.push(args);
});
for (let i = 0; i < 5; i += 1) { for (let i = 0; i < 5; i += 1) {
notification.open({ act(() => {
message: 'Notification Title', notification.open({
duration: 0, message: 'Notification Title',
prefixCls: 'additional-holder', duration: 0,
prefixCls: 'additional-holder',
});
}); });
} }
argsList.forEach(args => { await sleep();
originRender(...args);
});
const count = document.querySelectorAll('.additional-holder').length; const count = document.querySelectorAll('.additional-holder').length;
expect(count).toEqual(1); expect(count).toEqual(1);
spyRender.mockRestore();
}); });
it('should be able to hide manually', async () => { it('should be able to hide manually', async () => {
notification.open({ act(() => {
message: 'Notification Title', notification.open({
duration: 0, message: 'Notification Title 1',
key: '1', duration: 0,
}); key: '1',
notification.open({ });
message: 'Notification Title', jest.runAllTimers();
duration: 0,
key: '2',
}); });
await Promise.resolve(); act(() => {
jest.runAllTimers();
});
act(() => {
notification.open({
message: 'Notification Title 2',
duration: 0,
key: '2',
});
jest.runAllTimers();
});
act(() => {
jest.runAllTimers();
});
await act(async () => {
await Promise.resolve();
});
expect(document.querySelectorAll('.ant-notification-notice').length).toBe(2); expect(document.querySelectorAll('.ant-notification-notice').length).toBe(2);
notification.close('1'); act(() => {
jest.runAllTimers(); notification.close('1');
jest.runAllTimers();
});
await act(async () => {
await Promise.resolve();
});
expect((await getInstance('ant-notification-topRight')).component.state.notices).toHaveLength( expect((await getInstance('ant-notification-topRight')).component.state.notices).toHaveLength(
1, 1,
); );
notification.close('2'); act(() => {
jest.runAllTimers(); notification.close('2');
jest.runAllTimers();
});
await act(async () => {
await Promise.resolve();
});
expect((await getInstance('ant-notification-topRight')).component.state.notices).toHaveLength( expect((await getInstance('ant-notification-topRight')).component.state.notices).toHaveLength(
0, 0,
); );
}); });
it('should be able to destroy globally', async () => { it('should be able to destroy globally', async () => {
notification.open({ act(() => {
message: 'Notification Title', notification.open({
duration: 0, message: 'Notification Title',
duration: 0,
});
}); });
notification.open({
message: 'Notification Title', act(() => {
duration: 0, notification.open({
message: 'Notification Title',
duration: 0,
});
}); });
await Promise.resolve();
await act(async () => {
await Promise.resolve();
});
expect(document.querySelectorAll('.ant-notification').length).toBe(1); expect(document.querySelectorAll('.ant-notification').length).toBe(1);
expect(document.querySelectorAll('.ant-notification-notice').length).toBe(2); expect(document.querySelectorAll('.ant-notification-notice').length).toBe(2);
notification.destroy();
await Promise.resolve(); act(() => {
notification.destroy();
});
await act(async () => {
await Promise.resolve();
});
expect(document.querySelectorAll('.ant-notification').length).toBe(0); expect(document.querySelectorAll('.ant-notification').length).toBe(0);
expect(document.querySelectorAll('.ant-notification-notice').length).toBe(0); expect(document.querySelectorAll('.ant-notification-notice').length).toBe(0);
}); });
it('should be able to destroy after config', () => { it('should be able to destroy after config', () => {
notification.config({ act(() => {
bottom: 100, notification.config({
bottom: 100,
});
});
act(() => {
notification.destroy();
}); });
notification.destroy();
}); });
it('should be able to config rtl', () => { it('should be able to config rtl', () => {
notification.config({ act(() => {
rtl: true, notification.config({
rtl: true,
});
}); });
notification.open({
message: 'whatever', act(() => {
notification.open({
message: 'whatever',
});
}); });
expect(document.querySelectorAll('.ant-notification-rtl').length).toBe(1); expect(document.querySelectorAll('.ant-notification-rtl').length).toBe(1);
}); });
it('should be able to global config rootPrefixCls', () => { it('should be able to global config rootPrefixCls', () => {
ConfigProvider.config({ prefixCls: 'prefix-test', iconPrefixCls: 'bamboo' }); ConfigProvider.config({ prefixCls: 'prefix-test', iconPrefixCls: 'bamboo' });
notification.success({ message: 'Notification Title', duration: 0 });
act(() => {
notification.success({ message: 'Notification Title', duration: 0 });
});
expect(document.querySelectorAll('.ant-notification-notice')).toHaveLength(0); expect(document.querySelectorAll('.ant-notification-notice')).toHaveLength(0);
expect(document.querySelectorAll('.prefix-test-notification-notice')).toHaveLength(1); expect(document.querySelectorAll('.prefix-test-notification-notice')).toHaveLength(1);
expect(document.querySelectorAll('.bamboo-check-circle')).toHaveLength(1); expect(document.querySelectorAll('.bamboo-check-circle')).toHaveLength(1);
@ -114,68 +176,93 @@ describe('notification', () => {
notification.config({ notification.config({
prefixCls: 'prefix-test', prefixCls: 'prefix-test',
}); });
notification.open({
message: 'Notification Title', act(() => {
duration: 0, notification.open({
message: 'Notification Title',
duration: 0,
});
}); });
expect(document.querySelectorAll('.ant-notification-notice')).toHaveLength(0); expect(document.querySelectorAll('.ant-notification-notice')).toHaveLength(0);
expect(document.querySelectorAll('.prefix-test-notice')).toHaveLength(1); expect(document.querySelectorAll('.prefix-test-notice')).toHaveLength(1);
notification.config({ notification.config({
prefixCls: '', prefixCls: '',
}); });
}); });
it('should be able to open with icon', async () => { it('should be able to open with icon', async () => {
const iconPrefix = '.ant-notification-notice-icon';
const openNotificationWithIcon = async type => { const openNotificationWithIcon = async type => {
const iconPrefix = '.ant-notification-notice-icon'; act(() => {
notification[type]({ notification[type]({
message: 'Notification Title', message: 'Notification Title',
duration: 0, duration: 0,
description: 'This is the content of the notification.', description: 'This is the content of the notification.',
});
jest.runAllTimers();
}); });
await Promise.resolve();
expect(document.querySelectorAll(`${iconPrefix}-${type}`).length).toBe(1);
}; };
const promises = ['success', 'info', 'warning', 'error'].map(type => const list = ['success', 'info', 'warning', 'error'];
openNotificationWithIcon(type),
);
await Promise.all(promises); const promises = list.map(type => openNotificationWithIcon(type));
await act(async () => {
await Promise.all(promises);
});
list.forEach(type => {
expect(document.querySelectorAll(`${iconPrefix}-${type}`).length).toBe(1);
});
}); });
it('should be able to add parent class for different notification types', async () => { it('should be able to add parent class for different notification types', async () => {
const openNotificationWithIcon = async type => { const openNotificationWithIcon = async type => {
notification[type]({ act(() => {
message: 'Notification Title', notification[type]({
duration: 0, message: 'Notification Title',
description: 'This is the content of the notification.', duration: 0,
description: 'This is the content of the notification.',
});
jest.runAllTimers();
}); });
await Promise.resolve();
expect(document.querySelectorAll(`.ant-notification-notice-${type}`).length).toBe(1);
}; };
const promises = ['success', 'info', 'warning', 'error'].map(type => const list = ['success', 'info', 'warning', 'error'];
openNotificationWithIcon(type), const promises = list.map(type => openNotificationWithIcon(type));
);
await Promise.all(promises); await act(async () => {
await Promise.all(promises);
});
list.forEach(type => {
expect(document.querySelectorAll(`.ant-notification-notice-${type}`).length).toBe(1);
});
}); });
it('trigger onClick', () => { it('trigger onClick', () => {
notification.open({ act(() => {
message: 'Notification Title', notification.open({
duration: 0, message: 'Notification Title',
duration: 0,
});
}); });
expect(document.querySelectorAll('.ant-notification').length).toBe(1); expect(document.querySelectorAll('.ant-notification').length).toBe(1);
}); });
it('support closeIcon', () => { it('support closeIcon', () => {
notification.open({ act(() => {
message: 'Notification Title', notification.open({
duration: 0, message: 'Notification Title',
closeIcon: <span className="test-customize-icon" />, duration: 0,
closeIcon: <span className="test-customize-icon" />,
});
}); });
expect(document.querySelectorAll('.test-customize-icon').length).toBe(1); expect(document.querySelectorAll('.test-customize-icon').length).toBe(1);
}); });
@ -183,45 +270,64 @@ describe('notification', () => {
notification.config({ notification.config({
closeIcon: <span className="test-customize-icon" />, closeIcon: <span className="test-customize-icon" />,
}); });
notification.open({
message: 'Notification Title', act(() => {
duration: 0, notification.open({
closeIcon: <span className="test-customize-icon" />, message: 'Notification Title',
duration: 0,
closeIcon: <span className="test-customize-icon" />,
});
}); });
expect(document.querySelectorAll('.test-customize-icon').length).toBe(1); expect(document.querySelectorAll('.test-customize-icon').length).toBe(1);
}); });
it('closeIcon should be update', async () => { it('closeIcon should be update', async () => {
const openNotificationWithCloseIcon = async type => { const openNotificationWithCloseIcon = async type => {
notification.open({ act(() => {
message: 'Notification Title', notification.open({
closeIcon: <span className={`test-customize-icon-${type}`} />, message: 'Notification Title',
closeIcon: <span className={`test-customize-icon-${type}`} />,
});
jest.runAllTimers();
}); });
await Promise.resolve();
expect(document.querySelectorAll(`.test-customize-icon-${type}`).length).toBe(1);
}; };
const promises = ['1', '2'].map(type => openNotificationWithCloseIcon(type)); const list = ['1', '2'];
const promises = list.map(type => openNotificationWithCloseIcon(type));
await Promise.all(promises); await act(async () => {
await Promise.all(promises);
});
list.forEach(type => {
expect(document.querySelectorAll(`.test-customize-icon-${type}`).length).toBe(1);
});
}); });
it('support config duration', () => { it('support config duration', () => {
notification.config({ notification.config({
duration: 0, duration: 0,
}); });
notification.open({
message: 'whatever', act(() => {
notification.open({
message: 'whatever',
});
}); });
expect(document.querySelectorAll('.ant-notification').length).toBe(1); expect(document.querySelectorAll('.ant-notification').length).toBe(1);
}); });
it('support icon', () => { it('support icon', () => {
notification.open({ act(() => {
message: 'Notification Title', notification.open({
duration: 0, message: 'Notification Title',
icon: <UserOutlined />, duration: 0,
icon: <UserOutlined />,
});
}); });
expect(document.querySelectorAll('.anticon-user').length).toBe(1); expect(document.querySelectorAll('.anticon-user').length).toBe(1);
}); });
}); });

View File

@ -1,3 +1,4 @@
import { act } from 'react-dom/test-utils';
import notification from '..'; import notification from '..';
describe('Notification.placement', () => { describe('Notification.placement', () => {
@ -25,7 +26,10 @@ describe('Notification.placement', () => {
notification.config({ notification.config({
...args, ...args,
}); });
open();
act(() => {
open();
});
} }
describe('placement', () => { describe('placement', () => {
@ -35,10 +39,13 @@ describe('Notification.placement', () => {
let style; let style;
// top // top
open({ act(() => {
placement: 'top', open({
top: 50, placement: 'top',
top: 50,
});
}); });
style = getStyle($$('.ant-notification-top')[0]); style = getStyle($$('.ant-notification-top')[0]);
expect(style.top).toBe('50px'); expect(style.top).toBe('50px');
expect(style.left).toBe('50%'); expect(style.left).toBe('50%');
@ -46,44 +53,56 @@ describe('Notification.placement', () => {
expect(style.right).toBe(''); expect(style.right).toBe('');
expect(style.bottom).toBe(''); expect(style.bottom).toBe('');
open({ act(() => {
placement: 'top', open({
placement: 'top',
});
}); });
expect($$('.ant-notification-top').length).toBe(1); expect($$('.ant-notification-top').length).toBe(1);
// topLeft // topLeft
open({ act(() => {
placement: 'topLeft', open({
top: 50, placement: 'topLeft',
top: 50,
});
}); });
style = getStyle($$('.ant-notification-topLeft')[0]); style = getStyle($$('.ant-notification-topLeft')[0]);
expect(style.top).toBe('50px'); expect(style.top).toBe('50px');
expect(style.left).toBe('0px'); expect(style.left).toBe('0px');
expect(style.bottom).toBe(''); expect(style.bottom).toBe('');
open({ act(() => {
placement: 'topLeft', open({
placement: 'topLeft',
});
}); });
expect($$('.ant-notification-topLeft').length).toBe(1); expect($$('.ant-notification-topLeft').length).toBe(1);
// topRight // topRight
open({ act(() => {
placement: 'topRight', open({
placement: 'topRight',
});
}); });
style = getStyle($$('.ant-notification-topRight')[0]); style = getStyle($$('.ant-notification-topRight')[0]);
expect(style.top).toBe(defaultTop); expect(style.top).toBe(defaultTop);
expect(style.right).toBe('0px'); expect(style.right).toBe('0px');
expect(style.bottom).toBe(''); expect(style.bottom).toBe('');
open({ act(() => {
placement: 'topRight', open({
placement: 'topRight',
});
}); });
expect($$('.ant-notification-topRight').length).toBe(1); expect($$('.ant-notification-topRight').length).toBe(1);
// bottom // bottom
open({ act(() => {
placement: 'bottom', open({
bottom: 100, placement: 'bottom',
bottom: 100,
});
}); });
style = getStyle($$('.ant-notification-bottom')[0]); style = getStyle($$('.ant-notification-bottom')[0]);
expect(style.top).toBe(''); expect(style.top).toBe('');
@ -92,37 +111,47 @@ describe('Notification.placement', () => {
expect(style.right).toBe(''); expect(style.right).toBe('');
expect(style.bottom).toBe('100px'); expect(style.bottom).toBe('100px');
open({ act(() => {
placement: 'bottom', open({
placement: 'bottom',
});
}); });
expect($$('.ant-notification-bottom').length).toBe(1); expect($$('.ant-notification-bottom').length).toBe(1);
// bottomRight // bottomRight
open({ act(() => {
placement: 'bottomRight', open({
bottom: 100, placement: 'bottomRight',
bottom: 100,
});
}); });
style = getStyle($$('.ant-notification-bottomRight')[0]); style = getStyle($$('.ant-notification-bottomRight')[0]);
expect(style.top).toBe(''); expect(style.top).toBe('');
expect(style.right).toBe('0px'); expect(style.right).toBe('0px');
expect(style.bottom).toBe('100px'); expect(style.bottom).toBe('100px');
open({ act(() => {
placement: 'bottomRight', open({
placement: 'bottomRight',
});
}); });
expect($$('.ant-notification-bottomRight').length).toBe(1); expect($$('.ant-notification-bottomRight').length).toBe(1);
// bottomLeft // bottomLeft
open({ act(() => {
placement: 'bottomLeft', open({
placement: 'bottomLeft',
});
}); });
style = getStyle($$('.ant-notification-bottomLeft')[0]); style = getStyle($$('.ant-notification-bottomLeft')[0]);
expect(style.top).toBe(''); expect(style.top).toBe('');
expect(style.left).toBe('0px'); expect(style.left).toBe('0px');
expect(style.bottom).toBe(defaultBottom); expect(style.bottom).toBe(defaultBottom);
open({ act(() => {
placement: 'bottomLeft', open({
placement: 'bottomLeft',
});
}); });
expect($$('.ant-notification-bottomLeft').length).toBe(1); expect($$('.ant-notification-bottomLeft').length).toBe(1);
}); });
@ -186,16 +215,19 @@ describe('Notification.placement', () => {
}); });
it('can be configured per notification using the `open` method', () => { it('can be configured per notification using the `open` method', () => {
open({ act(() => {
getContainer: () => $container, open({
getContainer: () => $container,
});
}); });
expect($container.querySelector('.ant-notification')).not.toBe(null); expect($container.querySelector('.ant-notification')).not.toBe(null);
notification.destroy(); notification.destroy();
setTimeout(() => { setTimeout(() => {
// Upcoming notifications still use their default mountNode and not $container // Upcoming notifications still use their default mountNode and not $container
open(); act(() => {
open();
});
expect($container.querySelector('.ant-notification')).toBe(null); expect($container.querySelector('.ant-notification')).toBe(null);
}); });
}); });
@ -205,12 +237,12 @@ describe('Notification.placement', () => {
getContainer: () => $container, getContainer: () => $container,
}); });
expect($container.querySelector('.ant-notification')).not.toBe(null); expect($container.querySelector('.ant-notification')).not.toBe(null);
notification.destroy(); notification.destroy();
setTimeout(() => { setTimeout(() => {
// Upcoming notifications are mounted in $container // Upcoming notifications are mounted in $container
open(); act(() => {
open();
});
expect($container.querySelector('.ant-notification')).not.toBe(null); expect($container.querySelector('.ant-notification')).not.toBe(null);
}); });
}); });

View File

@ -156,6 +156,7 @@ function getNotificationInstance(
const cacheKey = `${prefixCls}-${placement}`; const cacheKey = `${prefixCls}-${placement}`;
const cacheInstance = notificationInstance[cacheKey]; const cacheInstance = notificationInstance[cacheKey];
if (cacheInstance) { if (cacheInstance) {
Promise.resolve(cacheInstance).then(instance => { Promise.resolve(cacheInstance).then(instance => {
callback({ prefixCls: `${prefixCls}-notice`, iconPrefixCls, instance }); callback({ prefixCls: `${prefixCls}-notice`, iconPrefixCls, instance });

View File

@ -2,7 +2,6 @@ import React from 'react';
import { mount } from 'enzyme'; import { mount } from 'enzyme';
import { spyElementPrototype } from 'rc-util/lib/test/domHook'; import { spyElementPrototype } from 'rc-util/lib/test/domHook';
import { render, fireEvent } from '@testing-library/react'; import { render, fireEvent } from '@testing-library/react';
import '@testing-library/jest-dom';
import Popconfirm from '..'; import Popconfirm from '..';
import mountTest from '../../../tests/shared/mountTest'; import mountTest from '../../../tests/shared/mountTest';
import { sleep } from '../../../tests/utils'; import { sleep } from '../../../tests/utils';

View File

@ -1,7 +1,6 @@
import React from 'react'; import React from 'react';
import { mount } from 'enzyme'; import { mount } from 'enzyme';
import { render } from '@testing-library/react'; import { render } from '@testing-library/react';
import '@testing-library/jest-dom';
import Popover from '..'; import Popover from '..';
import mountTest from '../../../tests/shared/mountTest'; import mountTest from '../../../tests/shared/mountTest';
import ConfigProvider from '../../config-provider'; import ConfigProvider from '../../config-provider';

View File

@ -1,7 +1,6 @@
import React from 'react'; import React from 'react';
import { mount } from 'enzyme'; import { mount } from 'enzyme';
import { render } from '@testing-library/react'; import { render } from '@testing-library/react';
import '@testing-library/jest-dom';
import Result from '..'; import Result from '..';
import Button from '../../button'; import Button from '../../button';
import mountTest from '../../../tests/shared/mountTest'; import mountTest from '../../../tests/shared/mountTest';

View File

@ -3,7 +3,6 @@ import React from 'react';
import { mount } from 'enzyme'; import { mount } from 'enzyme';
import { act } from 'react-dom/test-utils'; import { act } from 'react-dom/test-utils';
import { render, fireEvent } from '@testing-library/react'; import { render, fireEvent } from '@testing-library/react';
import '@testing-library/jest-dom';
import Table from '..'; import Table from '..';
import Input from '../../input'; import Input from '../../input';
import Tooltip from '../../tooltip'; import Tooltip from '../../tooltip';

View File

@ -1,7 +1,6 @@
import React from 'react'; import React from 'react';
import { mount } from 'enzyme'; import { mount } from 'enzyme';
import { render } from '@testing-library/react'; import { render } from '@testing-library/react';
import '@testing-library/jest-dom';
import Table from '..'; import Table from '..';
import mountTest from '../../../tests/shared/mountTest'; import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest'; import rtlTest from '../../../tests/shared/rtlTest';

View File

@ -1,7 +1,6 @@
import React from 'react'; import React from 'react';
import { mount } from 'enzyme'; import { mount } from 'enzyme';
import { render } from '@testing-library/react'; import { render } from '@testing-library/react';
import '@testing-library/jest-dom';
import Transfer from '../index'; import Transfer from '../index';
describe('Transfer.Customize', () => { describe('Transfer.Customize', () => {

View File

@ -1,7 +1,6 @@
import React from 'react'; import React from 'react';
import { mount } from 'enzyme'; import { mount } from 'enzyme';
import { render, fireEvent } from '@testing-library/react'; import { render, fireEvent } from '@testing-library/react';
import '@testing-library/jest-dom';
import Search from '../search'; import Search from '../search';
import Transfer from '../index'; import Transfer from '../index';

View File

@ -1,7 +1,6 @@
import React from 'react'; import React from 'react';
import { mount } from 'enzyme'; import { mount } from 'enzyme';
import { render } from '@testing-library/react'; import { render } from '@testing-library/react';
import '@testing-library/jest-dom';
import { SmileOutlined, LikeOutlined, HighlightOutlined, CheckOutlined } from '@ant-design/icons'; import { SmileOutlined, LikeOutlined, HighlightOutlined, CheckOutlined } from '@ant-design/icons';
import KeyCode from 'rc-util/lib/KeyCode'; import KeyCode from 'rc-util/lib/KeyCode';
import { resetWarned } from 'rc-util/lib/warning'; import { resetWarned } from 'rc-util/lib/warning';

View File

@ -2,7 +2,6 @@
import React from 'react'; import React from 'react';
import { mount } from 'enzyme'; import { mount } from 'enzyme';
import { render, fireEvent } from '@testing-library/react'; import { render, fireEvent } from '@testing-library/react';
import '@testing-library/jest-dom';
import { act } from 'react-dom/test-utils'; import { act } from 'react-dom/test-utils';
import produce from 'immer'; import produce from 'immer';
import { cloneDeep } from 'lodash'; import { cloneDeep } from 'lodash';

View File

@ -136,7 +136,7 @@
"rc-mentions": "~1.7.0", "rc-mentions": "~1.7.0",
"rc-menu": "~9.5.1", "rc-menu": "~9.5.1",
"rc-motion": "^2.4.4", "rc-motion": "^2.4.4",
"rc-notification": "~4.5.7", "rc-notification": "~4.6.0",
"rc-pagination": "~3.1.9", "rc-pagination": "~3.1.9",
"rc-picker": "~2.6.4", "rc-picker": "~2.6.4",
"rc-progress": "~3.2.1", "rc-progress": "~3.2.1",

View File

@ -1,3 +1,4 @@
import { toHaveNoViolations } from 'jest-axe'; import { toHaveNoViolations } from 'jest-axe';
import '@testing-library/jest-dom';
expect.extend(toHaveNoViolations); expect.extend(toHaveNoViolations);

View File

@ -1,6 +1,5 @@
import React from 'react'; import React from 'react';
import { render } from '@testing-library/react'; import { render } from '@testing-library/react';
import '@testing-library/jest-dom';
import { mount, ReactWrapper } from 'enzyme'; import { mount, ReactWrapper } from 'enzyme';
import { sleep } from '../utils'; import { sleep } from '../utils';