diff --git a/package.json b/package.json
index 581215a61981dc..150677b15bb7cf 100644
--- a/package.json
+++ b/package.json
@@ -50,6 +50,7 @@
"@babel/preset-env": "^7.6.0",
"@babel/preset-react": "^7.0.0",
"@babel/register": "^7.6.2",
+ "@testing-library/dom": "^6.8.1",
"@testing-library/react": "^9.2.0",
"@types/chai": "^4.2.3",
"@types/chai-dom": "^0.0.8",
diff --git a/packages/material-ui-lab/src/SpeedDialAction/SpeedDialAction.test.js b/packages/material-ui-lab/src/SpeedDialAction/SpeedDialAction.test.js
index fbafcf4fdb61aa..15739f0bd422d1 100644
--- a/packages/material-ui-lab/src/SpeedDialAction/SpeedDialAction.test.js
+++ b/packages/material-ui-lab/src/SpeedDialAction/SpeedDialAction.test.js
@@ -17,7 +17,7 @@ describe('', () => {
};
before(() => {
- // StrictModeViolation: unknown
+ // StrictModeViolation: uses Tooltip
mount = createMount({ strict: false });
classes = getClasses();
});
diff --git a/packages/material-ui/src/Button/Button.test.js b/packages/material-ui/src/Button/Button.test.js
index 5a3fdf98b42f52..1d10779b04119a 100644
--- a/packages/material-ui/src/Button/Button.test.js
+++ b/packages/material-ui/src/Button/Button.test.js
@@ -8,12 +8,11 @@ import ButtonBase from '../ButtonBase';
describe('', () => {
let mount;
- // StrictModeViolation uses ButtonBase
- const render = createClientRender({ strict: false });
+ const render = createClientRender({ strict: true });
let classes;
before(() => {
- mount = createMount({ strict: false });
+ mount = createMount({ strict: true });
classes = getClasses();
});
diff --git a/packages/material-ui/src/ExpansionPanelSummary/ExpansionPanelSummary.js b/packages/material-ui/src/ExpansionPanelSummary/ExpansionPanelSummary.js
index dc995b5efa3129..81d71a8ff81b4c 100644
--- a/packages/material-ui/src/ExpansionPanelSummary/ExpansionPanelSummary.js
+++ b/packages/material-ui/src/ExpansionPanelSummary/ExpansionPanelSummary.js
@@ -93,6 +93,7 @@ const ExpansionPanelSummary = React.forwardRef(function ExpansionPanelSummary(pr
onBlur(event);
}
};
+ // TODO: Remove in v5 and use onClick in ExpansionPanel.js
const handleChange = event => {
if (onChange) {
onChange(event);
diff --git a/packages/material-ui/src/ExpansionPanelSummary/ExpansionPanelSummary.test.js b/packages/material-ui/src/ExpansionPanelSummary/ExpansionPanelSummary.test.js
index 0be9a293e46acf..73c129ef216b3a 100644
--- a/packages/material-ui/src/ExpansionPanelSummary/ExpansionPanelSummary.test.js
+++ b/packages/material-ui/src/ExpansionPanelSummary/ExpansionPanelSummary.test.js
@@ -1,125 +1,119 @@
import React from 'react';
-import { assert } from 'chai';
+import { expect } from 'chai';
import { spy } from 'sinon';
-import { createMount, findOutermostIntrinsic, getClasses } from '@material-ui/core/test-utils';
+import { createMount, getClasses } from '@material-ui/core/test-utils';
import describeConformance from '../test-utils/describeConformance';
+import { createClientRender, fireEvent } from 'test/utils/createClientRender';
import ExpansionPanelSummary from './ExpansionPanelSummary';
import ButtonBase from '../ButtonBase';
describe('', () => {
let mount;
let classes;
-
- function findExpandButton(wrapper) {
- return wrapper.find('[role="button"]:not([aria-hidden=true])');
- }
+ const render = createClientRender({ strict: true });
before(() => {
- // StrictModeViolation: uses simulate
- mount = createMount({ strict: false });
+ mount = createMount({ strict: true });
classes = getClasses();
});
- after(() => {
- mount.cleanUp();
- });
-
describeConformance(, () => ({
classes,
inheritComponent: ButtonBase,
mount,
refInstanceof: window.HTMLDivElement,
skip: ['componentProp'],
+ after: () => mount.cleanUp(),
}));
- it('should render with the content', () => {
- const wrapper = mount(The Summary);
- const itemsWrap = wrapper.find(`.${classes.content}`);
- assert.strictEqual(itemsWrap.text(), 'The Summary');
+ it('renders the children inside the .content element', () => {
+ const { container } = render(The Summary);
+
+ expect(container.querySelector(`.${classes.content}`)).to.have.text('The Summary');
});
it('when disabled should have disabled class', () => {
- const wrapper = mount();
- assert.strictEqual(findExpandButton(wrapper).hasClass(classes.disabled), true);
+ const { getByRole } = render();
+
+ expect(getByRole('button')).to.have.class(classes.disabled);
});
- it('when expanded should have expanded class', () => {
- const wrapper = mount();
- assert.strictEqual(wrapper.find('[aria-expanded=true]').every(`.${classes.expanded}`), true);
+ it('when expanded adds the expanded class to any button regardless of a11y', () => {
+ const { getAllByRole } = render();
+
+ const buttons = getAllByRole('button', { hidden: true });
+ expect(buttons).to.have.length(2);
+ expect(buttons[0]).to.have.class(classes.expanded);
+ expect(buttons[0]).to.have.attribute('aria-expanded', 'true');
+ expect(buttons[0]).not.to.be.inaccessible;
+ expect(buttons[1]).to.have.class(classes.expanded);
+ expect(buttons[1]).to.be.inaccessible;
});
it('should render with the expand icon and have the expandIcon class', () => {
- const wrapper = mount(Icon} />);
- const iconWrap = wrapper.find(`.${classes.expandIcon}`).first();
- assert.strictEqual(iconWrap.text(), 'Icon');
+ const { getAllByRole } = render(Icon} />);
+
+ const expandButton = getAllByRole('button', { hidden: true })[1];
+ expect(expandButton).to.have.class(classes.expandIcon);
+ expect(expandButton).to.have.text('Icon');
+ expect(expandButton).to.be.inaccessible;
});
- it('handleFocusVisible() should set focused state', () => {
- const wrapper = mount();
- wrapper
- .find(ButtonBase)
- .props()
- .onFocusVisible();
- wrapper.update();
- assert.strictEqual(findExpandButton(wrapper).hasClass(classes.focused), true);
+ it('focusing adds the `focused` class if focused visible', () => {
+ // TODO: Rename `focused` -> `focus-visible`
+ // `focused` is a global state which is applied on focus
+ // only here do we constrain it to focus-visible. THe name is also not consistent
+ // with :focus
+ const { getByRole } = render();
+ fireEvent.mouseDown(document.body); // pointer device
+
+ fireEvent.keyDown(document.activeElement, { key: 'Tab' }); // not actually focusing (yet)
+ getByRole('button').focus();
+
+ expect(getByRole('button')).to.be.focused;
+ expect(getByRole('button')).to.have.class(classes.focused);
});
- it('handleBlur() should unset focused state', () => {
- const wrapper = mount();
- wrapper
- .find(ButtonBase)
- .props()
- .onFocusVisible();
- wrapper.update();
- wrapper
- .find(ButtonBase)
- .props()
- .onBlur();
- wrapper.update();
- assert.strictEqual(findExpandButton(wrapper).hasClass(classes.focused), false);
+ it('blur should unset focused state', () => {
+ const { getByRole } = render();
+ fireEvent.mouseDown(document.body); // pointer device
+ fireEvent.keyDown(document.activeElement, { key: 'Tab' }); // not actually focusing (yet)
+ getByRole('button').focus();
+
+ getByRole('button').blur();
+
+ expect(getByRole('button')).not.to.be.focused;
+ expect(getByRole('button')).not.to.have.class(classes.focused);
});
- describe('event callbacks', () => {
- it('should fire event callbacks', () => {
- const events = ['onClick', 'onFocusVisible', 'onBlur'];
-
- const handlers = events.reduce((result, n) => {
- result[n] = spy();
- return result;
- }, {});
-
- const wrapper = mount();
-
- events.forEach(event => {
- wrapper
- .find(ButtonBase)
- .props()
- [event]({ persist: () => {} });
- assert.strictEqual(handlers[event].callCount, 1, `should have called the ${event} handler`);
- });
- });
+ it('should fire onClick callbacks', () => {
+ const handleClick = spy();
+ const { getByRole } = render();
+
+ getByRole('button').click();
+
+ expect(handleClick.callCount).to.equal(1);
});
- describe('prop: onChange', () => {
- it('fires onChange if the summary control is clicked', () => {
- const handleChange = spy();
- const wrapper = mount();
+ it('calls onChange when clicking', () => {
+ const handleChange = spy();
+ const { getByRole } = render();
- const control = findOutermostIntrinsic(wrapper);
- const eventMock = 'woofExpansionPanelSummary';
- control.simulate('click', { eventMock });
+ getByRole('button').click();
- assert.strictEqual(handleChange.callCount, 1);
- assert.strictEqual(handleChange.calledWithMatch({ eventMock }), true);
- });
+ expect(handleChange.callCount).to.equal(1);
});
- describe('prop: click', () => {
- it('should trigger onClick', () => {
- const handleClick = spy();
- const wrapper = mount();
- wrapper.simulate('click');
- assert.strictEqual(handleClick.callCount, 1);
- });
+ it('calls onFocusVisible if focused visibly', () => {
+ const handleFocusVisible = spy();
+ const { getByRole } = render();
+ // simulate pointer device
+ fireEvent.mouseDown(document.body);
+
+ // this doesn't actually apply focus like in the browser. we need to move focus manually
+ fireEvent.keyDown(document.body, { key: 'Tab' });
+ getByRole('button').focus();
+
+ expect(handleFocusVisible.callCount).to.equal(1);
});
});
diff --git a/packages/material-ui/src/RadioGroup/RadioGroup.test.js b/packages/material-ui/src/RadioGroup/RadioGroup.test.js
index 04a63c2d473c83..e426f44575253a 100644
--- a/packages/material-ui/src/RadioGroup/RadioGroup.test.js
+++ b/packages/material-ui/src/RadioGroup/RadioGroup.test.js
@@ -13,7 +13,7 @@ describe('', () => {
let mount;
before(() => {
- // StrictModeViolation: uses #simulate
+ // StrictModeViolation: test uses #simulate
mount = createMount({ strict: false });
});
diff --git a/packages/material-ui/src/Select/Select.test.js b/packages/material-ui/src/Select/Select.test.js
index 1dd9b4fa4e7e62..8e3f64384f7643 100644
--- a/packages/material-ui/src/Select/Select.test.js
+++ b/packages/material-ui/src/Select/Select.test.js
@@ -16,7 +16,7 @@ describe('', () => {
before(() => {
classes = getClasses();
- // StrictModeViolation: test uses MenuItem
+ // StrictModeViolation: uses Menu
mount = createMount({ strict: false });
});
diff --git a/packages/material-ui/src/Tab/Tab.test.js b/packages/material-ui/src/Tab/Tab.test.js
index 2b2cfef4849314..c9294dc223b864 100644
--- a/packages/material-ui/src/Tab/Tab.test.js
+++ b/packages/material-ui/src/Tab/Tab.test.js
@@ -20,7 +20,7 @@ describe('', () => {
before(() => {
shallow = createShallow({ dive: true });
- // StrictModeViolation: uses text()
+ // StrictModeViolation: test uses text()
mount = createMount({ strict: false });
classes = getClasses();
});
diff --git a/packages/material-ui/src/TablePagination/TablePagination.test.js b/packages/material-ui/src/TablePagination/TablePagination.test.js
index da41203c51a1f6..206e9a2daa1495 100644
--- a/packages/material-ui/src/TablePagination/TablePagination.test.js
+++ b/packages/material-ui/src/TablePagination/TablePagination.test.js
@@ -32,7 +32,7 @@ describe('', () => {
classes = getClasses(
{}} page={0} rowsPerPage={10} />,
);
- // StrictModeViolation: uses #html()
+ // StrictModeViolation: test uses #html()
mount = createMount({ strict: false });
});
diff --git a/packages/material-ui/test/integration/Menu.test.js b/packages/material-ui/test/integration/Menu.test.js
index c88b1a80159597..504346ed3884c3 100644
--- a/packages/material-ui/test/integration/Menu.test.js
+++ b/packages/material-ui/test/integration/Menu.test.js
@@ -74,6 +74,7 @@ describe(' integration', () => {
* @type {ReturnType}
*/
let clock;
+ // StrictModeViolation: uses Popover
const render = createClientRender({ strict: false });
beforeEach(() => {
diff --git a/packages/material-ui/test/integration/NestedMenu.test.js b/packages/material-ui/test/integration/NestedMenu.test.js
index 39430c3d401095..510e77fd7e6c1a 100644
--- a/packages/material-ui/test/integration/NestedMenu.test.js
+++ b/packages/material-ui/test/integration/NestedMenu.test.js
@@ -38,7 +38,7 @@ function NestedMenu(props) {
}
describe(' integration', () => {
- // StrictModeViolation: test uses Popover
+ // StrictModeViolation: uses Popover
const render = createClientRender({ strict: false });
afterEach(() => {
diff --git a/test/utils/init.d.ts b/test/utils/init.d.ts
index 27229b7913d899..df15c7cbbeac0d 100644
--- a/test/utils/init.d.ts
+++ b/test/utils/init.d.ts
@@ -5,8 +5,13 @@ declare namespace Chai {
/**
* checks if the element in question is considered aria-hidden
* Does not replace accessibility check as that requires display/visibility/layout
+ * @deprecated Use `inaccessible` + `visible` instead
*/
ariaHidden: Assertion;
+ /**
+ * checks if the element is inaccessible
+ */
+ inaccessible: Assertion;
/**
* checks if the element is focused
*/
diff --git a/test/utils/initMatchers.js b/test/utils/initMatchers.js
index 1f0ed376f65e83..1ea2ca24774e45 100644
--- a/test/utils/initMatchers.js
+++ b/test/utils/initMatchers.js
@@ -1,5 +1,6 @@
import chai from 'chai';
import chaiDom from 'chai-dom';
+import { isInaccessible } from '@testing-library/dom';
import { prettyDOM } from '@testing-library/react';
chai.use(chaiDom);
@@ -48,4 +49,16 @@ chai.use((chaiAPI, utils) => {
)} had aria-hidden="true" instead\n${prettyDOM(previousNode)}`,
);
});
+
+ chai.Assertion.addProperty('inaccessible', function elementIsAccessible() {
+ const element = utils.flag(this, 'object');
+
+ const inaccessible = isInaccessible(element);
+
+ this.assert(
+ inaccessible === true,
+ `expected ${utils.elToString(element)} to be inaccessible but it was accessible`,
+ `expected ${utils.elToString(element)} to be accessible but it was inaccessible`,
+ );
+ });
});
diff --git a/yarn.lock b/yarn.lock
index 46b97aab42feeb..6c312f091db9b8 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -825,7 +825,7 @@
core-js "^2.6.5"
regenerator-runtime "^0.13.2"
-"@babel/runtime@7.0.0", "@babel/runtime@7.1.2", "@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.1.5", "@babel/runtime@^7.2.0", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.0", "@babel/runtime@^7.4.2", "@babel/runtime@^7.4.4", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.0":
+"@babel/runtime@7.0.0", "@babel/runtime@7.1.2", "@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.1.5", "@babel/runtime@^7.2.0", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.0", "@babel/runtime@^7.4.2", "@babel/runtime@^7.4.4", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.0", "@babel/runtime@^7.6.2":
version "7.6.2"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.6.2.tgz#c3d6e41b304ef10dcf13777a33e7694ec4a9a6dd"
integrity sha512-EXxN64agfUqqIGeEjI5dL5z0Sw0ZwWo1mLTi4mQowCZ42O59b7DRpZAnTC6OqdF28wMBMFKNb/4uFGrVaigSpg==
@@ -1068,14 +1068,14 @@
unique-filename "^1.1.1"
which "^1.3.1"
-"@jest/types@^24.8.0":
- version "24.8.0"
- resolved "https://registry.yarnpkg.com/@jest/types/-/types-24.8.0.tgz#f31e25948c58f0abd8c845ae26fcea1491dea7ad"
- integrity sha512-g17UxVr2YfBtaMUxn9u/4+siG1ptg9IGYAYwvpwn61nBg779RXnjE/m7CxYcIzEt0AbHZZAHSEZNhkE2WxURVg==
+"@jest/types@^24.9.0":
+ version "24.9.0"
+ resolved "https://registry.yarnpkg.com/@jest/types/-/types-24.9.0.tgz#63cb26cb7500d069e5a389441a7c6ab5e909fc59"
+ integrity sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==
dependencies:
"@types/istanbul-lib-coverage" "^2.0.0"
"@types/istanbul-reports" "^1.1.1"
- "@types/yargs" "^12.0.9"
+ "@types/yargs" "^13.0.0"
"@lerna/add@3.16.2":
version "3.16.2"
@@ -1986,17 +1986,17 @@
dependencies:
defer-to-connect "^1.0.1"
-"@testing-library/dom@^6.3.0":
- version "6.4.1"
- resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-6.4.1.tgz#4efd38d896b9b2255025acf9567e2360e1f4814f"
- integrity sha512-bjPHLO5NzlTvA57Tfz8txHEUmnOed3NuvObB2ttoKfO6A/utr7TZt9bDHHcYymcZIG2IsQZLix/m4ZKkedDDwQ==
+"@testing-library/dom@^6.3.0", "@testing-library/dom@^6.8.1":
+ version "6.8.1"
+ resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-6.8.1.tgz#47b4e0cc0742302fc9d122ac43010e6fc60bee65"
+ integrity sha512-b+Q4wryafqsSTFBV14cc5xqhN/OVB9oMeUQvZwy1kVx+kdqs4zQAcyvCsFkdcqx7NxibWpUN/fBIUaqyAEhitw==
dependencies:
- "@babel/runtime" "^7.5.5"
+ "@babel/runtime" "^7.6.2"
"@sheerun/mutationobserver-shim" "^0.3.2"
"@types/testing-library__dom" "^6.0.0"
aria-query "3.0.0"
- pretty-format "^24.8.0"
- wait-for-expect "^1.3.0"
+ pretty-format "^24.9.0"
+ wait-for-expect "^3.0.0"
"@testing-library/react@^9.2.0":
version "9.3.0"
@@ -2297,10 +2297,17 @@
resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-2.3.5.tgz#9da44ed75571999b65c37b60c9b2b88db54c585d"
integrity sha512-SCcK7mvGi3+ZNz833RRjFIxrn4gI1PPR3NtuIS+6vMkvmsGjosqTJwRt5bAEFLRz+wtJMWv8+uOnZf2hi2QXTg==
-"@types/yargs@^12.0.9":
- version "12.0.12"
- resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-12.0.12.tgz#45dd1d0638e8c8f153e87d296907659296873916"
- integrity sha512-SOhuU4wNBxhhTHxYaiG5NY4HBhDIDnJF60GU+2LqHAdKKer86//e4yg69aENCtQ04n0ovz+tq2YPME5t5yp4pw==
+"@types/yargs-parser@*":
+ version "13.1.0"
+ resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-13.1.0.tgz#c563aa192f39350a1d18da36c5a8da382bbd8228"
+ integrity sha512-gCubfBUZ6KxzoibJ+SCUc/57Ms1jz5NjHe4+dI2krNmU5zCPAphyLJYyTOg06ueIyfj+SaCUqmzun7ImlxDcKg==
+
+"@types/yargs@^13.0.0":
+ version "13.0.3"
+ resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-13.0.3.tgz#76482af3981d4412d65371a318f992d33464a380"
+ integrity sha512-K8/LfZq2duW33XW/tFwEAfnZlqIfVsoyRB3kfXdPXYhl0nfM8mmh7GS0jg7WrX2Dgq/0Ha/pR1PaR+BvmWwjiQ==
+ dependencies:
+ "@types/yargs-parser" "*"
"@webassemblyjs/ast@1.7.11":
version "1.7.11"
@@ -11729,12 +11736,12 @@ pretty-format@^23.6.0:
ansi-regex "^3.0.0"
ansi-styles "^3.2.0"
-pretty-format@^24.8.0:
- version "24.8.0"
- resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-24.8.0.tgz#8dae7044f58db7cb8be245383b565a963e3c27f2"
- integrity sha512-P952T7dkrDEplsR+TuY7q3VXDae5Sr7zmQb12JU/NDQa/3CH7/QW0yvqLcGN6jL+zQFKaoJcPc+yJxMTGmosqw==
+pretty-format@^24.9.0:
+ version "24.9.0"
+ resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-24.9.0.tgz#12fac31b37019a4eea3c11aa9a959eb7628aa7c9"
+ integrity sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA==
dependencies:
- "@jest/types" "^24.8.0"
+ "@jest/types" "^24.9.0"
ansi-regex "^4.0.0"
ansi-styles "^3.2.0"
react-is "^16.8.4"
@@ -14960,10 +14967,10 @@ w3c-xmlserializer@^1.1.2:
webidl-conversions "^4.0.2"
xml-name-validator "^3.0.0"
-wait-for-expect@^1.3.0:
- version "1.3.0"
- resolved "https://registry.yarnpkg.com/wait-for-expect/-/wait-for-expect-1.3.0.tgz#65241ce355425f907f5d127bdb5e72c412ff830c"
- integrity sha512-8fJU7jiA96HfGPt+P/UilelSAZfhMBJ52YhKzlmZQvKEZU2EcD1GQ0yqGB6liLdHjYtYAoGVigYwdxr5rktvzA==
+wait-for-expect@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/wait-for-expect/-/wait-for-expect-3.0.0.tgz#d6fbf28959e3b8779dc172fb1ea56bf1e833bf7a"
+ integrity sha512-9LyJL+MugZdcQn5V9PBSEC4d2UPTy1xX2U9wTc6LvG/18qeeYqdE/CgmAQJxc/Vcjs+VzH+wiyIXxz05F3nrpQ==
warning@^4.0.1:
version "4.0.3"