Skip to content
This repository was archived by the owner on Mar 5, 2022. It is now read-only.

Commit 400f85d

Browse files
authored
feat: mocking ES6 imports when using Babelrc (#278)
During test runs, there is a Babel plugin that transforms ES6 imports into plain objects that can be stubbed using [cy.stub](https://on.cypress.io/stub). In essence ```js // component imports named ES6 import from "calc.js import { getRandomNumber } from './calc' const Component = () => { // then calls it const n = getRandomNumber() return <div className="random">{n}</div> } ``` The test can mock that import before mounting the component ```js import Component from './Component.jsx' import * as calc from './calc' describe('Component', () => { it('mocks call from the component', () => { cy.stub(calc, 'getRandomNumber') .as('lucky') .returns(777) mount(<Component />) }) }) ```
1 parent d12ddcd commit 400f85d

File tree

10 files changed

+103
-32
lines changed

10 files changed

+103
-32
lines changed

docs/recipes.md

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ See example in [bahmutov/Jscrambler-Webpack-React](https://github.com/bahmutov/J
4949

5050
## Your `.babelrc` file
5151

52-
If you are using Babel without Webpack to transpile, you can use the plugin that tells Babel loader to use your configuration file.
52+
If you are using Babel without Webpack to transpile, you can use the plugin that tells Babel loader to use your `.babelrc` configuration file.
5353

5454
```js
5555
// cypress/plugins/index.js
@@ -62,24 +62,46 @@ module.exports = (on, config) => {
6262
}
6363
```
6464

65-
**Bonus:** in order to enable code instrumentation, add the `babel-plugin-istanbul` (included in this plugin) to your `.babelrc` setup. You can place it under `test` environment to avoid instrumenting production code. Example `.babelrc` config file that you can execute with `BABEL_ENV=test npx cypress open`
65+
### Add Babel plugins
66+
67+
If you want to use code instrumentation, add the [babel-plugin-istanbul](https://github.com/istanbuljs/babel-plugin-istanbul) to your `.babelrc` setup. You do not even need to install it separately, as it is already included in `cypress-react-unit-test` as a dependency.
68+
69+
If you want to use ES6 import mocking, add the [@babel/plugin-transform-modules-commonjs](https://github.com/babel/babel/tree/master/packages/babel-plugin-transform-modules-commonjs) to the list of plugins. This module is also included in `cypress-react-unit-test` as a dependency.
70+
71+
```json
72+
{
73+
"presets": ["@babel/preset-env", "@babel/preset-react"],
74+
"plugins": [
75+
"babel-plugin-istanbul",
76+
[
77+
"@babel/plugin-transform-modules-commonjs",
78+
{
79+
"loose": true
80+
}
81+
]
82+
]
83+
}
84+
```
85+
86+
When loading your `.babelrc` settings, `cypress-react-unit-test` sets `BABEL_ENV` and `NODE_ENV` to `test` if they are not set already. Thus you can move the above plugins into the `test` environment to exclude them from being used in production bundle.
6687

6788
```json
6889
{
69-
"presets": [
70-
"@babel/preset-env",
71-
"@babel/preset-react",
72-
{
73-
"plugins": ["@babel/plugin-proposal-class-properties"]
74-
},
75-
"@emotion/babel-preset-css-prop"
76-
],
90+
"presets": ["@babel/preset-env", "@babel/preset-react"],
7791
"env": {
7892
"test": {
79-
"plugins": ["babel-plugin-istanbul"]
93+
"plugins": [
94+
"babel-plugin-istanbul",
95+
[
96+
"@babel/plugin-transform-modules-commonjs",
97+
{
98+
"loose": true
99+
}
100+
]
101+
]
80102
}
81103
}
82104
}
83105
```
84106

85-
See [bahmutov/react-loading-skeleton](https://github.com/bahmutov/react-loading-skeleton) example
107+
See [examples/using-babel](examples/using-babel) folder for full example.

examples/using-babel/.babelrc

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,19 @@
11
{
2-
"sourceType": "unambiguous",
3-
"presets": [
4-
"@babel/preset-env",
5-
"@babel/preset-react",
6-
// {
7-
// "plugins": ["@babel/plugin-proposal-class-properties"]
8-
// },
9-
"@emotion/babel-preset-css-prop"
10-
],
2+
"presets": ["@babel/preset-env", "@babel/preset-react"],
113
"plugins": ["@babel/plugin-proposal-class-properties"],
124
"env": {
13-
// TODO switch this to "development" name
14-
"development": {
5+
// place plugins for Cypress tests into "test" environment
6+
// so that production bundle is not instrumented
7+
"test": {
158
"plugins": [
169
// during Cypress tests we want to instrument source code
1710
// to get code coverage from tests
18-
// "babel-plugin-istanbul"
11+
"babel-plugin-istanbul",
1912
// we also want to export ES6 modules as objects
2013
// to allow mocking named imports
2114
[
2215
"@babel/plugin-transform-modules-commonjs",
2316
{
24-
"allowCommonJSExports": true,
2517
"loose": true
2618
}
2719
]

examples/using-babel/README.md

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ npm test
1414

1515
## Specs
1616

17-
See specs [src/Post.spec.js](src/Post.spec.js) and [src/Skeleton.spec.js](src/Skeleton.spec.js). The specs are bundled using [.babelrc](.babelrc) settings via [cypress/plugins/index.js](cypress/plugins/index.js) file that includes file preprocessor
17+
See spec files [src/\*.spec.js](src). The specs are bundled using [.babelrc](.babelrc) settings via [cypress/plugins/index.js](cypress/plugins/index.js) file that includes file preprocessor
1818

1919
```js
2020
// let's bundle spec files and the components they include using
@@ -27,3 +27,32 @@ module.exports = (on, config) => {
2727
return config
2828
}
2929
```
30+
31+
## Mocking
32+
33+
During test runs, there is a Babel plugin that transforms ES6 imports into plain objects that can be stubbed using [cy.stub](https://on.cypress.io/stub). In essence
34+
35+
```js
36+
// component imports named ES6 import from "calc.js
37+
import { getRandomNumber } from './calc'
38+
const Component = () => {
39+
// then calls it
40+
const n = getRandomNumber()
41+
return <div className="random">{n}</div>
42+
}
43+
```
44+
45+
The test can mock that import before mounting the component
46+
47+
```js
48+
import Component from './Component.jsx'
49+
import * as calc from './calc'
50+
describe('Component', () => {
51+
it('mocks call from the component', () => {
52+
cy.stub(calc, 'getRandomNumber')
53+
.as('lucky')
54+
.returns(777)
55+
mount(<Component />)
56+
})
57+
})
58+
```

examples/using-babel/src/Component.jsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22
import React from 'react'
33
import { getRandomNumber } from './calc'
44

5+
/**
6+
* Example React component that imports `getRandomNumber`
7+
* function from another file and uses it to show a random
8+
* number in the UI.
9+
*/
510
const Component = () => {
611
const n = getRandomNumber()
712
return <div className="random">{n}</div>

examples/using-babel/src/Component.spec.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ import { mount } from 'cypress-react-unit-test'
44
import Component from './Component.jsx'
55
import * as calc from './calc'
66

7+
// import the component and the file it imports
8+
// stub the method on the imported "calc" and
9+
// confirm the component renders the mock value
710
describe('Component', () => {
811
it('mocks call from the component', () => {
912
cy.stub(calc, 'getRandomNumber')
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
/// <reference types="cypress" />
2+
// component mixes `require` and `export` keywords
23
const React = require('react')
34
const { getRandomNumber } = require('./calc')
45

@@ -7,4 +8,4 @@ const Component = () => {
78
return <div className="random">{n}</div>
89
}
910

10-
module.exports = Component
11+
export default Component

examples/using-babel/src/ComponentReq.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/// <reference types="cypress" />
22
const React = require('react')
33
const { mount } = require('cypress-react-unit-test')
4-
const Component = require('./ComponentReq.jsx')
4+
const Component = require('./ComponentReq.jsx').default
55
const calc = require('./calc')
66

77
describe('Component', () => {

examples/using-babel/src/Mock.spec.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@ import { mount } from 'cypress-react-unit-test'
44
import Post from './Post'
55
import * as calc from './calc'
66

7+
// confirm the Post component that imports and calls the "getRandomNumber"
8+
// renders the mock value because the test stubs it
79
describe('Mocking', () => {
8-
// https://github.com/bahmutov/cypress-react-unit-test/issues/266
910
it('mocks import used by the Post', () => {
1011
cy.stub(calc, 'getRandomNumber')
1112
.as('lucky')

examples/using-babel/src/Post.spec.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,21 @@
22
import React from 'react'
33
import { mount } from 'cypress-react-unit-test'
44

5+
import * as calc from './calc'
56
import SideBySide from './SideBySide'
67
import Post from './Post'
78
import { SkeletonTheme } from 'react-loading-skeleton'
89

9-
// matches Post.story.js
1010
describe('Post skeletons', () => {
11+
it('mocks the es6 import', () => {
12+
cy.stub(calc, 'getRandomNumber')
13+
.as('lucky')
14+
.returns(777)
15+
16+
mount(<Post />)
17+
cy.contains('.random', '777')
18+
})
19+
1120
it('default', () => {
1221
mount(
1322
<SideBySide>

plugins/babelrc/file-preprocessor.js

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
const debug = require('debug')('cypress-react-unit-test')
44
const webpackPreprocessor = require('@cypress/webpack-preprocessor')
55
const { addImageRedirect } = require('../utils/add-image-redirect')
6-
const babelCore = require('@babel/core')
76

87
// note: modifies the input object
98
function enableBabelrc(webpackOptions) {
@@ -43,7 +42,12 @@ function enableBabelrc(webpackOptions) {
4342
module.exports = config => {
4443
debug('env object %o', config.env)
4544

46-
const nodeEnvironment = 'development'
45+
debug('initial environments %o', {
46+
BABEL_ENV: process.env.BABEL_ENV,
47+
NODE_ENV: process.env.NODE_ENV,
48+
})
49+
50+
const nodeEnvironment = 'test'
4751
if (!process.env.BABEL_ENV) {
4852
debug('setting BABEL_ENV to %s', nodeEnvironment)
4953
process.env.BABEL_ENV = nodeEnvironment
@@ -53,6 +57,11 @@ module.exports = config => {
5357
process.env.NODE_ENV = nodeEnvironment
5458
}
5559

60+
debug('environments %o', {
61+
BABEL_ENV: process.env.BABEL_ENV,
62+
NODE_ENV: process.env.NODE_ENV,
63+
})
64+
5665
const coverageIsDisabled =
5766
config && config.env && config.env.coverage === false
5867
debug('coverage is disabled? %o', { coverageIsDisabled })

0 commit comments

Comments
 (0)