diff --git a/packages-internal/test-utils/src/createRenderer.tsx b/packages-internal/test-utils/src/createRenderer.tsx
index 170c179a7a862c..85889bc3d911a8 100644
--- a/packages-internal/test-utils/src/createRenderer.tsx
+++ b/packages-internal/test-utils/src/createRenderer.tsx
@@ -19,6 +19,7 @@ import {
} from '@testing-library/react/pure';
import { userEvent } from '@testing-library/user-event';
import { useFakeTimers } from 'sinon';
+import reactMajor from './reactMajor';
interface Interaction {
id: number;
@@ -568,7 +569,7 @@ export function createRenderer(globalOptions: CreateRendererOptions = {}): Rende
wrapper: InnerWrapper = React.Fragment,
} = options;
- const usesLegacyRoot = !React.version.startsWith('18');
+ const usesLegacyRoot = reactMajor < 18;
const Mode = strict && (strictEffects || usesLegacyRoot) ? React.StrictMode : React.Fragment;
return function Wrapper({ children }: { children?: React.ReactNode }) {
return (
diff --git a/packages-internal/test-utils/src/fireDiscreteEvent.ts b/packages-internal/test-utils/src/fireDiscreteEvent.ts
index 65157f9acf9689..a48f70d9ecbf9a 100644
--- a/packages-internal/test-utils/src/fireDiscreteEvent.ts
+++ b/packages-internal/test-utils/src/fireDiscreteEvent.ts
@@ -1,5 +1,5 @@
-import * as React from 'react';
import { configure, fireEvent, getConfig } from '@testing-library/react';
+import reactMajor from './reactMajor';
const noWrapper = (callback: () => void) => callback();
@@ -8,7 +8,7 @@ const noWrapper = (callback: () => void) => callback();
* @returns {void}
*/
function withMissingActWarningsIgnored(callback: () => void) {
- if (React.version.startsWith('18')) {
+ if (reactMajor >= 18) {
callback();
return;
}
diff --git a/packages-internal/test-utils/src/index.ts b/packages-internal/test-utils/src/index.ts
index ab14ee78f83aa0..f7c008b2b95ac7 100644
--- a/packages-internal/test-utils/src/index.ts
+++ b/packages-internal/test-utils/src/index.ts
@@ -15,9 +15,12 @@ export {} from './initMatchers';
export * as fireDiscreteEvent from './fireDiscreteEvent';
export * as userEvent from './userEvent';
export { default as flushMicrotasks } from './flushMicrotasks';
+export { default as reactMajor } from './reactMajor';
/**
* Set to true if console logs during [lifecycles that are invoked twice in `React.StrictMode`](https://reactjs.org/docs/strict-mode.html#detecting-unexpected-side-effects) are suppressed.
* Useful for asserting on `console.warn` or `console.error` via `toErrorDev()`.
+ * TODO: Refactor to use reactMajor when fixing the React 17 cron test.
+ * https://github.com/mui/material-ui/issues/43153
*/
export const strictModeDoubleLoggingSuppressed = React.version.startsWith('17');
diff --git a/packages-internal/test-utils/src/reactMajor.ts b/packages-internal/test-utils/src/reactMajor.ts
new file mode 100644
index 00000000000000..69ae8dc426bafd
--- /dev/null
+++ b/packages-internal/test-utils/src/reactMajor.ts
@@ -0,0 +1,3 @@
+import * as React from 'react';
+
+export default Number(React.version.split('.')[0]);
diff --git a/packages-internal/test-utils/src/userEvent.ts b/packages-internal/test-utils/src/userEvent.ts
index 7bf5cd34950572..ada372c3c4eae5 100644
--- a/packages-internal/test-utils/src/userEvent.ts
+++ b/packages-internal/test-utils/src/userEvent.ts
@@ -1,6 +1,6 @@
-import * as React from 'react';
import { click, mouseDown, mouseUp, keyDown, keyUp } from './fireDiscreteEvent';
import { act, fireEvent } from './createRenderer';
+import reactMajor from './reactMajor';
export function touch(target: Element): void {
fireEvent.touchStart(target);
@@ -11,7 +11,7 @@ export const mousePress: (...args: Parameters<(typeof fireEvent)['mouseUp']>) =>
target,
options,
) => {
- if (React.version.startsWith('18')) {
+ if (reactMajor >= 18) {
fireEvent.mouseDown(target, options);
fireEvent.mouseUp(target, options);
fireEvent.click(target, options);
@@ -24,7 +24,7 @@ export const mousePress: (...args: Parameters<(typeof fireEvent)['mouseUp']>) =>
};
export function keyPress(target: Element, options: { key: string; [key: string]: any }): void {
- if (React.version.startsWith('18')) {
+ if (reactMajor >= 18) {
fireEvent.keyDown(target, options);
fireEvent.keyUp(target, options);
} else {
diff --git a/packages/mui-base/src/Unstable_NumberInput/NumberInput.test.tsx b/packages/mui-base/src/Unstable_NumberInput/NumberInput.test.tsx
index 5f3b0fc32b4604..0a2c5ed6697bd3 100644
--- a/packages/mui-base/src/Unstable_NumberInput/NumberInput.test.tsx
+++ b/packages/mui-base/src/Unstable_NumberInput/NumberInput.test.tsx
@@ -166,7 +166,7 @@ describe('', () => {
}}
/>,
);
- }).toErrorDev('Warning: Unknown event handler property `onInputChange`. It will be ignored.');
+ }).toErrorDev('Unknown event handler property `onInputChange`. It will be ignored.');
});
it('should fire on keyboard input in the textbox instead of onChange', async () => {
diff --git a/packages/mui-base/src/useAutocomplete/useAutocomplete.test.js b/packages/mui-base/src/useAutocomplete/useAutocomplete.test.js
index 72ac62297340d0..ea2198ba61c93e 100644
--- a/packages/mui-base/src/useAutocomplete/useAutocomplete.test.js
+++ b/packages/mui-base/src/useAutocomplete/useAutocomplete.test.js
@@ -1,6 +1,13 @@
import * as React from 'react';
import { expect } from 'chai';
-import { createRenderer, screen, ErrorBoundary, act, fireEvent } from '@mui/internal-test-utils';
+import {
+ createRenderer,
+ screen,
+ ErrorBoundary,
+ act,
+ fireEvent,
+ reactMajor,
+} from '@mui/internal-test-utils';
import { spy } from 'sinon';
import { useAutocomplete, createFilterOptions } from '@mui/base/useAutocomplete';
@@ -276,28 +283,39 @@ describe('useAutocomplete', () => {
);
}
+ const muiErrorMessage = 'MUI: Unable to find the input element.';
+ const aboveErrorUlElementMessage = 'The above error occurred in the
component';
+ const aboveErrorTestComponentMessage = 'The above error occurred in the component';
const node16ErrorMessage =
- "Error: Uncaught [TypeError: Cannot read properties of null (reading 'removeAttribute')]";
- const olderNodeErrorMessage =
- "Error: Uncaught [TypeError: Cannot read property 'removeAttribute' of null]";
+ "TypeError: Cannot read properties of null (reading 'removeAttribute')";
+ const olderNodeErrorMessage = "TypeError: Cannot read property 'removeAttribute' of null";
const nodeVersion = Number(process.versions.node.split('.')[0]);
- const errorMessage = nodeVersion >= 16 ? node16ErrorMessage : olderNodeErrorMessage;
-
- const devErrorMessages = [
- errorMessage,
- 'MUI: Unable to find the input element.',
- errorMessage,
- // strict effects runs effects twice
- React.version.startsWith('18') && 'MUI: Unable to find the input element.',
- React.version.startsWith('18') && errorMessage,
- 'The above error occurred in the component',
- React.version.startsWith('16') && 'The above error occurred in the component',
- 'The above error occurred in the component',
- // strict effects runs effects twice
- React.version.startsWith('18') && 'The above error occurred in the component',
- React.version.startsWith('16') && 'The above error occurred in the component',
- ];
+ const nodeErrorMessage = nodeVersion >= 16 ? node16ErrorMessage : olderNodeErrorMessage;
+
+ const defaultErrorMessages = [muiErrorMessage, nodeErrorMessage, nodeErrorMessage];
+
+ const errorMessagesByReactMajor = {
+ 17: [
+ nodeErrorMessage,
+ muiErrorMessage,
+ nodeErrorMessage,
+ aboveErrorUlElementMessage,
+ aboveErrorTestComponentMessage,
+ ],
+ 18: [
+ nodeErrorMessage,
+ muiErrorMessage,
+ nodeErrorMessage,
+ muiErrorMessage,
+ nodeErrorMessage,
+ aboveErrorUlElementMessage,
+ aboveErrorTestComponentMessage,
+ aboveErrorTestComponentMessage,
+ ],
+ };
+
+ const devErrorMessages = errorMessagesByReactMajor[reactMajor] || defaultErrorMessages;
expect(() => {
render(
diff --git a/packages/mui-joy/src/Autocomplete/Autocomplete.test.tsx b/packages/mui-joy/src/Autocomplete/Autocomplete.test.tsx
index 0faa8ba03dc113..3a080d07f1df77 100644
--- a/packages/mui-joy/src/Autocomplete/Autocomplete.test.tsx
+++ b/packages/mui-joy/src/Autocomplete/Autocomplete.test.tsx
@@ -8,6 +8,7 @@ import {
act,
fireEvent,
strictModeDoubleLoggingSuppressed,
+ reactMajor,
} from '@mui/internal-test-utils';
import Autocomplete, {
autocompleteClasses as classes,
@@ -1209,19 +1210,19 @@ describe('Joy ', () => {
const value = 'not a good value';
const options = ['first option', 'second option'];
+ const errorMessage = 'None of the options match with `"not a good value"`';
+
+ let expectedOccurrences = 4;
+
+ if (reactMajor === 18) {
+ expectedOccurrences = 6;
+ } else if (reactMajor === 17) {
+ expectedOccurrences = 2;
+ }
+
expect(() => {
render();
- }).toWarnDev([
- 'None of the options match with `"not a good value"`',
- !strictModeDoubleLoggingSuppressed && 'None of the options match with `"not a good value"`',
- 'None of the options match with `"not a good value"`',
- !strictModeDoubleLoggingSuppressed && 'None of the options match with `"not a good value"`',
- // React 18 Strict Effects run mount effects twice which lead to a cascading update
- React.version.startsWith('18') && 'None of the options match with `"not a good value"`',
- React.version.startsWith('18') &&
- !strictModeDoubleLoggingSuppressed &&
- 'None of the options match with `"not a good value"`',
- ]);
+ }).toWarnDev(Array(expectedOccurrences).fill(errorMessage));
});
it('warn if groups options are not sorted', () => {
@@ -1930,9 +1931,11 @@ describe('Joy ', () => {
await user.click(screen.getByText('Reset'));
- expect(handleInputChange.callCount).to.equal(4);
- expect(handleInputChange.args[3][1]).to.equal(options[1].name);
- expect(handleInputChange.args[3][2]).to.equal('reset');
+ const expectedCallCount = reactMajor === 18 ? 4 : 2;
+
+ expect(handleInputChange.callCount).to.equal(expectedCallCount);
+ expect(handleInputChange.args[expectedCallCount - 1][1]).to.equal(options[1].name);
+ expect(handleInputChange.args[expectedCallCount - 1][2]).to.equal('reset');
});
it('provides a reason on clear', async () => {
@@ -2186,10 +2189,10 @@ describe('Joy ', () => {
);
expect(handleHighlightChange.callCount).to.equal(
// FIXME: highlighted index implementation should be implemented using React not the DOM.
- React.version.startsWith('18') ? 2 : 1,
+ reactMajor >= 18 ? 2 : 1,
);
expect(handleHighlightChange.args[0]).to.deep.equal([undefined, options[0], 'auto']);
- if (React.version.startsWith('18')) {
+ if (reactMajor >= 18) {
expect(handleHighlightChange.args[1]).to.deep.equal([undefined, options[0], 'auto']);
}
});
@@ -2206,9 +2209,9 @@ describe('Joy ', () => {
expect(handleHighlightChange.callCount).to.equal(
// FIXME: highlighted index implementation should be implemented using React not the DOM.
- React.version.startsWith('18') ? 4 : 3,
+ reactMajor >= 18 ? 4 : 3,
);
- if (React.version.startsWith('18')) {
+ if (reactMajor >= 18) {
expect(handleHighlightChange.args[2][0]).to.equal(undefined);
expect(handleHighlightChange.args[2][1]).to.equal(null);
expect(handleHighlightChange.args[2][2]).to.equal('auto');
@@ -2220,7 +2223,7 @@ describe('Joy ', () => {
fireEvent.keyDown(textbox, { key: 'ArrowDown' });
expect(handleHighlightChange.callCount).to.equal(
// FIXME: highlighted index implementation should be implemented using React not the DOM.
- React.version.startsWith('18') ? 5 : 4,
+ reactMajor >= 18 ? 5 : 4,
);
expect(handleHighlightChange.lastCall.args[0]).not.to.equal(undefined);
expect(handleHighlightChange.lastCall.args[1]).to.equal(options[1]);
@@ -2237,9 +2240,9 @@ describe('Joy ', () => {
fireEvent.mouseMove(firstOption);
expect(handleHighlightChange.callCount).to.equal(
// FIXME: highlighted index implementation should be implemented using React not the DOM.
- React.version.startsWith('18') ? 4 : 3,
+ reactMajor >= 18 ? 4 : 3,
);
- if (React.version.startsWith('18')) {
+ if (reactMajor >= 18) {
expect(handleHighlightChange.args[2][0]).to.equal(undefined);
expect(handleHighlightChange.args[2][1]).to.equal(null);
expect(handleHighlightChange.args[2][2]).to.equal('auto');
@@ -2284,7 +2287,11 @@ describe('Joy ', () => {
checkHighlightIs(getByRole('listbox'), 'one');
setProps({ options: ['four', 'five'] });
checkHighlightIs(getByRole('listbox'), 'four');
- expect(handleHighlightChange).to.deep.equal([null, 'one', 'four']);
+
+ const expectedCallHistory =
+ reactMajor >= 19 ? [null, 'one', 'one', 'four'] : [null, 'one', 'four'];
+
+ expect(handleHighlightChange).to.deep.equal(expectedCallHistory);
});
});
diff --git a/packages/mui-lab/src/Masonry/Masonry.test.js b/packages/mui-lab/src/Masonry/Masonry.test.js
index c5e56f2a0ac5b0..790ff5e3a16c19 100644
--- a/packages/mui-lab/src/Masonry/Masonry.test.js
+++ b/packages/mui-lab/src/Masonry/Masonry.test.js
@@ -1,5 +1,5 @@
import * as React from 'react';
-import { createRenderer } from '@mui/internal-test-utils';
+import { createRenderer, reactMajor } from '@mui/internal-test-utils';
import { expect } from 'chai';
import { createTheme } from '@mui/material/styles';
import defaultTheme from '@mui/material/styles/defaultTheme';
@@ -100,7 +100,7 @@ describe('', () => {
});
it('should throw console error when children are empty', function test() {
- if (!/jsdom/.test(window.navigator.userAgent)) {
+ if (!/jsdom/.test(window.navigator.userAgent) || reactMajor >= 19) {
this.skip();
}
expect(() => render()).toErrorDev(
diff --git a/packages/mui-material/src/Accordion/Accordion.test.js b/packages/mui-material/src/Accordion/Accordion.test.js
index 815f8afba2bf5f..4a22abe61089ca 100644
--- a/packages/mui-material/src/Accordion/Accordion.test.js
+++ b/packages/mui-material/src/Accordion/Accordion.test.js
@@ -2,7 +2,7 @@ import * as React from 'react';
import PropTypes from 'prop-types';
import { expect } from 'chai';
import { spy } from 'sinon';
-import { createRenderer, fireEvent } from '@mui/internal-test-utils';
+import { createRenderer, fireEvent, reactMajor } from '@mui/internal-test-utils';
import Accordion, { accordionClasses as classes } from '@mui/material/Accordion';
import Paper from '@mui/material/Paper';
import AccordionSummary from '@mui/material/AccordionSummary';
@@ -158,7 +158,12 @@ describe('', () => {
describe('prop: children', () => {
describe('first child', () => {
- beforeEach(() => {
+ beforeEach(function beforeEachCallback() {
+ if (reactMajor >= 19) {
+ // React 19 removed prop types support
+ this.skip();
+ }
+
PropTypes.resetWarningCache();
});
diff --git a/packages/mui-material/src/Autocomplete/Autocomplete.test.js b/packages/mui-material/src/Autocomplete/Autocomplete.test.js
index 7fda0169707e28..4cea6195fb534c 100644
--- a/packages/mui-material/src/Autocomplete/Autocomplete.test.js
+++ b/packages/mui-material/src/Autocomplete/Autocomplete.test.js
@@ -7,6 +7,7 @@ import {
fireEvent,
screen,
strictModeDoubleLoggingSuppressed,
+ reactMajor,
} from '@mui/internal-test-utils';
import { spy } from 'sinon';
import userEvent from '@testing-library/user-event';
@@ -1722,6 +1723,16 @@ describe('', () => {
const value = 'not a good value';
const options = ['first option', 'second option'];
+ const errorMessage = 'None of the options match with `"not a good value"`';
+
+ let expectedOccurrences = 4;
+
+ if (reactMajor === 18) {
+ expectedOccurrences = 6;
+ } else if (reactMajor === 17) {
+ expectedOccurrences = 2;
+ }
+
expect(() => {
render(
', () => {
renderInput={(params) => }
/>,
);
- }).toWarnDev([
- 'None of the options match with `"not a good value"`',
- !strictModeDoubleLoggingSuppressed && 'None of the options match with `"not a good value"`',
- 'None of the options match with `"not a good value"`',
- !strictModeDoubleLoggingSuppressed && 'None of the options match with `"not a good value"`',
- // React 18 Strict Effects run mount effects twice which lead to a cascading update
- React.version.startsWith('18') && 'None of the options match with `"not a good value"`',
- React.version.startsWith('18') &&
- !strictModeDoubleLoggingSuppressed &&
- 'None of the options match with `"not a good value"`',
- ]);
+ }).toWarnDev(Array(expectedOccurrences).fill(errorMessage));
});
it('warn if groups options are not sorted', () => {
@@ -2866,10 +2867,10 @@ describe('', () => {
);
expect(handleHighlightChange.callCount).to.equal(
// FIXME: highlighted index implementation should be implemented using React not the DOM.
- React.version.startsWith('18') ? 2 : 1,
+ reactMajor >= 18 ? 2 : 1,
);
expect(handleHighlightChange.args[0]).to.deep.equal([undefined, options[0], 'auto']);
- if (React.version.startsWith('18')) {
+ if (reactMajor >= 18) {
expect(handleHighlightChange.args[1]).to.deep.equal([undefined, options[0], 'auto']);
}
});
@@ -2889,10 +2890,10 @@ describe('', () => {
);
expect(handleHighlightChange.callCount).to.equal(
// FIXME: highlighted index implementation should be implemented using React not the DOM.
- React.version.startsWith('18') ? 2 : 1,
+ reactMajor >= 18 ? 2 : 1,
);
expect(handleHighlightChange.args[0]).to.deep.equal([undefined, options[0], 'auto']);
- if (React.version.startsWith('18')) {
+ if (reactMajor >= 18) {
expect(handleHighlightChange.args[1]).to.deep.equal([undefined, options[0], 'auto']);
}
});
@@ -2914,9 +2915,9 @@ describe('', () => {
expect(handleHighlightChange.callCount).to.equal(
// FIXME: highlighted index implementation should be implemented using React not the DOM.
- React.version.startsWith('18') ? 4 : 3,
+ reactMajor >= 18 ? 4 : 3,
);
- if (React.version.startsWith('18')) {
+ if (reactMajor >= 18) {
expect(handleHighlightChange.args[2][0]).to.equal(undefined);
expect(handleHighlightChange.args[2][1]).to.equal(null);
expect(handleHighlightChange.args[2][2]).to.equal('auto');
@@ -2928,7 +2929,7 @@ describe('', () => {
fireEvent.keyDown(textbox, { key: 'ArrowDown' });
expect(handleHighlightChange.callCount).to.equal(
// FIXME: highlighted index implementation should be implemented using React not the DOM.
- React.version.startsWith('18') ? 5 : 4,
+ reactMajor >= 18 ? 5 : 4,
);
expect(handleHighlightChange.lastCall.args[0]).not.to.equal(undefined);
expect(handleHighlightChange.lastCall.args[1]).to.equal(options[1]);
@@ -2950,9 +2951,9 @@ describe('', () => {
fireEvent.mouseMove(firstOption);
expect(handleHighlightChange.callCount).to.equal(
// FIXME: highlighted index implementation should be implemented using React not the DOM.
- React.version.startsWith('18') ? 4 : 3,
+ reactMajor >= 18 ? 4 : 3,
);
- if (React.version.startsWith('18')) {
+ if (reactMajor >= 18) {
expect(handleHighlightChange.args[2][0]).to.equal(undefined);
expect(handleHighlightChange.args[2][1]).to.equal(null);
expect(handleHighlightChange.args[2][2]).to.equal('auto');
@@ -3001,7 +3002,11 @@ describe('', () => {
checkHighlightIs(getByRole('listbox'), 'one');
setProps({ options: ['four', 'five'] });
checkHighlightIs(getByRole('listbox'), 'four');
- expect(handleHighlightChange).to.deep.equal([null, 'one', 'four']);
+
+ const expectedCallHistory =
+ reactMajor >= 19 ? [null, 'one', 'one', 'four'] : [null, 'one', 'four'];
+
+ expect(handleHighlightChange).to.deep.equal(expectedCallHistory);
});
});
diff --git a/packages/mui-material/src/Grid/Grid.test.js b/packages/mui-material/src/Grid/Grid.test.js
index 68ce2ec894958f..dd2c069b9ff9da 100644
--- a/packages/mui-material/src/Grid/Grid.test.js
+++ b/packages/mui-material/src/Grid/Grid.test.js
@@ -1,6 +1,6 @@
import * as React from 'react';
import { expect } from 'chai';
-import { createRenderer, screen } from '@mui/internal-test-utils';
+import { createRenderer, screen, reactMajor } from '@mui/internal-test-utils';
import { createTheme, ThemeProvider } from '@mui/material/styles';
import defaultTheme from '@mui/material/styles/defaultTheme';
import Grid, { gridClasses as classes } from '@mui/material/Grid';
@@ -647,7 +647,12 @@ describe('Material UI ', () => {
});
});
- it('should ignore grid item with spacing object', () => {
+ it('should ignore grid item with spacing object', function test() {
+ if (reactMajor < 19) {
+ // React 19 removed prop types support
+ this.skip();
+ }
+
const theme = createTheme({
breakpoints: {
keys: ['mobile', 'desktop'],
@@ -673,19 +678,42 @@ describe('Material UI ', () => {
},
},
});
+ const { container } = render(
+
+
+ ,
+ );
+ expect(container.firstChild).to.not.have.class('MuiGrid-spacing-mobile-1.5');
+ expect(container.firstChild).to.not.have.class('MuiGrid-spacing-desktop-3');
+ expect(container.firstChild).to.not.toHaveComputedStyle({
+ position: 'relative',
+ top: '30px',
+ left: '50px',
+ });
+ });
+
+ it('should warn of failed prop types when providing spacing object without the `container` prop', function test() {
+ if (reactMajor >= 19) {
+ // React 19 removed prop types support
+ this.skip();
+ }
+
+ const theme = createTheme({
+ breakpoints: {
+ keys: ['mobile', 'desktop'],
+ values: {
+ mobile: 0,
+ desktop: 1200,
+ },
+ },
+ });
+
expect(() => {
- const { container } = render(
+ render(
-
+
,
);
- expect(container.firstChild).to.not.have.class('MuiGrid-spacing-mobile-1.5');
- expect(container.firstChild).to.not.have.class('MuiGrid-spacing-desktop-3');
- expect(container.firstChild).to.not.toHaveComputedStyle({
- position: 'relative',
- top: '30px',
- left: '50px',
- });
}).toErrorDev(
'Warning: Failed prop type: The prop `spacing` of `Grid` can only be used together with the `container` prop.',
);
diff --git a/packages/mui-material/src/IconButton/IconButton.test.js b/packages/mui-material/src/IconButton/IconButton.test.js
index 3fad1d8384fbb5..25ca0ca6b2b9d3 100644
--- a/packages/mui-material/src/IconButton/IconButton.test.js
+++ b/packages/mui-material/src/IconButton/IconButton.test.js
@@ -1,7 +1,7 @@
import * as React from 'react';
import { expect } from 'chai';
import PropTypes from 'prop-types';
-import { createRenderer } from '@mui/internal-test-utils';
+import { createRenderer, reactMajor } from '@mui/internal-test-utils';
import capitalize from '@mui/utils/capitalize';
import { ThemeProvider, createTheme } from '@mui/material/styles';
import IconButton, { iconButtonClasses as classes } from '@mui/material/IconButton';
@@ -108,7 +108,12 @@ describe('', () => {
});
});
- it('should raise a warning about onClick in children because of Firefox', () => {
+ it('should raise a warning about onClick in children because of Firefox', function test() {
+ if (reactMajor >= 19) {
+ // React 19 removed prop types support
+ this.skip();
+ }
+
expect(() => {
PropTypes.checkPropTypes(
IconButton.propTypes,
diff --git a/packages/mui-material/src/InputBase/InputBase.test.js b/packages/mui-material/src/InputBase/InputBase.test.js
index 05307db95e3af6..0b44695890bd41 100644
--- a/packages/mui-material/src/InputBase/InputBase.test.js
+++ b/packages/mui-material/src/InputBase/InputBase.test.js
@@ -2,7 +2,7 @@ import * as React from 'react';
import PropTypes from 'prop-types';
import { expect } from 'chai';
import { spy } from 'sinon';
-import { act, createRenderer, fireEvent, screen } from '@mui/internal-test-utils';
+import { act, createRenderer, fireEvent, screen, reactMajor } from '@mui/internal-test-utils';
import { ThemeProvider } from '@emotion/react';
import FormControl, { useFormControl } from '@mui/material/FormControl';
import InputAdornment from '@mui/material/InputAdornment';
@@ -276,16 +276,20 @@ describe('', () => {
const triggerChangeRef = React.createRef();
+ const errorMessage =
+ 'MUI: You have provided a `inputComponent` to the input component\nthat does not correctly handle the `ref` prop.\nMake sure the `ref` prop is called with a HTMLInputElement.';
+
+ let expectedOccurrences = 1;
+
+ if (reactMajor === 18) {
+ expectedOccurrences = 2;
+ }
+
expect(() => {
render(
,
);
- }).toErrorDev([
- 'MUI: You have provided a `inputComponent` to the input component\nthat does not correctly handle the `ref` prop.\nMake sure the `ref` prop is called with a HTMLInputElement.',
- // React 18 Strict Effects run mount effects twice
- React.version.startsWith('18') &&
- 'MUI: You have provided a `inputComponent` to the input component\nthat does not correctly handle the `ref` prop.\nMake sure the `ref` prop is called with a HTMLInputElement.',
- ]);
+ }).toErrorDev(Array(expectedOccurrences).fill(errorMessage));
});
});
});
@@ -497,6 +501,14 @@ describe('', () => {
describe('registering input', () => {
it("should warn if more than one input is rendered regardless how it's nested", () => {
+ const errorMessage =
+ 'MUI: There are multiple `InputBase` components inside a FormControl.\nThis creates visual inconsistencies, only use one `InputBase`.';
+
+ let expectedOccurrences = 1;
+
+ if (reactMajor === 18) {
+ expectedOccurrences = 2;
+ }
expect(() => {
render(
@@ -507,12 +519,7 @@ describe('', () => {
,
);
- }).toErrorDev([
- 'MUI: There are multiple `InputBase` components inside a FormControl.\nThis creates visual inconsistencies, only use one `InputBase`.',
- // React 18 Strict Effects run mount effects twice
- React.version.startsWith('18') &&
- 'MUI: There are multiple `InputBase` components inside a FormControl.\nThis creates visual inconsistencies, only use one `InputBase`.',
- ]);
+ }).toErrorDev(Array(expectedOccurrences).fill(errorMessage));
});
it('should not warn if only one input is rendered', () => {
diff --git a/packages/mui-material/src/ListItem/ListItem.test.js b/packages/mui-material/src/ListItem/ListItem.test.js
index 9fde560ae2b03b..4c9101020de95d 100644
--- a/packages/mui-material/src/ListItem/ListItem.test.js
+++ b/packages/mui-material/src/ListItem/ListItem.test.js
@@ -1,7 +1,7 @@
import * as React from 'react';
import { expect } from 'chai';
import PropTypes from 'prop-types';
-import { act, createRenderer, fireEvent, queries } from '@mui/internal-test-utils';
+import { act, createRenderer, fireEvent, queries, reactMajor } from '@mui/internal-test-utils';
import { ThemeProvider, createTheme } from '@mui/material/styles';
import ListItemText from '@mui/material/ListItemText';
import ListItemSecondaryAction from '@mui/material/ListItemSecondaryAction';
@@ -168,7 +168,12 @@ describe('', () => {
PropTypes.resetWarningCache();
});
- it('warns if it cant detect the secondary action properly', () => {
+ it('warns if it cant detect the secondary action properly', function test() {
+ if (reactMajor >= 19) {
+ // React 19 removed prop types support
+ this.skip();
+ }
+
expect(() => {
PropTypes.checkPropTypes(
ListItem.propTypes,
@@ -191,7 +196,7 @@ describe('', () => {
}).toErrorDev([
'MUI: Unable to set focus to a ListItem whose component has not been rendered.',
// React 18 Strict Effects run mount effects twice
- React.version.startsWith('18') &&
+ reactMajor === 18 &&
'MUI: Unable to set focus to a ListItem whose component has not been rendered.',
]);
});
diff --git a/packages/mui-material/src/Menu/Menu.test.js b/packages/mui-material/src/Menu/Menu.test.js
index f29294702731af..1d9ca77a1861cd 100644
--- a/packages/mui-material/src/Menu/Menu.test.js
+++ b/packages/mui-material/src/Menu/Menu.test.js
@@ -6,6 +6,7 @@ import {
screen,
fireEvent,
strictModeDoubleLoggingSuppressed,
+ reactMajor,
} from '@mui/internal-test-utils';
import Menu, { menuClasses as classes } from '@mui/material/Menu';
import Popover from '@mui/material/Popover';
@@ -59,7 +60,7 @@ describe('', () => {
expect(handleEnter.callCount).to.equal(
// onEnter is called on mount which is run twice with Strict Effects
- React.version.startsWith('18') ? 2 : 1,
+ reactMajor >= 18 ? 2 : 1,
);
expect(handleEnter.args[0].length).to.equal(2);
expect(handleEntering.callCount).to.equal(1);
diff --git a/packages/mui-material/src/Popover/Popover.test.js b/packages/mui-material/src/Popover/Popover.test.js
index db6210e99c2976..526027469d8c40 100644
--- a/packages/mui-material/src/Popover/Popover.test.js
+++ b/packages/mui-material/src/Popover/Popover.test.js
@@ -1,7 +1,7 @@
import * as React from 'react';
import { expect } from 'chai';
import { spy, stub, match } from 'sinon';
-import { act, createRenderer, screen } from '@mui/internal-test-utils';
+import { act, createRenderer, reactMajor, screen } from '@mui/internal-test-utils';
import PropTypes from 'prop-types';
import Modal from '@mui/material/Modal';
import Paper, { paperClasses } from '@mui/material/Paper';
@@ -206,7 +206,7 @@ describe('', () => {
expect(handleEnter.callCount).to.equal(
// onEnter is called on mount which is run twice with Strict Effects
- React.version.startsWith('18') ? 2 : 1,
+ reactMajor >= 18 ? 2 : 1,
);
});
@@ -245,7 +245,7 @@ describe('', () => {
onExiting: handleExiting.callCount,
}).to.deep.equal({
// onEnter is called on mount which is run twice with Strict Effects
- onEnter: React.version.startsWith('18') ? 2 : 1,
+ onEnter: reactMajor >= 18 ? 2 : 1,
onEntering: 1,
onEntered: 0,
onExit: 0,
@@ -264,7 +264,7 @@ describe('', () => {
onExiting: handleExiting.callCount,
}).to.deep.equal({
// onEnter is called on mount which is run twice with Strict Effects
- onEnter: React.version.startsWith('18') ? 2 : 1,
+ onEnter: reactMajor >= 18 ? 2 : 1,
onEntering: 1,
onEntered: 1,
onExit: 0,
@@ -283,7 +283,7 @@ describe('', () => {
onExiting: handleExiting.callCount,
}).to.deep.equal({
// onEnter is called on mount which is run twice with Strict Effects
- onEnter: React.version.startsWith('18') ? 2 : 1,
+ onEnter: reactMajor >= 18 ? 2 : 1,
onEntering: 1,
onEntered: 1,
onExit: 1,
@@ -302,7 +302,7 @@ describe('', () => {
onExiting: handleExiting.callCount,
}).to.deep.equal({
// onEnter is called on mount which is run twice with Strict Effects
- onEnter: React.version.startsWith('18') ? 2 : 1,
+ onEnter: reactMajor >= 18 ? 2 : 1,
onEntering: 1,
onEntered: 1,
onExit: 1,
diff --git a/packages/mui-material/src/Select/Select.test.js b/packages/mui-material/src/Select/Select.test.js
index d425e6e82b40c0..c135d483a9fae7 100644
--- a/packages/mui-material/src/Select/Select.test.js
+++ b/packages/mui-material/src/Select/Select.test.js
@@ -1,7 +1,14 @@
import * as React from 'react';
import { expect } from 'chai';
import { spy, stub } from 'sinon';
-import { ErrorBoundary, act, createRenderer, fireEvent, screen } from '@mui/internal-test-utils';
+import {
+ ErrorBoundary,
+ act,
+ createRenderer,
+ fireEvent,
+ screen,
+ reactMajor,
+} from '@mui/internal-test-utils';
import { createTheme, ThemeProvider } from '@mui/material/styles';
import MenuItem, { menuItemClasses } from '@mui/material/MenuItem';
import ListSubheader from '@mui/material/ListSubheader';
@@ -368,6 +375,15 @@ describe('', () => {
describe('warnings', () => {
it('warns when the value is not present in any option', () => {
+ const errorMessage =
+ 'MUI: You have provided an out-of-range value `20` for the select component.';
+
+ let expectedOccurrences = 2;
+
+ if (reactMajor === 18) {
+ expectedOccurrences = 3;
+ }
+
expect(() =>
render(
,
),
- ).toWarnDev([
- 'MUI: You have provided an out-of-range value `20` for the select component.',
- // React 18 Strict Effects run mount effects twice
- React.version.startsWith('18') &&
- 'MUI: You have provided an out-of-range value `20` for the select component.',
- 'MUI: You have provided an out-of-range value `20` for the select component.',
- ]);
+ ).toWarnDev(Array(expectedOccurrences).fill(errorMessage));
});
});
});
@@ -1175,8 +1185,8 @@ describe('', () => {
}).toErrorDev([
'MUI: The `value` prop must be an array',
// React 18 Strict Effects run mount effects twice
- React.version.startsWith('18') && 'MUI: The `value` prop must be an array',
- 'The above error occurred in the component',
+ reactMajor === 18 && 'MUI: The `value` prop must be an array',
+ reactMajor < 19 && 'The above error occurred in the component',
]);
const {
current: { errors },
diff --git a/packages/mui-material/src/Tabs/Tabs.test.js b/packages/mui-material/src/Tabs/Tabs.test.js
index 5324acf9245dc4..8b9f426c35e1ee 100644
--- a/packages/mui-material/src/Tabs/Tabs.test.js
+++ b/packages/mui-material/src/Tabs/Tabs.test.js
@@ -5,6 +5,7 @@ import {
act,
createRenderer,
fireEvent,
+ reactMajor,
screen,
strictModeDoubleLoggingSuppressed,
waitFor,
@@ -358,10 +359,10 @@ describe('', () => {
}).toErrorDev([
'You can provide one of the following values: 1, 3',
// React 18 Strict Effects run mount effects twice
- React.version.startsWith('18') && 'You can provide one of the following values: 1, 3',
+ reactMajor === 18 && 'You can provide one of the following values: 1, 3',
'You can provide one of the following values: 1, 3',
// React 18 Strict Effects run mount effects twice
- React.version.startsWith('18') && 'You can provide one of the following values: 1, 3',
+ reactMajor === 18 && 'You can provide one of the following values: 1, 3',
'You can provide one of the following values: 1, 3',
'You can provide one of the following values: 1, 3',
]);
diff --git a/packages/mui-material/src/Tooltip/Tooltip.test.js b/packages/mui-material/src/Tooltip/Tooltip.test.js
index 69cb616337e416..0a4d51d7a2fa39 100644
--- a/packages/mui-material/src/Tooltip/Tooltip.test.js
+++ b/packages/mui-material/src/Tooltip/Tooltip.test.js
@@ -9,6 +9,7 @@ import {
simulatePointerDevice,
focusVisible,
programmaticFocusTriggersFocusVisible,
+ reactMajor,
} from '@mui/internal-test-utils';
import { camelCase } from 'lodash/string';
import Tooltip, { tooltipClasses as classes } from '@mui/material/Tooltip';
@@ -1057,7 +1058,12 @@ describe('', () => {
);
});
- it('should warn when children is a string', () => {
+ it('should warn when children is a string', function test() {
+ if (reactMajor >= 19) {
+ // React 19 removed prop types support
+ this.skip();
+ }
+
expect(() => {
render(Hello World);
}).toErrorDev('Invalid prop `children` of type `string` supplied');
diff --git a/packages/mui-material/src/internal/SwitchBase.test.js b/packages/mui-material/src/internal/SwitchBase.test.js
index 52432fc37037e3..7e177979efa1ba 100644
--- a/packages/mui-material/src/internal/SwitchBase.test.js
+++ b/packages/mui-material/src/internal/SwitchBase.test.js
@@ -1,7 +1,7 @@
import * as React from 'react';
import { expect } from 'chai';
import { spy } from 'sinon';
-import { act, createRenderer } from '@mui/internal-test-utils';
+import { act, createRenderer, reactMajor } from '@mui/internal-test-utils';
import SwitchBase from './SwitchBase';
import FormControl, { useFormControl } from '../FormControl';
import ButtonBase from '../ButtonBase';
@@ -410,9 +410,12 @@ describe('', () => {
setProps({ checked: true });
global.didWarnControlledToUncontrolled = true;
}).toErrorDev([
- React.version.startsWith('16')
- ? 'Warning: A component is changing an uncontrolled input of type checkbox to be controlled.'
- : 'Warning: A component is changing an uncontrolled input to be controlled.',
+ reactMajor === 16 &&
+ 'Warning: A component is changing an uncontrolled input of type checkbox to be controlled.',
+ reactMajor >= 19 && 'A component is changing an uncontrolled input to be controlled.',
+ reactMajor < 19 &&
+ reactMajor !== 16 &&
+ 'Warning: A component is changing an uncontrolled input to be controlled.',
'MUI: A component is changing the uncontrolled checked state of SwitchBase to be controlled.',
]);
});
@@ -433,9 +436,12 @@ describe('', () => {
setProps({ checked: undefined });
global.didWarnControlledToUncontrolled = true;
}).toErrorDev([
- React.version.startsWith('16')
- ? 'Warning: A component is changing an uncontrolled input of type checkbox to be controlled.'
- : 'Warning: A component is changing an uncontrolled input to be controlled.',
+ reactMajor === 16 &&
+ 'Warning: A component is changing an uncontrolled input of type checkbox to be controlled.',
+ reactMajor >= 19 && 'A component is changing an uncontrolled input to be controlled.',
+ reactMajor < 19 &&
+ reactMajor !== 16 &&
+ 'Warning: A component is changing an uncontrolled input to be controlled.',
'MUI: A component is changing the controlled checked state of SwitchBase to be uncontrolled.',
]);
});
diff --git a/packages/mui-material/src/useAutocomplete/useAutocomplete.test.js b/packages/mui-material/src/useAutocomplete/useAutocomplete.test.js
index ffe5f780446fd6..285be9ecad2a62 100644
--- a/packages/mui-material/src/useAutocomplete/useAutocomplete.test.js
+++ b/packages/mui-material/src/useAutocomplete/useAutocomplete.test.js
@@ -1,6 +1,13 @@
import * as React from 'react';
import { expect } from 'chai';
-import { createRenderer, screen, ErrorBoundary, act, fireEvent } from '@mui/internal-test-utils';
+import {
+ createRenderer,
+ screen,
+ ErrorBoundary,
+ act,
+ fireEvent,
+ reactMajor,
+} from '@mui/internal-test-utils';
import { spy } from 'sinon';
import useAutocomplete, { createFilterOptions } from '@mui/material/useAutocomplete';
@@ -276,28 +283,39 @@ describe('useAutocomplete', () => {
);
}
+ const muiErrorMessage = 'MUI: Unable to find the input element.';
+ const aboveErrorUlElementMessage = 'The above error occurred in the component';
+ const aboveErrorTestComponentMessage = 'The above error occurred in the component';
const node16ErrorMessage =
- "Error: Uncaught [TypeError: Cannot read properties of null (reading 'removeAttribute')]";
- const olderNodeErrorMessage =
- "Error: Uncaught [TypeError: Cannot read property 'removeAttribute' of null]";
+ "TypeError: Cannot read properties of null (reading 'removeAttribute')";
+ const olderNodeErrorMessage = "TypeError: Cannot read property 'removeAttribute' of null";
const nodeVersion = Number(process.versions.node.split('.')[0]);
- const errorMessage = nodeVersion >= 16 ? node16ErrorMessage : olderNodeErrorMessage;
-
- const devErrorMessages = [
- errorMessage,
- 'MUI: Unable to find the input element.',
- errorMessage,
- // strict effects runs effects twice
- React.version.startsWith('18') && 'MUI: Unable to find the input element.',
- React.version.startsWith('18') && errorMessage,
- 'The above error occurred in the component',
- React.version.startsWith('16') && 'The above error occurred in the component',
- 'The above error occurred in the component',
- // strict effects runs effects twice
- React.version.startsWith('18') && 'The above error occurred in the component',
- React.version.startsWith('16') && 'The above error occurred in the component',
- ];
+ const nodeErrorMessage = nodeVersion >= 16 ? node16ErrorMessage : olderNodeErrorMessage;
+
+ const defaultErrorMessages = [muiErrorMessage, nodeErrorMessage, nodeErrorMessage];
+
+ const errorMessagesByReactMajor = {
+ 17: [
+ nodeErrorMessage,
+ muiErrorMessage,
+ nodeErrorMessage,
+ aboveErrorUlElementMessage,
+ aboveErrorTestComponentMessage,
+ ],
+ 18: [
+ nodeErrorMessage,
+ muiErrorMessage,
+ nodeErrorMessage,
+ muiErrorMessage,
+ nodeErrorMessage,
+ aboveErrorUlElementMessage,
+ aboveErrorTestComponentMessage,
+ aboveErrorTestComponentMessage,
+ ],
+ };
+
+ const devErrorMessages = errorMessagesByReactMajor[reactMajor] || defaultErrorMessages;
expect(() => {
render(
diff --git a/packages/mui-styles/src/makeStyles/makeStyles.test.js b/packages/mui-styles/src/makeStyles/makeStyles.test.js
index d2a9b3b896dca3..b156125e86a1f6 100644
--- a/packages/mui-styles/src/makeStyles/makeStyles.test.js
+++ b/packages/mui-styles/src/makeStyles/makeStyles.test.js
@@ -2,7 +2,13 @@ import { expect } from 'chai';
import * as React from 'react';
import PropTypes from 'prop-types';
import { SheetsRegistry } from 'jss';
-import { createRenderer, screen, renderHook, fireEvent } from '@mui/internal-test-utils';
+import {
+ createRenderer,
+ screen,
+ renderHook,
+ fireEvent,
+ reactMajor,
+} from '@mui/internal-test-utils';
import { createTheme } from '@mui/material/styles';
import createGenerateClassName from '../createGenerateClassName';
import makeStyles from './makeStyles';
@@ -81,19 +87,28 @@ describe('makeStyles', () => {
it('should warn if missing theme', () => {
const useStyles2 = makeStyles((theme) => ({ root: { padding: theme.spacing(2) } }));
+ const muiErrorMessage =
+ 'MUI: The `styles` argument provided is invalid.\nYou are providing a function without a theme in the context.';
+ const nodeErrorMessage = 'TypeError: theme.spacing is not a function';
+
+ let devErrorMessages = [muiErrorMessage, muiErrorMessage];
+
+ if (reactMajor < 19) {
+ devErrorMessages = [
+ ...devErrorMessages,
+ nodeErrorMessage,
+ muiErrorMessage,
+ muiErrorMessage,
+ nodeErrorMessage,
+ 'The above error occurred in the component',
+ ];
+ }
+
expect(() => {
expect(() => {
renderHook(() => useStyles2({}));
}).to.throw('theme.spacing is not a function');
- }).toErrorDev([
- 'MUI: The `styles` argument provided is invalid.\nYou are providing a function without a theme in the context.',
- 'MUI: The `styles` argument provided is invalid.\nYou are providing a function without a theme in the context.',
- 'Uncaught [TypeError: theme.spacing is not a function',
- 'MUI: The `styles` argument provided is invalid.\nYou are providing a function without a theme in the context.',
- 'MUI: The `styles` argument provided is invalid.\nYou are providing a function without a theme in the context.',
- 'Uncaught [TypeError: theme.spacing is not a function',
- 'The above error occurred in the component',
- ]);
+ }).toErrorDev(devErrorMessages);
});
it('should warn but not throw if providing an invalid styles type', () => {
diff --git a/packages/mui-utils/src/elementAcceptingRef/elementAcceptingRef.test.tsx b/packages/mui-utils/src/elementAcceptingRef/elementAcceptingRef.test.tsx
index 5a06cdb42f7dec..414df07d9519a1 100644
--- a/packages/mui-utils/src/elementAcceptingRef/elementAcceptingRef.test.tsx
+++ b/packages/mui-utils/src/elementAcceptingRef/elementAcceptingRef.test.tsx
@@ -2,7 +2,7 @@
import * as React from 'react';
import { expect } from 'chai';
import PropTypes from 'prop-types';
-import { createRenderer, waitFor } from '@mui/internal-test-utils';
+import { createRenderer, waitFor, reactMajor } from '@mui/internal-test-utils';
import elementAcceptingRef from './elementAcceptingRef';
describe('elementAcceptingRef', () => {
@@ -22,6 +22,13 @@ describe('elementAcceptingRef', () => {
});
describe('acceptance when not required', () => {
+ before(function beforeCallback() {
+ if (reactMajor >= 19) {
+ // React 19 removed prop types support
+ this.skip();
+ }
+ });
+
function assertPass(element: any, { shouldMount = true } = {}) {
function testAct() {
checkPropType(element);
@@ -107,6 +114,13 @@ describe('elementAcceptingRef', () => {
});
describe('rejections', () => {
+ before(function beforeCallback() {
+ if (reactMajor >= 19) {
+ // React 19 removed prop types support
+ this.skip();
+ }
+ });
+
function assertFail(Component: any, hint: string) {
expect(() => {
checkPropType(Component);