From fdc246dc24b038da0f8f43ecf6716e244b90fa18 Mon Sep 17 00:00:00 2001 From: Matheus Wichman Date: Tue, 25 Feb 2020 19:05:42 -0300 Subject: [PATCH 01/13] Migrate DataGridCell tests to react-testing-library --- .../src/list/DatagridCell.spec.js | 32 ++++++++++++------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/packages/ra-ui-materialui/src/list/DatagridCell.spec.js b/packages/ra-ui-materialui/src/list/DatagridCell.spec.js index 55be959ff63..d3aa6128a0c 100644 --- a/packages/ra-ui-materialui/src/list/DatagridCell.spec.js +++ b/packages/ra-ui-materialui/src/list/DatagridCell.spec.js @@ -1,12 +1,21 @@ import assert from 'assert'; import React from 'react'; import PropTypes from 'prop-types'; -import { shallow } from 'enzyme'; +import { render, cleanup } from '@testing-library/react'; import DatagridCell from './DatagridCell'; +const renderWithTable = element => + render( + + + {element} + +
+ ); + describe('', () => { - const Field = () =>
; + const Field = ({ basePath }) =>
{basePath}
; Field.propTypes = { type: PropTypes.string, basePath: PropTypes.string, @@ -16,25 +25,24 @@ describe('', () => { type: 'foo', }; - it('should render as a mui component', () => { - const wrapper = shallow(} />); - const col = wrapper.find('WithStyles(ForwardRef(TableCell))'); - assert.equal(col.length, 1); + it('should render as a mui component', () => { + const { getByRole } = renderWithTable( + } /> + ); + assert.equal(getByRole('cell').className, 'MuiTableCell-root'); }); it('should pass the Datagrid basePath by default', () => { - const wrapper = shallow( + const { queryByText } = renderWithTable( } /> ); - const col = wrapper.find('Field'); - assert.equal(col.prop('basePath'), 'default'); + assert.notEqual(queryByText('default'), null); }); it('should allow to overwrite the `basePath` field', () => { - const wrapper = shallow( + const { queryByText } = renderWithTable( } /> ); - const col = wrapper.find('Field'); - assert.equal(col.prop('basePath'), 'new'); + assert.notEqual(queryByText('new'), null); }); }); From e5930e5fccef2140107045f19782b6cedb853505 Mon Sep 17 00:00:00 2001 From: Matheus Wichman Date: Tue, 25 Feb 2020 19:06:02 -0300 Subject: [PATCH 02/13] Migrate SingleFieldList tests to react-testing-library --- .../src/list/SingleFieldList.spec.js | 166 ++++++++---------- 1 file changed, 72 insertions(+), 94 deletions(-) diff --git a/packages/ra-ui-materialui/src/list/SingleFieldList.spec.js b/packages/ra-ui-materialui/src/list/SingleFieldList.spec.js index 0fa69802177..658f72327d0 100644 --- a/packages/ra-ui-materialui/src/list/SingleFieldList.spec.js +++ b/packages/ra-ui-materialui/src/list/SingleFieldList.spec.js @@ -1,13 +1,26 @@ import React from 'react'; import assert from 'assert'; -import { shallow } from 'enzyme'; +import { render, cleanup } from '@testing-library/react'; +import { createMemoryHistory } from 'history'; +import { Router } from 'react-router-dom'; import SingleFieldList from './SingleFieldList'; import ChipField from '../field/ChipField'; +const renderWithRouter = children => { + const history = createMemoryHistory(); + + return { + history, + ...render({children}), + }; +}; + describe('', () => { + afterEach(cleanup); + it('should render a link to the Edit page of the related record by default', () => { - const wrapper = shallow( + const { queryAllByRole } = renderWithRouter( ', () => { ); - const linkElements = wrapper.find('Link'); + const linkElements = queryAllByRole('link'); assert.equal(linkElements.length, 2); - assert.deepEqual(linkElements.map(link => link.prop('to')), [ + assert.deepEqual(linkElements.map(link => link.getAttribute('href')), [ '/posts/1', '/posts/2', ]); }); it('should render a link to the Edit page of the related record when the resource contains slashes', () => { - const wrapper = shallow( + const { queryAllByRole } = renderWithRouter( ', () => { ); - const linkElements = wrapper.find('Link'); + const linkElements = queryAllByRole('link'); assert.equal(linkElements.length, 2); - assert.deepEqual(linkElements.map(link => link.prop('to')), [ + assert.deepEqual(linkElements.map(link => link.getAttribute('href')), [ '/posts/1', '/posts/2', ]); }); it('should render a link to the Edit page of the related record when the resource is named edit or show', () => { - let wrapper = shallow( - - - - ); - let linkElements = wrapper.find('Link'); - assert.equal(linkElements.length, 2); - assert.deepEqual(linkElements.map(link => link.prop('to')), [ - '/edit/1', - '/edit/2', - ]); - - wrapper = shallow( - - - - ); - linkElements = wrapper.find('Link'); - assert.equal(linkElements.length, 2); - assert.deepEqual(linkElements.map(link => link.prop('to')), [ - '/show/1', - '/show/2', - ]); + ['edit', 'show'].forEach(action => { + const { queryAllByRole } = renderWithRouter( + + + + ); + const linkElements = queryAllByRole('link'); + assert.equal(linkElements.length, 2); + assert.deepEqual( + linkElements.map(link => link.getAttribute('href')), + [`/${action}/1`, `/${action}/2`] + ); + cleanup(); + }); }); it('should render a link to the Show page of the related record when the linkType is show', () => { - const wrapper = shallow( + const { queryAllByRole } = renderWithRouter( ', () => { ); - const linkElements = wrapper.find('Link'); + const linkElements = queryAllByRole('link'); assert.equal(linkElements.length, 2); - assert.deepEqual(linkElements.map(link => link.prop('to')), [ + assert.deepEqual(linkElements.map(link => link.getAttribute('href')), [ '/prefix/bar/1/show', '/prefix/bar/2/show', ]); }); it('should render a link to the Edit page of the related record when the resource is named edit or show and linkType is show', () => { - let wrapper = shallow( - - - - ); - let linkElements = wrapper.find('Link'); - assert.equal(linkElements.length, 2); - assert.deepEqual(linkElements.map(link => link.prop('to')), [ - '/edit/1/show', - '/edit/2/show', - ]); - - wrapper = shallow( - - - - ); - linkElements = wrapper.find('Link'); - assert.equal(linkElements.length, 2); - assert.deepEqual(linkElements.map(link => link.prop('to')), [ - '/show/1/show', - '/show/2/show', - ]); + ['edit', 'show'].forEach(action => { + const { queryAllByRole } = renderWithRouter( + + + + ); + const linkElements = queryAllByRole('link'); + assert.equal(linkElements.length, 2); + assert.deepEqual( + linkElements.map(link => link.getAttribute('href')), + [`/${action}/1/show`, `/${action}/2/show`] + ); + cleanup(); + }); }); it('should render no link when the linkType is false', () => { - const wrapper = shallow( + const { queryAllByRole, queryByText } = renderWithRouter( ', () => { ); - const linkElements = wrapper.find('Link'); + const linkElements = queryAllByRole('link'); assert.equal(linkElements.length, 0); - const chipElements = wrapper.find('EnhancedChipField'); - assert.equal(chipElements.length, 2); + assert.notEqual(queryByText('foo'), null); + assert.notEqual(queryByText('bar'), null); }); }); From 40c787e8130b6f8a552184ae1d4864e0da96e13f Mon Sep 17 00:00:00 2001 From: Matheus Wichman Date: Tue, 25 Feb 2020 19:24:05 -0300 Subject: [PATCH 03/13] Migrate CloneButton tests to react-testing-library --- .../src/button/CloneButton.spec.tsx | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/packages/ra-ui-materialui/src/button/CloneButton.spec.tsx b/packages/ra-ui-materialui/src/button/CloneButton.spec.tsx index c786f58f6c3..bd738644fc8 100644 --- a/packages/ra-ui-materialui/src/button/CloneButton.spec.tsx +++ b/packages/ra-ui-materialui/src/button/CloneButton.spec.tsx @@ -1,19 +1,28 @@ import expect from 'expect'; -import { shallow } from 'enzyme'; +import { ThemeProvider, createMuiTheme } from '@material-ui/core'; +import { render } from '@testing-library/react'; import React from 'react'; +import { createMemoryHistory } from 'history'; +import { Router } from 'react-router-dom'; import { CloneButton } from './CloneButton'; +const theme = createMuiTheme(); + describe('', () => { it('should pass a clone of the record in the location state', () => { - const wrapper = shallow( - + const history = createMemoryHistory(); + const { getByRole } = render( + + + + + ); - expect(wrapper.prop('to')).toEqual( - expect.objectContaining({ - search: 'source=%7B%22foo%22%3A%22bar%22%7D', - }) + const button = getByRole('button'); + expect(button.getAttribute('href')).toEqual( + '/create?source=%7B%22foo%22%3A%22bar%22%7D' ); }); }); From f257275783ee534c2e1271b0d2b335ae829292fd Mon Sep 17 00:00:00 2001 From: Matheus Wichman Date: Tue, 25 Feb 2020 21:29:24 -0300 Subject: [PATCH 04/13] Migrate PaginationActions tests to react-testing-library --- .../src/list/PaginationActions.spec.js | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/ra-ui-materialui/src/list/PaginationActions.spec.js b/packages/ra-ui-materialui/src/list/PaginationActions.spec.js index 6f0c46d126b..52d5842841f 100644 --- a/packages/ra-ui-materialui/src/list/PaginationActions.spec.js +++ b/packages/ra-ui-materialui/src/list/PaginationActions.spec.js @@ -1,11 +1,13 @@ import React from 'react'; -import { shallow } from 'enzyme'; +import { render, cleanup } from '@testing-library/react'; import PaginationActions from './PaginationActions'; describe('', () => { + afterEach(cleanup); + it('should not render any actions when no pagination is necessary', () => { - const wrapper = shallow( + const { queryAllByRole } = render( ', () => { classes={{}} /> ); - expect(wrapper.find('WithStyles(ForwardRef(Button))')).toHaveLength(0); - expect(wrapper.find('WithStyles(ForwardRef(Typography))')).toHaveLength( - 0 - ); + expect(queryAllByRole('button')).toHaveLength(0); }); + it('should render action buttons when pagination is necessary', () => { - const wrapper = shallow( + const { queryAllByRole } = render( ', () => { /> ); // 1 2 3 next - expect(wrapper.find('WithStyles(ForwardRef(Button))')).toHaveLength(4); + expect(queryAllByRole('button')).toHaveLength(4); }); it('should skip page action buttons when there are too many', () => { - const wrapper = shallow( + const { queryAllByRole } = render( ', () => { /> ); // prev 1 ... 7 8 9 ... 15 next - expect(wrapper.find('WithStyles(ForwardRef(Button))')).toHaveLength(7); + expect(queryAllByRole('button')).toHaveLength(7); }); }); From c33db70b3481e0edd195166704d100fe0d4f8b39 Mon Sep 17 00:00:00 2001 From: Matheus Wichman Date: Wed, 26 Feb 2020 20:49:17 -0300 Subject: [PATCH 05/13] Migrate FunctionField tests to react-testing-library --- .../src/field/FunctionField.spec.js | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/packages/ra-ui-materialui/src/field/FunctionField.spec.js b/packages/ra-ui-materialui/src/field/FunctionField.spec.js index fb1c751d576..26e99fa6e20 100644 --- a/packages/ra-ui-materialui/src/field/FunctionField.spec.js +++ b/packages/ra-ui-materialui/src/field/FunctionField.spec.js @@ -1,26 +1,27 @@ import React from 'react'; import assert from 'assert'; -import { render, shallow } from 'enzyme'; +import { render, cleanup } from '@testing-library/react'; import FunctionField from './FunctionField'; describe('', () => { + afterEach(cleanup); + it('should render using the render function', () => { const record = { foo: 'bar' }; - const wrapper = render( + const { queryByText } = render( r.foo.substr(0, 2)} /> ); - assert.equal(wrapper.text(), 'ba'); + assert.notEqual(queryByText('ba'), null); }); - it('should use custom className', () => - assert.deepEqual( - shallow( - r.foo.substr(0, 2)} - className="foo" - /> - ).prop('className'), - 'foo' - )); + it('should use custom className', () => { + const { queryByText } = render( + r.foo} + className="foo" + /> + ); + assert.ok(queryByText('bar').classList.contains('foo')); + }); }); From e4b9a7c6a472cb4ec74e9d022defc7b4a14e4403 Mon Sep 17 00:00:00 2001 From: Matheus Wichman Date: Wed, 26 Feb 2020 21:06:53 -0300 Subject: [PATCH 06/13] Migrate SimpleShowLayout tests to react-testing-library --- .../src/detail/SimpleShowLayout.spec.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/ra-ui-materialui/src/detail/SimpleShowLayout.spec.js b/packages/ra-ui-materialui/src/detail/SimpleShowLayout.spec.js index bb8fad1c7ff..22cf9ecd7a1 100644 --- a/packages/ra-ui-materialui/src/detail/SimpleShowLayout.spec.js +++ b/packages/ra-ui-materialui/src/detail/SimpleShowLayout.spec.js @@ -1,18 +1,18 @@ import React from 'react'; -import { shallow } from 'enzyme'; +import { render } from '@testing-library/react'; import SimpleShowLayout from './SimpleShowLayout'; import TextField from '../field/TextField'; describe('', () => { it('should display children inputs of SimpleShowLayout', () => { - const wrapper = shallow( - + const { queryByText } = render( + ); - const inputs = wrapper.find('EnhancedTextField'); - expect(inputs.map(i => i.prop('source'))).toEqual(['foo', 'bar']); + expect(queryByText('foo')).not.toBeNull(); + expect(queryByText('bar')).not.toBeNull(); }); }); From 3e338fcbe6f2b9db9ac379c136eb282489ced449 Mon Sep 17 00:00:00 2001 From: Matheus Wichman Date: Wed, 26 Feb 2020 22:04:16 -0300 Subject: [PATCH 07/13] Migrate FormField tests to react-testing-library --- packages/ra-core/src/form/FormField.spec.tsx | 46 +++++++++++++++----- 1 file changed, 35 insertions(+), 11 deletions(-) diff --git a/packages/ra-core/src/form/FormField.spec.tsx b/packages/ra-core/src/form/FormField.spec.tsx index fd937831427..449a03b34fe 100644 --- a/packages/ra-core/src/form/FormField.spec.tsx +++ b/packages/ra-core/src/form/FormField.spec.tsx @@ -1,21 +1,45 @@ import assert from 'assert'; -import { shallow } from 'enzyme'; +import { render, fireEvent, cleanup } from '@testing-library/react'; +import { Form } from 'react-final-form'; import React from 'react'; import FormField from './FormField'; describe('', () => { - const Foo = () =>
; + afterEach(cleanup); + + const Foo = ({ input }) => ; + it('should render a component for the input component', () => { - const wrapper = shallow(); - const component = wrapper.find('Field'); - assert.equal(component.length, 1); - assert.equal(wrapper.prop('component'), Foo); + let formApi; + const { getByRole } = render( +
{ + formApi = form; + return ; + }} + /> + ); + const input = getByRole('textbox'); + fireEvent.change(input, { target: { value: 'Lorem' } }); + assert.equal(formApi.getState().values.title, 'Lorem'); }); - it('should not render a component the field has an input', () => { - const wrapper = shallow( - + + it('should not render a component if the field has an input', () => { + let formApi; + const { getByRole } = render( + { + formApi = form; + return ( + + ); + }} + /> ); - const component = wrapper.find('Field'); - assert.equal(component.length, 0); + const input = getByRole('textbox'); + fireEvent.change(input, { target: { value: 'Lorem' } }); + assert.notEqual(formApi.getState().values.title, 'Lorem'); }); }); From 7a9ccce0edebaebde44be10675256a8c3135e8b5 Mon Sep 17 00:00:00 2001 From: Matheus Wichman Date: Wed, 26 Feb 2020 22:12:00 -0300 Subject: [PATCH 08/13] Migrate RoutesWithLayout tests to react-testing-library --- .../src/core/RoutesWithLayout.spec.tsx | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/packages/ra-core/src/core/RoutesWithLayout.spec.tsx b/packages/ra-core/src/core/RoutesWithLayout.spec.tsx index f55ac6009ba..6f68767f91d 100644 --- a/packages/ra-core/src/core/RoutesWithLayout.spec.tsx +++ b/packages/ra-core/src/core/RoutesWithLayout.spec.tsx @@ -2,12 +2,14 @@ import React from 'react'; import { Route, MemoryRouter } from 'react-router-dom'; import { createStore } from 'redux'; import { Provider } from 'react-redux'; -import { mount } from 'enzyme'; +import { render, cleanup } from '@testing-library/react'; import assert from 'assert'; import RoutesWithLayout from './RoutesWithLayout'; describe('', () => { + afterEach(cleanup); + const Dashboard = () =>
Dashboard
; const Custom = ({ name }) =>
Custom
; const FirstResource = ({ name }) =>
Default
; @@ -20,7 +22,7 @@ describe('', () => { })); it('should show dashboard on / when provided', () => { - const wrapper = mount( + const { queryByText } = render( @@ -31,11 +33,12 @@ describe('', () => { ); - assert.equal(wrapper.find(Dashboard).length, 1); + + assert.notEqual(queryByText('Dashboard'), null); }); it('should show the first resource on / when there is only one resource and no dashboard', () => { - const wrapper = mount( + const { queryByText } = render( @@ -45,11 +48,11 @@ describe('', () => { ); - assert.equal(wrapper.find(FirstResource).length, 1); + assert.notEqual(queryByText('Default'), null); }); it('should show the first resource on / when there are multiple resource and no dashboard', () => { - const wrapper = mount( + const { queryByText } = render( @@ -61,15 +64,15 @@ describe('', () => { ); - assert.equal(wrapper.find(FirstResource).length, 1); - assert.equal(wrapper.find(Resource).length, 0); + assert.notEqual(queryByText('Default'), null); + assert.equal(queryByText('Resource'), null); }); it('should accept custom routes', () => { const customRoutes = [ , ]; // eslint-disable-line react/jsx-key - const wrapper = mount( + const { queryByText } = render( @@ -80,6 +83,6 @@ describe('', () => { ); - assert.equal(wrapper.find(Custom).length, 1); + assert.notEqual(queryByText('Custom'), null); }); }); From 70ca1120a3d586e697b11ce8b8b0126714360d73 Mon Sep 17 00:00:00 2001 From: Matheus Wichman Date: Thu, 27 Feb 2020 16:21:52 -0300 Subject: [PATCH 09/13] Migrate Responsive tests to react-testing-library --- .../src/layout/Responsive.spec.js | 184 ++++++++---------- 1 file changed, 86 insertions(+), 98 deletions(-) diff --git a/packages/ra-ui-materialui/src/layout/Responsive.spec.js b/packages/ra-ui-materialui/src/layout/Responsive.spec.js index 7e4eba1a119..06b380960e0 100644 --- a/packages/ra-ui-materialui/src/layout/Responsive.spec.js +++ b/packages/ra-ui-materialui/src/layout/Responsive.spec.js @@ -1,16 +1,17 @@ -import assert from 'assert'; -import { shallow } from 'enzyme'; +import { render, cleanup } from '@testing-library/react'; import React from 'react'; import { Responsive } from './Responsive'; describe('', () => { - const Small = () =>
; - const Medium = () =>
; - const Large = () =>
; + afterEach(cleanup); + + const Small = () =>
Small
; + const Medium = () =>
Medium
; + const Large = () =>
Large
; it('should render the small component on small screens', () => { - const wrapper = shallow( + const { queryByText } = render( } medium={} @@ -18,22 +19,22 @@ describe('', () => { width="xs" /> ); - const component = wrapper.find('Small'); - assert.equal(component.length, 1); + expect(queryByText('Small')).not.toBeNull(); + expect(queryByText('Medium')).toBeNull(); + expect(queryByText('Large')).toBeNull(); }); - it('should render the small component on small screens and small is null', () => { - const wrapper = shallow( - } - large={} - width="xs" - /> + + it('should render the medium component on small screens and small is null', () => { + const { queryByText } = render( + } large={} width="xs" /> ); - assert.equal(wrapper.get(0), null); + expect(queryByText('Small')).toBeNull(); + expect(queryByText('Medium')).not.toBeNull(); + expect(queryByText('Large')).toBeNull(); }); + it('should render the medium component on medium screens', () => { - const wrapper = shallow( + const { queryByText } = render( } medium={} @@ -41,22 +42,22 @@ describe('', () => { width="md" /> ); - const component = wrapper.find('Medium'); - assert.equal(component.length, 1); + expect(queryByText('Small')).toBeNull(); + expect(queryByText('Medium')).not.toBeNull(); + expect(queryByText('Large')).toBeNull(); }); - it('should render the medium component on medium screens and medium is null', () => { - const wrapper = shallow( - } - medium={null} - large={} - width="md" - /> + + it('should render the large component on medium screens and medium is null', () => { + const { queryByText } = render( + } large={} width="md" /> ); - assert.equal(wrapper.get(0), null); + expect(queryByText('Small')).toBeNull(); + expect(queryByText('Medium')).toBeNull(); + expect(queryByText('Large')).not.toBeNull(); }); + it('should render the large component on large screens', () => { - const wrapper = shallow( + const { queryByText } = render( } medium={} @@ -64,93 +65,80 @@ describe('', () => { width="lg" /> ); - const component = wrapper.find('Large'); - assert.equal(component.length, 1); + expect(queryByText('Small')).toBeNull(); + expect(queryByText('Medium')).toBeNull(); + expect(queryByText('Large')).not.toBeNull(); }); - it('should render the large component on large screens and large is null', () => { - const wrapper = shallow( - } - medium={} - large={null} - width="lg" - /> + + it('should render the medium component on large screens and large is null', () => { + const { queryByText } = render( + } medium={} width="lg" /> ); - assert.equal(wrapper.get(0), null); + expect(queryByText('Small')).toBeNull(); + expect(queryByText('Medium')).not.toBeNull(); + expect(queryByText('Large')).toBeNull(); }); + it('should render the small component on all screens when no other component is passed', () => { - assert.equal( - shallow(} width="xs" />).find('Small') - .length, - 1 - ); - assert.equal( - shallow(} width="sm" />).find('Small') - .length, - 1 - ); - assert.equal( - shallow(} width="lg" />).find('Small') - .length, - 1 - ); + ['xs', 'sm', 'lg'].forEach(width => { + const { queryByText } = render( + } width={width} /> + ); + expect(queryByText('Small')).not.toBeNull(); + expect(queryByText('Medium')).toBeNull(); + expect(queryByText('Large')).toBeNull(); + cleanup(); + }); }); + it('should render the medium component on all screens when no other component is passed', () => { - assert.equal( - shallow(} width="xs" />).find( - 'Medium' - ).length, - 1 - ); - assert.equal( - shallow(} width="sm" />).find( - 'Medium' - ).length, - 1 - ); - assert.equal( - shallow(} width="lg" />).find( - 'Medium' - ).length, - 1 - ); + ['xs', 'sm', 'lg'].forEach(width => { + const { queryByText } = render( + } width={width} /> + ); + expect(queryByText('Small')).toBeNull(); + expect(queryByText('Medium')).not.toBeNull(); + expect(queryByText('Large')).toBeNull(); + cleanup(); + }); }); + it('should render the large component on all screens when no other component is passed', () => { - assert.equal( - shallow(} width="xs" />).find('Large') - .length, - 1 - ); - assert.equal( - shallow(} width="sm" />).find('Large') - .length, - 1 - ); - assert.equal( - shallow(} width="lg" />).find('Large') - .length, - 1 - ); + ['xs', 'sm', 'lg'].forEach(width => { + const { queryByText } = render( + } width={width} /> + ); + expect(queryByText('Small')).toBeNull(); + expect(queryByText('Medium')).toBeNull(); + expect(queryByText('Large')).not.toBeNull(); + cleanup(); + }); }); + it('should fallback to the large component on medium screens', () => { - const wrapper = shallow( + const { queryByText } = render( } large={} width="md" /> ); - const component = wrapper.find('Large'); - assert.equal(component.length, 1); + expect(queryByText('Small')).toBeNull(); + expect(queryByText('Medium')).toBeNull(); + expect(queryByText('Large')).not.toBeNull(); }); + it('should fallback to the medium component on small screens', () => { - const wrapper = shallow( + const { queryByText } = render( } large={} width="sm" /> ); - const component = wrapper.find('Medium'); - assert.equal(component.length, 1); + expect(queryByText('Small')).toBeNull(); + expect(queryByText('Medium')).not.toBeNull(); + expect(queryByText('Large')).toBeNull(); }); + it('should fallback to the medium component on large screens', () => { - const wrapper = shallow( + const { queryByText } = render( } medium={} width="lg" /> ); - const component = wrapper.find('Medium'); - assert.equal(component.length, 1); + expect(queryByText('Small')).toBeNull(); + expect(queryByText('Medium')).not.toBeNull(); + expect(queryByText('Large')).toBeNull(); }); }); From 8fbfab9f7f2b34c9583aa46d9afea2d4aa029cab Mon Sep 17 00:00:00 2001 From: Matheus Wichman Date: Thu, 27 Feb 2020 19:20:10 -0300 Subject: [PATCH 10/13] Migrate ReferenceArrayInput tests to react-testing-library --- .../src/input/ReferenceArrayInput.spec.js | 107 ++++++++---------- 1 file changed, 49 insertions(+), 58 deletions(-) diff --git a/packages/ra-ui-materialui/src/input/ReferenceArrayInput.spec.js b/packages/ra-ui-materialui/src/input/ReferenceArrayInput.spec.js index 9dd2ce64520..5c8d17597d4 100644 --- a/packages/ra-ui-materialui/src/input/ReferenceArrayInput.spec.js +++ b/packages/ra-ui-materialui/src/input/ReferenceArrayInput.spec.js @@ -1,9 +1,10 @@ import React from 'react'; -import assert from 'assert'; -import { shallow } from 'enzyme'; +import { render, cleanup } from '@testing-library/react'; import { ReferenceArrayInputView } from './ReferenceArrayInput'; describe('', () => { + afterEach(cleanup); + const defaultProps = { input: {}, meta: {}, @@ -13,10 +14,10 @@ describe('', () => { source: 'tag_ids', translate: x => `*${x}*`, }; - const MyComponent = () => ; - it('should render a LinearProgress if loading is true', () => { - const wrapper = shallow( + it('should render a progress bar if loading is true', () => { + const MyComponent = () =>
MyComponent
; + const { queryByRole, queryByText } = render( ', () => { ); - const MyComponentElement = wrapper.find('MyComponent'); - assert.equal(MyComponentElement.length, 0); - const LinearProgressElement = wrapper.find('LinearProgress'); - assert.equal(LinearProgressElement.length, 1); + expect(queryByRole('progressbar')).not.toBeNull(); + expect(queryByText('MyComponent')).toBeNull(); }); it('should display an error if error is defined', () => { - const wrapper = shallow( + const MyComponent = () =>
MyComponent
; + const { queryByDisplayValue, queryByText } = render( ); - const MyComponentElement = wrapper.find('MyComponent'); - assert.equal(MyComponentElement.length, 0); - const ErrorElement = wrapper.find('ReferenceError'); - assert.equal(ErrorElement.length, 1); - assert.equal( - ErrorElement.prop('error'), - 'ra.input.references.all_missing' - ); + expect(queryByDisplayValue('error')).not.toBeNull(); + expect(queryByText('MyComponent')).toBeNull(); }); it('should send an error to the children if warning is defined', () => { - const wrapper = shallow( + const MyComponent = ({ meta }) =>
{meta.helperText}
; + const { queryByText, queryByRole } = render( ', () => { ); - const ErrorElement = wrapper.find('ReferenceError'); - assert.equal(ErrorElement.length, 0); - const MyComponentElement = wrapper.find('MyComponent'); - assert.equal(MyComponentElement.length, 1); - assert.deepEqual(MyComponentElement.prop('meta'), { - helperText: 'fetch error', - }); + expect(queryByRole('textbox')).toBeNull(); + expect(queryByText('fetch error')).not.toBeNull(); }); it('should not send an error to the children if warning is not defined', () => { - const wrapper = shallow( + const MyComponent = ({ meta }) =>
{JSON.stringify(meta)}
; + const { queryByText, queryByRole } = render( ', () => { ); - const ErrorElement = wrapper.find('ReferenceError'); - assert.equal(ErrorElement.length, 0); - const MyComponentElement = wrapper.find('MyComponent'); - assert.equal(MyComponentElement.length, 1); - assert.deepEqual(MyComponentElement.prop('meta'), { - helperText: false, - }); + expect(queryByRole('textbox')).toBeNull(); + expect( + queryByText(JSON.stringify({ helperText: false })) + ).not.toBeNull(); }); it('should render enclosed component if references present in input are available in state', () => { - const wrapper = shallow( + const MyComponent = ({ choices }) => ( +
{JSON.stringify(choices)}
+ ); + const { queryByRole, queryByText } = render( ', () => { ); - const ErrorElement = wrapper.find('ReferenceError'); - assert.equal(ErrorElement.length, 0); - const MyComponentElement = wrapper.find('MyComponent'); - assert.equal(MyComponentElement.length, 1); - assert.deepEqual(MyComponentElement.prop('choices'), [1]); + expect(queryByRole('textbox')).toBeNull(); + expect(queryByText(JSON.stringify([1]))).not.toBeNull(); }); it('should render enclosed component even if the choices are empty', () => { - const wrapper = shallow( + const MyComponent = ({ choices }) => ( +
{JSON.stringify(choices)}
+ ); + const { queryByRole, queryByText } = render( ', () => { ); - const LinearProgressElement = wrapper.find( - 'WithStyles(LinearProgress)' - ); - assert.equal(LinearProgressElement.length, 0); - const ErrorElement = wrapper.find('ReferenceError'); - assert.equal(ErrorElement.length, 0); - const MyComponentElement = wrapper.find('MyComponent'); - assert.equal(MyComponentElement.length, 1); - assert.deepEqual(MyComponentElement.prop('choices'), []); + expect(queryByRole('progressbar')).toBeNull(); + expect(queryByRole('textbox')).toBeNull(); + expect(queryByText(JSON.stringify([]))).not.toBeNull(); }); it('should pass onChange down to child component', () => { + let onChangeCallback; + const MyComponent = ({ onChange }) => { + onChangeCallback = onChange; + return
; + }; const onChange = jest.fn(); - const wrapper = shallow( + render( ', () => { ); - wrapper.find('MyComponent').simulate('change', 'foo'); - assert.deepEqual(onChange.mock.calls[0], ['foo']); + onChangeCallback('foo'); + expect(onChange).toBeCalledWith('foo'); }); it('should pass meta down to child component', () => { - const wrapper = shallow( + const MyComponent = ({ meta }) =>
{JSON.stringify(meta)}
; + const { queryByText } = render( ', () => { ); - - const myComponent = wrapper.find('MyComponent'); - assert.notEqual(myComponent.prop('meta'), undefined); + expect( + queryByText(JSON.stringify({ touched: false, helperText: false })) + ).not.toBeNull(); }); }); From f34f229e97a5cde1d6117db1a034b3acb2e5da1b Mon Sep 17 00:00:00 2001 From: Matheus Wichman Date: Thu, 27 Feb 2020 21:47:05 -0300 Subject: [PATCH 11/13] Migrate FormDataConsumer tests to react-testing-library --- .../src/form/FormDataConsumer.spec.tsx | 8 +- .../src/field/ReferenceManyField.spec.js | 152 +++++++++--------- 2 files changed, 81 insertions(+), 79 deletions(-) diff --git a/packages/ra-core/src/form/FormDataConsumer.spec.tsx b/packages/ra-core/src/form/FormDataConsumer.spec.tsx index 798f19b97ed..1c67ce6b9bb 100644 --- a/packages/ra-core/src/form/FormDataConsumer.spec.tsx +++ b/packages/ra-core/src/form/FormDataConsumer.spec.tsx @@ -1,14 +1,16 @@ import React from 'react'; -import { shallow } from 'enzyme'; +import { render, cleanup } from '@testing-library/react'; import { FormDataConsumerView } from './FormDataConsumer'; describe('FormDataConsumerView', () => { + afterEach(cleanup); + it('does not call its children function with scopedFormData and getSource if it did not receive an index prop', () => { const children = jest.fn(); const formData = { id: 123, title: 'A title' }; - shallow( + render( { }); const formData = { id: 123, title: 'A title', authors: [{ id: 0 }] }; - shallow( + render( ', () => { + afterEach(cleanup); + it('should render a list of the child component', () => { const data = { 1: { id: 1, title: 'hello' }, 2: { id: 2, title: 'world' }, }; - const wrapper = shallow( - - - - - , - { disableLifecycleMethods: true } + const history = createMemoryHistory(); + const { queryAllByRole } = render( + + + + + + + ); - const ProgressElements = wrapper.find('WithStyles(LinearProgress)'); - assert.equal(ProgressElements.length, 0); - const SingleFieldListElement = wrapper.find('SingleFieldList'); - assert.equal(SingleFieldListElement.length, 1); - assert.equal(SingleFieldListElement.at(0).prop('resource'), 'bar'); - assert.deepEqual(SingleFieldListElement.at(0).prop('data'), data); - assert.deepEqual(SingleFieldListElement.at(0).prop('ids'), [1, 2]); + expect(queryAllByRole('progressbar')).toHaveLength(0); + const links = queryAllByRole('link'); + expect(links).toHaveLength(2); + expect(links[0].textContent).toEqual('hello'); + expect(links[1].textContent).toEqual('world'); + expect(links[0].getAttribute('href')).toEqual('/posts/1'); + expect(links[1].getAttribute('href')).toEqual('/posts/2'); }); it('should render nothing when there are no related records', () => { - const wrapper = shallow( + const { queryAllByRole } = render( - , - { disableLifecycleMethods: true } + ); - const ProgressElements = wrapper.find('WithStyles(LinearProgress)'); - assert.equal(ProgressElements.length, 0); - const SingleFieldListElement = wrapper.find('SingleFieldList'); - assert.equal(SingleFieldListElement.length, 1); - assert.equal(SingleFieldListElement.at(0).prop('resource'), 'bar'); - assert.deepEqual(SingleFieldListElement.at(0).prop('data'), {}); - assert.deepEqual(SingleFieldListElement.at(0).prop('ids'), []); + expect(queryAllByRole('progressbar')).toHaveLength(0); + expect(queryAllByRole('link')).toHaveLength(0); }); it('should support record with string identifier', () => { @@ -63,30 +62,29 @@ describe('', () => { 'abc-1': { id: 'abc-1', title: 'hello' }, 'abc-2': { id: 'abc-2', title: 'world' }, }; - const wrapper = shallow( - - - - - , - { disableLifecycleMethods: true } + const history = createMemoryHistory(); + const { queryAllByRole } = render( + + + + + + + ); - const ProgressElements = wrapper.find('widthStyles(LinearProgress)'); - assert.equal(ProgressElements.length, 0); - const SingleFieldListElement = wrapper.find('SingleFieldList'); - assert.equal(SingleFieldListElement.length, 1); - assert.equal(SingleFieldListElement.at(0).prop('resource'), 'bar'); - assert.deepEqual(SingleFieldListElement.at(0).prop('data'), data); - assert.deepEqual(SingleFieldListElement.at(0).prop('ids'), [ - 'abc-1', - 'abc-2', - ]); + expect(queryAllByRole('progressbar')).toHaveLength(0); + const links = queryAllByRole('link'); + expect(links).toHaveLength(2); + expect(links[0].textContent).toEqual('hello'); + expect(links[1].textContent).toEqual('world'); + expect(links[0].getAttribute('href')).toEqual('/posts/abc-1'); + expect(links[1].getAttribute('href')).toEqual('/posts/abc-2'); }); it('should support record with number identifier', () => { @@ -94,26 +92,28 @@ describe('', () => { 1: { id: 1, title: 'hello' }, 2: { id: 2, title: 'world' }, }; - const wrapper = shallow( - - - - - , - { disableLifecycleMethods: true } + const history = createMemoryHistory(); + const { queryAllByRole } = render( + + + + + + + ); - const ProgressElements = wrapper.find('WithStyles(LinearProgress)'); - assert.equal(ProgressElements.length, 0); - const SingleFieldListElement = wrapper.find('SingleFieldList'); - assert.equal(SingleFieldListElement.length, 1); - assert.equal(SingleFieldListElement.at(0).prop('resource'), 'bar'); - assert.deepEqual(SingleFieldListElement.at(0).prop('data'), data); - assert.deepEqual(SingleFieldListElement.at(0).prop('ids'), [1, 2]); + expect(queryAllByRole('progressbar')).toHaveLength(0); + const links = queryAllByRole('link'); + expect(links).toHaveLength(2); + expect(links[0].textContent).toEqual('hello'); + expect(links[1].textContent).toEqual('world'); + expect(links[0].getAttribute('href')).toEqual('/posts/1'); + expect(links[1].getAttribute('href')).toEqual('/posts/2'); }); }); From 8760d0163c5805d775340cb8a9b14247d4480a92 Mon Sep 17 00:00:00 2001 From: Matheus Wichman Date: Thu, 27 Feb 2020 22:04:00 -0300 Subject: [PATCH 12/13] Replace assert with expect --- .../src/core/RoutesWithLayout.spec.tsx | 12 ++++---- packages/ra-core/src/form/FormField.spec.tsx | 9 +++--- .../src/field/FunctionField.spec.js | 5 ++-- .../src/list/DatagridCell.spec.js | 9 +++--- .../src/list/SingleFieldList.spec.js | 29 +++++++++---------- 5 files changed, 30 insertions(+), 34 deletions(-) diff --git a/packages/ra-core/src/core/RoutesWithLayout.spec.tsx b/packages/ra-core/src/core/RoutesWithLayout.spec.tsx index 6f68767f91d..17c0f30ae26 100644 --- a/packages/ra-core/src/core/RoutesWithLayout.spec.tsx +++ b/packages/ra-core/src/core/RoutesWithLayout.spec.tsx @@ -3,7 +3,6 @@ import { Route, MemoryRouter } from 'react-router-dom'; import { createStore } from 'redux'; import { Provider } from 'react-redux'; import { render, cleanup } from '@testing-library/react'; -import assert from 'assert'; import RoutesWithLayout from './RoutesWithLayout'; @@ -34,7 +33,7 @@ describe('', () => { ); - assert.notEqual(queryByText('Dashboard'), null); + expect(queryByText('Dashboard')).not.toBeNull(); }); it('should show the first resource on / when there is only one resource and no dashboard', () => { @@ -48,7 +47,7 @@ describe('', () => { ); - assert.notEqual(queryByText('Default'), null); + expect(queryByText('Default')).not.toBeNull(); }); it('should show the first resource on / when there are multiple resource and no dashboard', () => { @@ -64,8 +63,8 @@ describe('', () => { ); - assert.notEqual(queryByText('Default'), null); - assert.equal(queryByText('Resource'), null); + expect(queryByText('Default')).not.toBeNull(); + expect(queryByText('Resource')).toBeNull(); }); it('should accept custom routes', () => { @@ -83,6 +82,7 @@ describe('', () => { ); - assert.notEqual(queryByText('Custom'), null); + + expect(queryByText('Custom')).not.toBeNull(); }); }); diff --git a/packages/ra-core/src/form/FormField.spec.tsx b/packages/ra-core/src/form/FormField.spec.tsx index 449a03b34fe..93e60a9003f 100644 --- a/packages/ra-core/src/form/FormField.spec.tsx +++ b/packages/ra-core/src/form/FormField.spec.tsx @@ -1,7 +1,6 @@ -import assert from 'assert'; -import { render, fireEvent, cleanup } from '@testing-library/react'; -import { Form } from 'react-final-form'; import React from 'react'; +import { Form } from 'react-final-form'; +import { render, fireEvent, cleanup } from '@testing-library/react'; import FormField from './FormField'; describe('', () => { @@ -22,7 +21,7 @@ describe('', () => { ); const input = getByRole('textbox'); fireEvent.change(input, { target: { value: 'Lorem' } }); - assert.equal(formApi.getState().values.title, 'Lorem'); + expect(formApi.getState().values.title).toEqual('Lorem'); }); it('should not render a component if the field has an input', () => { @@ -40,6 +39,6 @@ describe('', () => { ); const input = getByRole('textbox'); fireEvent.change(input, { target: { value: 'Lorem' } }); - assert.notEqual(formApi.getState().values.title, 'Lorem'); + expect(formApi.getState().values.title).not.toEqual('Lorem'); }); }); diff --git a/packages/ra-ui-materialui/src/field/FunctionField.spec.js b/packages/ra-ui-materialui/src/field/FunctionField.spec.js index 26e99fa6e20..16ad3a897f5 100644 --- a/packages/ra-ui-materialui/src/field/FunctionField.spec.js +++ b/packages/ra-ui-materialui/src/field/FunctionField.spec.js @@ -1,5 +1,4 @@ import React from 'react'; -import assert from 'assert'; import { render, cleanup } from '@testing-library/react'; import FunctionField from './FunctionField'; @@ -11,7 +10,7 @@ describe('', () => { const { queryByText } = render( r.foo.substr(0, 2)} /> ); - assert.notEqual(queryByText('ba'), null); + expect(queryByText('ba')).not.toBeNull(); }); it('should use custom className', () => { @@ -22,6 +21,6 @@ describe('', () => { className="foo" /> ); - assert.ok(queryByText('bar').classList.contains('foo')); + expect(queryByText('bar').classList).toContain('foo'); }); }); diff --git a/packages/ra-ui-materialui/src/list/DatagridCell.spec.js b/packages/ra-ui-materialui/src/list/DatagridCell.spec.js index d3aa6128a0c..8554d42eacf 100644 --- a/packages/ra-ui-materialui/src/list/DatagridCell.spec.js +++ b/packages/ra-ui-materialui/src/list/DatagridCell.spec.js @@ -1,4 +1,3 @@ -import assert from 'assert'; import React from 'react'; import PropTypes from 'prop-types'; import { render, cleanup } from '@testing-library/react'; @@ -15,6 +14,8 @@ const renderWithTable = element => ); describe('', () => { + afterEach(cleanup); + const Field = ({ basePath }) =>
{basePath}
; Field.propTypes = { type: PropTypes.string, @@ -29,20 +30,20 @@ describe('', () => { const { getByRole } = renderWithTable( } /> ); - assert.equal(getByRole('cell').className, 'MuiTableCell-root'); + expect(getByRole('cell').className).toEqual('MuiTableCell-root'); }); it('should pass the Datagrid basePath by default', () => { const { queryByText } = renderWithTable( } /> ); - assert.notEqual(queryByText('default'), null); + expect(queryByText('default')).not.toBeNull(); }); it('should allow to overwrite the `basePath` field', () => { const { queryByText } = renderWithTable( } /> ); - assert.notEqual(queryByText('new'), null); + expect(queryByText('new')).not.toBeNull(); }); }); diff --git a/packages/ra-ui-materialui/src/list/SingleFieldList.spec.js b/packages/ra-ui-materialui/src/list/SingleFieldList.spec.js index 658f72327d0..61666eee390 100644 --- a/packages/ra-ui-materialui/src/list/SingleFieldList.spec.js +++ b/packages/ra-ui-materialui/src/list/SingleFieldList.spec.js @@ -1,5 +1,4 @@ import React from 'react'; -import assert from 'assert'; import { render, cleanup } from '@testing-library/react'; import { createMemoryHistory } from 'history'; import { Router } from 'react-router-dom'; @@ -34,8 +33,8 @@ describe('', () => { ); const linkElements = queryAllByRole('link'); - assert.equal(linkElements.length, 2); - assert.deepEqual(linkElements.map(link => link.getAttribute('href')), [ + expect(linkElements).toHaveLength(2); + expect(linkElements.map(link => link.getAttribute('href'))).toEqual([ '/posts/1', '/posts/2', ]); @@ -56,8 +55,8 @@ describe('', () => { ); const linkElements = queryAllByRole('link'); - assert.equal(linkElements.length, 2); - assert.deepEqual(linkElements.map(link => link.getAttribute('href')), [ + expect(linkElements).toHaveLength(2); + expect(linkElements.map(link => link.getAttribute('href'))).toEqual([ '/posts/1', '/posts/2', ]); @@ -79,9 +78,8 @@ describe('', () => { ); const linkElements = queryAllByRole('link'); - assert.equal(linkElements.length, 2); - assert.deepEqual( - linkElements.map(link => link.getAttribute('href')), + expect(linkElements).toHaveLength(2); + expect(linkElements.map(link => link.getAttribute('href'))).toEqual( [`/${action}/1`, `/${action}/2`] ); cleanup(); @@ -105,8 +103,8 @@ describe('', () => { ); const linkElements = queryAllByRole('link'); - assert.equal(linkElements.length, 2); - assert.deepEqual(linkElements.map(link => link.getAttribute('href')), [ + expect(linkElements).toHaveLength(2); + expect(linkElements.map(link => link.getAttribute('href'))).toEqual([ '/prefix/bar/1/show', '/prefix/bar/2/show', ]); @@ -129,9 +127,8 @@ describe('', () => { ); const linkElements = queryAllByRole('link'); - assert.equal(linkElements.length, 2); - assert.deepEqual( - linkElements.map(link => link.getAttribute('href')), + expect(linkElements).toHaveLength(2); + expect(linkElements.map(link => link.getAttribute('href'))).toEqual( [`/${action}/1/show`, `/${action}/2/show`] ); cleanup(); @@ -155,8 +152,8 @@ describe('', () => { ); const linkElements = queryAllByRole('link'); - assert.equal(linkElements.length, 0); - assert.notEqual(queryByText('foo'), null); - assert.notEqual(queryByText('bar'), null); + expect(linkElements).toHaveLength(0); + expect(queryByText('foo')).not.toBeNull(); + expect(queryByText('bar')).not.toBeNull(); }); }); From eb572f8545ea3c0ff80906e6a5e7f5cba74e5d30 Mon Sep 17 00:00:00 2001 From: Matheus Wichman Date: Fri, 28 Feb 2020 00:23:31 -0300 Subject: [PATCH 13/13] Remove enzyme setup --- test-setup.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/test-setup.js b/test-setup.js index cbc9c6b830d..f98772e9692 100644 --- a/test-setup.js +++ b/test-setup.js @@ -6,11 +6,6 @@ require('raf/polyfill'); */ require('mutationobserver-shim'); -var enzyme = require('enzyme'); -var Adapter = require('enzyme-adapter-react-16'); - -enzyme.configure({ adapter: new Adapter() }); - /** * Mock PopperJS *