Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[code-infra] vitest browser & jsdom modes #14508

Open
wants to merge 112 commits into
base: master
Choose a base branch
from

Conversation

JCQuintas
Copy link
Member

@JCQuintas JCQuintas commented Sep 5, 2024

Related mui/material-ui#43625

Minimal changes to have charts tests working in browser mode
Charts are the smallest test suit though

Help toward mui/mui-public#170.

Currently ignoring DateFnsV3 adapter tests in the browser due to vitest-dev/vitest#6483 they work normally in jsdom though.

Running tests

# all
pnpm vitest

# browser
pnpm vitest --project "browser/*"

# jsdom
pnpm vitest --project "jsdom/*"

@JCQuintas JCQuintas added test scope: code-infra Specific to the core-infra product labels Sep 5, 2024
@JCQuintas JCQuintas self-assigned this Sep 5, 2024
@mui-bot
Copy link

mui-bot commented Sep 5, 2024

Deploy preview: https://deploy-preview-14508--material-ui-x.netlify.app/

Generated by 🚫 dangerJS against 98013ae

@github-actions github-actions bot added the PR: out-of-date The pull request has merge conflicts and can't be merged label Sep 9, 2024
Copy link

github-actions bot commented Sep 9, 2024

This pull request has conflicts, please resolve those before we can evaluate the pull request.

@github-actions github-actions bot removed the PR: out-of-date The pull request has merge conflicts and can't be merged label Sep 10, 2024
@github-actions github-actions bot removed the PR: out-of-date The pull request has merge conflicts and can't be merged label Oct 14, 2024
Copy link
Member Author

@JCQuintas JCQuintas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With these last changes, ALL X packages are running in vitest.

There are some details I commented below.

Main takes:

  • x-license and x-internals are only running in jsdom mode.
  • x-date-pickers Hijiri adapter is only running in jsdom mode.

I can isolate the this.skip changes if necessary.

Tomorrow I'll check if I can make circle-ci work with vitest (I didn't try yet). So we can have proper comparisons

"@types/node": "^20.16.10",
"@playwright/test": "1.44.1",
"playwright": "1.44.1",
"@mui/internal-test-utils": "https://pkg.csb.dev/mui/material-ui/commit/92c23999/@mui/internal-test-utils"
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be changed when mui/material-ui#43625 is merged.

I would still leave it as a resolution so all packages use the same.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally, I would avoid all resolutions if we can, it tends to be misunderstood and overused and could result into setups that break on the user end but not on our end. (probably not for this dependency though)

If the version ranges are equal, there should be no duplication. And it shouldn't break in the first place if there was duplication.

Comment on lines +44 to +49
it('should provide the right context as second argument', function test(t = {}) {
if (isJSDOM) {
// can't do Pointer event with JSDom https://github.com/jsdom/jsdom/issues/2527
this.skip();
// @ts-expect-error to support mocha and vitest
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
this?.skip?.() || t?.skip();
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You will see this pattern across multiple files, t = {} tells mocha to treat this as an "empty" parameter, thus not triggering the logic to provide the done() callback to it.

Without this mocha will fail because it waits for the done() to be called.

This can then be migrated to just a .skip, but vitest offers many ways of skipping test.

function test(t) {
      if (isJSDOM) {
        t.skip();
      }
}

Copy link
Member

@Janpot Janpot Oct 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a shim in ./setupVitest that we cold use while the tests run in both vitest and mocha. That prevents us in core from having to update all of these right now:
https://github.com/mui/material-ui/pull/43625/files#diff-21f1d3337bd5d6e3869ad10c93fa99770a34761876017b90bc1ed1ceaa7c93a9R45-R55
Want to move this to it.skipIf eventually

The same couldn't be done for describe.skipIf that only exists in vitest, I added a shim for that in test utils:

https://github.com/mui/material-ui/pull/43625/files#diff-52e69abe336786febd0b84dbd2d9b32ba0e8f74fcf5f71669c8917d9a434c4dcR3-R9

Don't necessarily have to use it, but it can keep the amount of code changes in tests down.

Comment on lines +31 to +41
beforeEach(() => {
if (window?.document?.body?.style) {
window.document.body.style.margin = '0';
}
});

afterEach(() => {
if (window?.document?.body?.style) {
window.document.body.style.margin = '8px';
}
});
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some charts tests are reliant screen position, and margins are different for karma and vitest, so we force one. 🙃

Comment on lines 269 to 282
// fireEvent.mouseDown(getCell(2, 0), { ctrlKey: true });
// fireEvent.mouseOver(getCell(3, 0), { ctrlKey: true });
const isMac = window.navigator.platform.toUpperCase().indexOf('MAC') >= 0;

await user.keyboard(isMac ? '{Meta>}' : '{Control>}');
await user.pointer([
// touch the screen at element1
{ keys: '[MouseLeft>]', target: getCell(2, 0) },
// move the touch pointer to element2
{ target: getCell(3, 0) },
// release the touch pointer at the last position (element2)
{ keys: '[/MouseLeft]' },
]);
await user.keyboard(isMac ? '{/Meta}' : '{/Control}');
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only the userEvents worked here. Gotta change from meta/control based on OS.

Comment on lines +173 to +174
// These test are flaky in JSDOM
return;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Early returns, we should eventually remove these into native skip describes to make tests more reliable.

I didn't find an api that worked for both mocha and vitest except for returning.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines +218 to +220
act(() => {
screen.getByLabelText(/choose date/i).click();
});
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrap in act, .click() so it doesn't scroll into view.

});

expect(onErrorMock.callCount).to.equal(2);
expect(onErrorMock.lastCall.args[0]).to.deep.equal(['disablePast', 'disablePast']);
testInvalidStatus([true, true], isSingleInput);

act(() => {
setValue(adapterToUse.date(now));
setValue(adapterToUse.addHours(now, 1));
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Had to add one hour to "now" for it to work here. I guess previously the timer didn't run?

Comment on lines +17 to +19
// Ideally we move the configuration to each package.
// Currently it doesn't work because vitest doesn't detect two different configurations in the same package.
// We could bypass this limitation by having a folder per configuration. Eg: `packages/x-charts/browser` & `packages/x-charts/jsdom`.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// comment

Comment on lines 67 to 83
// TODO: Decide on
// Manually changing the process.env in browser tests doesn't work.
// And alternative is to use `const {NODE_ENV} = process.env` in the code instead.
// x-license relies on `process.env.NODE_ENV` to determine the environment.
{
extends: './vitest.config.mts',
plugins: [react()],
test: {
include: [`packages/x-license/src/**/*.test.?(c|m)[jt]s?(x)`],
exclude: [`packages/x-license/src/**/*.browser.test.?(c|m)[jt]s?(x)`],
name: `jsdom/x-license`,
environment: 'jsdom',
env: {
MUI_JSDOM: 'true',
},
},
},
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

x-license required to be run on jsdom only due to the process.env requirement.

Comment on lines 67 to 84
// TODO: Decide on
// Manually changing the process.env in browser tests doesn't work.
// And alternative is to use `const {NODE_ENV} = process.env` in the code instead.
// x-license relies on `process.env.NODE_ENV` to determine the environment.
// x-internals has a weird chai import issue I couldn't fix
...jsdomOnlyPackages.map((name) => ({
extends: './vitest.config.mts',
plugins: [react()],
test: {
include: [`packages/${name}}/src/**/*.test.?(c|m)[jt]s?(x)`],
exclude: [`packages/${name}}/src/**/*.browser.test.?(c|m)[jt]s?(x)`],
name: `jsdom/${name}}`,
environment: 'jsdom',
env: {
MUI_JSDOM: 'true',
},
},
})),
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Running these on jsdom only as x-license requires process.env.NODE_ENV and x-internals has a weird chai error I couldn't fix.

Comment on lines +141 to +155
test_unit_vitest:
<<: *default-job
steps:
- checkout
- install_js
- run:
name: Tests fake browser (vitest)
command: pnpm vitest --project "jsdom/*" --coverage
- run:
name: Check coverage generated
command: |
if ! [[ -s coverage/lcov.info ]]
then
exit 1
fi
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Janpot is it possible to test circleci steps without merging? 🤔

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be yes, when you add it to the workflow:

@@ -7,6 +8,8 @@ module.exports = {
// Mocha seems to ignore .next anyway (maybe because dotfiles?).
// We're leaving this to make sure.
'docs/.next/**',
'packages/**/*.browser.test.{js,ts,tsx,jsx}',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a new file extension we're introducing? Can't find any such file.

@@ -106,17 +106,17 @@ module.exports = function getBabelConfig(api) {
plugins.push([
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add a TODO here to remove these plugins once fully migrated to vitest?

// And alternative is to use `const {NODE_ENV} = process.env` in the code instead.
// x-license relies on `process.env.NODE_ENV` to determine the environment.
// x-internals has a weird chai import issue I couldn't fix
...(jsdomOnlyPackages.includes(name)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It feels suboptimial to me to have to define this on the workspace level and not on the individual package level.
Would it be possible to give each vitest project their own config file which extends from one or more base files?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For instance we could just define workspaces as

// ./vitest.workspace.ts
export default [
  './packages/*/vitest.config.{browser|jsdom}.ts'
]

and in the projects:

// ./packages/x-data-grid/vitest.config.browser.ts
import { mergeConfig } from 'vitest/config'
import browserDefault from '@mui/test-utils/browserDefault'
export default mergeConfig(browserDefault, {
  // ...
})

// ./packages/x-data-grid/vitest.config.jsdom.ts
import jsdomDefault from '@mui/test-utils/jsdomDefault'
export default jsdomDefault

and omit the browser file for projects that are jsdom only

Not sure whether that works the way I expect it to be

Copy link

This pull request has conflicts, please resolve those before we can evaluate the pull request.

@github-actions github-actions bot added the PR: out-of-date The pull request has merge conflicts and can't be merged label Oct 16, 2024
@@ -244,7 +244,7 @@ module.exports = {
},
},
{
files: ['packages/*/src/**/*{.ts,.tsx,.js}'],
files: ['packages/*/src/**/*.?(c|m)[jt]s?(x)'],
excludedFiles: ['*.d.ts', '*.spec.ts', '*.spec.tsx'],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I propose as a pattern we use ?(c|m)[jt]s?(x) to select files, but use wildcard to exclude.

Suggested change
excludedFiles: ['*.d.ts', '*.spec.ts', '*.spec.tsx'],
excludedFiles: ['*.d.ts', '*.spec.*'],

@@ -58,7 +58,7 @@ const RESTRICTED_TOP_LEVEL_IMPORTS = [
// It needs to know about the parent "no-restricted-imports" to not override them.
const buildPackageRestrictedImports = (packageName, root, allowRootImports = true) => [
{
files: [`packages/${root}/src/**/*{.ts,.tsx,.js}`],
files: [`packages/${root}/src/**/*.?(c|m)[jt]s?(x)`],
excludedFiles: ['*.d.ts', '*.spec.ts', '*.spec.tsx', '**.test.tx', '**.test.tsx'],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"test.tx" 🙂

Suggested change
excludedFiles: ['*.d.ts', '*.spec.ts', '*.spec.tsx', '**.test.tx', '**.test.tsx'],
excludedFiles: ['*.d.ts', '*.spec.*', '*.test.*'],

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
PR: out-of-date The pull request has merge conflicts and can't be merged scope: code-infra Specific to the core-infra product test
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants