diff --git a/packages/material-ui/src/Avatar/Avatar.test.js b/packages/material-ui/src/Avatar/Avatar.test.js
index 18def87d0c424b..38b96b8e8cfad7 100644
--- a/packages/material-ui/src/Avatar/Avatar.test.js
+++ b/packages/material-ui/src/Avatar/Avatar.test.js
@@ -74,21 +74,17 @@ describe('', () => {
});
describe('font icon avatar', () => {
- let container;
- let avatar;
- let icon;
-
- before(() => {
- container = render(
-
- icon
+ it('should render a div containing an font icon', () => {
+ const { container } = render(
+
+
+ icon
+
,
- ).container;
- avatar = container.firstChild;
- icon = avatar.firstChild;
- });
+ );
+ const avatar = container.firstChild;
+ const icon = avatar.firstChild;
- it('should render a div containing an font icon', () => {
expect(avatar).to.have.tagName('div');
expect(icon).to.have.tagName('span');
expect(icon).to.have.class('my-icon-font');
@@ -96,100 +92,125 @@ describe('', () => {
});
it('should merge user classes & spread custom props to the root node', () => {
+ const { container } = render(
+
+ icon
+ ,
+ );
+ const avatar = container.firstChild;
+
expect(avatar).to.have.class(classes.root);
expect(avatar).to.have.class('my-avatar');
expect(avatar).to.have.attribute('data-my-prop', 'woofAvatar');
});
it('should apply the colorDefault class', () => {
+ const { container } = render(
+
+ icon
+ ,
+ );
+ const avatar = container.firstChild;
+
expect(avatar).to.have.class(classes.colorDefault);
});
});
describe('svg icon avatar', () => {
- let container;
- let avatar;
-
- before(() => {
- container = render(
-
+ it('should render a div containing an svg icon', () => {
+ const container = render(
+
,
).container;
- avatar = container.firstChild;
- });
+ const avatar = container.firstChild;
- it('should render a div containing an svg icon', () => {
expect(avatar).to.have.tagName('div');
const cancelIcon = avatar.firstChild;
expect(cancelIcon).to.have.attribute('data-testid', 'CancelIcon');
});
it('should merge user classes & spread custom props to the root node', () => {
+ const container = render(
+
+
+ ,
+ ).container;
+ const avatar = container.firstChild;
+
expect(avatar).to.have.class(classes.root);
expect(avatar).to.have.class('my-avatar');
expect(avatar).to.have.attribute('data-my-prop', 'woofAvatar');
});
it('should apply the colorDefault class', () => {
+ const container = render(
+
+
+ ,
+ ).container;
+ const avatar = container.firstChild;
+
expect(avatar).to.have.class(classes.colorDefault);
});
});
describe('text avatar', () => {
- let container;
- let avatar;
-
- before(() => {
- container = render(
-
- OT
- ,
- ).container;
- avatar = container.firstChild;
- });
-
it('should render a div containing a string', () => {
+ const container = render(OT).container;
+ const avatar = container.firstChild;
+
expect(avatar).to.have.tagName('div');
expect(avatar.firstChild).to.text('OT');
});
it('should merge user classes & spread custom props to the root node', () => {
+ const container = render(
+
+ OT
+ ,
+ ).container;
+ const avatar = container.firstChild;
+
expect(avatar).to.have.class(classes.root);
expect(avatar).to.have.class('my-avatar');
expect(avatar).to.have.attribute('data-my-prop', 'woofAvatar');
});
it('should apply the colorDefault class', () => {
+ const container = render(OT).container;
+ const avatar = container.firstChild;
+
expect(avatar).to.have.class(classes.colorDefault);
});
});
describe('falsey avatar', () => {
- let container;
- let avatar;
-
- before(() => {
- container = render(
-
- {0}
- ,
- ).container;
- avatar = container.firstChild;
- });
-
it('should render with defaultColor class when supplied with a child with falsey value', () => {
+ const container = render({0}).container;
+ const avatar = container.firstChild;
+
expect(avatar).to.have.tagName('div');
expect(avatar.firstChild).to.text('0');
});
it('should merge user classes & spread custom props to the root node', () => {
+ const container = render(
+
+ {0}
+ ,
+ ).container;
+ const avatar = container.firstChild;
+
expect(avatar).to.have.class(classes.root);
expect(avatar).to.have.class('my-avatar');
expect(avatar).to.have.attribute('data-my-prop', 'woofAvatar');
});
it('should apply the colorDefault class', () => {
+ const container = render({0}).container;
+ const avatar = container.firstChild;
+
expect(avatar).to.have.class(classes.colorDefault);
});
});
diff --git a/packages/material-ui/src/ButtonBase/Ripple.test.js b/packages/material-ui/src/ButtonBase/Ripple.test.js
index 023f19dc704185..8a1ba50a2d2466 100644
--- a/packages/material-ui/src/ButtonBase/Ripple.test.js
+++ b/packages/material-ui/src/ButtonBase/Ripple.test.js
@@ -18,108 +18,119 @@ describe('', () => {
});
describe('starting and stopping', () => {
- let wrapper;
-
- before(() => {
- wrapper = render(
+ it('should start the ripple', () => {
+ const { container, setProps } = render(
,
);
- });
- it('should start the ripple', () => {
- wrapper.setProps({ in: true });
- const ripple = wrapper.container.querySelector('span');
+ setProps({ in: true });
+
+ const ripple = container.querySelector('span');
expect(ripple).to.have.class(classes.rippleVisible);
});
it('should stop the ripple', () => {
- wrapper.setProps({ in: true });
- wrapper.setProps({ in: false });
- const child = wrapper.container.querySelector('span > span');
+ const { container, setProps } = render(
+ ,
+ );
+
+ setProps({ in: false });
+
+ const child = container.querySelector('span > span');
expect(child).to.have.class(classes.childLeaving);
});
});
describe('pulsating and stopping 1', () => {
- let wrapper;
-
- before(() => {
- wrapper = render(
- ,
+ it('should render the ripple inside a pulsating Ripple', () => {
+ const { container } = render(
+ ,
);
- });
- it('should render the ripple inside a pulsating Ripple', () => {
- const ripple = wrapper.container.querySelector('span');
+ const ripple = container.querySelector('span');
expect(ripple).to.have.class(classes.ripple);
expect(ripple).to.have.class(classes.ripplePulsate);
- const child = wrapper.container.querySelector('span > span');
+ const child = container.querySelector('span > span');
expect(child).to.have.class(classes.childPulsate);
});
it('should start the ripple', () => {
- wrapper.setProps({ in: true });
- const ripple = wrapper.container.querySelector('span');
+ const { container, setProps } = render(
+ ,
+ );
+
+ setProps({ in: true });
+
+ const ripple = container.querySelector('span');
expect(ripple).to.have.class(classes.rippleVisible);
- const child = wrapper.container.querySelector('span > span');
+ const child = container.querySelector('span > span');
expect(child).to.have.class(classes.childPulsate);
});
it('should stop the ripple', () => {
- wrapper.setProps({ in: true });
- wrapper.setProps({ in: false });
- const child = wrapper.container.querySelector('span > span');
+ const { container, setProps } = render(
+ ,
+ );
+
+ setProps({ in: true });
+ setProps({ in: false });
+ const child = container.querySelector('span > span');
expect(child).to.have.class(classes.childLeaving);
});
});
describe('pulsating and stopping 2', () => {
- let wrapper;
let clock;
- let callbackSpy;
beforeEach(() => {
- callbackSpy = spy();
- wrapper = render(
+ clock = useFakeTimers();
+ });
+
+ afterEach(() => {
+ clock.restore();
+ });
+
+ it('handleExit should trigger a timer', () => {
+ const handleExited = spy();
+ const { setProps } = render(
,
);
- clock = useFakeTimers();
- });
-
- afterEach(() => {
- clock.restore();
- });
- it('handleExit should trigger a timer', () => {
- wrapper.setProps({ in: false });
+ setProps({ in: false });
clock.tick(549);
- expect(callbackSpy.callCount).to.equal(0);
+ expect(handleExited.callCount).to.equal(0);
clock.tick(1);
- expect(callbackSpy.callCount).to.equal(1);
+ expect(handleExited.callCount).to.equal(1);
});
it('unmount should defuse the handleExit timer', () => {
- wrapper.setProps({ in: false });
- wrapper.unmount();
+ const handleExited = spy();
+ const { setProps, unmount } = render(
+ ,
+ );
+
+ setProps({ in: false });
+ unmount();
clock.tick(550);
- expect(callbackSpy.callCount).to.equal(0);
+ expect(handleExited.callCount).to.equal(0);
});
});
});
diff --git a/test/utils/createClientRender.js b/test/utils/createClientRender.js
index 13b24c96cb7d09..b7b8567c421bb7 100644
--- a/test/utils/createClientRender.js
+++ b/test/utils/createClientRender.js
@@ -1,6 +1,8 @@
/* eslint-env mocha */
import * as React from 'react';
import PropTypes from 'prop-types';
+import createEmotionCache from '@emotion/cache';
+import { CacheProvider as EmotionCacheProvider } from '@emotion/react';
import {
act as rtlAct,
buildQueries,
@@ -175,49 +177,46 @@ const customQueries = {
};
/**
- * @typedef {object} RenderOptions
- * @property {HTMLElement} [options.baseElement] - https://testing-library.com/docs/react-testing-library/api#baseelement-1
- * @property {HTMLElement} [options.container] - https://testing-library.com/docs/react-testing-library/api#container
- * @property {boolean} [options.disableUnnmount] - if true does not cleanup before mount
- * @property {boolean} [options.hydrate] - https://testing-library.com/docs/react-testing-library/api#hydrate
- * @property {boolean} [options.strict] - wrap in React.StrictMode?
+ * @typedef {object} RenderConfiguration
+ * @property {HTMLElement} [baseElement] - https://testing-library.com/docs/react-testing-library/api#baseelement-1
+ * @property {HTMLElement} [container] - https://testing-library.com/docs/react-testing-library/api#container
+ * @property {boolean} [disableUnnmount] - if true does not cleanup before mount
+ * @property {import('@emotion/cache').EmotionCache} emotionCache - Value for the CacheProvider of emotion
+ * @property {boolean} [hydrate] - https://testing-library.com/docs/react-testing-library/api#hydrate
+ * @property {typeof Profiler} profiler
+ * @property {boolean} [strict] - wrap in React.StrictMode?
+ */
+
+/**
+ * @typedef {Omit} RenderOptions
*/
/**
* @param {React.ReactElement} element
- * @param {RenderOptions} [options]
+ * @param {RenderConfiguration} configuration
* @returns {import('@testing-library/react').RenderResult & { setProps(props: object): void}}
* TODO: type return RenderResult in setProps
*/
-function clientRender(element, options = {}) {
+function clientRender(element, configuration) {
const {
baseElement,
container,
+ emotionCache,
hydrate,
strict = true,
profiler,
wrapper: InnerWrapper = React.Fragment,
- } = options;
-
- if (profiler === null) {
- // TODO: remove tests rendering in mocha hooks
- // throw new Error('Rendered outside of a test. Use the test renderer only in tests.');
- }
+ } = configuration;
const Mode = strict ? React.StrictMode : React.Fragment;
function Wrapper({ children }) {
- if (profiler !== null) {
- return (
-
+ return (
+
+
{children}
-
- );
- }
- return (
-
- {children}
+
);
}
@@ -256,8 +255,8 @@ function clientRender(element, options = {}) {
}
/**
- * @param {RenderOptions} globalOptions
- * @returns {clientRender}
+ * @param {RenderOptions} [globalOptions]
+ * @returns {(element: React.ReactElement, options?: RenderOptions) => import('@testing-library/react').RenderResult & { setProps(props: object): void}}
*/
export function createClientRender(globalOptions = {}) {
const { strict: globalStrict } = globalOptions;
@@ -290,6 +289,15 @@ export function createClientRender(globalOptions = {}) {
}
});
+ /**
+ * @type {import('@emotion/cache').EmotionCache}
+ */
+ let emotionCache = null;
+ /**
+ * Flag whether all setup for `configuredClientRender` was completed.
+ * For legacy reasons `configuredClientRender` might accidentally be called in a before(Each) hook.
+ */
+ let prepared = false;
let profiler = null;
beforeEach(function beforeEachHook() {
if (!wasCalledInSuite) {
@@ -307,6 +315,10 @@ export function createClientRender(globalOptions = {}) {
);
}
profiler = new Profiler(this.currentTest);
+
+ emotionCache = createEmotionCache({ key: 'emotion-client-render' });
+
+ prepared = true;
});
afterEach(() => {
@@ -325,12 +337,25 @@ export function createClientRender(globalOptions = {}) {
cleanup();
profiler.report();
profiler = null;
+
+ emotionCache.sheet.tags.forEach((styleTag) => {
+ styleTag.remove();
+ });
+ emotionCache = null;
});
return function configuredClientRender(element, options = {}) {
+ if (!prepared) {
+ throw new Error(
+ 'Unable to finish setup before `render()` was called. ' +
+ 'This usually indicates that `render()` was called in a `before()` or `beforeEach` hook. ' +
+ 'Move the call into each `it()`. Otherwise you cannot run a specific test and we cannot isolate each test.',
+ );
+ }
+
const { strict = globalStrict, ...localOptions } = options;
- return clientRender(element, { ...localOptions, strict, profiler });
+ return clientRender(element, { ...localOptions, strict, profiler, emotionCache });
};
}