diff --git a/.eslintrc.js b/.eslintrc.js index e3f8549ff0a6cb..ca4a7e0025fe79 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -16,7 +16,7 @@ module.exports = { 'airbnb-typescript', 'prettier', 'prettier/react', - 'prettier/@typescript-eslint', + 'prettier/@typescript-eslint' ], parser: '@typescript-eslint/parser', parserOptions: { @@ -137,7 +137,7 @@ module.exports = { env: { mocha: true, }, - extends: ['plugin:mocha/recommended'], + extends: ['plugin:mocha/recommended', 'plugin:chai-friendly/recommended'], rules: { // does not work with wildcard imports. Mistakes will throw at runtime anyway 'import/named': 'off', @@ -187,6 +187,7 @@ module.exports = { // components that are defined in test are isolated enough // that they don't need type-checking 'react/prop-types': 'off', + '@typescript-eslint/no-unused-expressions': 'off', }, }, { diff --git a/package.json b/package.json index 248df30fc4e184..1c294b11776d8a 100644 --- a/package.json +++ b/package.json @@ -101,6 +101,7 @@ "eslint-config-prettier": "^6.11.0", "eslint-import-resolver-webpack": "^0.13.0", "eslint-plugin-babel": "^5.3.1", + "eslint-plugin-chai-friendly": "^0.6.0", "eslint-plugin-import": "^2.22.0", "eslint-plugin-jsx-a11y": "^6.3.1", "eslint-plugin-mocha": "^8.0.0", diff --git a/packages/material-ui/src/Accordion/Accordion.test.js b/packages/material-ui/src/Accordion/Accordion.test.js index 8d3575a25cded4..ed32b01a444788 100644 --- a/packages/material-ui/src/Accordion/Accordion.test.js +++ b/packages/material-ui/src/Accordion/Accordion.test.js @@ -2,16 +2,20 @@ import * as React from 'react'; import PropTypes from 'prop-types'; import { expect } from 'chai'; import { spy } from 'sinon'; -import { createMount, describeConformance, getClasses, findOutermostIntrinsic } from 'test/utils'; +import { + createClientRender, + describeConformance, + getClasses, + fireEvent +} from 'test/utils'; import Paper from '../Paper'; import Accordion from './Accordion'; import AccordionSummary from '../AccordionSummary'; -import Collapse from '../Collapse'; describe('', () => { - const mount = createMount({ strict: true }); + const mount = createClientRender({ strict: true }); let classes; - const minimalChildren = []; + const minimalChildren = [(Header)]; before(() => { classes = getClasses({minimalChildren}); @@ -23,80 +27,82 @@ describe('', () => { mount, refInstanceof: window.HTMLDivElement, skip: ['componentProp'], + useRTL: true, })); it('should render and not be controlled', () => { - const wrapper = mount({minimalChildren}); - const root = wrapper.find(`.${classes.root}`).first(); - expect(root.type()).to.equal(Paper); - expect(root.props().square).to.equal(false); - expect(root.hasClass(classes.expanded)).to.equal(false); + const accordion = mount({minimalChildren}); + expect(accordion.container.firstChild.tagName).to.equal("DIV"); + expect(accordion.container.firstChild.classList.contains(classes.expanded)).to.be.false; + expect(accordion.container.firstChild.classList.contains(classes.square)).to.be.false; }); it('should handle defaultExpanded prop', () => { - const wrapper = mount({minimalChildren}); - expect(findOutermostIntrinsic(wrapper).hasClass(classes.expanded)).to.equal(true); + const accordion = mount({minimalChildren}); + expect(accordion.container.firstChild.classList.contains(classes.expanded)).to.be.true; }); it('should render the summary and collapse elements', () => { - const wrapper = mount( + const accordion = mount( Summary
Hello
, ); - expect(wrapper.find('[aria-expanded=false]').hostNodes().text()).to.equal('Summary'); - expect(wrapper.find(Collapse).find('div#panel-content').text()).to.equal('Hello'); + expect(accordion.getByText("Summary").parentNode.getAttribute("aria-expanded")).to.be.equal('false'); + expect(accordion.getByText("Hello")).to.not.be.visible; }); it('should be controlled', () => { - const wrapper = mount({minimalChildren}); - const panel = wrapper.find(`.${classes.root}`).first(); - expect(panel.hasClass(classes.expanded)).to.equal(true); - wrapper.setProps({ expanded: false }); - expect(wrapper.hasClass(classes.expanded)).to.equal(false); + const accordion = mount({minimalChildren}); + const panel = accordion.container.firstChild; + expect(panel.classList.contains(classes.expanded)).to.be.true; + accordion.rerender({minimalChildren}); + expect(panel.classList.contains(classes.expanded)).to.be.false; }); it('should call onChange when clicking the summary element', () => { const handleChange = spy(); - const wrapper = mount({minimalChildren}); - wrapper.find(AccordionSummary).simulate('click'); + const accordion = mount({minimalChildren}); + const summary = accordion.getByText("Header"); + fireEvent.click(summary); expect(handleChange.callCount).to.equal(1); }); it('when controlled should call the onChange', () => { const handleChange = spy(); - const wrapper = mount( + const accordion = mount( {minimalChildren} , ); - wrapper.find(AccordionSummary).simulate('click'); + const summary = accordion.getByText("Header"); + fireEvent.click(summary); expect(handleChange.callCount).to.equal(1); expect(handleChange.args[0][1]).to.equal(false); }); it('when undefined onChange and controlled should not call the onChange', () => { const handleChange = spy(); - const wrapper = mount( - + const accordion = mount( + {minimalChildren} , ); - wrapper.setProps({ onChange: undefined }); - wrapper.find(AccordionSummary).simulate('click'); + const summary = accordion.getByText("Header"); + fireEvent.click(summary); expect(handleChange.callCount).to.equal(0); }); it('when disabled should have the disabled class', () => { - const wrapper = mount({minimalChildren}); - expect(findOutermostIntrinsic(wrapper).hasClass(classes.disabled)).to.equal(true); + const accordion = mount({minimalChildren}); + expect(accordion.container.firstChild.classList.contains(classes.disabled)).to.be.true; }); it('should handle the TransitionComponent prop', () => { const NoTransitionCollapse = (props) => { - return props.in ?
{props.children}
: null; + return props.in ?
{props.children}
: null; }; NoTransitionCollapse.propTypes = { children: PropTypes.node, @@ -104,7 +110,7 @@ describe('', () => { }; const CustomContent = () =>
Hello
; - const wrapper = mount( + const accordion = mount( @@ -112,15 +118,16 @@ describe('', () => { ); // Collapse is initially shown - const collapse = wrapper.find(NoTransitionCollapse); - expect(collapse.props().in).to.equal(true); - expect(wrapper.find(CustomContent).length).to.equal(1); + expect(accordion.getByText("Hello")).to.be.visible; // Hide the collapse - wrapper.setProps({ expanded: false }); - const collapse2 = wrapper.find(NoTransitionCollapse); - expect(collapse2.props().in).to.equal(false); - expect(wrapper.find(CustomContent).length).to.equal(0); + accordion.rerender( + + + + + ); + expect(accordion.queryByText("Hello")).to.not.exist }); describe('prop: children', () => { @@ -153,7 +160,7 @@ describe('', () => { }); it('should accept empty content', () => { - mount( + createClientRender( {null} diff --git a/test/utils/describeConformance.js b/test/utils/describeConformance.js index 1a5b975cf8fe2b..b9ce540dfddcb2 100644 --- a/test/utils/describeConformance.js +++ b/test/utils/describeConformance.js @@ -59,15 +59,18 @@ function randomStringValue() { */ function testClassName(element, getOptions) { it('applies the className to the root component', () => { - const { mount } = getOptions(); + const { mount, useRTL } = getOptions(); const className = randomStringValue(); const wrapper = mount(React.cloneElement(element, { className })); - - expect(findOutermostIntrinsic(wrapper).hasClass(className)).to.equal( - true, - 'does have a custom `className`', - ); + if (useRTL) { + expect(wrapper.container.firstChild.classList.contains(className)).to.be.equal(true); + } else { + expect(findOutermostIntrinsic(wrapper).hasClass(className)).to.equal( + true, + 'does have a custom `className`', + ); + } }); } @@ -98,14 +101,17 @@ function testComponentProp(element, getOptions) { function testPropsSpread(element, getOptions) { it(`spreads props to the root component`, () => { // type def in ConformanceOptions - const { classes, inheritComponent, mount } = getOptions(); + const { classes, inheritComponent, mount, useRTL } = getOptions(); const testProp = 'data-test-props-spread'; const value = randomStringValue(); const wrapper = mount(React.cloneElement(element, { [testProp]: value })); - const root = findRootComponent(wrapper, { classes, component: inheritComponent }); - - expect(root.props()[testProp]).to.equal(value); + if(useRTL) { + expect(wrapper.container.firstChild.getAttribute(testProp)).to.equal(value); + } else { + const root = findRootComponent(wrapper, { classes, component: inheritComponent }); + expect(root.props()[testProp]).to.equal(value); + } }); } @@ -121,14 +127,20 @@ function describeRef(element, getOptions) { describe('ref', () => { it(`attaches the ref`, () => { // type def in ConformanceOptions - const { inheritComponent, mount, refInstanceof } = getOptions(); + const { inheritComponent, mount, refInstanceof, useRTL } = getOptions(); testRef(element, mount, (instance, wrapper) => { expect(instance).to.be.instanceof(refInstanceof); if (inheritComponent && instance.nodeType === 1) { - const rootHost = findOutermostIntrinsic(wrapper); - expect(instance).to.equal(rootHost.instance()); + if (useRTL) { + const rootHost = wrapper.container.firstChild; + expect(instance).to.equal(rootHost); + } else { + const rootHost = findOutermostIntrinsic(wrapper); + expect(instance).to.equal(rootHost.instance()); + } + } }); }); @@ -142,7 +154,7 @@ function describeRef(element, getOptions) { */ function testRootClass(element, getOptions) { it('applies the root class to the root component if it has this class', () => { - const { classes, mount } = getOptions(); + const { classes, mount, useRTL } = getOptions(); if (classes.root == null) { return; } @@ -154,8 +166,13 @@ function testRootClass(element, getOptions) { // jump to the host component because some components pass the `root` class // to the `classes` prop of the root component. // https://github.com/mui-org/material-ui/blob/f9896bcd129a1209153106296b3d2487547ba205/packages/material-ui/src/OutlinedInput/OutlinedInput.js#L101 - expect(findOutermostIntrinsic(wrapper).hasClass(classes.root)).to.equal(true); - expect(findOutermostIntrinsic(wrapper).hasClass(className)).to.equal(true); + if (useRTL) { + expect(wrapper.container.firstChild.classList.contains(classes.root)).to.be.equal(true); + expect(wrapper.container.firstChild.classList.contains(className)).to.be.equal(true); + } else { + expect(findOutermostIntrinsic(wrapper).hasClass(classes.root)).to.equal(true); + expect(findOutermostIntrinsic(wrapper).hasClass(className)).to.equal(true); + } }); } diff --git a/yarn.lock b/yarn.lock index c410404ec2a207..9a9673f82c6efc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7141,6 +7141,11 @@ eslint-plugin-babel@^5.3.1: dependencies: eslint-rule-composer "^0.3.0" +eslint-plugin-chai-friendly@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-chai-friendly/-/eslint-plugin-chai-friendly-0.6.0.tgz#54052fab79302ed0cea76ab997351ea4809bfb77" + integrity sha512-Uvvv1gkbRGp/qfN15B0kQyQWg+oFA8buDSqrwmW3egNSk/FpqH2MjQqKOuKwmEL6w4QIQrIjDp+gg6kGGmD3oQ== + eslint-plugin-import@^2.22.0: version "2.22.1" resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.22.1.tgz#0896c7e6a0cf44109a2d97b95903c2bb689d7702"