From 85fd0640c5199fdd13cdd6ade098c437c9b2c12d Mon Sep 17 00:00:00 2001 From: Zack Chang <73225408+jrr997@users.noreply.github.com> Date: Wed, 4 May 2022 20:44:37 +0800 Subject: [PATCH 01/12] test: refactor test cases of Space (#35372) --- .../__snapshots__/index.test.js.snap | 4 +- components/space/__tests__/gap.test.js | 8 +-- components/space/__tests__/index.test.js | 61 +++++++++---------- 3 files changed, 34 insertions(+), 39 deletions(-) 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 6b88a9afc9..f41dca6099 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,13 @@ jest.mock('../../_util/styleChecker', () => ({ describe('flex gap', () => { it('should render width empty children', () => { - const wrapper = mount( + const { container } = render( , ); - expect(wrapper.getDOMNode().style['column-gap']).toBe('8px'); - expect(wrapper.getDOMNode().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(
From 44b9ab7e40060b6697845157b5555b06814c9ad7 Mon Sep 17 00:00:00 2001 From: linqiqi077 <865530219@qq.com> Date: Wed, 4 May 2022 23:14:07 +0800 Subject: [PATCH 02/12] test: migrate part of anchor tests (#35371) --- components/anchor/__tests__/Anchor.test.tsx | 436 ++++++++++++------ .../anchor/__tests__/cached-context.test.tsx | 20 +- 2 files changed, 305 insertions(+), 151 deletions(-) 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); }); From 50d3d99a78bedd8e84bc1466f7a0c354344178ed Mon Sep 17 00:00:00 2001 From: TrickyPi <33021497+TrickyPi@users.noreply.github.com> Date: Thu, 5 May 2022 10:25:22 +0800 Subject: [PATCH 03/12] test: migrate part of checkbox tests (#35354) --- .../checkbox/__tests__/checkbox.test.js | 12 +- components/checkbox/__tests__/group.test.js | 109 +++++++++--------- 2 files changed, 62 insertions(+), 59 deletions(-) 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']); }); }); From a217301cb723c9bc1068f1221f9e92eaacc86e17 Mon Sep 17 00:00:00 2001 From: haipeng Date: Thu, 5 May 2022 10:52:41 +0800 Subject: [PATCH 04/12] fix: Carousel compatible with vertical property (#35349) --- components/carousel/index.tsx | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/components/carousel/index.tsx b/components/carousel/index.tsx index d8975a38d0..7953ee9706 100644 --- a/components/carousel/index.tsx +++ b/components/carousel/index.tsx @@ -30,7 +30,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(); @@ -60,6 +70,7 @@ const Carousel = React.forwardRef( }, [props.children]); const newProps = { + vertical, ...props, }; @@ -69,7 +80,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( @@ -80,7 +90,7 @@ const Carousel = React.forwardRef( const className = classNames(prefixCls, { [`${prefixCls}-rtl`]: direction === 'rtl', - [`${prefixCls}-vertical`]: newProps.vertical, + [`${prefixCls}-vertical`]: dotPosition === 'left' || dotPosition === 'right', }); return ( From 978226fb13a7ec2c4ee07bb8eeba6bb18188a622 Mon Sep 17 00:00:00 2001 From: afc163 Date: Thu, 5 May 2022 10:58:35 +0800 Subject: [PATCH 05/12] chore: fix npm start error in Gitpod close #33444 --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 2486dea145..0320a005c9 100644 --- a/package.json +++ b/package.json @@ -287,6 +287,7 @@ "stylelint-order": "^5.0.0", "theme-switcher": "^1.0.2", "typescript": "~4.6.2", + "webpack": "^4.46.0", "webpack-bundle-analyzer": "^4.1.0", "xhr-mock": "^2.4.1", "yaml-front-matter": "^4.0.0" From e039b10b2f72f01df81829f478960d2ccc50d5d5 Mon Sep 17 00:00:00 2001 From: afc163 Date: Thu, 5 May 2022 11:27:49 +0800 Subject: [PATCH 06/12] Revert "chore: fix npm start error in Gitpod" This reverts commit 978226fb13a7ec2c4ee07bb8eeba6bb18188a622. --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index 0320a005c9..2486dea145 100644 --- a/package.json +++ b/package.json @@ -287,7 +287,6 @@ "stylelint-order": "^5.0.0", "theme-switcher": "^1.0.2", "typescript": "~4.6.2", - "webpack": "^4.46.0", "webpack-bundle-analyzer": "^4.1.0", "xhr-mock": "^2.4.1", "yaml-front-matter": "^4.0.0" From 6c0f67e4074e7e33108f5def556225971ce4fb26 Mon Sep 17 00:00:00 2001 From: lalalazero Date: Thu, 5 May 2022 12:17:15 +0800 Subject: [PATCH 07/12] fix: table column filter reset is not working (#35226) (#35386) --- .../table/__tests__/Table.filter.test.js | 49 +++++++++++++++++++ .../table/hooks/useFilter/FilterDropdown.tsx | 18 ++++--- 2 files changed, 61 insertions(+), 6 deletions(-) diff --git a/components/table/__tests__/Table.filter.test.js b/components/table/__tests__/Table.filter.test.js index cf74e8d1cd..569c6badfa 100644 --- a/components/table/__tests__/Table.filter.test.js +++ b/components/table/__tests__/Table.filter.test.js @@ -2305,4 +2305,53 @@ describe('Table.filter', () => { ); 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()}
-