From 5371eecc42c49fac73665f45ae713fa2dffa3fb0 Mon Sep 17 00:00:00 2001 From: Janpot <2109932+Janpot@users.noreply.github.com> Date: Thu, 21 Aug 2025 12:01:21 +0200 Subject: [PATCH 01/17] [code-infra] Bring batch of changes rom vitest PR --- .gitignore | 1 + .../src/Slider/ZeroSlider.test.jsx | 2 +- .../src/pages/fixtures/index.test.js | 2 +- docs/src/theming.test.tsx | 4 +- package.json | 6 +- .../input.d.ts | 0 .../output.js | 0 .../input.d.ts | 0 .../output.js | 0 .../input.d.ts | 0 .../output.js | 0 .../input.tsx | 0 .../options.ts | 2 +- .../output.js | 0 .../input.tsx | 0 .../options.ts | 2 +- .../output.js | 0 .../input.tsx | 0 .../options.ts | 2 +- .../output.js | 0 .../input.tsx | 0 .../output.js | 0 .../input.tsx | 0 .../options.ts | 2 +- .../output.js | 0 .../test/typescript-to-proptypes.test.ts | 10 +- .../test-utils/src/chai-augmentation.d.ts | 8 + .../test-utils/src/chaiPlugin.ts | 18 +- .../test-utils/src/createRenderer.tsx | 2 +- .../test-utils/src/describeConformance.tsx | 17 +- .../test-utils/src/fireDiscreteEvent.ts | 2 +- .../test-utils/src/focusVisible.ts | 4 +- .../test-utils/src/initMatchers.ts | 2 +- .../test-utils/src/initPlaywrightMatchers.ts | 10 +- packages/mui-codemod/package.json | 2 + .../mui-codemod/src/v4.0.0/optimal-imports.js | 7 - .../src/v4.0.0/top-level-imports.js | 6 +- .../mui-codemod/src/v5.0.0/optimal-imports.js | 11 -- .../src/v5.0.0/top-level-imports.js | 14 +- .../src/v5.0.0/top-level-imports.test.js | 1 + packages/mui-docs/vitest.config.mts | 4 + packages/mui-envinfo/src/envinfo.test.js | 2 +- packages/mui-icons-material/builder.mjs | 2 +- packages/mui-icons-material/builder.test.mjs | 92 +++++++--- .../AccordionDetails.test.tsx | 12 +- .../src/Autocomplete/Autocomplete.test.tsx | 24 +-- packages/mui-joy/src/Box/Box.test.tsx | 18 +- packages/mui-joy/src/Button/Button.test.tsx | 6 +- .../src/CssBaseline/CssBaseline.test.tsx | 4 +- packages/mui-joy/src/Drawer/Drawer.test.tsx | 2 +- packages/mui-joy/src/Input/Input.test.tsx | 6 +- packages/mui-joy/src/Link/Link.test.tsx | 2 +- packages/mui-joy/src/List/List.test.tsx | 24 +-- .../src/ListDivider/ListDivider.test.tsx | 18 +- .../mui-joy/src/ListItem/ListItem.test.tsx | 18 +- .../ListItemButton/ListItemButton.test.tsx | 2 +- packages/mui-joy/src/Menu/Menu.test.tsx | 20 +-- .../mui-joy/src/MenuItem/MenuItem.test.tsx | 18 +- .../mui-joy/src/MenuList/MenuList.test.tsx | 8 +- packages/mui-joy/src/Modal/Modal.test.tsx | 2 +- packages/mui-joy/src/Option/Option.test.tsx | 4 +- .../src/RadioGroup/RadioGroup.test.tsx | 4 +- packages/mui-joy/src/Select/Select.test.tsx | 26 +-- packages/mui-joy/src/SvgIcon/SvgIcon.test.tsx | 6 +- packages/mui-joy/src/Switch/Switch.test.tsx | 12 +- packages/mui-joy/src/Tab/Tab.test.tsx | 8 +- packages/mui-joy/src/TabList/TabList.test.tsx | 14 +- .../mui-joy/src/TabPanel/TabPanel.test.tsx | 6 +- packages/mui-joy/src/Tabs/Tabs.test.tsx | 10 +- .../mui-joy/src/Textarea/Textarea.test.tsx | 6 +- .../ToggleButtonGroup.test.tsx | 4 +- .../src/Typography/Typography.test.tsx | 2 +- .../src/styles/CssVarsProvider.test.tsx | 14 +- .../mui-joy/src/styles/extendTheme.test.js | 2 +- packages/mui-joy/src/styles/styled.test.tsx | 6 +- .../mui-lab/src/AlertTitle/AlertTitle.test.js | 3 + packages/mui-lab/src/Masonry/Masonry.test.js | 25 +-- .../mui-lab/src/Timeline/Timeline.test.tsx | 4 +- .../src/Accordion/Accordion.test.js | 16 +- .../AccordionActions/AccordionActions.test.js | 2 +- .../AccordionSummary/AccordionSummary.test.js | 2 +- packages/mui-material/src/Alert/Alert.test.js | 28 +-- .../mui-material/src/AppBar/AppBar.test.js | 12 +- .../src/Autocomplete/Autocomplete.test.js | 26 +-- .../mui-material/src/Avatar/Avatar.test.js | 6 +- packages/mui-material/src/Box/Box.test.js | 2 +- .../src/Breadcrumbs/Breadcrumbs.test.js | 14 +- .../mui-material/src/Button/Button.test.js | 58 +++--- .../src/ButtonBase/ButtonBase.test.js | 66 +++---- .../src/ButtonGroup/ButtonGroup.test.js | 10 +- .../src/CardActions/CardActions.test.js | 2 +- .../src/CardMedia/CardMedia.test.js | 4 +- .../src/Checkbox/Checkbox.test.js | 2 +- packages/mui-material/src/Chip/Chip.test.js | 10 +- .../ClickAwayListener.test.js | 16 +- .../src/Collapse/Collapse.test.js | 2 +- .../src/CssBaseline/CssBaseline.test.js | 6 +- .../mui-material/src/Dialog/Dialog.test.js | 28 +-- .../src/DialogActions/DialogActions.test.js | 2 +- .../mui-material/src/Divider/Divider.test.js | 76 ++++---- .../mui-material/src/Drawer/Drawer.test.js | 28 +-- packages/mui-material/src/Fab/Fab.test.js | 12 +- packages/mui-material/src/Fade/Fade.test.js | 6 +- .../src/GlobalStyles/GlobalStyles.test.js | 4 +- packages/mui-material/src/Grid/Grid.test.js | 6 +- .../src/GridLegacy/GridLegacy.test.js | 22 +-- .../src/IconButton/IconButton.test.js | 16 +- .../src/ImageList/ImageList.test.js | 12 +- .../src/InputBase/InputBase.test.js | 14 +- .../src/LinearProgress/LinearProgress.test.js | 34 ++-- packages/mui-material/src/Link/Link.test.js | 2 +- .../src/ListItem/ListItem.test.js | 2 +- .../src/ListItemButton/ListItemButton.test.js | 37 ++-- packages/mui-material/src/Menu/Menu.test.js | 39 ++-- .../src/MenuItem/MenuItem.test.js | 30 ++-- .../src/MobileStepper/MobileStepper.test.js | 28 +-- packages/mui-material/src/Modal/Modal.test.js | 8 +- .../src/NativeSelect/NativeSelect.test.js | 2 +- .../NativeSelect/NativeSelectInput.test.js | 4 +- .../src/OutlinedInput/NotchedOutline.test.js | 2 +- packages/mui-material/src/Paper/Paper.test.js | 10 +- .../mui-material/src/Popover/Popover.test.js | 54 +++--- .../mui-material/src/Popper/Popper.test.js | 10 +- .../mui-material/src/Portal/Portal.test.tsx | 10 +- packages/mui-material/src/Radio/Radio.test.js | 6 +- .../src/RadioGroup/RadioGroup.test.js | 6 +- .../mui-material/src/Rating/Rating.test.js | 45 +++-- .../mui-material/src/Select/Select.test.js | 43 +++-- packages/mui-material/src/Slide/Slide.test.js | 58 +++--- .../mui-material/src/Slider/Slider.test.js | 24 +-- .../mui-material/src/Slider/useSlider.test.js | 6 +- .../src/Snackbar/Snackbar.test.js | 6 +- .../SnackbarContent/SnackbarContent.test.js | 6 +- .../src/SpeedDial/SpeedDial.test.js | 103 +++++++---- .../mui-material/src/SvgIcon/SvgIcon.test.js | 6 +- .../SwipeableDrawer/SwipeableDrawer.test.js | 46 ++--- packages/mui-material/src/Tab/Tab.test.js | 8 +- .../src/Tabs/ScrollbarSize.test.js | 4 +- packages/mui-material/src/Tabs/Tabs.test.js | 97 ++++++---- .../src/TextField/TextField.test.js | 2 +- .../TextareaAutosize.test.tsx | 22 +-- .../src/ToggleButton/ToggleButton.test.js | 10 +- .../ToggleButtonGroup.test.js | 8 +- .../mui-material/src/Tooltip/Tooltip.test.js | 167 +++++++++--------- .../src/Unstable_TrapFocus/FocusTrap.test.tsx | 50 +++--- packages/mui-material/src/Zoom/Zoom.test.js | 6 +- .../src/internal/SwitchBase.test.js | 14 +- .../mui-material/src/internal/animate.test.js | 75 ++++---- .../src/styles/ThemeProviderWithVars.test.js | 37 ++-- .../src/styles/createTheme.test.js | 2 +- .../src/styles/extendTheme.test.js | 2 +- .../mui-material/src/styles/styled.test.js | 6 +- .../useAutocomplete/useAutocomplete.test.js | 11 +- .../src/useMediaQuery/useMediaQuery.test.js | 29 ++- .../useScrollTrigger/useScrollTrigger.test.js | 14 +- .../integration/DialogIntegration.test.js | 6 +- .../test/integration/Menu.test.js | 10 +- .../test/integration/MenuList.test.js | 19 +- .../integration/PopperChildrenLayout.test.js | 17 +- .../src/GlobalStyles/GlobalStyles.test.js | 10 +- .../src/GlobalStyles/GlobalStyles.test.js | 10 +- packages/mui-system/src/Box/Box.test.js | 8 +- packages/mui-system/src/Grid/Grid.test.js | 22 +-- .../InitColorSchemeScript.test.js | 2 +- .../src/createTheme/createTheme.test.js | 4 +- .../src/cssVars/createCssVarsProvider.test.js | 50 +++--- .../src/cssVars/useCurrentColorScheme.test.js | 12 +- packages/mui-system/src/index.js | 1 + packages/mui-system/src/styled/styled.test.js | 6 +- .../mui-utils/src/deepmerge/deepmerge.test.ts | 6 +- .../elementAcceptingRef.test.tsx | 19 +- .../getScrollbarSize/getScrollbarSize.test.ts | 5 +- .../src/isFocusVisible/isFocusVisible.ts | 2 +- .../src/useForkRef/useForkRef.test.tsx | 7 +- packages/mui-utils/src/useId/useId.test.tsx | 17 +- .../useIsFocusVisible.test.tsx | 11 +- .../src/usePreviousProps/usePreviousProps.ts | 4 +- stylelint.config.mjs | 2 +- test/e2e/index.test.ts | 2 +- .../mui-system/theme-scoping.test.tsx | 2 +- test/karma.conf.js | 5 +- test/regressions/index.test.js | 2 +- 182 files changed, 1283 insertions(+), 1256 deletions(-) rename packages-internal/scripts/typescript-to-proptypes/test/{boolean-values/optional => boolean-values-optional}/input.d.ts (100%) rename packages-internal/scripts/typescript-to-proptypes/test/{boolean-values/optional => boolean-values-optional}/output.js (100%) rename packages-internal/scripts/typescript-to-proptypes/test/{boolean-values/required => boolean-values-required}/input.d.ts (100%) rename packages-internal/scripts/typescript-to-proptypes/test/{boolean-values/required => boolean-values-required}/output.js (100%) rename packages-internal/scripts/typescript-to-proptypes/test/{generator/html-elements => generator-html-elements}/input.d.ts (100%) rename packages-internal/scripts/typescript-to-proptypes/test/{generator/html-elements => generator-html-elements}/output.js (100%) rename packages-internal/scripts/typescript-to-proptypes/test/{injector/all-props-ignored => injector-all-props-ignored}/input.tsx (100%) rename packages-internal/scripts/typescript-to-proptypes/test/{injector/all-props-ignored => injector-all-props-ignored}/options.ts (74%) rename packages-internal/scripts/typescript-to-proptypes/test/{injector/all-props-ignored => injector-all-props-ignored}/output.js (100%) rename packages-internal/scripts/typescript-to-proptypes/test/{injector/should-include-component-based => injector-should-include-component-based}/input.tsx (100%) rename packages-internal/scripts/typescript-to-proptypes/test/{injector/should-include-component-based => injector-should-include-component-based}/options.ts (83%) rename packages-internal/scripts/typescript-to-proptypes/test/{injector/should-include-component-based => injector-should-include-component-based}/output.js (100%) rename packages-internal/scripts/typescript-to-proptypes/test/{injector/should-include-filename-based => injector-should-include-filename-based}/input.tsx (100%) rename packages-internal/scripts/typescript-to-proptypes/test/{injector/should-include-filename-based => injector-should-include-filename-based}/options.ts (90%) rename packages-internal/scripts/typescript-to-proptypes/test/{injector/should-include-filename-based => injector-should-include-filename-based}/output.js (100%) rename packages-internal/scripts/typescript-to-proptypes/test/{injector/string-props => injector-string-props}/input.tsx (100%) rename packages-internal/scripts/typescript-to-proptypes/test/{injector/string-props => injector-string-props}/output.js (100%) rename packages-internal/scripts/typescript-to-proptypes/test/{injector/whitelisted-props => injector-whitelisted-props}/input.tsx (100%) rename packages-internal/scripts/typescript-to-proptypes/test/{injector/whitelisted-props => injector-whitelisted-props}/options.ts (74%) rename packages-internal/scripts/typescript-to-proptypes/test/{injector/whitelisted-props => injector-whitelisted-props}/output.js (100%) create mode 100644 packages-internal/test-utils/src/chai-augmentation.d.ts create mode 100644 packages/mui-docs/vitest.config.mts create mode 100644 packages/mui-lab/src/AlertTitle/AlertTitle.test.js diff --git a/.gitignore b/.gitignore index cbad2d4b1882e3..e5ebbcb60d79d0 100644 --- a/.gitignore +++ b/.gitignore @@ -46,3 +46,4 @@ docs/public/static/blog/feed/* .nx/workspace-data screenshots packed +test-results diff --git a/apps/pigment-css-vite-app/src/Slider/ZeroSlider.test.jsx b/apps/pigment-css-vite-app/src/Slider/ZeroSlider.test.jsx index 27d8d3af5dcc45..c055c1aae605d3 100644 --- a/apps/pigment-css-vite-app/src/Slider/ZeroSlider.test.jsx +++ b/apps/pigment-css-vite-app/src/Slider/ZeroSlider.test.jsx @@ -1,5 +1,5 @@ /* globals expect */ -import { render } from '@testing-library/react'; +import { render } from '@testing-library/react/pure'; import Slider from './ZeroSlider'; describe('Slider', () => { diff --git a/apps/pigment-css-vite-app/src/pages/fixtures/index.test.js b/apps/pigment-css-vite-app/src/pages/fixtures/index.test.js index 52c2da94dabb1b..32ccd46d425a3c 100644 --- a/apps/pigment-css-vite-app/src/pages/fixtures/index.test.js +++ b/apps/pigment-css-vite-app/src/pages/fixtures/index.test.js @@ -101,7 +101,7 @@ async function main() { it(`creates screenshots of ${route}`, async function test() { // With the playwright inspector we might want to call `page.pause` which would lead to a timeout. if (process.env.PWDEBUG) { - this.timeout(0); + this?.timeout?.(0); } const testcase = await renderFixture(index); diff --git a/docs/src/theming.test.tsx b/docs/src/theming.test.tsx index f1f93bb64a1a89..17af38b5227e7d 100644 --- a/docs/src/theming.test.tsx +++ b/docs/src/theming.test.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { expect } from 'chai'; -import { createRenderer, screen, fireEvent } from '@mui/internal-test-utils'; +import { createRenderer, fireEvent } from '@mui/internal-test-utils'; import { ThemeProvider, createTheme, useColorScheme } from '@mui/material/styles'; import Box from '@mui/material/Box'; import { THEME_ID as JOY_THEME_ID, extendTheme } from '@mui/joy/styles'; @@ -29,7 +29,7 @@ describe('docs demo theming', () => { const { render } = createRenderer(); it('should inherit the theme.palette.mode from upper theme', () => { - render( + const screen = render( import path from 'path'; import fs from 'fs'; import * as ts from 'typescript'; @@ -11,7 +12,7 @@ import { getPropTypesFromFile } from '../src/getPropTypesFromFile'; import { TestOptions } from './types'; const testCases = glob - .sync('**/input.{d.ts,ts,tsx}', { absolute: true, cwd: __dirname }) + .sync('*/input.{d.ts,ts,tsx}', { absolute: true, cwd: __dirname }) .map((testPath) => { const dirname = path.dirname(testPath); const name = path.dirname(path.relative(__dirname, testPath)); @@ -32,9 +33,10 @@ describe('typescript-to-proptypes', () => { return cachedProject; } + // @ts-expect-error Second argument is vitest before(function beforeHook() { // Creating a TS program might take a while. - this.timeout(20000); + this?.timeout?.(20000); const buildProject = createTypeScriptProjectBuilder({ test: { @@ -52,7 +54,7 @@ describe('typescript-to-proptypes', () => { // testCases.map((testCase) => testCase.inputPath), // ttp.loadConfig(path.resolve(__dirname, '../tsconfig.json')), // ); - }); + }, 20000); testCases.forEach((testCase) => { const { name: testName, inputPath, inputJS, outputPath } = testCase; @@ -61,7 +63,7 @@ describe('typescript-to-proptypes', () => { const project = getProject(); let options: TestOptions = {}; try { - const optionsModule = await import(`./${testName}/options`); + const optionsModule: any = await import(`./${testName}/options.ts`); options = optionsModule.default; } catch (error) { // Assume "Cannot find module" which means "no options". diff --git a/packages-internal/test-utils/src/chai-augmentation.d.ts b/packages-internal/test-utils/src/chai-augmentation.d.ts new file mode 100644 index 00000000000000..f21a470c3f9135 --- /dev/null +++ b/packages-internal/test-utils/src/chai-augmentation.d.ts @@ -0,0 +1,8 @@ +/* eslint-disable import/prefer-default-export */ +import 'chai'; +import type { AssertionError as RealAssertionError } from 'assertion-error'; + +declare module 'chai' { + // Looks like they forgot to export the AssertionError type in @types/chai + export const AssertionError: typeof RealAssertionError; +} diff --git a/packages-internal/test-utils/src/chaiPlugin.ts b/packages-internal/test-utils/src/chaiPlugin.ts index bbaeeb30e55797..4bd8c34780aeb8 100644 --- a/packages-internal/test-utils/src/chaiPlugin.ts +++ b/packages-internal/test-utils/src/chaiPlugin.ts @@ -1,16 +1,16 @@ import { isInaccessible } from '@testing-library/dom'; import { prettyDOM } from '@testing-library/react/pure'; -import type chaiType from 'chai'; -import { AssertionError } from 'chai'; +import * as chai from 'chai'; import { computeAccessibleDescription, computeAccessibleName } from 'dom-accessibility-api'; import formatUtil from 'format-util'; -import _ from 'lodash'; +// avoid loading whole lodash, it takes ~50ms to initialize +import kebabCase from 'lodash.kebabcase'; import './chai.types'; const isKarma = Boolean(process.env.KARMA); function isInJSDOM() { - return /jsdom/.test(window.navigator.userAgent); + return window.navigator.userAgent.includes('jsdom'); } // chai#utils.elToString that looks like stringified elements in testing-library @@ -21,7 +21,7 @@ function elementToString(element: Element | null | undefined) { return String(element); } -const chaiPlugin: Parameters<(typeof chaiType)['use']>[0] = (chaiAPI, utils) => { +const chaiPlugin: Parameters[0] = (chaiAPI, utils) => { const blockElements = new Set([ 'html', 'address', @@ -202,7 +202,7 @@ const chaiPlugin: Parameters<(typeof chaiType)['use']>[0] = (chaiAPI, utils) => // This is closer to actual CSS and required for getPropertyValue anyway. const expectedStyle: Record = {}; Object.keys(expectedStyleUnnormalized).forEach((cssProperty) => { - const hyphenCasedPropertyName = _.kebabCase(cssProperty); + const hyphenCasedPropertyName = kebabCase(cssProperty); const isVendorPrefixed = /^(moz|ms|o|webkit)-/.test(hyphenCasedPropertyName); const propertyName = isVendorPrefixed ? `-${hyphenCasedPropertyName}` @@ -279,7 +279,7 @@ const chaiPlugin: Parameters<(typeof chaiType)['use']>[0] = (chaiAPI, utils) => const jsdomHint = 'Styles in JSDOM e.g. from `test:unit` are often misleading since JSDOM does not implement the Cascade nor actual CSS property value computation. ' + - 'If results differ between real browsers and JSDOM, skip the test in JSDOM e.g. `if (/jsdom/.test(window.navigator.userAgent)) this.skip();`'; + 'If results differ between real browsers and JSDOM, skip the test in JSDOM e.g. `if (window.navigator.userAgent.includes("jsdom")) this.skip();`'; const shorthandHint = 'Browsers can compute shorthand properties differently. Prefer longhand properties e.g. `borderTopColor`, `borderRightColor` etc. instead of `border` or `border-color`.'; const messageHint = `${jsdomHint}\n${shorthandHint}`; @@ -316,7 +316,7 @@ const chaiPlugin: Parameters<(typeof chaiType)['use']>[0] = (chaiAPI, utils) => const element = utils.flag(this, 'object') as HTMLElement; if (element?.nodeType !== 1) { // Same pre-condition for negated and unnegated assertion - throw new AssertionError(`Expected an Element but got ${String(element)}`); + throw new chai.AssertionError(`Expected an Element but got ${String(element)}`); } assertMatchingStyles.call(this, element.style, expectedStyleUnnormalized, { @@ -331,7 +331,7 @@ const chaiPlugin: Parameters<(typeof chaiType)['use']>[0] = (chaiAPI, utils) => const element = utils.flag(this, 'object') as HTMLElement; if (element?.nodeType !== 1) { // Same pre-condition for negated and unnegated assertion - throw new AssertionError(`Expected an Element but got ${String(element)}`); + throw new chai.AssertionError(`Expected an Element but got ${String(element)}`); } const computedStyle = element.ownerDocument.defaultView!.getComputedStyle(element); diff --git a/packages-internal/test-utils/src/createRenderer.tsx b/packages-internal/test-utils/src/createRenderer.tsx index 541689c49f6d7f..4b52baacecf40b 100644 --- a/packages-internal/test-utils/src/createRenderer.tsx +++ b/packages-internal/test-utils/src/createRenderer.tsx @@ -806,6 +806,6 @@ function act(callback: () => void | T | Promise) { const bodyBoundQueries = within(document.body, { ...queries, ...customQueries }); -export * from '@testing-library/react/pure'; export { act, fireEvent }; export const screen: Screen & typeof bodyBoundQueries = { ...rtlScreen, ...bodyBoundQueries }; +export { within, waitFor, renderHook } from '@testing-library/react/pure'; diff --git a/packages-internal/test-utils/src/describeConformance.tsx b/packages-internal/test-utils/src/describeConformance.tsx index 551c8a2633cd67..5851084310afa5 100644 --- a/packages-internal/test-utils/src/describeConformance.tsx +++ b/packages-internal/test-utils/src/describeConformance.tsx @@ -774,7 +774,7 @@ function testThemeStyleOverrides( ) { describe('theme style overrides:', () => { it("respect theme's styleOverrides custom state", async function test(t = {}) { - if (/jsdom/.test(window.navigator.userAgent)) { + if (window.navigator.userAgent.includes('jsdom')) { // @ts-ignore // eslint-disable-next-line @typescript-eslint/no-unused-expressions this?.skip?.() ?? t?.skip(); @@ -831,7 +831,7 @@ function testThemeStyleOverrides( }); it("respect theme's styleOverrides slots", async function test(t = {}) { - if (/jsdom/.test(window.navigator.userAgent)) { + if (window.navigator.userAgent.includes('jsdom')) { // @ts-ignore // eslint-disable-next-line @typescript-eslint/no-unused-expressions this?.skip?.() ?? t?.skip(); @@ -944,7 +944,7 @@ function testThemeStyleOverrides( }); it('overrideStyles does not replace each other in slots', async function test(t = {}) { - if (/jsdom/.test(window.navigator.userAgent)) { + if (window.navigator.userAgent.includes('jsdom')) { // @ts-ignore // eslint-disable-next-line @typescript-eslint/no-unused-expressions this?.skip?.() ?? t?.skip(); @@ -1027,7 +1027,7 @@ function testThemeVariants( ) { describe('theme variants:', () => { it("respect theme's variants", async function test(t = {}) { - if (/jsdom/.test(window.navigator.userAgent)) { + if (window.navigator.userAgent.includes('jsdom')) { // @ts-ignore // eslint-disable-next-line @typescript-eslint/no-unused-expressions this?.skip?.() ?? t?.skip(); @@ -1084,7 +1084,7 @@ function testThemeVariants( }); it('supports custom variant', async function test(t = {}) { - if (/jsdom/.test(window.navigator.userAgent)) { + if (window.navigator.userAgent.includes('jsdom')) { // @ts-ignore // eslint-disable-next-line @typescript-eslint/no-unused-expressions this?.skip?.() ?? t?.skip(); @@ -1140,7 +1140,12 @@ function testThemeCustomPalette( describe('theme extended palette:', () => { it('should render without errors', function test(t = {}) { const { render, ThemeProvider, createTheme } = getOptions(); - if (!/jsdom/.test(window.navigator.userAgent) || !render || !ThemeProvider || !createTheme) { + if ( + !window.navigator.userAgent.includes('jsdom') || + !render || + !ThemeProvider || + !createTheme + ) { // @ts-ignore // eslint-disable-next-line @typescript-eslint/no-unused-expressions this?.skip?.() ?? t?.skip(); diff --git a/packages-internal/test-utils/src/fireDiscreteEvent.ts b/packages-internal/test-utils/src/fireDiscreteEvent.ts index a48f70d9ecbf9a..245b65ed4e29fd 100644 --- a/packages-internal/test-utils/src/fireDiscreteEvent.ts +++ b/packages-internal/test-utils/src/fireDiscreteEvent.ts @@ -1,4 +1,4 @@ -import { configure, fireEvent, getConfig } from '@testing-library/react'; +import { configure, fireEvent, getConfig } from '@testing-library/react/pure'; import reactMajor from './reactMajor'; const noWrapper = (callback: () => void) => callback(); diff --git a/packages-internal/test-utils/src/focusVisible.ts b/packages-internal/test-utils/src/focusVisible.ts index 1c6e10b23d1c29..af0ef8dfc5c2c8 100644 --- a/packages-internal/test-utils/src/focusVisible.ts +++ b/packages-internal/test-utils/src/focusVisible.ts @@ -3,7 +3,9 @@ import { act, fireEvent } from './createRenderer'; export default function focusVisible(element: HTMLElement) { act(() => { element.blur(); - fireEvent.keyDown(document.body, { key: 'Tab' }); + }); + fireEvent.keyDown(document.body, { key: 'Tab' }); + act(() => { element.focus(); }); } diff --git a/packages-internal/test-utils/src/initMatchers.ts b/packages-internal/test-utils/src/initMatchers.ts index 8ab13d2b5fd6e4..5f15eefb55e48d 100644 --- a/packages-internal/test-utils/src/initMatchers.ts +++ b/packages-internal/test-utils/src/initMatchers.ts @@ -1,4 +1,4 @@ -import chai from 'chai'; +import * as chai from 'chai'; import chaiDom from 'chai-dom'; import './chai.types'; import chaiPlugin from './chaiPlugin'; diff --git a/packages-internal/test-utils/src/initPlaywrightMatchers.ts b/packages-internal/test-utils/src/initPlaywrightMatchers.ts index 753cbe475fcb7a..7a7f11579ac27a 100644 --- a/packages-internal/test-utils/src/initPlaywrightMatchers.ts +++ b/packages-internal/test-utils/src/initPlaywrightMatchers.ts @@ -1,4 +1,4 @@ -import chai, { AssertionError } from 'chai'; +import * as chai from 'chai'; import * as DomTestingLibrary from '@testing-library/dom'; import type { ElementHandle } from '@playwright/test'; @@ -32,7 +32,9 @@ chai.use((chaiAPI, utils) => { chai.Assertion.addMethod('toHaveFocus', async function elementHandleIsFocused() { const $elementOrHandle: ElementHandle | Promise = utils.flag(this, 'object'); if ($elementOrHandle == null) { - throw new AssertionError(`Expected an element handle but got ${String($elementOrHandle)}.`); + throw new chai.AssertionError( + `Expected an element handle but got ${String($elementOrHandle)}.`, + ); } const $element = typeof ($elementOrHandle as Promise).then === 'function' @@ -65,7 +67,9 @@ chai.use((chaiAPI, utils) => { async function elementHandleHasAttribute(attributeName: string, attributeValue?: string) { const $elementOrHandle: ElementHandle | Promise = utils.flag(this, 'object'); if ($elementOrHandle == null) { - throw new AssertionError(`Expected an element handle but got ${String($elementOrHandle)}.`); + throw new chai.AssertionError( + `Expected an element handle but got ${String($elementOrHandle)}.`, + ); } const $element = typeof ($elementOrHandle as Promise).then === 'function' diff --git a/packages/mui-codemod/package.json b/packages/mui-codemod/package.json index ec0593f9f1c401..f10319bdde148d 100644 --- a/packages/mui-codemod/package.json +++ b/packages/mui-codemod/package.json @@ -40,6 +40,8 @@ "yargs": "^17.7.2" }, "devDependencies": { + "@material-ui/core": "^4.12.4", + "@mui/material-v5": "npm:@mui/material@5.17.1", "@types/chai": "^4.3.20", "@types/jscodeshift": "0.12.0", "chai": "^4.5.0" diff --git a/packages/mui-codemod/src/v4.0.0/optimal-imports.js b/packages/mui-codemod/src/v4.0.0/optimal-imports.js index 567ae9e85374fd..2428e3ef2ac7e6 100644 --- a/packages/mui-codemod/src/v4.0.0/optimal-imports.js +++ b/packages/mui-codemod/src/v4.0.0/optimal-imports.js @@ -2,13 +2,6 @@ import { dirname } from 'path'; import addImports from 'jscodeshift-add-imports'; import getJSExports from '../util/getJSExports'; -// istanbul ignore next -if (process.env.NODE_ENV === 'test') { - const resolve = require.resolve; - require.resolve = (source) => - resolve(source.replace(/^@material-ui\/core\/es/, '../../../mui-material/src')); -} - export default function transformer(fileInfo, api, options) { const j = api.jscodeshift; const importModule = options.importModule || '@material-ui/core'; diff --git a/packages/mui-codemod/src/v4.0.0/top-level-imports.js b/packages/mui-codemod/src/v4.0.0/top-level-imports.js index 3851a4b2227b51..669c7dc924968e 100644 --- a/packages/mui-codemod/src/v4.0.0/top-level-imports.js +++ b/packages/mui-codemod/src/v4.0.0/top-level-imports.js @@ -5,11 +5,7 @@ export default function transformer(fileInfo, api, options) { const importModule = options.importModule || '@material-ui/core'; const targetModule = options.targetModule || '@material-ui/core'; - let requirePath = importModule; - - if (process.env.NODE_ENV === 'test') { - requirePath = requirePath.replace(/^@material-ui\/core/, '../../../mui-material/src'); - } + const requirePath = importModule; // eslint-disable-next-line global-require, import/no-dynamic-require const whitelist = require(requirePath); diff --git a/packages/mui-codemod/src/v5.0.0/optimal-imports.js b/packages/mui-codemod/src/v5.0.0/optimal-imports.js index a0be6c3e078916..a5a97cebd0880d 100644 --- a/packages/mui-codemod/src/v5.0.0/optimal-imports.js +++ b/packages/mui-codemod/src/v5.0.0/optimal-imports.js @@ -2,17 +2,6 @@ import { dirname } from 'path'; import addImports from 'jscodeshift-add-imports'; import getJSExports from '../util/getJSExports'; -// istanbul ignore next -if (process.env.NODE_ENV === 'test') { - const resolve = require.resolve; - require.resolve = (source) => - resolve( - source - .replace(/^@material-ui\/core\/es/, '../../../mui-material/src') - .replace(/^@material-ui\/core\/modern/, '../../../mui-material/src'), - ); -} - export default function transformer(fileInfo, api, options) { const j = api.jscodeshift; const importModule = options.importModule || '@material-ui/core'; diff --git a/packages/mui-codemod/src/v5.0.0/top-level-imports.js b/packages/mui-codemod/src/v5.0.0/top-level-imports.js index 347af368ad86cc..6609a8c105f283 100644 --- a/packages/mui-codemod/src/v5.0.0/top-level-imports.js +++ b/packages/mui-codemod/src/v5.0.0/top-level-imports.js @@ -2,20 +2,18 @@ import { dirname } from 'path'; import addImports from 'jscodeshift-add-imports'; import getJSExports from '../util/getJSExports'; -// istanbul ignore next -if (process.env.NODE_ENV === 'test') { - const resolve = require.resolve; - require.resolve = (source) => - resolve(source.replace(/^@mui\/material\/modern/, '../../../mui-material/src')); -} - export default function transformer(fileInfo, api, options) { const j = api.jscodeshift; const importModule = options.importModule || '@mui/material'; const targetModule = options.targetModule || '@mui/material'; + let resolveModule = importModule; + if (process.env.NODE_ENV === 'test') { + resolveModule = resolveModule.replace(/^@mui\/material/, '@mui/material-v5'); + } + const whitelist = getJSExports( - require.resolve(`${importModule}/modern`, { + require.resolve(`${resolveModule}/modern`, { paths: [dirname(fileInfo.path)], }), ); diff --git a/packages/mui-codemod/src/v5.0.0/top-level-imports.test.js b/packages/mui-codemod/src/v5.0.0/top-level-imports.test.js index 29018b0b90104c..ed4e9322d1d41b 100644 --- a/packages/mui-codemod/src/v5.0.0/top-level-imports.test.js +++ b/packages/mui-codemod/src/v5.0.0/top-level-imports.test.js @@ -16,6 +16,7 @@ describe('@mui/codemod', () => { describe('v5.0.0', () => { describe('top-level-imports', () => { it('convert path as needed', () => { + this?.timeout?.(20000); const actual = transform( { source: read('./top-level-imports.test/actual.js'), diff --git a/packages/mui-docs/vitest.config.mts b/packages/mui-docs/vitest.config.mts new file mode 100644 index 00000000000000..f1f21ca13ccdd6 --- /dev/null +++ b/packages/mui-docs/vitest.config.mts @@ -0,0 +1,4 @@ +// eslint-disable-next-line import/no-relative-packages +import sharedConfig from '../../vitest.shared.mts'; + +export default sharedConfig(import.meta.url, { jsdom: true }); diff --git a/packages/mui-envinfo/src/envinfo.test.js b/packages/mui-envinfo/src/envinfo.test.js index 6a362b47e43c91..030ae7cc7de4b0 100644 --- a/packages/mui-envinfo/src/envinfo.test.js +++ b/packages/mui-envinfo/src/envinfo.test.js @@ -17,7 +17,7 @@ describe('@mui/envinfo', () => { it('includes info about the environment relevant to MUI', function test() { // only run in node - if (!/jsdom/.test(window.navigator.userAgent)) { + if (!window.navigator.userAgent.includes('jsdom')) { this.skip(); } diff --git a/packages/mui-icons-material/builder.mjs b/packages/mui-icons-material/builder.mjs index fd606570d45f05..bb1d2244472c9b 100755 --- a/packages/mui-icons-material/builder.mjs +++ b/packages/mui-icons-material/builder.mjs @@ -9,7 +9,7 @@ import { fileURLToPath } from 'url'; import intersection from 'lodash/intersection.js'; import { Queue } from '@mui/internal-waterfall'; -const currentDirectory = fileURLToPath(new URL('.', import.meta.url)); +const currentDirectory = path.dirname(fileURLToPath(new URL(import.meta.url))); export const RENAME_FILTER_DEFAULT = './renameFilters/default.mjs'; export const RENAME_FILTER_MUI = './renameFilters/material-design-icons.mjs'; diff --git a/packages/mui-icons-material/builder.test.mjs b/packages/mui-icons-material/builder.test.mjs index 866492a53c0a8f..52e675f9b72bea 100644 --- a/packages/mui-icons-material/builder.test.mjs +++ b/packages/mui-icons-material/builder.test.mjs @@ -5,7 +5,7 @@ import path from 'path'; import { fileURLToPath } from 'url'; import { RENAME_FILTER_MUI, RENAME_FILTER_DEFAULT, getComponentName, handler } from './builder.mjs'; -const currentDirectory = fileURLToPath(new URL('.', import.meta.url)); +const currentDirectory = path.dirname(fileURLToPath(new URL(import.meta.url))); const DISABLE_LOG = true; @@ -42,15 +42,27 @@ describe('builder', () => { outputDir: null, }; - beforeEach(async function beforeEachHook() { - // DON'T CLEAN UP TO MAKE TEST INSPECTABLE - options.outputDir = path.join( - os.tmpdir(), - 'material-ui-icons-builder-test', - this.currentTest.fullTitle(), - ); - await emptyDir(options.outputDir); - }); + beforeEach( + process.env.VITEST + ? async function beforeEachHook(ctx) { + // DON'T CLEAN UP TO MAKE TEST INSPECTABLE + options.outputDir = path.join( + os.tmpdir(), + 'material-ui-icons-builder-test', + ctx.task.name, + ); + await emptyDir(options.outputDir); + } + : async function beforeEachHook() { + // DON'T CLEAN UP TO MAKE TEST INSPECTABLE + options.outputDir = path.join( + os.tmpdir(), + 'material-ui-icons-builder-test', + this.currentTest.fullTitle(), + ); + await emptyDir(options.outputDir); + }, + ); it('script outputs to directory', async () => { await handler(options); @@ -69,15 +81,27 @@ describe('builder', () => { outputDir: null, }; - beforeEach(async function beforeEachHook() { - // DON'T CLEAN UP TO MAKE TEST INSPECTABLE - options.outputDir = path.join( - os.tmpdir(), - 'material-ui-icons-builder-test', - this.currentTest.fullTitle(), - ); - await emptyDir(options.outputDir); - }); + beforeEach( + process.env.VITEST + ? async function beforeEachHook(ctx) { + // DON'T CLEAN UP TO MAKE TEST INSPECTABLE + options.outputDir = path.join( + os.tmpdir(), + 'material-ui-icons-builder-test', + ctx.task.name, + ); + await emptyDir(options.outputDir); + } + : async function beforeEachHook() { + // DON'T CLEAN UP TO MAKE TEST INSPECTABLE + options.outputDir = path.join( + os.tmpdir(), + 'material-ui-icons-builder-test', + this.currentTest.fullTitle(), + ); + await emptyDir(options.outputDir); + }, + ); it('script outputs to directory', async () => { await handler(options); @@ -110,15 +134,27 @@ describe('builder', () => { outputDir: null, }; - beforeEach(async function beforeEachHook() { - // DON'T CLEAN UP TO MAKE TEST INSPECTABLE - options.outputDir = path.join( - os.tmpdir(), - 'material-ui-icons-builder-test', - this.currentTest.fullTitle(), - ); - await emptyDir(options.outputDir); - }); + beforeEach( + process.env.VITEST + ? async function beforeEachHook(ctx) { + // DON'T CLEAN UP TO MAKE TEST INSPECTABLE + options.outputDir = path.join( + os.tmpdir(), + 'material-ui-icons-builder-test', + ctx.task.name, + ); + await emptyDir(options.outputDir); + } + : async function beforeEachHook() { + // DON'T CLEAN UP TO MAKE TEST INSPECTABLE + options.outputDir = path.join( + os.tmpdir(), + 'material-ui-icons-builder-test', + this.currentTest.fullTitle(), + ); + await emptyDir(options.outputDir); + }, + ); it('should produce the expected output', async () => { await handler(options); diff --git a/packages/mui-joy/src/AccordionDetails/AccordionDetails.test.tsx b/packages/mui-joy/src/AccordionDetails/AccordionDetails.test.tsx index b25ed232dc3985..dd8b9ee3917292 100644 --- a/packages/mui-joy/src/AccordionDetails/AccordionDetails.test.tsx +++ b/packages/mui-joy/src/AccordionDetails/AccordionDetails.test.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { expect } from 'chai'; -import { createRenderer, fireEvent, screen } from '@mui/internal-test-utils'; +import { createRenderer, fireEvent } from '@mui/internal-test-utils'; import { ThemeProvider } from '@mui/joy/styles'; import Accordion from '@mui/joy/Accordion'; import AccordionSummary from '@mui/joy/AccordionSummary'; @@ -31,7 +31,7 @@ describe('', () => { describe('tab index', () => { it('[initial] interactive content should have tab index -1', () => { - render( + const screen = render( @@ -47,7 +47,7 @@ describe('', () => { }); it('[expanded] interactive content should not have tab index 0', () => { - render( + const screen = render( @@ -63,7 +63,7 @@ describe('', () => { }); it('interactive content should preserve the tab index when closed', () => { - render( + const screen = render( title @@ -87,7 +87,7 @@ describe('', () => { }); it('should retain the default tab index if not explicitly set', () => { - render( + const screen = render( title @@ -112,7 +112,7 @@ describe('', () => { }); it('should retain the -1 tab index when explicitly set', () => { - render( + const screen = render( title diff --git a/packages/mui-joy/src/Autocomplete/Autocomplete.test.tsx b/packages/mui-joy/src/Autocomplete/Autocomplete.test.tsx index b8ff2c801884f9..1c37571d332210 100644 --- a/packages/mui-joy/src/Autocomplete/Autocomplete.test.tsx +++ b/packages/mui-joy/src/Autocomplete/Autocomplete.test.tsx @@ -256,7 +256,7 @@ describe('Joy ', () => { }); expect(container.textContent).to.equal('onetwothree'); // Depending on the subset of components used in this test run the computed `visibility` changes in JSDOM. - if (!/jsdom/.test(window.navigator.userAgent)) { + if (!window.navigator.userAgent.includes('jsdom')) { expect(getAllByRole('button', { hidden: false })).to.have.lengthOf(5); } }); @@ -280,7 +280,7 @@ describe('Joy ', () => { }); expect(container.textContent).to.equal('onetwothree'); // Depending on the subset of components used in this test run the computed `visibility` changes in JSDOM. - if (!/jsdom/.test(window.navigator.userAgent)) { + if (!window.navigator.userAgent.includes('jsdom')) { expect(getAllByRole('button', { hidden: false })).to.have.lengthOf(5); } }); @@ -531,42 +531,42 @@ describe('Joy ', () => { expect(screen.getByRole('combobox')).to.have.property('value', ''); }); - it('should fail validation if a required field has no value', function test() { - if (/jsdom/.test(window.navigator.userAgent)) { + it('should fail validation if a required field has no value', async function test() { + if (window.navigator.userAgent.includes('jsdom')) { // Enable once https://github.com/jsdom/jsdom/issues/2898 is resolved this.skip(); } const handleSubmit = spy((event) => event.preventDefault()); - render( + const { user } = render(
, ); - screen.getByRole('button', { name: 'Submit' }).click(); + await user.click(screen.getByRole('button', { name: 'Submit' })); expect(handleSubmit.callCount).to.equal(0); }); - it('should fail validation if a required field has a value', function test() { + it('should fail validation if a required field has a value', async function test() { // Unclear how native Constraint validation can be enabled for `multiple` - if (/jsdom/.test(window.navigator.userAgent)) { + if (window.navigator.userAgent.includes('jsdom')) { // Enable once https://github.com/jsdom/jsdom/issues/2898 is resolved // The test is passing in JSDOM but form validation is buggy in JSDOM so we rather skip than have false confidence this.skip(); } const handleSubmit = spy((event) => event.preventDefault()); - render( + const { user } = render(
, ); - screen.getByRole('button', { name: 'Submit' }).click(); + await user.click(screen.getByRole('button', { name: 'Submit' })); expect(handleSubmit.callCount).to.equal(0); }); @@ -932,7 +932,7 @@ describe('Joy ', () => { it('should ignore keydown event until the IME is confirmed', function test() { // TODO: Often times out in Firefox 78. // Is this slow because of testing-library or because of the implementation? - this.timeout(4000); + this?.timeout?.(4000); const { getByRole } = render(); const textbox = getByRole('combobox'); @@ -1283,7 +1283,7 @@ describe('Joy ', () => { describe('prop: options', () => { it('should scroll selected option into view when multiple elements with role as listbox available', function test() { - if (/jsdom/.test(window.navigator.userAgent)) { + if (window.navigator.userAgent.includes('jsdom')) { this.skip(); } render( diff --git a/packages/mui-joy/src/Box/Box.test.tsx b/packages/mui-joy/src/Box/Box.test.tsx index 87644fe248ef20..286e4ecca53a80 100644 --- a/packages/mui-joy/src/Box/Box.test.tsx +++ b/packages/mui-joy/src/Box/Box.test.tsx @@ -28,7 +28,7 @@ describe('Joy ', () => { })); it('respects theme from context', function test() { - const isJSDOM = /jsdom/.test(window.navigator.userAgent); + const isJSDOM = window.navigator.userAgent.includes('jsdom'); if (isJSDOM) { this.skip(); @@ -93,7 +93,7 @@ describe('Joy ', () => { }); it('color', function test() { - const isJSDOM = /jsdom/.test(window.navigator.userAgent); + const isJSDOM = window.navigator.userAgent.includes('jsdom'); if (isJSDOM) { this.skip(); @@ -111,7 +111,7 @@ describe('Joy ', () => { }); it('bgcolor', function test() { - const isJSDOM = /jsdom/.test(window.navigator.userAgent); + const isJSDOM = window.navigator.userAgent.includes('jsdom'); if (isJSDOM) { this.skip(); @@ -129,7 +129,7 @@ describe('Joy ', () => { }); it('backgroundColor', function test() { - const isJSDOM = /jsdom/.test(window.navigator.userAgent); + const isJSDOM = window.navigator.userAgent.includes('jsdom'); if (isJSDOM) { this.skip(); @@ -147,7 +147,7 @@ describe('Joy ', () => { }); it('borderRadius', function test() { - const isJSDOM = /jsdom/.test(window.navigator.userAgent); + const isJSDOM = window.navigator.userAgent.includes('jsdom'); if (isJSDOM) { this.skip(); @@ -168,7 +168,7 @@ describe('Joy ', () => { }); it('boxShadow', function test() { - const isJSDOM = /jsdom/.test(window.navigator.userAgent); + const isJSDOM = window.navigator.userAgent.includes('jsdom'); if (isJSDOM) { this.skip(); @@ -186,7 +186,7 @@ describe('Joy ', () => { }); it('fontSize', function test() { - const isJSDOM = /jsdom/.test(window.navigator.userAgent); + const isJSDOM = window.navigator.userAgent.includes('jsdom'); if (isJSDOM) { this.skip(); @@ -204,7 +204,7 @@ describe('Joy ', () => { }); it('fontWeight', function test() { - const isJSDOM = /jsdom/.test(window.navigator.userAgent); + const isJSDOM = window.navigator.userAgent.includes('jsdom'); if (isJSDOM) { this.skip(); @@ -222,7 +222,7 @@ describe('Joy ', () => { }); it('lineHeight', function test() { - const isJSDOM = /jsdom/.test(window.navigator.userAgent); + const isJSDOM = window.navigator.userAgent.includes('jsdom'); if (isJSDOM) { this.skip(); diff --git a/packages/mui-joy/src/Button/Button.test.tsx b/packages/mui-joy/src/Button/Button.test.tsx index 47e10fce89fec8..208313d30c8598 100644 --- a/packages/mui-joy/src/Button/Button.test.tsx +++ b/packages/mui-joy/src/Button/Button.test.tsx @@ -137,7 +137,7 @@ describe('Joy