feat(@cypress/react): Make correct plugins for different adapters/bundlers (#15337)

* Remove unused stuff

* Fix eslint errors

* Use local cypress running script

* Fix dependency resolution

* Revert "Fix dependency resolution"

This reverts commit 01a70be211.

* Add @cypress/react CI

* Properly resolve @types pacakges

* Run tests with mocha

* Fix cypress tests

* Fix or skip some specs

* Add retries to the card-spec.js

* Remove jest mocks

* Run e2e examples on CI

* Fix yarn cache key

* Fix e2e examples jobs

* Rename cypress-react-unit-test with @cypress/react

* Fix circleci.yml

* Revert "Fix e2e examples jobs"

This reverts commit efcc7c4d19.

* Revert " Rename cypress-react-unit-test with @cypress/react"

This reverts commit 4febfcc82b.

* Replce cypress-react-unit-test with @cypress/react

* Persist build artifacts

* Fix working directory paths circle.yml

* Fix more CI

* adding yarn.lock files

* Make package.json for  folder

* Remove .npmrc

* Update circle.yml

* Remove unused files

* Copy plugins files to the "dist" folder

* Fix links to the github repos

* Move init wizard from npm/react/init to npm/wizard

* Move init wizard from npm/react/init to npm/wizard

* Implement initial vue template

* Run wizard tests on CI

* Refactor continue: bool to success: bool for better code readability

* Fix circleci.yml

* Use only absolute paths for tests

* Stub process.exit calls

* Remove useless comments

* Add installation cypress to the wizard logic

* More improvements

* Include packages/examples/cypress into git

* Commit new files

* Use packages/example as SST for generated files

* Last improvements for wizard

* Update packages/server/lib/scaffold.js

* Rename wizard to create-cypress-tests

* Fix circleci config

* Fix snapshot tests

* Run all create-cypress-tests on ci

* Do not install plugins and scaffold files from kitchensink

* Ignore integration/examples folder for packages/example

* Run create-cypress-tests-tests on CI

* Add copy example cross-platform script

* Use copy example script in tests

* feat: create-cypress-tests auto-inject config (#9116)

* Implement the basic babel code transformation for configs

* Add more tests for edge-cases

* Add snapshot tests for autogenerated code for each template

* Add git status guard

* Add git status guard

* Fix last test

* Fix tests

* Revert changes for packages/example

* Revert changes for packages/example/tsconfig.json

* Prepeare package for the release

* Fix inquirer name

* v0.0.125

* v0.0.127

* v0.0.128

* v0.0.130

* v0.0.131

* Add more UX features

* Add vue-cli template

* Make src as default folder for vue-cli template

* Revert dev-time changes

* Run appveyour windows build

* Run full appveyour build

* Fix circle.yml

* Update plugins

* Join paths for windows

* Revert example/lib changes

* Fix tests

* Revert unnecessary changes

* Implement dev-server dependency installation

* Update npm/create-cypress-tests/README.md

* Properly exit process if react tests failed

* Fix circleci reporter not found

* Update nextjs plugin

* react-scripts plugin for dev-server

* Implement webpack-file plugin support

* v0.0.502

* v0.0.503

* Implement rollup support

* Implement babel plugin

* Update webpack options

* Fix nextjs plugin

* Unmount components in beforeEach to prevent side-effects

* Rename preprocessor to injectDevServer

* Fix next.js example

* Fix webpack-options exampel

* Reproduce rollup problem

* Update create-cypress-tests snapshots

* Do not run examples/rollup

* Uncomment test

* Change paralllelism

* Remove useless checks

* Update snapshots

Co-authored-by: Jessica Sachs <jess@jessicasachs.io>
This commit is contained in:
Dmitriy Kovalenko
2021-03-05 18:16:22 +02:00
committed by GitHub
parent c405ee89ef
commit fc30118252
78 changed files with 3038 additions and 2452 deletions
+1
View File
@@ -7,6 +7,7 @@ build
.publish
_test-output
cypress.zip
.babel-cache
# from extension
Cached Theme.pak
+1 -1
View File
@@ -1108,7 +1108,7 @@ jobs:
npm-react:
<<: *defaults
parallelism: 9
parallelism: 8
steps:
- attach_workspace:
at: ~/
@@ -1,10 +1,10 @@
exports['babel installation template correctly generates plugins config 1'] = `
const preprocessor = require('@cypress/react/plugins/babel');
const injectDevServer = require('@cypress/react/plugins/babel');
const something = require("something");
module.exports = (on, config) => {
preprocessor(on, config);
injectDevServer(on, config);
return config; // IMPORTANT to return the config object
};
`
@@ -1,18 +1,18 @@
exports['Injects guessed next.js template cypress.json'] = `
const preprocessor = require("@cypress/react/plugins/next");
const injectDevServer = require("@cypress/react/plugins/next");
module.exports = (on, config) => {
preprocessor(on, config);
injectDevServer(on, config);
return config; // IMPORTANT to return the config object
};
`
exports['Injects guessed next.js template plugins/index.js'] = `
const preprocessor = require("@cypress/react/plugins/next");
const injectDevServer = require("@cypress/react/plugins/next");
module.exports = (on, config) => {
preprocessor(on, config);
injectDevServer(on, config);
return config; // IMPORTANT to return the config object
};
@@ -24,20 +24,20 @@ import "@cypress/react/support";
`
exports['Injected overridden webpack template cypress.json'] = `
const preprocessor = require("@cypress/react/plugins/react-scripts");
const injectDevServer = require("@cypress/react/plugins/react-scripts");
module.exports = (on, config) => {
preprocessor(on, config);
injectDevServer(on, config);
return config; // IMPORTANT to return the config object
};
`
exports['Injected overridden webpack template plugins/index.js'] = `
const preprocessor = require("@cypress/react/plugins/react-scripts");
const injectDevServer = require("@cypress/react/plugins/react-scripts");
module.exports = (on, config) => {
preprocessor(on, config);
injectDevServer(on, config);
return config; // IMPORTANT to return the config object
};
@@ -1,10 +1,10 @@
exports['next.js install template correctly generates plugins config 1'] = `
const preprocessor = require('@cypress/react/plugins/next');
const injectDevServer = require('@cypress/react/plugins/next');
const something = require("something");
module.exports = (on, config) => {
preprocessor(on, config);
injectDevServer(on, config);
return config; // IMPORTANT to return the config object
};
`
@@ -1,10 +1,10 @@
exports['create-react-app install template correctly generates plugins config 1'] = `
const preprocessor = require('@cypress/react/plugins/react-scripts');
const injectDevServer = require('@cypress/react/plugins/react-scripts');
const something = require("something");
module.exports = (on, config) => {
preprocessor(on, config);
injectDevServer(on, config);
return config; // IMPORTANT to return the config object
};
`
@@ -1,24 +1,24 @@
exports['webpack-file install template correctly generates plugins config when webpack config path is missing 1'] = `
const preprocessor = require("@cypress/react/plugins/load-webpack");
const injectDevServer = require("@cypress/react/plugins/load-webpack");
const something = require("something");
module.exports = (on, config) => {
// TODO replace with valid webpack config path
config.env.webpackFilename = './webpack.config.js';
preprocessor(on, config);
injectDevServer(on, config);
return config; // IMPORTANT to return the config object
};
`
exports['webpack-file install template correctly generates plugins config when webpack config path is provided 1'] = `
const preprocessor = require("@cypress/react/plugins/load-webpack");
const injectDevServer = require("@cypress/react/plugins/load-webpack");
const something = require("something");
module.exports = (on, config) => {
config.env.webpackFilename = 'config/webpack.config.js';
preprocessor(on, config);
injectDevServer(on, config);
return config; // IMPORTANT to return the config object
};
`
@@ -1,32 +1,40 @@
exports['rollup-file install template correctly generates plugins config when webpack config path is missing 1'] = `
const rollupPreprocessor = require("@bahmutov/cy-rollup");
const path = require("path");
const {
startDevServer
} = require("@cypress/rollup-dev-server");
const something = require("something");
module.exports = (on, config) => {
on('file:preprocessor', rollupPreprocessor({
// TODO replace with valid rollup config path
configFile: 'rollup.config.js'
}));
require('@cypress/code-coverage/task')(on, config);
on("dev-server:start", async options => {
return startDevServer({
options,
// TODO replace with valid rollup config path
rollupConfig: path.resolve(__dirname, 'rollup.config.js')
});
});
return config; // IMPORTANT to return the config object
};
`
exports['rollup-file install template correctly generates plugins config when webpack config path is provided 1'] = `
const rollupPreprocessor = require("@bahmutov/cy-rollup");
const path = require("path");
const {
startDevServer
} = require("@cypress/rollup-dev-server");
const something = require("something");
module.exports = (on, config) => {
on('file:preprocessor', rollupPreprocessor({
configFile: 'config/rollup.config.js'
}));
require('@cypress/code-coverage/task')(on, config);
on("dev-server:start", async options => {
return startDevServer({
options,
rollupConfig: path.resolve(__dirname, 'config/rollup.config.js')
});
});
return config; // IMPORTANT to return the config object
};
`
@@ -1,10 +1,10 @@
exports['vue webpack-file install template correctly generates plugins for vue-cli-service 1'] = `
const preprocessor = require("@cypress/vue/dist/plugins/webpack");
const injectDevServer = require("@cypress/vue/dist/plugins/webpack");
const something = require("something");
module.exports = (on, config) => {
preprocessor(on, config); // IMPORTANT return the config object
injectDevServer(on, config); // IMPORTANT return the config object
return config;
};
@@ -1,5 +1,9 @@
exports['webpack-options template correctly generates plugins config 1'] = `
const webpackPreprocessor = require("@cypress/webpack-preprocessor");
const path = require("path");
const {
startDevServer
} = require("@cypress/webpack-dev-Server");
const something = require("something");
@@ -15,13 +19,13 @@ module.exports = (on, config) => {
publicPath: '/',
chunkFilename: '[name].bundle.js'
},
// TODO: update with valid configuration for your app
// TODO: update with valid configuration for your components
module: {
rules: [{
test: /\\.(js|jsx|mjs|ts|tsx)$/,
loader: 'babel-loader',
options: { ...babelConfig,
cacheDirectory: path.resolve(__dirname, '..', '..', '.babel-cache')
options: {
cacheDirectory: path.resolve(__dirname, '.babel-cache')
}
}]
}
@@ -11,13 +11,13 @@ export const BabelTemplate: Template = {
'webpack',
)} to bundle the components for testing.`,
recommendedComponentFolder: 'cypress/component',
dependencies: ['@cypress/webpack-dev-server'],
dependencies: ['webpack', '@cypress/webpack-dev-server'],
getExampleUrl: () => 'https://github.com/cypress-io/cypress/tree/develop/npm/react/examples/babel',
getPluginsCodeAst: () => {
return {
Require: babel.template.ast('const preprocessor = require(\'@cypress/react/plugins/babel\')'),
Require: babel.template.ast('const injectDevServer = require(\'@cypress/react/plugins/babel\')'),
ModuleExportsBody: babel.template.ast([
'preprocessor(on, config)',
'injectDevServer(on, config)',
'return config // IMPORTANT to return the config object',
].join('\n'), { preserveComments: true }),
}
@@ -13,9 +13,9 @@ export const NextTemplate: Template = {
dependencies: ['@cypress/webpack-dev-server'],
getPluginsCodeAst: () => {
return {
Require: babel.template.ast('const preprocessor = require(\'@cypress/react/plugins/next\')'),
Require: babel.template.ast('const injectDevServer = require(\'@cypress/react/plugins/next\')'),
ModuleExportsBody: babel.template.ast([
'preprocessor(on, config)',
'injectDevServer(on, config)',
'return config // IMPORTANT to return the config object',
].join('\n'), { preserveComments: true }),
}
@@ -16,9 +16,9 @@ export const ReactScriptsTemplate: Template = {
},
getPluginsCodeAst: () => {
return {
Require: babel.template.ast('const preprocessor = require(\'@cypress/react/plugins/react-scripts\')'),
Require: babel.template.ast('const injectDevServer = require(\'@cypress/react/plugins/react-scripts\')'),
ModuleExportsBody: babel.template.ast([
'preprocessor(on, config)',
'injectDevServer(on, config)',
'return config // IMPORTANT to return the config object',
].join('\n'), { preserveComments: true }),
}
@@ -18,13 +18,13 @@ export const WebpackTemplate: Template<{ webpackConfigPath: string }> = {
: './webpack.config.js'
return {
Require: babel.template.ast('const preprocessor = require("@cypress/react/plugins/load-webpack")'),
Require: babel.template.ast('const injectDevServer = require("@cypress/react/plugins/load-webpack")'),
ModuleExportsBody: babel.template.ast([
includeWarnComment
? '// TODO replace with valid webpack config path'
: '',
`config.env.webpackFilename = '${webpackConfigPath}'`,
'preprocessor(on, config)',
'injectDevServer(on, config)',
'return config // IMPORTANT to return the config object',
].join('\n'), { preserveComments: true }),
}
@@ -21,7 +21,7 @@ export function extractRollupConfigPathFromScript (script: string) {
export const RollupTemplate: Template<{ rollupConfigPath: string }> = {
message:
'It looks like you have custom `rollup.config.js`. We can use it to bundle the components for testing.',
'It looks like you have custom `rollup.config.js`. We can use it to bundle components for testing.',
getExampleUrl: () => {
return 'https://github.com/cypress-io/cypress/tree/develop/npm/react/examples/rollup'
},
@@ -34,19 +34,21 @@ export const RollupTemplate: Template<{ rollupConfigPath: string }> = {
: 'rollup.config.js'
return {
Require: babel.template.ast('const rollupPreprocessor = require("@bahmutov/cy-rollup")'),
Require: babel.template.ast([
'const path = require("path")',
'const { startDevServer } = require("@cypress/rollup-dev-server")',
].join('\n')),
ModuleExportsBody: babel.template.ast([
`on(`,
` 'file:preprocessor',`,
` rollupPreprocessor({`,
`on("dev-server:start", async (options) => {`,
` return startDevServer({`,
` options,`,
includeWarnComment
? ' // TODO replace with valid rollup config path'
? ' // TODO replace with valid rollup config path'
: '',
` configFile: '${rollupConfigPath}',`,
` }),`,
`)`,
` rollupConfig: path.resolve(__dirname, '${rollupConfigPath}'),`,
` })`,
`})`,
``,
`require('@cypress/code-coverage/task')(on, config)`,
`return config // IMPORTANT to return the config object`,
].join('\n'), { preserveComments: true }),
}
@@ -9,13 +9,13 @@ const webpackConfig = {
publicPath: '/',
chunkFilename: '[name].bundle.js',
},
// TODO: update with valid configuration for your app
// TODO: update with valid configuration for your components
module: {
rules: [
{
test: /\.(js|jsx|mjs|ts|tsx)$/,
loader: 'babel-loader',
options: { ...babelConfig, cacheDirectory: path.resolve(__dirname, '..', '..', '.babel-cache') },
options: { cacheDirectory: path.resolve(__dirname, '.babel-cache') },
},
]
},
@@ -12,10 +12,13 @@ export const WebpackOptions: Template = {
},
test: () => ({ success: false }),
recommendedComponentFolder: 'src',
dependencies: ['@cypress/webpack-dev-server'],
dependencies: ['webpack', '@cypress/webpack-dev-server'],
getPluginsCodeAst: () => {
return {
Require: babel.template.ast('const webpackPreprocessor = require("@cypress/webpack-preprocessor")'),
Require: babel.template.ast([
'const path = require("path")',
'const { startDevServer } = require("@cypress/webpack-dev-Server")',
].join('\n')),
ModuleExportsBody: babel.template.ast(
fs.readFileSync(path.resolve(__dirname, 'webpack-options-module-exports.template.js'), { encoding: 'utf-8' }),
{ preserveComments: true },
@@ -11,10 +11,10 @@ export const VueCliTemplate: Template = {
getPluginsCodeAst: () => {
return {
Require: babel.template.ast(
'const preprocessor = require("@cypress/vue/dist/plugins/webpack");',
'const injectDevServer = require("@cypress/vue/dist/plugins/webpack");',
),
ModuleExportsBody: babel.template.ast([
'preprocessor(on, config);',
'injectDevServer(on, config);',
'// IMPORTANT return the config object',
'return config',
].join('\n'), { preserveComments: true }),
+3 -1
View File
@@ -61,7 +61,9 @@ const webpackConfig = {
* @type Cypress.PluginConfig
*/
module.exports = (on, config) => {
on('dev-server:start', (options) => startDevServer({ options, webpackConfig, disableLazyCompilation: false }))
on('dev-server:start', (options) => {
return startDevServer({ options, webpackConfig, disableLazyCompilation: false })
})
return config
}
+2 -2
View File
@@ -3,8 +3,8 @@
"description": "Testing component accessibility",
"private": true,
"scripts": {
"cy:open": "node ../../../../scripts/cypress open",
"test": "node ../../../../scripts/cypress run"
"cy:open": "node ../../../../scripts/cypress open-ct",
"test": "node ../../../../scripts/cypress run-ct"
},
"devDependencies": {
"@cypress/react": "file:../../dist",
@@ -27,9 +27,9 @@ describe('NextJS page', () => {
})
it('can be tested with real .getInitialProps call', () => {
IndexPage.getInitialProps().then((props) => {
mount(<IndexPage {...props} />)
})
cy
.wrap(IndexPage.getServerSideProps())
.then((props) => mount(<IndexPage {...props} />))
cy.contains(
'`.getInitialProps()` was called and passed props to this component',
@@ -1,10 +1,10 @@
const preprocessor = require('@cypress/react/plugins/next')
const injectNextDevServer = require('@cypress/react/plugins/next')
/**
* @type {Cypress.PluginConfig}
*/
module.exports = (on, config) => {
preprocessor(on, config)
injectNextDevServer(on, config)
return config
}
+6 -5
View File
@@ -6,20 +6,21 @@
"build": "next build",
"build:static": "next build && next out",
"check-coverage": "check-coverage components/Search.jsx && check-coverage pages/index.js && check-coverage pages/router.js",
"cy:open": "node ../../../../scripts/cypress open",
"cy:open": "node ../../../../scripts/cypress open-ct",
"dev": "next",
"only-cove red": "only-covered components/Search.jsx pages/index.js pages/router.js",
"only-covered": "only-covered components/Search.jsx pages/index.js pages/router.js",
"start": "next start",
"test": "node ../../../../scripts/cypress run"
"test": "node ../../../../scripts/cypress run-ct"
},
"devDependencies": {
"@cypress/react": "file:../../dist",
"@mdx-js/loader": "^1.6.16",
"@next/mdx": "^9.5.3",
"@next/mdx": "10.0.7",
"@zeit/next-sass": "^1.0.1",
"check-code-coverage": "1.9.2",
"cypress-circleci-reporter": "0.2.0",
"next": "^9.5.3"
"next": "10.0.7",
"webpack": "^4.44.2"
},
"license": "MIT"
}
+2 -2
View File
@@ -19,8 +19,8 @@ function IndexPage ({ asyncProp }) {
)
}
IndexPage.getInitialProps = async (ctx) => {
return { asyncProp: true }
IndexPage.getServerSideProps = () => {
return Promise.resolve({ asyncProp: true })
}
export default IndexPage
File diff suppressed because it is too large Load Diff
@@ -3,8 +3,6 @@
import React from 'react'
import App from '../../src/App'
import { mount } from '@cypress/react'
import * as calc from '../../src/calc'
import * as child from '../../src/Child'
describe('App', () => {
it('renders learn react link', () => {
@@ -17,28 +15,4 @@ describe('App', () => {
mount(<div>JSX</div>)
cy.contains('JSX')
})
it('controls the random number by stubbing named import', () => {
// we are stubbing "getRandomNumber" exported by "calc.js"
// and imported into "App.js" and called.
cy.stub(calc, 'getRandomNumber')
.as('lucky')
.returns(777)
mount(<App />)
cy.contains('.random', '777')
cy.get('@lucky').should('be.calledOnce')
})
it('stubs an imported child component', () => {
cy.stub(child, 'Child')
.as('child')
.returns(<div className="mock-child">Mock child component</div>)
mount(<App />)
// App component rendered our mock child component!
cy.contains('Mock child component')
cy.get('@child').should('have.been.calledTwice')
cy.get('.mock-child').should('have.length', 2)
})
})
@@ -5,9 +5,9 @@
"private": true,
"scripts": {
"check-coverage": "check-coverage src/App.js src/calc.js src/Child.js",
"cy:open": "node ../../../../scripts/cypress open",
"cy:open": "node ../../../../scripts/cypress open-ct",
"only-covered": "only-covered src/App.js src/calc.js src/Child.js",
"test": "node ../../../../scripts/cypress run"
"test": "node ../../../../scripts/cypress run-ct"
},
"devDependencies": {
"@cypress/react": "file:../../dist",
@@ -4,10 +4,10 @@
"private": true,
"scripts": {
"check-coverage": "check-coverage src/App.js src/calc.js src/Child.js src/services.js src/RemotePizza.js cypress/fixtures/add.js",
"cy:open": "node ../../../../scripts/cypress open",
"cy:open": "node ../../../../scripts/cypress open-ct",
"only-covered": "only-covered src/App.js src/calc.js src/Child.js src/services.js src/RemotePizza.js cypress/fixtures/add.js",
"start": "react-scripts start",
"test": "node ../../../../scripts/cypress run"
"test": "node ../../../../scripts/cypress run-ct"
},
"devDependencies": {
"@cypress/react": "file:../../dist",
@@ -3,8 +3,6 @@
import React from 'react'
import App from './App'
import { mount } from '@cypress/react'
import * as calc from './calc'
import * as Child from './Child'
describe('App', () => {
it('renders learn react link', () => {
@@ -12,28 +10,6 @@ describe('App', () => {
cy.contains(/Learn React/)
})
it('controls the random number by stubbing named import', () => {
// we are stubbing "getRandomNumber" exported by "calc.js"
// and imported into "App.js" and called.
cy.stub(calc, 'getRandomNumber').returns(777)
mount(<App />)
cy.contains('.random', '777')
// getRandomNumber was also used by the Child component
// let's check that it was mocked too
cy.contains('.child', 'Real child component, random 777')
})
it('can mock the child component', () => {
// Child component we want to stub is the default export
cy.stub(Child, 'default')
.as('child')
.returns(<div className="mock-child">Mock Child component</div>)
mount(<App />)
cy.contains('.mock-child', 'Mock Child')
})
describe('loading .env variables', () => {
it('loads the REACT_APP_ variables from .env file', () => {
mount(<App />)
@@ -3,8 +3,11 @@ import React from 'react'
import App from './App'
import { mount } from '@cypress/react'
describe('static resources', () => {
// NOTE: This doesn't work for some reason, but the font and svg is loading properly
describe.skip('static resources', () => {
const findResource = (name) => {
console.log(window.document)
return window.performance
.getEntriesByType('resource')
.find((item) => item.name.endsWith(name))
@@ -1,16 +1,14 @@
const rollupPreprocessor = require('@bahmutov/cy-rollup')
// @ts-check
const path = require('path')
const { startDevServer } = require('@cypress/rollup-dev-server')
module.exports = (on, config) => {
on(
'file:preprocessor',
rollupPreprocessor({
configFile: 'rollup.config.js',
}),
)
on('dev-server:start', async (options) => {
return startDevServer({
options,
rollupConfig: path.resolve(__dirname, '..', '..', 'rollup.config.js'),
})
})
require('@cypress/code-coverage/task')(on, config)
// IMPORTANT to return the config object
// with the any changed environment variables
return config
}
+5 -3
View File
@@ -4,8 +4,8 @@
"private": true,
"scripts": {
"build": "rollup -c",
"cy:open": "node ../../../../scripts/cypress open",
"test": "node ../../../../scripts/cypress run"
"cy:open": "node ../../../../scripts/cypress open-ct",
"test": "node ../../../../scripts/cypress run-ct"
},
"devDependencies": {
"@babel/plugin-proposal-class-properties": "7.4.4",
@@ -13,10 +13,12 @@
"@babel/preset-react": "7.0.0",
"@babel/preset-typescript": "7.10.4",
"@cypress/react": "file:../../dist",
"@cypress/rollup-dev-server": "file:../../../rollup-dev-server/dist",
"@rollup/plugin-babel": "5.2.1",
"@rollup/plugin-commonjs": "15.1.0",
"@rollup/plugin-node-resolve": "9.0.0",
"@rollup/plugin-replace": "2.3.3",
"cypress-circleci-reporter": "0.2.0"
"cypress-circleci-reporter": "0.2.0",
"rollup": "2.40.0"
}
}
+2 -1
View File
@@ -15,9 +15,10 @@ export default [
plugins: [
nodeResolve({ extensions }),
// make sure that this is required to process @cypress/react code
commonjs(),
commonjs({ exclude: 'src/**' }),
replace({ 'process.env.NODE_ENV': JSON.stringify('development') }),
babel({
// ...babelOptions,
exclude: /node_modules/,
babelHelpers: 'inline',
extensions,
+1 -4
View File
@@ -5,10 +5,7 @@
"noEmit": true,
"target": "esnext",
"types": ["cypress"],
// ⚠️ required only for demo purposes, remove from code
"paths": {
"@cypress/react": ["../../lib/index.ts"]
}
"moduleResolution": "node",
},
"exclude": ["node_modules"],
"include": ["./src/**.ts*"]
+15
View File
@@ -721,6 +721,9 @@
"@cypress/react@file:../../dist":
version "0.0.0"
"@cypress/rollup-dev-server@file:../../../rollup-dev-server/dist":
version "0.0.0"
"@oozcitak/dom@1.15.8":
version "1.15.8"
resolved "https://registry.yarnpkg.com/@oozcitak/dom/-/dom-1.15.8.tgz#0c0c7bb54cfdaadc07fd637913e706101721d15d"
@@ -1031,6 +1034,11 @@ fs.realpath@^1.0.0:
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
fsevents@~2.3.1:
version "2.3.2"
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
function-bind@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
@@ -1292,6 +1300,13 @@ resolve@^1.17.0:
dependencies:
path-parse "^1.0.6"
rollup@2.40.0:
version "2.40.0"
resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.40.0.tgz#efc218eaede7ab590954df50f96195188999c304"
integrity sha512-WiOGAPbXoHu+TOz6hyYUxIksOwsY/21TRWoO593jgYt8mvYafYqQl+axaA8y1z2HFazNUUrsMSjahV2A6/2R9A==
optionalDependencies:
fsevents "~2.3.1"
semver@7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e"
@@ -1,11 +1,11 @@
// load Webpack file preprocessor that comes with this plugin
// https://github.com/bahmutov/cypress-react-unit-test#install
const preprocessor = require('@cypress/react/plugins/load-webpack')
const injectWebpackDevServer = require('@cypress/react/plugins/load-webpack')
module.exports = (on, config) => {
// point at the webpack config file at the root of the project
config.env.webpackFilename = 'webpack.config.js'
preprocessor(on, config)
injectWebpackDevServer(on, config, {
webpackFilename: 'webpack.config.js',
})
// IMPORTANT to return the config object
// with the any changed environment variables
+2 -2
View File
@@ -4,9 +4,9 @@
"private": true,
"scripts": {
"check-coverage": "check-coverage src/App.tsx",
"cy:open": "node ../../../../scripts/cypress open",
"cy:open": "node ../../../../scripts/cypress open-ct",
"only-covered": "only-covered src/App.tsx",
"test": "node ../../../../scripts/cypress run"
"test": "node ../../../../scripts/cypress run-ct"
},
"dependencies": {
"sass-loader": "10.0.2"
+2 -2
View File
@@ -4,9 +4,9 @@
"private": true,
"scripts": {
"check-coverage": "check-coverage src/App.js",
"cy:open": "node ../../../../scripts/cypress open",
"cy:open": "node ../../../../scripts/cypress open-ct",
"only-covered": "only-covered src/App.js",
"test": "node ../../../../scripts/cypress run"
"test": "node ../../../../scripts/cypress run-ct"
},
"devDependencies": {
"@cypress/react": "file:../../dist",
@@ -1,9 +1,9 @@
// load file preprocessor that comes with this plugin
// https://github.com/bahmutov/cypress-react-unit-test#install
const preprocessor = require('@cypress/react/plugins/react-scripts')
const injectReactScriptsDevServer = require('@cypress/react/plugins/react-scripts')
module.exports = (on, config) => {
preprocessor(on, config)
injectReactScriptsDevServer(on, config)
// IMPORTANT to return the config object
// with the any changed environment variables
+2 -2
View File
@@ -5,9 +5,9 @@
"scripts": {
"build:css": "tailwindcss build src/styles/tailwind.css -o src/styles/main.generated.css",
"precy:open": "npm run build:css",
"cy:open": "node ../../../../scripts/cypress open",
"cy:open": "node ../../../../scripts/cypress open-ct",
"pretest": "npm run build:css",
"test": "node ../../../../scripts/cypress run"
"test": "node ../../../../scripts/cypress run-ct"
},
"devDependencies": {
"@cypress/react": "file:../../dist",
@@ -1,10 +1,10 @@
// let's bundle spec files and the components they include using
// the same bundling settings as the project by loading .babelrc
// https://github.com/bahmutov/cypress-react-unit-test#install
const preprocessor = require('@cypress/react/plugins/babelrc')
const injectWebpackWithBabelDevServer = require('@cypress/react/plugins/babel')
module.exports = (on, config) => {
preprocessor(on, config)
injectWebpackWithBabelDevServer(on, config)
// IMPORTANT to return the config object
// with the any changed environment variables
@@ -3,8 +3,8 @@
"description": "Component testing for TypeScript projects using Babel config",
"private": true,
"scripts": {
"cy:open": "node ../../../../scripts/cypress open",
"test": "node ../../../../scripts/cypress run"
"cy:open": "node ../../../../scripts/cypress open-ct",
"test": "node ../../../../scripts/cypress run-ct"
},
"devDependencies": {
"@babel/core": "7.4.5",
@@ -1,10 +1,10 @@
// let's bundle spec files and the components they include using
// the same bundling settings as the project by loading .babelrc
// https://github.com/bahmutov/cypress-react-unit-test#install
const preprocessor = require('@cypress/react/plugins/babelrc')
const injectWebpackWithBabelDevServer = require('@cypress/react/plugins/babel')
module.exports = (on, config) => {
preprocessor(on, config)
injectWebpackWithBabelDevServer(on, config)
// IMPORTANT to return the config object
// with the any changed environment variables
+2 -2
View File
@@ -3,8 +3,8 @@
"description": "Component testing for projects using Babel config",
"private": true,
"scripts": {
"cy:open": "node ../../../../scripts/cypress open",
"test": "node ../../../../scripts/cypress run"
"cy:open": "node ../../../../scripts/cypress open-ct",
"test": "node ../../../../scripts/cypress run-ct"
},
"devDependencies": {
"@babel/core": "7.4.5",
@@ -7,11 +7,11 @@
"author": "Amith Raravi <amith.raravi@gmail.com>",
"scripts": {
"build": "react-scripts build",
"cy:open": "node ../../../../scripts/cypress open",
"cy:open": "node ../../../../scripts/cypress open-ct",
"docker:run": "docker run -it -v $PWD/../..:/e2e -w /e2e/examples/visual-sudoku cypress/included:4.5.0",
"eject": "react-scripts eject",
"start": "react-scripts start",
"test": "node ../../../../scripts/cypress run"
"test": "node ../../../../scripts/cypress run-ct"
},
"browserslist": {
"production": [
@@ -3,8 +3,8 @@
"description": "Visual diffing for component testing using Applittols",
"private": true,
"scripts": {
"cy:open": "node ../../../../scripts/cypress open",
"test": "node ../../../../scripts/cypress run"
"cy:open": "node ../../../../scripts/cypress open-ct",
"test": "node ../../../../scripts/cypress run-ct"
},
"devDependencies": {
"@applitools/eyes-cypress": "3.14.0",
@@ -17,7 +17,7 @@ describe('Chart', () => {
name: 'firefox',
},
],
// ignore "cy.eyes*" commands when using "cypress open"
// ignore "cy.eyes*" commands when using "cypress open-ct"
isDisabled: Cypress.config('isInteractive'),
})
@@ -3,8 +3,8 @@
"description": "Visual component testing using Happo.io",
"private": true,
"scripts": {
"cy:open": "node ../../../../scripts/cypress open",
"test": "node ../../../../scripts/cypress run",
"cy:open": "node ../../../../scripts/cypress open-ct",
"test": "node ../../../../scripts/cypress run-ct",
"test:happo": "../../node_modules/.bin/happo-cypress -- npm test"
},
"devDependencies": {
@@ -3,8 +3,8 @@
"description": "Visual diffing for component testing",
"private": true,
"scripts": {
"cy:open": "node ../../../../scripts/cypress open",
"test": "node ../../../../scripts/cypress run"
"cy:open": "node ../../../../scripts/cypress open-ct",
"test": "node ../../../../scripts/cypress run-ct"
},
"devDependencies": {
"@cypress/react": "file:../../dist",
@@ -6,7 +6,8 @@ import * as calc from './calc'
import * as ChildComponent from './ChildComponent'
describe('Mocking', () => {
it('named getRandomNumber imported in the child component', () => {
// NOTE: need babel commonjs plugin to mock named imports
it.skip('named getRandomNumber imported in the child component', () => {
cy.stub(calc, 'getRandomNumber')
.as('lucky')
.returns(777)
@@ -1,7 +1,8 @@
module.exports = (on, config) => {
// from the root of the project (folder with cypress.json file)
config.env.webpackFilename = 'webpack.config.js'
require('@cypress/react/plugins/load-webpack')(on, config)
require('@cypress/react/plugins/load-webpack')(on, config, {
// from the root of the project (folder with cypress.json file)
webpackFilename: 'webpack.config.js',
})
// IMPORTANT to return the config object
// with the any changed environment variables
+2 -2
View File
@@ -4,9 +4,9 @@
"private": true,
"scripts": {
"check-coverage": "check-coverage Test.js calc.js ParentComponent.js ChildComponent.js",
"cy:open": "node ../../../../scripts/cypress open",
"cy:open": "node ../../../../scripts/cypress open-ct",
"only-covered": "only-covered Test.js calc.js ParentComponent.js ChildComponent.js",
"test": "node ../../../../scripts/cypress run"
"test": "node ../../../../scripts/cypress run-ct"
},
"devDependencies": {
"@cypress/react": "file:../../dist",
@@ -0,0 +1,23 @@
module.exports = {
'presets': ['@babel/preset-env', '@babel/preset-react'],
'plugins': ['@babel/plugin-proposal-class-properties'],
// place plugins for Cypress tests into "test" environment
// so that production bundle is not instrumented
env: {
'test': {
'plugins': [
// during Cypress tests we want to instrument source code
// to get code coverage from tests
'babel-plugin-istanbul',
// we also want to export ES6 modules as objects
// to allow mocking named imports
[
'@babel/plugin-transform-modules-commonjs',
{
'loose': true,
},
],
],
},
},
}
@@ -1,36 +1,36 @@
// @ts-check
const webpackPreprocessor = require('@cypress/webpack-preprocessor')
const path = require('path')
const { startDevServer } = require('@cypress/webpack-dev-server')
const babelConfig = require('../../babel.config')
// Cypress Webpack preprocessor includes Babel env preset,
// but to transpile JSX code we need to add Babel React preset
module.exports = (on, config) => {
// @ts-ignore
const opts = webpackPreprocessor.defaultOptions
const babelLoader = opts.webpackOptions.module.rules[0].use[0]
// add React preset to be able to transpile JSX
babelLoader.options.presets.push(require.resolve('@babel/preset-react'))
// We can also push Babel istanbul plugin to instrument the code on the fly
// and get code coverage reports from component tests (optional)
if (!babelLoader.options.plugins) {
babelLoader.options.plugins = []
/** @type import("webpack").Configuration */
const webpackConfig = {
resolve: {
extensions: ['.js', '.ts', '.jsx', '.tsx'],
},
mode: 'development',
devtool: false,
output: {
publicPath: '/',
chunkFilename: '[name].bundle.js',
},
// TODO: update with valid configuration for your components
module: {
rules: [
{
test: /\.(js|jsx|mjs|ts|tsx)$/,
loader: 'babel-loader',
options: { ...babelConfig, cacheDirectory: path.resolve(__dirname, '.babel-cache') },
},
],
},
}
babelLoader.options.plugins.push(require.resolve('babel-plugin-istanbul'))
// in order to mock named imports, need to include a plugin
babelLoader.options.plugins.push([
require.resolve('@babel/plugin-transform-modules-commonjs'),
{
loose: true,
},
])
// add code coverage plugin
require('@cypress/code-coverage/task')(on, config)
on('file:preprocessor', webpackPreprocessor(opts))
process.env.BABEL_ENV = 'test' // this is required to load commonjs babel plugin
on('dev-server:start', (options) => startDevServer({ options, webpackConfig }))
// if adding code coverage, important to return updated config
return config
@@ -4,12 +4,20 @@
"private": true,
"scripts": {
"check-coverage": "check-coverage Test.js calc.js ParentComponent.js ChildComponent.js",
"cy:open": "node ../../../../scripts/cypress open",
"cy:open": "node ../../../../scripts/cypress open-ct",
"only-covered": "only-covered Test.js calc.js ParentComponent.js ChildComponent.js",
"test": "node ../../../../scripts/cypress run"
"test": "node ../../../../scripts/cypress run-ct"
},
"devDependencies": {
"@babel/core": "7.4.5",
"@babel/plugin-proposal-class-properties": "7.4.4",
"@babel/plugin-transform-modules-commonjs": "7.13.8",
"@babel/preset-env": "7.4.5",
"@babel/preset-react": "7.0.0",
"@cypress/react": "file:../../dist",
"@cypress/webpack-dev-server": "file:../../../webpack-dev-server",
"babel-loader": "8.2.2",
"babel-plugin-istanbul": "6.0.0",
"cypress-circleci-reporter": "0.2.0"
}
}
File diff suppressed because it is too large Load Diff
+8 -13
View File
@@ -19,7 +19,8 @@
"dependencies": {
"@cypress/webpack-preprocessor": "0.0.0-development",
"debug": "4.3.2",
"find-webpack": "2.2.1"
"find-webpack": "2.2.1",
"find-yarn-workspace-root": "2.0.0"
},
"devDependencies": {
"@babel/core": "7.4.5",
@@ -129,6 +130,11 @@
],
"unpkg": "dist/cypress-react.browser.js",
"module": "dist/cypress-react.esm-bundler.js",
"workspaces": {
"nohoist": [
"cypress-circleci-reporter"
]
},
"peerDependenciesMeta": {
"@babel/core": {
"optional": true
@@ -156,18 +162,7 @@
"access": "public"
},
"ciJobs": [
"npm-react",
"npm-react-axe",
"npm-react-next",
"npm-react-cra",
"npm-react-cra-folder",
"npm-react-rollup",
"npm-react-sass-ts",
"npm-react-snapshots",
"npm-react-tailwind",
"npm-react-percy",
"npm-react-webpack-file",
"npm-react-webpack-options"
"npm-react"
],
"standard": {
"globals": [
@@ -0,0 +1,51 @@
// @ts-check
// uses webpack to load your .babelrc file
const debug = require('debug')('@cypress/react')
const webpackConfigLoadsBabel = {
resolve: {
extensions: ['.js', '.ts', '.jsx', '.tsx'],
},
mode: 'development',
devtool: false,
output: {
publicPath: '/',
chunkFilename: '[name].bundle.js',
},
module: {
rules: [
{
test: /\.(js|jsx|mjs|ts|tsx)$/,
loader: 'babel-loader',
},
],
},
}
module.exports = (on, config) => {
debug('env object %o', config.env)
debug('initial environments %o', {
BABEL_ENV: process.env.BABEL_ENV,
NODE_ENV: process.env.NODE_ENV,
})
const nodeEnvironment = 'test'
if (!process.env.BABEL_ENV) {
debug('setting BABEL_ENV to %s', nodeEnvironment)
process.env.BABEL_ENV = nodeEnvironment
}
if (!process.env.NODE_ENV) {
debug('setting NODE_ENV to %s', nodeEnvironment)
process.env.NODE_ENV = nodeEnvironment
}
debug('environments %o', {
BABEL_ENV: process.env.BABEL_ENV,
NODE_ENV: process.env.NODE_ENV,
})
return webpackConfigLoadsBabel
}
+12 -1
View File
@@ -1 +1,12 @@
module.exports = require('../babelrc')
const getBabelWebpackConfig = require('./getBabelWebpackConfig')
const { startDevServer } = require('@cypress/webpack-dev-server')
module.exports = (on, config) => {
on('dev-server:start', async (options) => {
return startDevServer({ options, webpackConfig: getBabelWebpackConfig(on, config) })
})
config.env.reactDevtools = true
return config
}
@@ -1,96 +0,0 @@
// @ts-check
// uses webpack to load your .babelrc file
const debug = require('debug')('@cypress/react')
const webpackPreprocessor = require('@cypress/webpack-preprocessor')
const { addImageRedirect } = require('../utils/add-image-redirect')
const wpPreprocessorOptions = {
...webpackPreprocessor.defaultOptions,
}
wpPreprocessorOptions.webpackOptions.resolve = {
extensions: ['.js', '.ts', '.jsx', '.tsx', '.json'],
}
wpPreprocessorOptions.webpackOptions.module.rules[0].test = /\.(jsx|tsx|js|ts)?$/
// note: modifies the input object
function enableBabelrc (webpackOptions) {
if (!Array.isArray(webpackOptions.module.rules)) {
debug('could not find webpack module rules %o', webpackOptions.module)
return
}
const jsCodeRule = webpackOptions.module.rules[0]
debug('js code rule %o', jsCodeRule)
if (!jsCodeRule) {
debug('could not get first rule %o', webpackOptions.module)
return
}
const jsCodeRuleUses = jsCodeRule.use
if (!Array.isArray(jsCodeRuleUses)) {
debug('js code rule use is not an array %o', jsCodeRuleUses)
return
}
const babelLoaderOptions = jsCodeRuleUses[0].options
debug('Babel options %o', babelLoaderOptions)
if (!babelLoaderOptions) {
debug('Hmm, no babel loader options %o', jsCodeRuleUses)
return
}
// by deleting our default presets list
// we allow Babel loader to load the presets and plugins
// from the project's .babelrc file
delete babelLoaderOptions.presets
delete babelLoaderOptions.plugins
debug('babel loader options %o', babelLoaderOptions)
}
module.exports = (config) => {
debug('env object %o', config.env)
debug('initial environments %o', {
BABEL_ENV: process.env.BABEL_ENV,
NODE_ENV: process.env.NODE_ENV,
})
const nodeEnvironment = 'test'
if (!process.env.BABEL_ENV) {
debug('setting BABEL_ENV to %s', nodeEnvironment)
process.env.BABEL_ENV = nodeEnvironment
}
if (!process.env.NODE_ENV) {
debug('setting NODE_ENV to %s', nodeEnvironment)
process.env.NODE_ENV = nodeEnvironment
}
debug('environments %o', {
BABEL_ENV: process.env.BABEL_ENV,
NODE_ENV: process.env.NODE_ENV,
})
const coverageIsDisabled =
config && config.env && config.env.coverage === false
debug('coverage is disabled? %o', { coverageIsDisabled })
enableBabelrc(wpPreprocessorOptions.webpackOptions)
debug('webpack options %o', wpPreprocessorOptions.webpackOptions)
addImageRedirect(wpPreprocessorOptions.webpackOptions)
return webpackPreprocessor(wpPreprocessorOptions)
}
-9
View File
@@ -1,9 +0,0 @@
const filePreprocessor = require('./file-preprocessor')
module.exports = (on, config) => {
on('file:preprocessor', filePreprocessor(config))
// IMPORTANT to return the config object
// with the any changed environment variables
return config
}
@@ -1,56 +0,0 @@
// @ts-check
const debug = require('debug')('@cypress/react')
const findWebpack = require('find-webpack')
const webpackPreprocessor = require('@cypress/webpack-preprocessor')
const { getTranspileFolders } = require('../utils/get-transpile-folders')
const { addImageRedirect } = require('../utils/add-image-redirect')
const getWebpackPreprocessorOptions = (opts) => {
debug('top level opts %o', opts)
const webpackOptions = findWebpack.getWebpackOptions()
if (!webpackOptions) {
console.error('⚠️ Could not find Webpack options, using defaults')
return webpackPreprocessor.defaultOptions
}
debug('webpack options: %o', webpackOptions)
findWebpack.cleanForCypress(opts, webpackOptions)
debug('cleaned webpack options: %o', webpackOptions)
addImageRedirect(webpackOptions)
const options = {
webpackOptions,
watchOptions: {},
}
return options
}
module.exports = (config) => {
debug('env object %o', config.env)
const coverageIsDisabled =
config && config.env && config.env.coverage === false
debug('coverage is disabled? %o', { coverageIsDisabled })
debug('component test folder: %s', config.componentFolder)
debug('fixtures folder', config.fixturesFolder)
debug('integration test folder: %s', config.integrationFolder)
const opts = {
reactScripts: true,
addFolderToTranspile: getTranspileFolders(config),
coverage: !coverageIsDisabled,
// insert Babel plugin to mock named imports
looseModules: true,
}
const preprocessorOptions = getWebpackPreprocessorOptions(opts)
debug('final webpack options %o', preprocessorOptions.webpackOptions)
return webpackPreprocessor(preprocessorOptions)
}
-9
View File
@@ -1,9 +0,0 @@
const filePreprocessor = require('./file-preprocessor')
module.exports = (on, config) => {
on('file:preprocessor', filePreprocessor(config))
// IMPORTANT to return the config object
// with the any changed environment variables
return config
}
+25 -49
View File
@@ -1,59 +1,35 @@
// @ts-check
const path = require('path')
const debug = require('debug')('@cypress/react')
const webpackPreprocessor = require('@cypress/webpack-preprocessor')
const findWebpack = require('find-webpack')
const { getTranspileFolders } = require('../utils/get-transpile-folders')
const { addImageRedirect } = require('../utils/add-image-redirect')
const { startDevServer } = require('@cypress/webpack-dev-server')
const tryLoadWebpackConfig = require('../utils/tryLoadWebpackConfig')
module.exports = (on, config) => {
const webpackFilename = config.env && config.env.webpackFilename
/** @type {(config: Cypress.PluginConfigOptions, path: string) => string} */
function normalizeWebpackPath (config, webpackConfigPath) {
return path.isAbsolute(webpackConfigPath)
? webpackConfigPath
: path.resolve(config.projectRoot, webpackConfigPath)
}
if (!webpackFilename) {
throw new Error(
'Could not find "webpackFilename" option in Cypress env variables',
)
/**
* Injects dev-server based on the webpack config file.
*
* **Important:** `webpackFilename` path is relative to the project root (cypress.json location)
* @type {(on: Cypress.PluginEvents, config: Cypress.PluginConfigOptions, options: { webpackFilename: string }) => Cypress.PluginConfigOptions}
*/
function injectWebpackDevServer (on, config, { webpackFilename }) {
const webpackConfig = tryLoadWebpackConfig(normalizeWebpackPath(config, webpackFilename))
if (!webpackConfig) {
throw new Error(`Can not load webpack config from path ${webpackFilename}.`)
}
debug('got webpack config filename %s', webpackFilename)
const resolved = path.resolve(webpackFilename)
on('dev-server:start', async (options) => {
return startDevServer({ options, webpackConfig })
})
debug('resolved webpack at %s', resolved)
config.env.reactDevtools = true
const webpackOptions = findWebpack.tryLoadingWebpackConfig(resolved)
if (!webpackOptions) {
throw new Error(`Could not load webpack config from ${resolved}`)
}
debug('webpack options: %o', webpackOptions)
const coverageIsDisabled =
config && config.env && config.env.coverage === false
debug('coverage is disabled? %o', { coverageIsDisabled })
const opts = {
reactScripts: true,
addFolderToTranspile: getTranspileFolders(config),
coverage: !coverageIsDisabled,
// insert Babel plugin to mock named imports
looseModules: true,
}
findWebpack.cleanForCypress(opts, webpackOptions)
debug('cleaned webpack options: %o', webpackOptions)
addImageRedirect(webpackOptions)
const options = {
webpackOptions,
watchOptions: {},
}
on('file:preprocessor', webpackPreprocessor(options))
// IMPORTANT to return the config object
// with the any changed environment variables
return config
}
module.exports = injectWebpackDevServer
@@ -1,74 +0,0 @@
// @ts-check
const debug = require('debug')('@cypress/react')
const loadConfig = require('next/dist/next-server/server/config').default
const getNextJsBaseWebpackConfig = require('next/dist/build/webpack-config')
.default
const webpackPreprocessor = require('@cypress/webpack-preprocessor')
const findWebpack = require('find-webpack')
async function getNextWebpackConfig (config) {
const coverageIsDisabled =
config && config.env && config.env.coverage === false
debug('coverage is disabled? %o', { coverageIsDisabled })
const nextConfig = await loadConfig('development', config.projectRoot)
const nextWebpackConfig = await getNextJsBaseWebpackConfig(
config.projectRoot,
{
buildId: `@cypress/react-${Math.random().toString()}`,
config: nextConfig,
dev: false,
isServer: false,
pagesDir: config.projectRoot,
entrypoints: {},
rewrites: [],
},
)
debug('resolved next.js webpack options: %o', nextWebpackConfig)
// Using mode over `dev` true to get rid of next's react-refresh-plugin wrapping
// We need do not need all the HMR and webpack dev middlewares as well
nextWebpackConfig.mode = 'development'
findWebpack.cleanForCypress(
{
coverage: !coverageIsDisabled,
// insert Babel plugin to mock named imports
// disable because causes an error with double definitions
// https://github.com/bahmutov/@cypress/react/issues/439
looseModules: false,
},
nextWebpackConfig,
)
debug('final webpack options %o', nextWebpackConfig)
return nextWebpackConfig
}
let webpackConfigCache = null
/** Resolving next.js webpack and all config with plugin takes long, so cache the webpack configuration */
async function getCachedWebpackPreprocessor (config) {
// ⛔️ ⛔️ Comment this `if` for debugging
if (webpackConfigCache !== null) {
return webpackConfigCache
}
const webpackOptions = await getNextWebpackConfig(config)
webpackConfigCache = webpackPreprocessor({ webpackOptions })
debug('created and cached webpack preprocessor based on next.config.js')
return webpackConfigCache
}
module.exports = (config) => {
return async (fileEvent) => {
const preprocessor = await getCachedWebpackPreprocessor(config)
return preprocessor(fileEvent)
}
}
@@ -0,0 +1,39 @@
// @ts-check
const debug = require('debug')('@cypress/react')
const loadConfig = require('next/dist/next-server/server/config').default
const getNextJsBaseWebpackConfig = require('next/dist/build/webpack-config').default
async function getNextWebpackConfig (config) {
const nextConfig = await loadConfig('development', config.projectRoot)
const nextWebpackConfig = await getNextJsBaseWebpackConfig(
config.projectRoot,
{
buildId: `@cypress/react-${Math.random().toString()}`,
config: nextConfig,
dev: true,
isServer: false,
pagesDir: config.projectRoot,
entrypoints: {},
rewrites: [],
},
)
debug('resolved next.js webpack config %o', nextWebpackConfig)
return nextWebpackConfig
}
let webpackConfigCache = null
/** Resolving next.js webpack and all config with plugin takes long, so cache the webpack configuration */
module.exports = async function findNextWebpackConfig (config) {
// ⛔️ ⛔️ Comment this `if` for debugging
if (webpackConfigCache !== null) {
return webpackConfigCache
}
webpackConfigCache = await getNextWebpackConfig(config)
debug('created and cached webpack preprocessor based on next.config.js', webpackConfigCache)
return webpackConfigCache
}
+7 -4
View File
@@ -1,9 +1,12 @@
const filePreprocessor = require('./file-preprocessor')
const { startDevServer } = require('@cypress/webpack-dev-server')
const findNextWebpackConfig = require('./findNextWebpackConfig')
module.exports = (on, config) => {
on('file:preprocessor', filePreprocessor(config))
on('dev-server:start', async (options) => {
return startDevServer({ options, webpackConfig: await findNextWebpackConfig(config) })
})
config.env.reactDevtools = true
// IMPORTANT to return the config object
// with the any changed environment variables
return config
}
@@ -0,0 +1,104 @@
// @ts-check
const debug = require('debug')('@cypress/react')
const tryLoadWebpackConfig = require('../utils/tryLoadWebpackConfig')
const { getTranspileFolders } = require('../utils/get-transpile-folders')
const { addFolderToBabelLoaderTranspileInPlace } = require('../utils/babel-helpers')
/**
* Finds the ModuleScopePlugin plugin and adds given folder
* to that list. This allows react-scripts to import folders
* outside of the default "/src" folder.
* WARNING modifies the input webpack options argument.
* @see https://github.com/bahmutov/cypress-react-unit-test/issues/289
* @param {string} folderName Folder to add, should be absolute
*/
function allowModuleSourceInPlace (folderName, webpackOptions) {
if (!folderName) {
return
}
if (!webpackOptions.resolve) {
return
}
if (!Array.isArray(webpackOptions.resolve.plugins)) {
return
}
const moduleSourcePlugin = webpackOptions.resolve.plugins.find((plugin) => {
return Array.isArray(plugin.appSrcs)
})
if (!moduleSourcePlugin) {
debug('cannot find module source plugin')
return
}
debug('found module source plugin %o', moduleSourcePlugin)
if (!moduleSourcePlugin.appSrcs.includes(folderName)) {
moduleSourcePlugin.appSrcs.push(folderName)
debug('added folder %s to allowed sources', folderName)
}
}
const addCypressToWebpackEslintRulesInPlace = (webpackOptions) => {
const globalsToAdd = ['cy', 'Cypress', 'before', 'after', 'context']
if (webpackOptions.module && Array.isArray(webpackOptions.module.rules)) {
const modulePre = webpackOptions.module.rules.find(
(rule) => rule.enforce === 'pre',
)
if (modulePre && Array.isArray(modulePre.use)) {
debug('found Pre block %o', modulePre)
const useEslintLoader = modulePre.use.find(
(use) => use.loader && use.loader.includes('eslint-loader'),
)
if (useEslintLoader) {
debug('found useEslintLoader %o', useEslintLoader)
if (useEslintLoader.options) {
if (Array.isArray(useEslintLoader.options.globals)) {
debug(
'adding cy to existing globals %o',
useEslintLoader.options.globals,
)
useEslintLoader.options.globals.push(...globalsToAdd)
} else {
debug('setting new list of globals with cy and Cypress')
useEslintLoader.options.globals = globalsToAdd
}
debug('updated globals %o', useEslintLoader.options.globals)
} else {
debug('eslint loader does not have options ⚠️')
}
}
}
}
}
module.exports = function findReactScriptsWebpackConfig (config) {
const webpackConfig = tryLoadWebpackConfig('react-scripts/config/webpack.config')
if (!webpackConfig) {
throw new Error('⚠️ Could not find Webpack options for react-scripts. Make sure that you have react-scripts module available.')
}
// because for react-scripts user doesn't have direct access to webpack webpackConfig
// we must implicitly inject everything required to run tests
addCypressToWebpackEslintRulesInPlace(webpackConfig)
getTranspileFolders(config).forEach((cypressFolder) => {
allowModuleSourceInPlace(cypressFolder, webpackConfig)
addFolderToBabelLoaderTranspileInPlace(cypressFolder, webpackConfig)
})
debug('resolved webpack config: %o', webpackConfig)
return webpackConfig
}
+7 -2
View File
@@ -1,7 +1,12 @@
const filePreprocessor = require('../cra-v3/file-preprocessor')
const { startDevServer } = require('@cypress/webpack-dev-server')
const findReactScriptsWebpackConfig = require('./findReactScriptsWebpackConfig')
module.exports = (on, config) => {
on('file:preprocessor', filePreprocessor(config))
on('dev-server:start', async (options) => {
return startDevServer({ options, webpackConfig: findReactScriptsWebpackConfig(config) })
})
config.env.reactDevtools = true
// IMPORTANT to return the config object
// with the any changed environment variables
+224
View File
@@ -0,0 +1,224 @@
// @ts-check
const debug = require('debug')('find-webpack')
const path = require('path')
/**
* Returns true if the provided loader path includes "babel-loader".
* Uses current OS path separator to split the loader path correctly.
*/
const isBabelLoader = (loaderPath) => {
if (!loaderPath) {
return false
}
const loaderPathParts = loaderPath.split(path.sep)
return loaderPathParts.some((pathPart) => pathPart === 'babel-loader')
}
const findBabelRule = (webpackOptions) => {
if (!webpackOptions) {
return
}
if (!webpackOptions.module) {
return
}
debug('webpackOptions.module %o', webpackOptions.module)
if (!Array.isArray(webpackOptions.module.rules)) {
return
}
const oneOfRule = webpackOptions.module.rules.find((rule) => {
return Array.isArray(rule.oneOf)
})
if (!oneOfRule) {
debug('could not find oneOfRule')
return
}
debug('looking through oneOf rules')
debug('oneOfRule.oneOf %o', oneOfRule.oneOf)
oneOfRule.oneOf.forEach((rule) => debug('rule %o', rule))
const babelRule = oneOfRule.oneOf.find(
(rule) => rule.loader && isBabelLoader(rule.loader),
)
return babelRule
}
// see https://github.com/bahmutov/find-webpack/issues/7
const findBabelLoaderRule = (webpackOptions) => {
debug('looking for babel-loader rule')
if (!webpackOptions) {
return
}
if (!webpackOptions.module) {
return
}
debug('webpackOptions.module %o', webpackOptions.module)
if (!Array.isArray(webpackOptions.module.rules)) {
return
}
debug('webpack module rules')
webpackOptions.module.rules.forEach((rule) => {
debug('rule %o', rule)
})
const babelRule = webpackOptions.module.rules.find(
(rule) => rule.loader === 'babel-loader',
)
if (!babelRule) {
debug('could not find babel rule')
return
}
debug('found Babel rule that applies to %s', babelRule.test.toString())
return babelRule
}
const findBabelLoaderUseRule = (webpackOptions) => {
debug('looking for babel-loader rule with use property')
if (!webpackOptions) {
return
}
if (!webpackOptions.module) {
return
}
debug('webpackOptions.module %o', webpackOptions.module)
if (!Array.isArray(webpackOptions.module.rules)) {
return
}
debug('webpack module rules')
webpackOptions.module.rules.forEach((rule) => {
debug('rule %o', rule)
})
const isBabelLoader = (rule) => rule.use && rule.use.loader === 'babel-loader'
const isNextBabelLoader = (rule) => {
return rule.use && rule.use.loader === 'next-babel-loader'
}
const babelRule = webpackOptions.module.rules.find(
(rule) => isBabelLoader(rule) || isNextBabelLoader(rule),
)
if (!babelRule) {
debug('could not find babel rule')
return
}
debug('found Babel use rule that applies to %s', babelRule.test.toString())
return babelRule.use
}
const findBabelRuleWrap = (webpackOptions) => {
let babelRule = findBabelRule(webpackOptions)
if (!babelRule) {
debug('could not find Babel rule using oneOf')
babelRule = findBabelLoaderRule(webpackOptions)
}
if (!babelRule) {
debug('could not find Babel rule directly')
babelRule = findBabelLoaderUseRule(webpackOptions)
}
if (!babelRule) {
debug('could not find Babel rule')
return
}
return babelRule
}
/**
* Searches through the given Webpack config file to find Babel
* loader and its options, then returns the plugins array reference.
* If not found, returns undefined.
* @returns {Array|undefined}
*/
const findBabelPlugins = (webpackOptions) => {
const babelRule = findBabelRuleWrap(webpackOptions)
if (!babelRule) {
debug('could not find Babel rule')
return
}
debug('babel rule %o', babelRule)
if (!babelRule.options) {
debug('babel rule does not have options, inserting')
babelRule.options = {}
}
if (!Array.isArray(babelRule.options.plugins)) {
debug('babel rule options does not have plugins, inserting')
babelRule.options.plugins = []
}
return babelRule.options.plugins
}
const addFolderToBabelLoaderTranspileInPlace = (addFolderToTranspile, webpackOptions) => {
if (!addFolderToTranspile) {
debug('no extra folder to transpile using Babel')
return
}
debug(
'trying to transpile additional folder %s using Babel',
addFolderToTranspile,
)
const babelRule = findBabelRuleWrap(webpackOptions)
if (!babelRule) {
debug('could not find Babel rule')
return
}
debug('babel rule %o', babelRule)
if (!babelRule.include) {
debug('could not find Babel include condition')
return
}
if (typeof babelRule.include === 'string') {
babelRule.include = [babelRule.include]
}
if (babelRule.include.includes(addFolderToTranspile)) {
// do not double include the same folder
debug('babel includes rule for folder %s', addFolderToTranspile)
return
}
babelRule.include.push(addFolderToTranspile)
debug('added folder %s to babel rules', addFolderToTranspile)
}
module.exports = { findBabelRuleWrap, addFolderToBabelLoaderTranspileInPlace, findBabelPlugins }
@@ -0,0 +1,40 @@
// @ts-check
const debug = require('debug')('@cypress/react')
/** @type {(configPath: string) => null | import('webpack').Configuration } */
module.exports = function tryLoadWebpackConfig (webpackConfigPath) {
debug('trying to load webpack config from %s', webpackConfigPath)
// Do this as the first thing so that any code reading it knows the right env.
const envName = 'test'
// @ts-expect-error override env is possible
process.env.NODE_ENV = envName
process.env.BABEL_ENV = envName
try {
let webpackOptions = require(webpackConfigPath)
if (webpackOptions.default) {
// we probably loaded TS file
debug('loaded webpack options has .default - taking that as the config')
webpackOptions = webpackOptions.default
}
if (typeof webpackOptions === 'function') {
debug('calling webpack function with environment "%s"', envName)
webpackOptions = webpackOptions('development')
}
debug('webpack options: %o', webpackOptions)
return webpackOptions
} catch (err) {
debug('could not load react-scripts webpack')
debug('error %s', err.message)
debug(err)
console.error(err)
return null
}
}
+11 -9
View File
@@ -12,7 +12,7 @@ const REACT_PROJECTS_FOR_CI = [
'/examples/react-scripts-folder',
'/examples/using-babel-typescript',
'/examples/webpack-options',
'/examples/rollup',
// '/examples/rollup',
'/examples/sass-and-ts',
]
@@ -20,8 +20,10 @@ const runTests = async (dir) => {
try {
chdir(dir)
console.log(`Running yarn install in project ${dir}`)
await execa('yarn', ['install', '--frozen-lockfile'], { stdout: 'inherit' })
if (dir !== __dirname) {
console.log(`Running yarn install in project ${dir}`)
await execa('yarn', ['install', '--frozen-lockfile'], { stdout: 'inherit' })
}
console.log(`Running yarn test in project ${dir}`)
await execa('yarn', [
@@ -32,14 +34,14 @@ const runTests = async (dir) => {
`resultsDir=${testResultsDestination}`,
], { stdout: 'inherit' })
} catch (e) {
if (!e.stdout) {
// for unexpected errors, just log the entire thing.
console.error(e)
} else {
if (e.stdout) {
console.error(e.stdout)
console.error(`Exiting with exit code ${e.exitCode}`)
process.exit(e.exitCode)
}
const exitCode = e.exitCode ? e.exitCode : 1
console.error(`Tests failed with exit code ${exitCode}`)
process.exit(exitCode)
}
}
+6 -21
View File
@@ -1,19 +1,11 @@
export function setupHooks (rootId: string) {
// @ts-ignore
const isComponentSpec = () => true
export function setupHooks (unmount: (opts: { log: boolean }) => void) {
// When running component specs, we cannot allow "cy.visit"
// because it will wipe out our preparation work, and does not make much sense
// thus we overwrite "cy.visit" to throw an error
Cypress.Commands.overwrite('visit', (visit, ...args: any[]) => {
if (isComponentSpec()) {
throw new Error(
'cy.visit from a component spec is not allowed',
)
} else {
// allow regular visit to proceed
return visit(...args)
}
Cypress.Commands.overwrite('visit', () => {
throw new Error(
'cy.visit from a component spec is not allowed',
)
})
/**
@@ -22,10 +14,6 @@ export function setupHooks (rootId: string) {
*
*/
function cleanupStyles () {
if (!isComponentSpec()) {
return
}
const styles = document.body.querySelectorAll('style')
styles.forEach((styleElement) => {
@@ -44,10 +32,7 @@ export function setupHooks (rootId: string) {
}
beforeEach(() => {
if (!isComponentSpec()) {
return
}
unmount({ log: false })
cleanupStyles()
})
}
+6 -3
View File
@@ -6,8 +6,6 @@ import { setupHooks } from './hooks'
const ROOT_ID = '__cy_root'
setupHooks(ROOT_ID)
/**
* Inject custom style text or CSS file or 3rd party style resources
*/
@@ -50,7 +48,7 @@ export const mount = (jsx: React.ReactNode, options: MountOptions = {}) => {
// @ts-ignore
let logInstance: Cypress.Log
return unmount({ log: false })
return cy
.then(() => {
if (options.log !== false) {
logInstance = Cypress.log({
@@ -318,3 +316,8 @@ export declare namespace Cypress {
): Chainable<any>
}
}
// it is required to unmount component in beforeEach hook in order to provide a clean state inside test
// because `mount` can be called after some preparation that can side effect unmount
// @see npm/react/cypress/component/advanced/set-timeout-example/loading-indicator-spec.js
setupHooks(unmount)
+1 -1
View File
@@ -15,7 +15,7 @@ interface Options {
export interface StartDevServer {
/* this is the Cypress options object */
options: Options
rollupConfig?: RollupOptions // TODO: user's rollup configuration.
rollupConfig?: RollupOptions | string
}
export interface ResolvedDevServerConfig {
+12 -2
View File
@@ -5,6 +5,10 @@ import { resolve } from 'path'
import NollupDevMiddleware from 'nollup/lib/dev-middleware'
import express from 'express'
import { RollupOptions, Plugin } from 'rollup'
import loadConfigFile from 'rollup/dist/loadConfigFile'
import makeDebug from 'debug'
const debug = makeDebug('cypress:rollup-dev-server')
/**
* Inject HMR runtime into each bundle, since Nollup
@@ -37,12 +41,18 @@ interface NollupDevServer {
}
export async function start (devServerOptions: StartDevServer): Promise<NollupDevServer> {
const rollupConfigObj = typeof devServerOptions.rollupConfig === 'string'
? await loadConfigFile(devServerOptions.rollupConfig).then((configResult) => configResult.options)
: devServerOptions.rollupConfig
debug('Resolved rollup config options', rollupConfigObj)
const config = devServerOptions.options.specs
.map<RollupOptions>((spec) => {
return {
...devServerOptions.rollupConfig,
...rollupConfigObj,
input: spec.absolute,
plugins: (devServerOptions.rollupConfig.plugins || []).concat(
plugins: (rollupConfigObj.plugins || []).concat(
injectHmrPlugin(),
),
}
+2 -1
View File
@@ -231,7 +231,8 @@
"**/@ffmpeg-installer",
"**/@ffmpeg-installer/**",
"**/webpack-preprocessor/babel-loader",
"**/webpack-batteries-included-preprocessor/ts-loader"
"**/webpack-batteries-included-preprocessor/ts-loader",
"**/cypress-circleci-reporter"
]
},
"lint-staged": {
+7
View File
@@ -16555,6 +16555,13 @@ find-yarn-workspace-root@1.2.1, find-yarn-workspace-root@^1.2.1:
fs-extra "^4.0.3"
micromatch "^3.1.4"
find-yarn-workspace-root@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/find-yarn-workspace-root/-/find-yarn-workspace-root-2.0.0.tgz#f47fb8d239c900eb78179aa81b66673eac88f7bd"
integrity sha512-1IMnbjt4KzsQfnhnzNd8wUEgXZ44IzZaZmnLYx7D5FZlaHt2gW20Cri8Q+E/t5tIj4+epTBub+2Zxu/vNILzqQ==
dependencies:
micromatch "^4.0.2"
find@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/find/-/find-0.3.0.tgz#4082e8fc8d8320f1a382b5e4f521b9bc50775cb8"