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

build: Cypress React 18 support and React version switcher #6933

Merged
merged 5 commits into from
Jul 12, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions cypress/support/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { mount } from 'cypress/react';
import { ContextObject, Result, RunOptions } from 'axe-core';
import { realPress } from 'cypress-real-events/commands/realPress';
import type { mountCommand } from './setup/mount';
import type { realMountCommand } from './setup/realMount';

type KeyOrShortcut = Parameters<typeof realPress>[0];
type RealPressOptions = Parameters<typeof realPress>[1];
Expand Down Expand Up @@ -30,13 +31,13 @@ declare global {
/**
* Mounts components with a basic `EuiProvider` wrapper
*/
mount: typeof mount;
mount: mountCommand;

/**
* This ensures the correct testing window has focus when using Cypress Real Events.
* @see https://github.com/dmtrKovalenko/cypress-real-events/issues/196
*/
realMount: typeof mount;
realMount: realMountCommand;

/**
* Repeat the Real Events `realPress()` method 2 or more times
Expand Down
15 changes: 0 additions & 15 deletions cypress/support/setup/mount.js

This file was deleted.

30 changes: 30 additions & 0 deletions cypress/support/setup/mount.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import React, { ReactNode } from 'react';
import { EuiProvider } from '../../../src';
import type { mount } from '@cypress/react18';

// Pick cypress mount function based on which React version is currently being
// tested. It has to be directly compared against process.env.REACT_VERSION
// for tree-shaking to work and not throw an error because of a missing import.
let cypressMount: typeof mount;
if (process.env.REACT_VERSION === '18') {
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 can't be extracted to a utility function because the if statement won't be properly tree-shaken anymore. It wouldn't be a big deal but keeping both requires includes them in the bundle and causes webpack module resolver to throw because of react-dom/client differences between v18 and earlier versions.

cypressMount = require('@cypress/react18').mount;
} else {
cypressMount = require('@cypress/react').mount;
Copy link
Contributor

Choose a reason for hiding this comment

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

Asking for my own edification/understanding - React 16 & 17 use the same mount fn/library, is that correct?

Copy link
Member Author

Choose a reason for hiding this comment

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

Correct, they can use the same @cypress/react version because the ReactDOM.render API is the same

}

const mountCommand = (children: ReactNode): ReturnType<typeof mount> => {
return cypressMount(<EuiProvider>{children}</EuiProvider>);
};

// Export only the type to not confuse code-completion tools
export type mountCommand = typeof mountCommand;

Cypress.Commands.add('mount', mountCommand);
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,26 @@
* Side Public License, v 1.
*/

import { React, Fragment } from 'react';
import React, { ReactNode } from 'react';
import './mount';

Cypress.Commands.add('realMount', (children) => {
const realMountCommand = (children: ReactNode) => {
cy.mount(
<Fragment>
<>
Copy link
Contributor

Choose a reason for hiding this comment

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

💯 🎉 thanks for cleaning this up! (+ other lint/type cleanups in the PR!)

<div
data-test-subj="cypress-real-event-target"
style={{ height: '1px', width: '1px' }}
/>
{children}
</Fragment>
</>
).then(() => {
cy.get('[data-test-subj="cypress-real-event-target"]').realClick({
position: 'topLeft',
});
});
});
};

// Export only the type to not confuse code-completion tools
export type realMountCommand = typeof realMountCommand;

Cypress.Commands.add('realMount', realMountCommand);
17 changes: 14 additions & 3 deletions cypress/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,15 @@ const { ProvidePlugin, DefinePlugin } = require('webpack');

const THEME_IMPORT = `'../../dist/eui_theme_${process.env.THEME}.css'`;

const alias = {};
const reactVersion = process.env.REACT_VERSION || '18';

// Setup module aliasing when we're testing an older React version
if (['16', '17'].includes(reactVersion)) {
alias.react = `react-${reactVersion}`;
alias['react-dom'] = `react-dom-${reactVersion}`;
}

module.exports = {
mode: 'development',

Expand All @@ -26,6 +35,7 @@ module.exports = {
os: false,
process: require.resolve('process/browser'),
},
alias,
},

module: {
Expand All @@ -46,9 +56,9 @@ module.exports = {
loader: 'style-loader',
options: {
insert: 'meta[name="css-styles"]',
}
},
},
'css-loader'
'css-loader',
],
exclude: /node_modules/,
},
Expand All @@ -62,7 +72,8 @@ module.exports = {
}),

new DefinePlugin({
THEME_IMPORT, // allow cypress/suport/index.js to require the correct css file
THEME_IMPORT, // allow cypress/support/component.tsx to require the correct css file
'process.env.REACT_VERSION': JSON.stringify(reactVersion),
}),
],
};
7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,8 @@
"@babel/preset-typescript": "^7.21.5",
"@babel/template": "^7.21.9",
"@cypress/code-coverage": "^3.10.0",
"@cypress/react": "^5.10.3",
"@cypress/react": "^7.0.3",
"@cypress/react18": "^2.0.0",
"@cypress/webpack-dev-server": "^1.7.0",
"@elastic/charts": "^53.1.1",
"@elastic/datemath": "^5.0.3",
Expand Down Expand Up @@ -226,8 +227,12 @@
"puppeteer": "^5.5.0",
"raw-loader": "^4.0.1",
"react": "^18.2.0",
"react-16": "npm:react@^16.14.0",
"react-17": "npm:react@^17.0.2",
"react-docgen-typescript": "^2.2.2",
"react-dom": "^18.2.0",
"react-dom-16": "npm:react-dom@^16.14.0",
"react-dom-17": "npm:react-dom@^17.0.2",
Copy link
Contributor

Choose a reason for hiding this comment

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

[not related to this PR, just asking a question to get your thoughts] Just curious, when do you think we should drop React 16/17 support / remove these dependencies? Once Kibana is on React 18?

Copy link
Member Author

Choose a reason for hiding this comment

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

We probably could drop React 16 support with the official release of React 18-compatible EUI, but that's something we should confirm with Kibana teams

"react-helmet": "^6.1.0",
"react-redux": "^7.2.1",
"react-refresh": "^0.11.0",
Expand Down
9 changes: 9 additions & 0 deletions scripts/cypress.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,24 @@ const argv = yargs(hideBin(process.argv))
dev: { type: 'boolean' },
theme: { type: 'string', default: 'light', choices: ['light', 'dark'] },
a11y: { type: 'boolean' },
'react-version': {
type: 'number',
default: 18,
choices: [16, 17, 18],
},
}).argv;

const isDev = argv.hasOwnProperty('dev');
const isA11y = argv.hasOwnProperty('a11y');
const skipScss = argv.hasOwnProperty('skip-css');
const theme = argv.theme;
const reactVersion = argv['react-version'];

const info = chalk.white;
const log = chalk.grey;

console.log(info(`Running tests on React v${reactVersion}`));

// compile scss -> css so tests can render correctly
if (!skipScss) {
console.log(info('Compiling SCSS'));
Expand All @@ -54,6 +62,7 @@ const cypressCommandParts = [
`THEME=${theme}`, // pass the theme
'BABEL_MODULES=false', // let webpack receive ES Module code
'NODE_ENV=cypress_test', // enable code coverage checks
`REACT_VERSION=${reactVersion}`, // set react version to test
`cypress ${testParams}`,
...argv._, // pass any extra options given to this script
];
Expand Down
5 changes: 5 additions & 0 deletions wiki/contributing-to-eui/testing/cypress-testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ By default tests are run using the light theme. Dark mode can be enabled by pass

To ensure tests use up-to-date styles, the test runner compiles our SCSS to CSS before executing Cypress. This adds some processing time before the tests can run, and often the existing locally-built styles are still accurate. The CSS compilation step can be skipped by passing the `--skip-css` flag to `yarn test-cypress`, `yarn test-cypress-dev` and `yarn test-cypress-a11y`.

### Testing specific version of React

By default, EUI Cypress tests are run using the latest supported version of React.
You can change that behavior and run e2e tests using a different React version by passing the `--react-version` option set to `16`, `17` or `18`.
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we / do we need to set up our test-ci script to run all our Cypress tests through each React version?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah we definitely should, but that probably means writing a script for handling all of that updated version switching logic. I'll think of a good solution this week and add it in a separate PR as it's not really needed straight away :)

Copy link
Contributor

Choose a reason for hiding this comment

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

Sounds great!


### Cypress arguments

You can also pass [Cypress CLI arguments](https://docs.cypress.io/guides/guides/command-line). For example:
Expand Down
Loading