diff --git a/components/anchor/__tests__/Anchor.test.tsx b/components/anchor/__tests__/Anchor.test.tsx index 1a87f8dd57..996fcf01b5 100644 --- a/components/anchor/__tests__/Anchor.test.tsx +++ b/components/anchor/__tests__/Anchor.test.tsx @@ -1,8 +1,7 @@ import React from 'react'; -import { mount } from 'enzyme'; import Anchor from '..'; import type { InternalAnchorClass } from '../Anchor'; -import { sleep, render } from '../../../tests/utils'; +import { sleep, render, fireEvent } from '../../../tests/utils'; const { Link } = Anchor; @@ -48,42 +47,54 @@ describe('Anchor Render', () => { it('Anchor render perfectly', () => { const hash = getHashUrl(); - const wrapper = mount( - + let anchorInstance: InternalAnchorClass; + const { container } = render( + { + anchorInstance = node as InternalAnchorClass; + }} + > , ); - wrapper.find(`a[href="#${hash}"]`).simulate('click'); - const anchorInstance = wrapper.find('Anchor').last().instance() as any as InternalAnchorClass; - anchorInstance.handleScroll(); - expect(anchorInstance.state).not.toBe(null); + fireEvent.click(container.querySelector(`a[href="#${hash}"]`)!); + anchorInstance!.handleScroll(); + expect(anchorInstance!.state).not.toBe(null); }); it('Anchor render perfectly for complete href - click', () => { const hash = getHashUrl(); - const wrapper = mount( - + let anchorInstance: InternalAnchorClass; + const { container } = render( + { + anchorInstance = node as InternalAnchorClass; + }} + > , ); - wrapper.find(`a[href="http://www.example.com/#${hash}"]`).simulate('click'); - const anchorInstance = wrapper.find('Anchor').last().instance() as any as InternalAnchorClass; - expect(anchorInstance.state.activeLink).toBe(`http://www.example.com/#${hash}`); + fireEvent.click(container.querySelector(`a[href="http://www.example.com/#${hash}"]`)!); + expect(anchorInstance!.state!.activeLink).toBe(`http://www.example.com/#${hash}`); }); it('Anchor render perfectly for complete href - hash router', async () => { const root = createDiv(); const scrollToSpy = jest.spyOn(window, 'scrollTo'); - mount(
Q1
, { attachTo: root }); - const wrapper = mount( - + render(
Q1
, { container: root }); + let anchorInstance: InternalAnchorClass; + render( + { + anchorInstance = node as InternalAnchorClass; + }} + > , ); - const anchorInstance = wrapper.find('Anchor').last().instance() as any as InternalAnchorClass; - anchorInstance.handleScrollTo('/#/faq?locale=en#Q1'); - expect(anchorInstance.state.activeLink).toBe('/#/faq?locale=en#Q1'); + anchorInstance!.handleScrollTo('/#/faq?locale=en#Q1'); + expect(anchorInstance!.state.activeLink).toBe('/#/faq?locale=en#Q1'); expect(scrollToSpy).not.toHaveBeenCalled(); await sleep(1000); expect(scrollToSpy).toHaveBeenCalled(); @@ -92,32 +103,39 @@ describe('Anchor Render', () => { it('Anchor render perfectly for complete href - scroll', () => { const hash = getHashUrl(); const root = createDiv(); - mount(
Hello
, { attachTo: root }); - const wrapper = mount( - + render(
Hello
, { container: root }); + let anchorInstance: InternalAnchorClass; + render( + { + anchorInstance = node as InternalAnchorClass; + }} + > , ); - const anchorInstance = wrapper.find('Anchor').last().instance() as any as InternalAnchorClass; - - anchorInstance.handleScroll(); - expect(anchorInstance.state.activeLink).toBe(`http://www.example.com/#${hash}`); + anchorInstance!.handleScroll(); + expect(anchorInstance!.state!.activeLink).toBe(`http://www.example.com/#${hash}`); }); it('Anchor render perfectly for complete href - scrollTo', async () => { const hash = getHashUrl(); const scrollToSpy = jest.spyOn(window, 'scrollTo'); const root = createDiv(); - mount(
Hello
, { attachTo: root }); - const wrapper = mount( - + render(
Hello
, { container: root }); + let anchorInstance: InternalAnchorClass; + render( + { + anchorInstance = node as InternalAnchorClass; + }} + > , ); - const anchorInstance = wrapper.find('Anchor').last().instance() as any as InternalAnchorClass; - anchorInstance.handleScrollTo(`##${hash}`); - expect(anchorInstance.state.activeLink).toBe(`##${hash}`); + anchorInstance!.handleScrollTo(`##${hash}`); + expect(anchorInstance!.state.activeLink).toBe(`##${hash}`); const calls = scrollToSpy.mock.calls.length; await sleep(1000); expect(scrollToSpy.mock.calls.length).toBeGreaterThan(calls); @@ -125,15 +143,19 @@ describe('Anchor Render', () => { it('should remove listener when unmount', async () => { const hash = getHashUrl(); - const wrapper = mount( - + let anchorInstance: InternalAnchorClass; + const { unmount } = render( + { + anchorInstance = node as InternalAnchorClass; + }} + > , ); - const anchorInstance = wrapper.find('Anchor').last().instance() as any as InternalAnchorClass; - const removeListenerSpy = jest.spyOn((anchorInstance as any).scrollEvent, 'remove'); - wrapper.unmount(); + const removeListenerSpy = jest.spyOn((anchorInstance! as any).scrollEvent, 'remove'); + unmount(); expect(removeListenerSpy).toHaveBeenCalled(); }); @@ -154,23 +176,27 @@ describe('Anchor Render', () => { it('should update links when link href update', async () => { const hash = getHashUrl(); + let anchorInstance: InternalAnchorClass; function AnchorUpdate({ href }: { href: string }) { return ( - + { + anchorInstance = node as InternalAnchorClass; + }} + > ); } - const wrapper = mount(); - const anchorInstance = wrapper.find('Anchor').last().instance() as any as InternalAnchorClass; + const { rerender } = render(); - if (anchorInstance == null) { + if (anchorInstance! == null) { throw new Error('anchorInstance should not be null'); } - expect((anchorInstance as any).links).toEqual([`#${hash}`]); - wrapper.setProps({ href: `#${hash}_1` }); - expect((anchorInstance as any).links).toEqual([`#${hash}_1`]); + expect((anchorInstance as any)!.links).toEqual([`#${hash}`]); + rerender(); + expect((anchorInstance as any)!.links).toEqual([`#${hash}_1`]); }); it('Anchor onClick event', () => { @@ -187,18 +213,20 @@ describe('Anchor Render', () => { const href = `#${hash}`; const title = hash; - - const wrapper = mount( - + let anchorInstance: InternalAnchorClass; + const { container } = render( + { + anchorInstance = node as InternalAnchorClass; + }} + > , ); - wrapper.find(`a[href="${href}"]`).simulate('click'); - - const anchorInstance = wrapper.find('Anchor').last().instance() as any as InternalAnchorClass; - - anchorInstance.handleScroll(); + fireEvent.click(container.querySelector(`a[href="${href}"]`)!); + anchorInstance!.handleScroll(); expect(event).not.toBe(undefined); expect(link).toEqual({ href, title }); }); @@ -206,20 +234,28 @@ describe('Anchor Render', () => { it('Different function returns the same DOM', async () => { const hash = getHashUrl(); const root = createDiv(); - mount(
Hello
, { attachTo: root }); + render(
Hello
, { container: root }); const getContainerA = createGetContainer(hash); const getContainerB = createGetContainer(hash); - - const wrapper = mount( - + let anchorInstance: InternalAnchorClass; + const { rerender } = render( + { + anchorInstance = node as InternalAnchorClass; + }} + > , ); - const anchorInstance = wrapper.find('Anchor').last().instance() as any as InternalAnchorClass; - const removeListenerSpy = jest.spyOn((anchorInstance as any).scrollEvent, 'remove'); + const removeListenerSpy = jest.spyOn((anchorInstance! as any).scrollEvent, 'remove'); await sleep(1000); - wrapper.setProps({ getContainer: getContainerB }); + rerender( + + + , + ); expect(removeListenerSpy).not.toHaveBeenCalled(); }); @@ -227,57 +263,73 @@ describe('Anchor Render', () => { const hash1 = getHashUrl(); const hash2 = getHashUrl(); const root = createDiv(); - mount( + render(
Hello
World
, - { attachTo: root }, + { container: root }, ); const getContainerA = createGetContainer(hash1); const getContainerB = createGetContainer(hash2); - const wrapper = mount( - + let anchorInstance: InternalAnchorClass; + const { rerender } = render( + { + anchorInstance = node as InternalAnchorClass; + }} + > , ); - const anchorInstance = wrapper.find('Anchor').last().instance() as any as InternalAnchorClass; - const removeListenerSpy = jest.spyOn((anchorInstance as any).scrollEvent, 'remove'); + const removeListenerSpy = jest.spyOn((anchorInstance! as any).scrollEvent, 'remove'); expect(removeListenerSpy).not.toHaveBeenCalled(); await sleep(1000); - wrapper.setProps({ getContainer: getContainerB }); + rerender( + + + + , + ); expect(removeListenerSpy).toHaveBeenCalled(); }); it('Same function returns the same DOM', () => { const hash = getHashUrl(); const root = createDiv(); - mount(
Hello
, { attachTo: root }); + render(
Hello
, { container: root }); const getContainer = createGetContainer(hash); - const wrapper = mount( - + let anchorInstance: InternalAnchorClass; + const { container } = render( + { + anchorInstance = node as InternalAnchorClass; + }} + > , ); - wrapper.find(`a[href="#${hash}"]`).simulate('click'); - const anchorInstance = wrapper.find('Anchor').last().instance() as any as InternalAnchorClass; - (anchorInstance as any).handleScroll(); - expect(anchorInstance.state).not.toBe(null); + fireEvent.click(container.querySelector(`a[href="#${hash}"]`)!); + + anchorInstance!.handleScroll(); + expect(anchorInstance!.state).not.toBe(null); }); it('Same function returns different DOM', async () => { const hash1 = getHashUrl(); const hash2 = getHashUrl(); const root = createDiv(); - mount( + render(
Hello
World
, - { attachTo: root }, + { container: root }, ); const holdContainer = { container: document.getElementById(hash1), @@ -288,19 +340,28 @@ describe('Anchor Render', () => { } return holdContainer.container; }; - const wrapper = mount( + let anchorInstance: InternalAnchorClass; + const { rerender } = render( + { + anchorInstance = node as InternalAnchorClass; + }} + > + + + , + ); + const removeListenerSpy = jest.spyOn((anchorInstance! as any).scrollEvent, 'remove'); + expect(removeListenerSpy).not.toHaveBeenCalled(); + await sleep(1000); + holdContainer.container = document.getElementById(hash2); + rerender( , ); - const anchorInstance = wrapper.find('Anchor').last().instance() as any as InternalAnchorClass; - - const removeListenerSpy = jest.spyOn((anchorInstance as any).scrollEvent, 'remove'); - expect(removeListenerSpy).not.toHaveBeenCalled(); - await sleep(1000); - holdContainer.container = document.getElementById(hash2); - wrapper.setProps({ 'data-only-trigger-re-render': true }); expect(removeListenerSpy).toHaveBeenCalled(); }); @@ -323,27 +384,45 @@ describe('Anchor Render', () => { const scrollToSpy = jest.spyOn(window, 'scrollTo'); const root = createDiv(); - mount(

Hello

, { attachTo: root }); - const wrapper = mount( - + render(

Hello

, { container: root }); + let anchorInstance: InternalAnchorClass; + const { rerender } = render( + { + anchorInstance = node as InternalAnchorClass; + }} + > , ); - const anchorInstance = wrapper.find('Anchor').last().instance() as any as InternalAnchorClass; - anchorInstance.handleScrollTo(`#${hash}`); + const setProps = (props: Record) => + rerender( + { + anchorInstance = node as InternalAnchorClass; + }} + {...props} + > + + , + ); + + anchorInstance!.handleScrollTo(`#${hash}`); await sleep(30); expect(scrollToSpy).toHaveBeenLastCalledWith(0, 1000); dateNowMock = dataNowMockFn(); - wrapper.setProps({ offsetTop: 100 }); - anchorInstance.handleScrollTo(`#${hash}`); + setProps({ offsetTop: 100 }); + + anchorInstance!.handleScrollTo(`#${hash}`); await sleep(30); expect(scrollToSpy).toHaveBeenLastCalledWith(0, 900); dateNowMock = dataNowMockFn(); - wrapper.setProps({ targetOffset: 200 }); - anchorInstance.handleScrollTo(`#${hash}`); + setProps({ targetOffset: 200 }); + + anchorInstance!.handleScrollTo(`#${hash}`); await sleep(30); expect(scrollToSpy).toHaveBeenLastCalledWith(0, 800); @@ -370,27 +449,43 @@ describe('Anchor Render', () => { const scrollToSpy = jest.spyOn(window, 'scrollTo'); const root = createDiv(); - mount(

Hello

, { attachTo: root }); - const wrapper = mount( - + render(

Hello

, { container: root }); + let anchorInstance: InternalAnchorClass; + const { rerender } = render( + { + anchorInstance = node as InternalAnchorClass; + }} + > , ); - const anchorInstance = wrapper.find('Anchor').last().instance() as any as InternalAnchorClass; - anchorInstance.handleScrollTo(`#${hash}`); + const setProps = (props: Record) => + rerender( + { + anchorInstance = node as InternalAnchorClass; + }} + {...props} + > + + , + ); + + anchorInstance!.handleScrollTo(`#${hash}`); await sleep(30); expect(scrollToSpy).toHaveBeenLastCalledWith(0, 1000); dateNowMock = dataNowMockFn(); - wrapper.setProps({ offsetTop: 100 }); - anchorInstance.handleScrollTo(`#${hash}`); + setProps({ offsetTop: 100 }); + anchorInstance!.handleScrollTo(`#${hash}`); await sleep(30); expect(scrollToSpy).toHaveBeenLastCalledWith(0, 900); dateNowMock = dataNowMockFn(); - wrapper.setProps({ targetOffset: 200 }); - anchorInstance.handleScrollTo(`#${hash}`); + setProps({ targetOffset: 200 }); + anchorInstance!.handleScrollTo(`#${hash}`); await sleep(30); expect(scrollToSpy).toHaveBeenLastCalledWith(0, 800); @@ -401,33 +496,44 @@ describe('Anchor Render', () => { const hash1 = getHashUrl(); const hash2 = getHashUrl(); const onChange = jest.fn(); - const wrapper = mount( - + let anchorInstance: InternalAnchorClass; + render( + { + anchorInstance = node as InternalAnchorClass; + }} + > , + // https://github.com/testing-library/react-testing-library/releases/tag/v13.0.0 + // @ts-ignore + { legacyRoot: true }, ); - const anchorInstance = wrapper.find('Anchor').last().instance() as any as InternalAnchorClass; expect(onChange).toHaveBeenCalledTimes(1); - anchorInstance.handleScrollTo(hash2); + anchorInstance!.handleScrollTo(hash2); expect(onChange).toHaveBeenCalledTimes(2); expect(onChange).toHaveBeenCalledWith(hash2); }); it('invalid hash', async () => { - const wrapper = mount( - + let anchorInstance: InternalAnchorClass; + const { container } = render( + { + anchorInstance = node as InternalAnchorClass; + }} + > , ); - wrapper.find(`a[href="notexsited"]`).simulate('click'); + fireEvent.click(container.querySelector(`a[href="notexsited"]`)!); - const anchorInstance = wrapper.find('Anchor').last().instance() as any as InternalAnchorClass; - - anchorInstance.handleScrollTo('notexsited'); - expect(anchorInstance.state).not.toBe(null); + anchorInstance!.handleScrollTo('notexsited'); + expect(anchorInstance!.state).not.toBe(null); }); it('test edge case when getBoundingClientRect return zero size', async () => { @@ -454,27 +560,42 @@ describe('Anchor Render', () => { const scrollToSpy = jest.spyOn(window, 'scrollTo'); const root = createDiv(); - mount(

Hello

, { attachTo: root }); - const wrapper = mount( - + render(

Hello

, { container: root }); + let anchorInstance: InternalAnchorClass; + const { rerender } = render( + { + anchorInstance = node as InternalAnchorClass; + }} + > , ); - const anchorInstance = wrapper.find('Anchor').last().instance() as any as InternalAnchorClass; - anchorInstance.handleScrollTo(`#${hash}`); + const setProps = (props: Record) => + rerender( + { + anchorInstance = node as InternalAnchorClass; + }} + {...props} + > + + , + ); + anchorInstance!.handleScrollTo(`#${hash}`); await sleep(30); expect(scrollToSpy).toHaveBeenLastCalledWith(0, 1000); dateNowMock = dataNowMockFn(); - wrapper.setProps({ offsetTop: 100 }); - anchorInstance.handleScrollTo(`#${hash}`); + setProps({ offsetTop: 100 }); + anchorInstance!.handleScrollTo(`#${hash}`); await sleep(30); expect(scrollToSpy).toHaveBeenLastCalledWith(0, 900); dateNowMock = dataNowMockFn(); - wrapper.setProps({ targetOffset: 200 }); - anchorInstance.handleScrollTo(`#${hash}`); + setProps({ targetOffset: 200 }); + anchorInstance!.handleScrollTo(`#${hash}`); await sleep(30); expect(scrollToSpy).toHaveBeenLastCalledWith(0, 800); @@ -505,27 +626,44 @@ describe('Anchor Render', () => { const scrollToSpy = jest.spyOn(window, 'scrollTo'); const root = createDiv(); - mount(

Hello

, { attachTo: root }); - const wrapper = mount( - document.body}> + render(

Hello

, { container: root }); + + let anchorInstance: InternalAnchorClass; + const { rerender } = render( + document.body} + ref={node => { + anchorInstance = node as InternalAnchorClass; + }} + > , ); - const anchorInstance = wrapper.find('Anchor').last().instance() as any as InternalAnchorClass; - anchorInstance.handleScrollTo(`#${hash}`); + const setProps = (props: Record) => + rerender( + document.body} + ref={node => { + anchorInstance = node as InternalAnchorClass; + }} + {...props} + > + + , + ); + anchorInstance!.handleScrollTo(`#${hash}`); await sleep(30); expect(scrollToSpy).toHaveBeenLastCalledWith(0, 800); dateNowMock = dataNowMockFn(); - wrapper.setProps({ offsetTop: 100 }); - anchorInstance.handleScrollTo(`#${hash}`); + setProps({ offsetTop: 100 }); + anchorInstance!.handleScrollTo(`#${hash}`); await sleep(30); expect(scrollToSpy).toHaveBeenLastCalledWith(0, 800); dateNowMock = dataNowMockFn(); - - wrapper.setProps({ targetOffset: 200 }); - anchorInstance.handleScrollTo(`#${hash}`); + setProps({ targetOffset: 200 }); + anchorInstance!.handleScrollTo(`#${hash}`); await sleep(30); expect(scrollToSpy).toHaveBeenLastCalledWith(0, 800); @@ -537,15 +675,20 @@ describe('Anchor Render', () => { const hash1 = getHashUrl(); const hash2 = getHashUrl(); const getCurrentAnchor = () => `#${hash2}`; - const wrapper = mount( - + let anchorInstance: InternalAnchorClass; + render( + { + anchorInstance = node as InternalAnchorClass; + }} + > , ); - const anchorInstance = wrapper.find('Anchor').last().instance() as any as InternalAnchorClass; - expect(anchorInstance.state.activeLink).toBe(`#${hash2}`); + expect(anchorInstance!.state.activeLink).toBe(`#${hash2}`); }); // https://github.com/ant-design/ant-design/issues/30584 @@ -553,16 +696,25 @@ describe('Anchor Render', () => { const hash1 = getHashUrl(); const hash2 = getHashUrl(); const onChange = jest.fn(); - const wrapper = mount( - hash1}> + let anchorInstance: InternalAnchorClass; + render( + hash1} + ref={node => { + anchorInstance = node as InternalAnchorClass; + }} + > , + // https://github.com/testing-library/react-testing-library/releases/tag/v13.0.0 + // @ts-ignore + { legacyRoot: true }, ); - const anchorInstance = wrapper.find('Anchor').last().instance() as any as InternalAnchorClass; expect(onChange).toHaveBeenCalledTimes(1); - anchorInstance.handleScrollTo(hash2); + anchorInstance!.handleScrollTo(hash2); expect(onChange).toHaveBeenCalledTimes(2); expect(onChange).toHaveBeenCalledWith(hash2); }); @@ -572,16 +724,16 @@ describe('Anchor Render', () => { const hash1 = getHashUrl(); const hash2 = getHashUrl(); const getCurrentAnchor = jest.fn(); - const wrapper = mount( + const { container } = render( , ); - wrapper.find(`a[href="#${hash1}"]`).simulate('click'); + fireEvent.click(container.querySelector(`a[href="#${hash1}"]`)!); expect(getCurrentAnchor).toHaveBeenCalledWith(`#${hash1}`); - wrapper.find(`a[href="#${hash2}"]`).simulate('click'); + fireEvent.click(container.querySelector(`a[href="#${hash2}"]`)!); expect(getCurrentAnchor).toHaveBeenCalledWith(`#${hash2}`); }); }); diff --git a/components/anchor/__tests__/cached-context.test.tsx b/components/anchor/__tests__/cached-context.test.tsx index 0fa0db6f93..27c3f683df 100644 --- a/components/anchor/__tests__/cached-context.test.tsx +++ b/components/anchor/__tests__/cached-context.test.tsx @@ -1,7 +1,7 @@ import React, { memo, useState, useRef, useContext } from 'react'; -import { mount } from 'enzyme'; import Anchor from '../Anchor'; import AnchorContext from '../context'; +import { getNodeText, render, fireEvent } from '../../../tests/utils'; // we use'memo' here in order to only render inner component while context changed. const CacheInner = memo(() => { @@ -38,14 +38,16 @@ const CacheOuter = () => { }; it("Rendering on Anchor without changed AnchorContext won't trigger rendering on child component.", () => { - const wrapper = mount(); - const childCount = wrapper.find('#child_count').text(); - wrapper.find('#parent_btn').at(0).simulate('click'); - expect(wrapper.find('#parent_count').text()).toBe('2'); + const { container } = render(); + const childCount = getNodeText(container.querySelector('#child_count')!); + + fireEvent.click(container.querySelector('#parent_btn')!); + + expect(getNodeText(container.querySelector('#parent_count')!)).toBe('2'); // child component won't rerender - expect(wrapper.find('#child_count').text()).toBe(childCount); - wrapper.find('#parent_btn').at(0).simulate('click'); - expect(wrapper.find('#parent_count').text()).toBe('3'); + expect(getNodeText(container.querySelector('#child_count')!)).toBe(childCount); + fireEvent.click(container.querySelector('#parent_btn')!); + expect(getNodeText(container.querySelector('#parent_count')!)).toBe('3'); // child component won't rerender - expect(wrapper.find('#child_count').text()).toBe(childCount); + expect(getNodeText(container.querySelector('#child_count')!)).toBe(childCount); }); diff --git a/components/carousel/index.tsx b/components/carousel/index.tsx index 8f8a99c7ba..23d386b1f2 100644 --- a/components/carousel/index.tsx +++ b/components/carousel/index.tsx @@ -31,7 +31,17 @@ export interface CarouselRef { } const Carousel = React.forwardRef( - ({ dots = true, arrows = false, draggable = false, dotPosition = 'bottom', ...props }, ref) => { + ( + { + dots = true, + arrows = false, + draggable = false, + dotPosition = 'bottom', + vertical = dotPosition === 'left' || dotPosition === 'right', + ...props + }, + ref, + ) => { const { getPrefixCls, direction } = React.useContext(ConfigContext); const slickRef = React.useRef(); @@ -61,6 +71,7 @@ const Carousel = React.forwardRef( }, [props.children]); const newProps = { + vertical, ...props, }; @@ -70,7 +81,6 @@ const Carousel = React.forwardRef( const prefixCls = getPrefixCls('carousel', newProps.prefixCls); const dotsClass = 'slick-dots'; - newProps.vertical = dotPosition === 'left' || dotPosition === 'right'; const enableDots = !!dots; const dsClass = classNames( diff --git a/components/checkbox/__tests__/checkbox.test.js b/components/checkbox/__tests__/checkbox.test.js index 274ef9c6c0..f7a4bf89f6 100644 --- a/components/checkbox/__tests__/checkbox.test.js +++ b/components/checkbox/__tests__/checkbox.test.js @@ -1,5 +1,5 @@ import React from 'react'; -import { mount } from 'enzyme'; +import { render, fireEvent } from '../../../tests/utils'; import Checkbox from '..'; import focusTest from '../../../tests/shared/focusTest'; import { resetWarned } from '../../_util/devWarning'; @@ -15,12 +15,14 @@ describe('Checkbox', () => { const onMouseEnter = jest.fn(); const onMouseLeave = jest.fn(); - const wrapper = mount(); + const { container } = render( + , + ); - wrapper.find('label').simulate('mouseenter'); + fireEvent.mouseEnter(container.querySelector('label')); expect(onMouseEnter).toHaveBeenCalled(); - wrapper.find('label').simulate('mouseleave'); + fireEvent.mouseLeave(container.querySelector('label')); expect(onMouseLeave).toHaveBeenCalled(); }); @@ -28,7 +30,7 @@ describe('Checkbox', () => { resetWarned(); const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); - mount(); + render(); expect(errorSpy).toHaveBeenCalledWith( 'Warning: [antd: Checkbox] `value` is not a valid prop, do you mean `checked`?', ); diff --git a/components/checkbox/__tests__/group.test.js b/components/checkbox/__tests__/group.test.js index b16a84d665..ff10263105 100644 --- a/components/checkbox/__tests__/group.test.js +++ b/components/checkbox/__tests__/group.test.js @@ -1,5 +1,4 @@ import React, { useState } from 'react'; -import { mount } from 'enzyme'; import Collapse from '../../collapse'; import Table from '../../table'; import Checkbox from '../index'; @@ -14,16 +13,16 @@ describe('CheckboxGroup', () => { it('should work basically', () => { const onChange = jest.fn(); - const wrapper = mount( + const { container } = render( , ); - wrapper.find('.ant-checkbox-input').at(0).simulate('change'); + fireEvent.click(container.querySelectorAll('.ant-checkbox-input')[0]); expect(onChange).toHaveBeenCalledWith(['Apple']); - wrapper.find('.ant-checkbox-input').at(1).simulate('change'); + fireEvent.click(container.querySelectorAll('.ant-checkbox-input')[1]); expect(onChange).toHaveBeenCalledWith(['Apple', 'Pear']); - wrapper.find('.ant-checkbox-input').at(2).simulate('change'); + fireEvent.click(container.querySelectorAll('.ant-checkbox-input')[2]); expect(onChange).toHaveBeenCalledWith(['Apple', 'Pear', 'Orange']); - wrapper.find('.ant-checkbox-input').at(1).simulate('change'); + fireEvent.click(container.querySelectorAll('.ant-checkbox-input')[1]); expect(onChange).toHaveBeenCalledWith(['Apple', 'Orange']); }); @@ -35,12 +34,12 @@ describe('CheckboxGroup', () => { { label: 'Pear', value: 'Pear' }, ]; - const groupWrapper = mount( + const { container } = render( , ); - groupWrapper.find('.ant-checkbox-input').at(0).simulate('change'); + fireEvent.click(container.querySelectorAll('.ant-checkbox-input')[0]); expect(onChangeGroup).not.toHaveBeenCalled(); - groupWrapper.find('.ant-checkbox-input').at(1).simulate('change'); + fireEvent.click(container.querySelectorAll('.ant-checkbox-input')[1]); expect(onChangeGroup).not.toHaveBeenCalled(); }); @@ -52,17 +51,17 @@ describe('CheckboxGroup', () => { { label: 'Orange', value: 'Orange', disabled: true }, ]; - const groupWrapper = mount(); - groupWrapper.find('.ant-checkbox-input').at(0).simulate('change'); + const { container } = render(); + fireEvent.click(container.querySelectorAll('.ant-checkbox-input')[0]); expect(onChangeGroup).toHaveBeenCalledWith(['Apple']); - groupWrapper.find('.ant-checkbox-input').at(1).simulate('change'); + fireEvent.click(container.querySelectorAll('.ant-checkbox-input')[1]); expect(onChangeGroup).toHaveBeenCalledWith(['Apple']); }); it('all children should have a name property', () => { - const wrapper = mount(); - wrapper.find('input[type="checkbox"]').forEach(el => { - expect(el.props().name).toEqual('checkboxgroup'); + const { container } = render(); + [...container.querySelectorAll('input[type="checkbox"]')].forEach(el => { + expect(el.getAttribute('name')).toEqual('checkboxgroup'); }); }); @@ -72,9 +71,9 @@ describe('CheckboxGroup', () => { { label: 'Orange', value: 'Orange', style: { fontSize: 12 } }, ]; - const wrapper = mount(); + const { container } = render(); - expect(wrapper.render()).toMatchSnapshot(); + expect(container.firstChild).toMatchSnapshot(); }); it('should be controlled by value', () => { @@ -82,23 +81,22 @@ describe('CheckboxGroup', () => { { label: 'Apple', value: 'Apple' }, { label: 'Orange', value: 'Orange' }, ]; - - const wrapper = mount(); - expect(wrapper.find('.ant-checkbox-checked').length).toBe(0); - wrapper.setProps({ value: ['Apple'] }); - wrapper.update(); - expect(wrapper.find('.ant-checkbox-checked').length).toBe(1); + const renderCheckbox = props => ; + const { container, rerender } = render(renderCheckbox({ options })); + expect(container.querySelectorAll('.ant-checkbox-checked').length).toBe(0); + rerender(renderCheckbox({ options, value: 'Apple' })); + expect(container.querySelectorAll('.ant-checkbox-checked').length).toBe(1); }); // https://github.com/ant-design/ant-design/issues/12642 it('should trigger onChange in sub Checkbox', () => { const onChange = jest.fn(); - const wrapper = mount( + const { container } = render( , ); - wrapper.find('.ant-checkbox-input').at(0).simulate('change'); + fireEvent.click(container.querySelectorAll('.ant-checkbox-input')[0]); expect(onChange).toHaveBeenCalled(); expect(onChange.mock.calls[0][0].target.value).toEqual('my'); }); @@ -143,7 +141,7 @@ describe('CheckboxGroup', () => { // https://github.com/ant-design/ant-design/issues/17297 it('onChange should keep the order of the original values', () => { const onChange = jest.fn(); - const wrapper = mount( + const { container } = render( @@ -151,20 +149,19 @@ describe('CheckboxGroup', () => { , ); - - wrapper.find('.ant-checkbox-input').at(0).simulate('change'); + fireEvent.click(container.querySelectorAll('.ant-checkbox-input')[0]); expect(onChange).toHaveBeenCalledWith([1]); - wrapper.find('.ant-checkbox-input').at(1).simulate('change'); + fireEvent.click(container.querySelectorAll('.ant-checkbox-input')[1]); expect(onChange).toHaveBeenCalledWith([1, 2]); - wrapper.find('.ant-checkbox-input').at(0).simulate('change'); + fireEvent.click(container.querySelectorAll('.ant-checkbox-input')[0]); expect(onChange).toHaveBeenCalledWith([2]); - wrapper.find('.ant-checkbox-input').at(0).simulate('change'); + fireEvent.click(container.querySelectorAll('.ant-checkbox-input')[0]); expect(onChange).toHaveBeenCalledWith([1, 2]); }); // https://github.com/ant-design/ant-design/issues/21134 it('should work when checkbox is wrapped by other components', () => { - const wrapper = mount( + const { container } = render( @@ -175,28 +172,31 @@ describe('CheckboxGroup', () => { , ); - wrapper.find('.ant-collapse-item').at(0).find('.ant-collapse-header').simulate('click'); - wrapper.find('.ant-checkbox-input').at(0).simulate('change'); - expect(wrapper.find('.ant-checkbox-checked').length).toBe(1); - wrapper.find('.ant-checkbox-input').at(0).simulate('change'); - expect(wrapper.find('.ant-checkbox-checked').length).toBe(0); + + fireEvent.click( + container.querySelector('.ant-collapse-item').querySelector('.ant-collapse-header'), + ); + fireEvent.click(container.querySelector('.ant-checkbox-input')); + expect(container.querySelectorAll('.ant-checkbox-checked').length).toBe(1); + fireEvent.click(container.querySelector('.ant-checkbox-input')); + expect(container.querySelectorAll('.ant-checkbox-checked').length).toBe(0); }); it('skipGroup', () => { const onChange = jest.fn(); - const wrapper = mount( + const { container } = render( , ); - wrapper.find('.ant-checkbox-input').at(1).simulate('change'); + fireEvent.click(container.querySelectorAll('.ant-checkbox-input')[1]); expect(onChange).not.toHaveBeenCalled(); }); it('Table rowSelection', () => { const onChange = jest.fn(); - const wrapper = mount( + const { container } = render( { /> , ); - wrapper.find('.ant-checkbox-input').at(1).simulate('change'); + fireEvent.click(container.querySelectorAll('.ant-checkbox-input')[1]); expect(onChange).not.toHaveBeenCalled(); }); it('should get div ref', () => { - mount( + const refCalls = []; + render( { - expect(node.nodeName).toBe('DIV'); + refCalls.push(node); }} />, ); + const [mountCall] = refCalls; + expect(mountCall.nodeName).toBe('DIV'); }); it('should support number option', () => { const onChange = jest.fn(); - const wrapper = mount(); + const { container } = render( + , + ); - wrapper.find('.ant-checkbox-input').at(0).simulate('change'); + fireEvent.click(container.querySelector('.ant-checkbox-input')); expect(onChange).toHaveBeenCalledWith([1]); }); @@ -251,17 +256,13 @@ describe('CheckboxGroup', () => { ); }; - const wrapper = mount(); - - wrapper.find('.ant-checkbox-input').first().simulate('change'); + const { container } = render(); + fireEvent.click(container.querySelector('.ant-checkbox-input')); expect(onChange).toHaveBeenCalledWith([]); - wrapper.find('.ant-checkbox-input').first().simulate('change'); + fireEvent.click(container.querySelector('.ant-checkbox-input')); expect(onChange).toHaveBeenCalledWith(['length1']); - wrapper - .find('.ant-input') - .first() - .simulate('change', { target: { value: '' } }); - wrapper.find('.ant-checkbox-input').first().simulate('change'); + fireEvent.change(container.querySelector('.ant-input'), { target: { value: '' } }); + fireEvent.click(container.querySelector('.ant-checkbox-input')); expect(onChange).toHaveBeenCalledWith(['A']); }); }); diff --git a/components/input/__tests__/index.test.tsx b/components/input/__tests__/index.test.tsx index 602f7fac0d..2b02ce39f9 100644 --- a/components/input/__tests__/index.test.tsx +++ b/components/input/__tests__/index.test.tsx @@ -1,5 +1,6 @@ import React, { useState } from 'react'; import { mount } from 'enzyme'; +import { createPortal } from 'react-dom'; import { render, fireEvent } from '../../../tests/utils'; // eslint-disable-next-line import/no-unresolved import Form from '../../form'; @@ -75,6 +76,40 @@ describe('Input', () => { }); }); + describe('click focus', () => { + it('click outside should also get focus', () => { + const { container } = render(} />); + const onFocus = jest.spyOn(container.querySelector('input')!, 'focus'); + fireEvent.mouseDown(container.querySelector('.test-suffix')!); + fireEvent.mouseUp(container.querySelector('.test-suffix')!); + expect(onFocus).toHaveBeenCalled(); + }); + + it('not get focus if out of component', () => { + const holder = document.createElement('span'); + document.body.appendChild(holder); + + const Popup = () => createPortal(, holder); + + const { container } = render( + + + + } + />, + ); + + const onFocus = jest.spyOn(container.querySelector('input')!, 'focus'); + fireEvent.mouseDown(document.querySelector('.popup')!); + fireEvent.mouseUp(document.querySelector('.popup')!); + + expect(onFocus).not.toHaveBeenCalled(); + document.body.removeChild(holder); + }); + }); + it('set mouse cursor position', () => { const defaultValue = '11111'; const valLength = defaultValue.length; diff --git a/components/input/__tests__/textarea.test.js b/components/input/__tests__/textarea.test.js index 5d314c7eb9..1f6788c9bc 100644 --- a/components/input/__tests__/textarea.test.js +++ b/components/input/__tests__/textarea.test.js @@ -404,27 +404,6 @@ describe('TextArea allowClear', () => { expect(wrapper.find('input').props().value).toEqual('Light'); }); - describe('click focus', () => { - it('click outside should also get focus', () => { - const wrapper = mount(} />); - const onFocus = jest.spyOn(wrapper.find('input').instance(), 'focus'); - wrapper.find('.test-suffix').simulate('mouseUp'); - expect(onFocus).toHaveBeenCalled(); - }); - - it('not get focus if out of component', () => { - const wrapper = mount(} />); - const onFocus = jest.spyOn(wrapper.find('input').instance(), 'focus'); - const ele = document.createElement('span'); - document.body.appendChild(ele); - wrapper.find('.test-suffix').simulate('mouseUp', { - target: ele, - }); - expect(onFocus).not.toHaveBeenCalled(); - document.body.removeChild(ele); - }); - }); - it('scroll to bottom when autoSize', async () => { const wrapper = mount(, { attachTo: document.body }); wrapper.find('textarea').simulate('focus'); diff --git a/components/notification/index.tsx b/components/notification/index.tsx index fe743147f7..40a1783baf 100755 --- a/components/notification/index.tsx +++ b/components/notification/index.tsx @@ -206,6 +206,7 @@ export interface ArgsProps { duration?: number | null; icon?: React.ReactNode; placement?: NotificationPlacement; + maxCount?: number; style?: React.CSSProperties; prefixCls?: string; className?: string; diff --git a/components/segmented/style/index.less b/components/segmented/style/index.less index ec15358a0e..fd44ee1189 100644 --- a/components/segmented/style/index.less +++ b/components/segmented/style/index.less @@ -12,7 +12,7 @@ padding: @segmented-container-padding; color: @segmented-label-color; background-color: @segmented-bg; - border-radius: 2px; + border-radius: @border-radius-base; transition: all 0.3s @ease-in-out; &-group { @@ -67,7 +67,7 @@ // syntactic sugar to add `icon` for Segmented Item &-icon + * { - margin-left: 6px; + margin-left: @margin-sm / 2; } &-input { diff --git a/components/space/__tests__/__snapshots__/index.test.js.snap b/components/space/__tests__/__snapshots__/index.test.js.snap index 97918ea1eb..4a9b6aa9b1 100644 --- a/components/space/__tests__/__snapshots__/index.test.js.snap +++ b/components/space/__tests__/__snapshots__/index.test.js.snap @@ -29,7 +29,7 @@ exports[`Space should render correct with children 1`] = ` `; exports[`Space should render width ConfigProvider 1`] = ` -Array [ +HTMLCollection [
@@ -91,7 +91,7 @@ Array [ `; exports[`Space should render width rtl 1`] = ` -Array [ +HTMLCollection [
diff --git a/components/space/__tests__/gap.test.js b/components/space/__tests__/gap.test.js index 748c61c632..b36125faaf 100644 --- a/components/space/__tests__/gap.test.js +++ b/components/space/__tests__/gap.test.js @@ -1,5 +1,5 @@ import React from 'react'; -import { mount } from 'enzyme'; +import { render } from '../../../tests/utils'; import Space from '..'; // eslint-disable-next-line no-unused-vars import * as styleChecker from '../../_util/styleChecker'; @@ -12,13 +12,14 @@ jest.mock('../../_util/styleChecker', () => ({ describe('flex gap', () => { it('should render width empty children', () => { - const wrapper = mount( + const { container } = render( , ); - expect(wrapper.getDOMNode()[1].style['column-gap']).toBe('8px'); - expect(wrapper.getDOMNode()[1].style['row-gap']).toBe('8px'); + + expect(container.querySelector('div.ant-space').style['column-gap']).toBe('8px'); + expect(container.querySelector('div.ant-space').style['row-gap']).toBe('8px'); }); }); diff --git a/components/space/__tests__/index.test.js b/components/space/__tests__/index.test.js index b811ff34a5..0af061cd00 100644 --- a/components/space/__tests__/index.test.js +++ b/components/space/__tests__/index.test.js @@ -1,6 +1,5 @@ import React, { useState } from 'react'; -import { mount } from 'enzyme'; -import { act } from 'react-dom/test-utils'; +import { render, fireEvent } from '../../../tests/utils'; import Space from '..'; import ConfigProvider from '../../config-provider'; import mountTest from '../../../tests/shared/mountTest'; @@ -11,13 +10,13 @@ describe('Space', () => { rtlTest(Space); it('should render width empty children', () => { - const wrapper = mount(); + const { container } = render(); - expect(wrapper.instance()).toBe(null); + expect(container.children.length).toBe(0); }); it('should render width ConfigProvider', () => { - const wrapper = mount( + const { container } = render( 1 @@ -34,11 +33,11 @@ describe('Space', () => { , ); - expect(wrapper.render()).toMatchSnapshot(); + expect(container.children).toMatchSnapshot(); }); it('should render width rtl', () => { - const wrapper = mount( + const { container } = render( 1 @@ -55,46 +54,46 @@ describe('Space', () => { , ); - expect(wrapper.render()).toMatchSnapshot(); + expect(container.children).toMatchSnapshot(); }); it('should render width customize size', () => { - const wrapper = mount( + const { container } = render( 1 2 , ); - expect(wrapper.find('div.ant-space-item').at(0).prop('style').marginRight).toBe(10); - expect(wrapper.find('div.ant-space-item').at(1).prop('style').marginRight).toBeUndefined(); + expect(container.querySelector('div.ant-space-item').style.marginRight).toBe('10px'); + expect(container.querySelectorAll('div.ant-space-item')[1].style.marginRight).toBe(''); }); it('should render width size 0', () => { - const wrapper = mount( + const { container } = render( 1 2 , ); - expect(wrapper.find('div.ant-space-item').at(0).prop('style').marginRight).toBe(0); + expect(container.querySelector('div.ant-space-item').style.marginRight).toBe('0px'); }); it('should render vertical space width customize size', () => { - const wrapper = mount( + const { container } = render( 1 2 , ); - expect(wrapper.find('div.ant-space-item').at(0).prop('style').marginBottom).toBe(10); - expect(wrapper.find('div.ant-space-item').at(1).prop('style').marginBottom).toBeUndefined(); + expect(container.querySelector('div.ant-space-item').style.marginBottom).toBe('10px'); + expect(container.querySelectorAll('div.ant-space-item')[1].style.marginBottom).toBe(''); }); it('should render correct with children', () => { - const wrapper = mount( + const { container } = render( text1text1 {/* eslint-disable-next-line react/jsx-no-useless-fragment */} @@ -102,18 +101,18 @@ describe('Space', () => { , ); - expect(wrapper.render()).toMatchSnapshot(); + expect(container.children[0]).toMatchSnapshot(); }); it('should render with invalidElement', () => { - const wrapper = mount( + const { container } = render( text1text1 text1 , ); - expect(wrapper.find('div.ant-space-item').length).toBe(3); + expect(container.querySelectorAll('div.ant-space-item').length).toBe(3); }); it('should be keep store', () => { @@ -144,25 +143,21 @@ describe('Space', () => { ); } - const wrapper = mount(); + const { container } = render(); - expect(wrapper.find('#demo').text()).toBe('1'); + expect(container.querySelector('#demo')).toHaveTextContent('1'); - act(() => { - wrapper.find('#demo').simulate('click'); - }); + fireEvent.click(container.querySelector('#demo')); - expect(wrapper.find('#demo').text()).toBe('2'); + expect(container.querySelector('#demo')).toHaveTextContent('2'); - act(() => { - wrapper.find('p').simulate('click'); - }); + fireEvent.click(container.querySelector('p')); - expect(wrapper.find('#demo').text()).toBe('2'); + expect(container.querySelector('#demo')).toHaveTextContent('2'); }); it('split', () => { - const wrapper = mount( + const { container } = render( text1text1 {/* eslint-disable-next-line react/jsx-no-useless-fragment */} @@ -170,13 +165,13 @@ describe('Space', () => { , ); - expect(wrapper.render()).toMatchSnapshot(); + expect(container.children[0]).toMatchSnapshot(); }); // https://github.com/ant-design/ant-design/issues/35305 it('should not throw duplicated key warning', () => { jest.spyOn(console, 'error').mockImplementation(() => undefined); - mount( + render(
diff --git a/components/space/index.tsx b/components/space/index.tsx index 5b54591841..72c1784433 100644 --- a/components/space/index.tsx +++ b/components/space/index.tsx @@ -92,15 +92,12 @@ const Space: React.FC = props => { latestIndex = i; } - const keyOfChild = child && child.key; - // Add `-space-item` as suffix in case simple key string trigger duplicated key warning - // https://github.com/ant-design/ant-design/issues/35305 - const defaultKey = `${i}-space-item`; + const key = (child && child.key) || `${itemClassName}-${i}`; return ( { ); expect(errorSpy).not.toBeCalled(); }); + + it('can reset if filterResetToDefaultFilteredValue and filter is changing', () => { + const wrapper = mount( + createTable({ + columns: [ + { + ...column, + filters: [ + { text: 'Jack', value: 'Jack' }, + { text: 'Lucy', value: 'Lucy' }, + ], + defaultFilteredValue: ['Jack'], + filterResetToDefaultFilteredValue: true, + }, + ], + }), + ); + expect(wrapper.find('tbody tr').length).toBe(1); + expect(wrapper.find('tbody tr').text()).toBe('Jack'); + + // open filter + wrapper.find('span.ant-dropdown-trigger').first().simulate('click'); + expect( + wrapper.find('.ant-table-filter-dropdown-btns .ant-btn-link').props().disabled, + ).toBeTruthy(); + expect(wrapper.find('li.ant-dropdown-menu-item').at(0).text()).toBe('Jack'); + expect(wrapper.find('li.ant-dropdown-menu-item').at(1).text()).toBe('Lucy'); + + // deselect default + wrapper.find('li.ant-dropdown-menu-item').at(0).simulate('click'); + expect( + wrapper.find('.ant-table-filter-dropdown-btns .ant-btn-link').props().disabled, + ).toBeFalsy(); + // select other one + wrapper.find('li.ant-dropdown-menu-item').at(1).simulate('click'); + expect( + wrapper.find('.ant-table-filter-dropdown-btns .ant-btn-link').props().disabled, + ).toBeFalsy(); + // deselect other one + wrapper.find('li.ant-dropdown-menu-item').at(1).simulate('click'); + expect( + wrapper.find('.ant-table-filter-dropdown-btns .ant-btn-link').props().disabled, + ).toBeFalsy(); + // select default + wrapper.find('li.ant-dropdown-menu-item').at(0).simulate('click'); + expect( + wrapper.find('.ant-table-filter-dropdown-btns .ant-btn-link').props().disabled, + ).toBeTruthy(); + }); }); diff --git a/components/table/hooks/useFilter/FilterDropdown.tsx b/components/table/hooks/useFilter/FilterDropdown.tsx index 4d5fa69f1e..8408fa1673 100644 --- a/components/table/hooks/useFilter/FilterDropdown.tsx +++ b/components/table/hooks/useFilter/FilterDropdown.tsx @@ -407,16 +407,22 @@ function FilterDropdown(props: FilterDropdownProps) { ); }; + const getResetDisabled = () => { + if (filterResetToDefaultFilteredValue) { + return isEqual( + (defaultFilteredValue || []).map(key => String(key)), + selectedKeys, + ); + } + + return selectedKeys.length === 0; + }; + dropdownContent = ( <> {getFilterComponent()}
-