Skip to content

Commit

Permalink
Merge branch 'develop' into 7.0-release
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisbreiding authored Apr 5, 2021
2 parents 9360ae1 + 1eb32cd commit 8b7ef26
Show file tree
Hide file tree
Showing 17 changed files with 266 additions and 56 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ function tryRequirePrettier () {
return null
}
}
const sharedBabelOptions = {
// disable user config
configFile: false,
babelrc: false,
presets: [],
root: process.env.BABEL_TEST_ROOT, // for testing
}

async function transformFileViaPlugin (filePath: string, babelPlugin: babel.PluginObj) {
try {
Expand All @@ -27,7 +34,7 @@ async function transformFileViaPlugin (filePath: string, babelPlugin: babel.Plug
filename: path.basename(filePath),
filenameRelative: path.relative(process.cwd(), filePath),
plugins: [babelPlugin],
presets: [],
...sharedBabelOptions,
})

if (!updatedResult) {
Expand Down Expand Up @@ -127,7 +134,7 @@ export async function getPluginsSourceExample (ast: PluginsConfigAst) {
const babelResult = await babel.transformAsync(exampleCode, {
filename: 'nothing.js',
plugins: [createTransformPluginsFileBabelPlugin(ast)],
presets: [],
...sharedBabelOptions,
})

if (!babelResult?.code) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ describe('init component tests script', () => {

await fs.remove(e2eTestOutputPath)
await fs.mkdir(e2eTestOutputPath)

process.env.BABEL_TEST_ROOT = e2eTestOutputPath
})

afterEach(() => {
Expand Down Expand Up @@ -170,7 +172,7 @@ describe('init component tests script', () => {
createTempFiles({
'/cypress.json': '{}',
'/webpack.config.js': 'module.exports = { }',
'/package.json': JSON.stringify({ dependencies: { react: '*', vue: '*' } }),
'/package.json': JSON.stringify({ dependencies: { react: '*', vue: '^2.4.5' } }),
})

promptSpy = sinon.stub(inquirer, 'prompt')
Expand All @@ -187,12 +189,44 @@ describe('init component tests script', () => {
await initComponentTesting({ config: {}, cypressConfigPath, useYarn: true })

expect(
someOfSpyCallsIncludes(global.console.log, `It looks like all these frameworks: ${chalk.yellow('react, vue')} are available from this directory.`),
someOfSpyCallsIncludes(global.console.log, `It looks like all these frameworks: ${chalk.yellow('react, vue@2')} are available from this directory.`),
).to.be.true
})

it('installs the right adapter', () => {
it('installs the right adapter', async () => {
createTempFiles({
'/cypress.json': '{}',
'/webpack.config.js': 'module.exports = { }',
'/package.json': JSON.stringify({ dependencies: { react: '16.4.5' } }),
})

promptSpy = sinon.stub(inquirer, 'prompt')
.onCall(0)
.returns(Promise.resolve({
chosenTemplateName: 'vite',
componentFolder: 'src',
}) as any)

await initComponentTesting({ config: {}, cypressConfigPath, useYarn: true })
expect(execStub).to.be.calledWith('yarn add @cypress/react --dev')
})

it('installs the right adapter for vue 3', async () => {
createTempFiles({
'/cypress.json': '{}',
'/vite.config.js': 'module.exports = { }',
'/package.json': JSON.stringify({ dependencies: { vue: '^3.0.0' } }),
})

promptSpy = sinon.stub(inquirer, 'prompt')
.onCall(0)
.returns(Promise.resolve({
chosenTemplateName: 'vite',
componentFolder: 'src',
}) as any)

await initComponentTesting({ config: {}, cypressConfigPath, useYarn: true })
expect(execStub).to.be.calledWith('yarn add @cypress/vue@3 --dev')
})

it('suggest the right instruction based on user template choice', async () => {
Expand Down Expand Up @@ -272,4 +306,36 @@ describe('init component tests script', () => {
),
).to.be.true
})

it('Doesn\'t affect injected code if user has custom babel.config.js', async () => {
createTempFiles({
'/cypress/plugins/index.js': 'module.exports = (on, config) => {}',
'/cypress.json': '{}',
'babel.config.js': `module.exports = ${JSON.stringify({
presets: [
'@babel/preset-env',
],
})}`,
'/package.json': JSON.stringify({
dependencies: {
babel: '*',
react: '^16.0.0',
},
}),
})

sinon.stub(inquirer, 'prompt').returns(Promise.resolve({
chosenTemplateName: 'create-react-app',
componentFolder: 'cypress/component',
}) as any)

await initComponentTesting({ config: {}, cypressConfigPath, useYarn: true })
const babelPluginsOutput = await fs.readFile(
path.join(e2eTestOutputPath, 'cypress', 'plugins', 'index.js'),
'utf-8',
)

expect(babelPluginsOutput).not.to.contain('use strict')
expect(babelPluginsOutput).to.contain('module.exports = (on, config) => {')
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ import { installDependency } from '../utils'
async function guessOrAskForFramework (cwd: string): Promise<'react' | 'vue'> {
// please sort this alphabetically
const frameworks = {
react: () => scanFSForAvailableDependency(cwd, ['react', 'react-dom']),
vue: () => scanFSForAvailableDependency(cwd, ['vue']),
react: () => scanFSForAvailableDependency(cwd, { react: '*', 'react-dom': '*' }),
'vue@2': () => scanFSForAvailableDependency(cwd, { vue: '2.x' }),
'vue@3': () => scanFSForAvailableDependency(cwd, { vue: '3.x' }),
}

const guesses = Object.keys(frameworks).filter((framework) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export const ViteTemplate: Template = {
},
test: (root) => {
return {
success: scanFSForAvailableDependency(root, ['vite']),
success: scanFSForAvailableDependency(root, { vite: '*' }),
}
},
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export const VueCliTemplate: Template = {
}
},
test: (root) => {
const hasVueCliService = scanFSForAvailableDependency(root, ['@vue/cli-service'])
const hasVueCliService = scanFSForAvailableDependency(root, { '@vue/cli-service': '>=4' })

return {
success: hasVueCliService,
Expand Down
12 changes: 9 additions & 3 deletions npm/create-cypress-tests/src/findPackageJson.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import path from 'path'
import fs from 'fs'
import findUp from 'find-up'
import { validateSemverVersion } from './utils'

type PackageJsonLike = {
name?: string
Expand Down Expand Up @@ -91,16 +92,21 @@ export function createFindPackageJsonIterator (rootPath = process.cwd()) {
}
}

export function scanFSForAvailableDependency (cwd: string, deps: string[]) {
export function scanFSForAvailableDependency (cwd: string, lookingForDeps: Record<string, string>) {
const { success } = createFindPackageJsonIterator(cwd)
.map(({ dependencies, devDependencies }, path) => {
if (!dependencies && !devDependencies) {
return { success: false }
}

return {
success: Object.keys({ ...dependencies, ...devDependencies })
.some((dependency) => deps.includes(dependency)),
success: Object.entries({ ...dependencies, ...devDependencies })
.some(([dependency, version]) => {
return (
Boolean(lookingForDeps[dependency])
&& validateSemverVersion(version, lookingForDeps[dependency], dependency)
)
}),
}
})

Expand Down
2 changes: 1 addition & 1 deletion npm/create-cypress-tests/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ async function shouldUseYarn () {
}

function shouldUseTypescript () {
return scanFSForAvailableDependency(process.cwd(), ['typescript'])
return scanFSForAvailableDependency(process.cwd(), { typescript: '*' })
}

async function askForComponentTesting () {
Expand Down
116 changes: 101 additions & 15 deletions npm/react/docs/recipes.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,28 @@
# Recipes

- [Recipes](#recipes)
- [Do nothing](#do-nothing)
- [React Scripts](#react-scripts)
- [Next.js](#nextjs)
- [Your webpack config](#your-webpack-config)
- [Your `.babelrc` file](#your-babelrc-file)
- [Add Babel plugins](#add-babel-plugins)
- [Configuration](#configuration)
- [Do nothing](#do-nothing)
- [React Scripts](#react-scripts)
- [Next.js](#nextjs)
- [Your Webpack config](#your-webpack-config)
- [Your `.babelrc` file](#your-babelrc-file)
- [Add Babel plugins](#add-babel-plugins)
- [Using rollup config](#using-rollup-config)
- [Usage](#usage)
- [Changing props](#changing-props)

## Do nothing
## Configuration

### Do nothing

Cypress Test Runner understands plain JSX by default, so for simple React applications it ... might just test components right out of the box!

But usually you want to point Cypress at your application's current Webpack configuration, so the specs can import your components correctly. The next recipes discuss common ways for doing this.

## React Scripts
### React Scripts

If you are using Create-React-App v3 or `react-scripts`, and want to reuse the built in webpack (even after ejecting), this module ships with Cypress preprocessor in [plugins](plugins) folder.
If you are using Create-React-App v3 or `react-scripts`, and want to reuse the built in Webpack (even after ejecting), this module ships with Cypress preprocessor in [plugins](plugins) folder.

```js
// cypress/plugins/index.js
Expand All @@ -32,7 +38,7 @@ See example repo [bahmutov/try-cra-with-unit-test](https://github.com/bahmutov/t

**Tip:** `plugins/react-scripts` is just loading `plugins/cra-v3`.

## Next.js
### Next.js

```js
// cypress/plugins/index.js
Expand All @@ -46,9 +52,9 @@ module.exports = (on, config) => {

See example in the folder [examples/nextjs](examples/nextjs).

## Your webpack config
### Your Webpack config

If you have your own webpack config, you can use included plugins file to load it. You can pass the webpack config file name (with respect to the root folder where `cypress.json` file sits) via plugins file or via an `env` variable in `cypress.json`
If you have your own Webpack config, you can use included plugins file to load it. You can pass the Webpack config file name (with respect to the root folder where `cypress.json` file sits) via plugins file or via an `env` variable in `cypress.json`

```js
// cypress/plugins/index.js
Expand All @@ -64,7 +70,7 @@ module.exports = (on, config) => {

See example in [bahmutov/Jscrambler-Webpack-React](https://github.com/bahmutov/Jscrambler-Webpack-React) or included example in the folder [examples/webpack-file](examples/webpack-file).

## Your `.babelrc` file
### Your `.babelrc` file

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

Expand All @@ -81,7 +87,7 @@ module.exports = (on, config) => {

See example in the folder [examples/using-babel](examples/using-babel) and [examples/using-babel-typescript](examples/using-babel-typescript).

### Add Babel plugins
#### Add Babel plugins

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` as a dependency.

Expand Down Expand Up @@ -125,7 +131,7 @@ When loading your `.babelrc` settings, `@cypress/react` sets `BABEL_ENV` and `NO

See [examples/using-babel](examples/using-babel) folder for full example.

### Using rollup config
#### Using rollup config

If you are using rollup for bundling – we can use it as well for the bundling. Check the example:

Expand Down Expand Up @@ -158,3 +164,83 @@ replace({ 'process.env.NODE_ENV': JSON.stringify('development') }),
```

See [examples/rollup](examples/rollup) folder for full example.

## Usage

### Changing props

Many components have some statefulness, whether explicitly through `useState`, or implicitly through `useEffect`. Therefore during testing it is useful to keep the component mounted, but change the props being passed to it in order to preserve its state. This is referred to in some testing frameworks as `rerender()`.

We recommend building a "wrapper" component that acts similarly to how your users will interact with the component under test. In isolation, you can add DOM controls to push new props to your component.

```js
const Accumulator = ({ value }) => {
const [storedValues, setStoredValues] = React.useState([])

React.useEffect(() => {
if (!value) {
return
}

setStoredValues((prev) => [...prev, value])
}, [value])

return (
<ul>
{storedValues.map((v) => (
<li key={v}>
{v}
</li>
))}
</ul>
)
}
```

This component is an accumulator that stores each `value` prop passed to it. We create a wrapper component that has an `input` and a `button` to push new values to the `value` prop.

```js
const TestAcc = () => {
const ref = React.useRef()
const [value, setValue] = React.useState()

return (
<div>
<input ref={ref} />
<button
onClick={() => {
setValue(ref.current.value)
}}
>
Add
</button>
<Acc value={value} />
</div>
)
}
```

With this, we can begin writing component tests to check the functionality of our `Accumulator` component.

```js
it('should store value', () => {
mount(<TestAcc />)

cy.get('input').type('Component testing is awesome!')
cy.get('button').click()

cy.get('li').eq(0).contains('Component testing is awesome!')

cy.get('input').clear().type('We are dynamically changing props')
cy.get('button').click()

cy.get('li').eq(1).contains('We are dynamically changing props')

cy.get('input').clear().type('to build a list of text')
cy.get('button').click()

cy.get('li').eq(0).contains('Component testing is awesome!')
cy.get('li').eq(1).contains('We are dynamically changing props')
cy.get('li').eq(2).contains('to build a list of text')
})
```
6 changes: 5 additions & 1 deletion npm/vite-dev-server/src/makeCypressPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,13 @@ export const makeCypressPlugin = (
},
transformIndexHtml () {
return [
// load the script at the end of the body
// script has to be loaded when the vite client is connected
{
tag: 'script',
attrs: { type: 'module', src: INIT_FILEPATH },
injectTo: 'body',
attrs: { type: 'module' },
children: `import(${JSON.stringify(INIT_FILEPATH)})`,
},
]
},
Expand Down
Loading

4 comments on commit 8b7ef26

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on 8b7ef26 Apr 5, 2021

Choose a reason for hiding this comment

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

Circle has built the linux x64 version of the Test Runner.

Learn more about this pre-release platform-specific build at https://on.cypress.io/installing-cypress#Install-pre-release-version.

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/6.9.0/circle-7.0-release-8b7ef269c5fb0a09cc5d7b49578c0dba03090604/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on 8b7ef26 Apr 5, 2021

Choose a reason for hiding this comment

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

AppVeyor has built the win32 ia32 version of the Test Runner.

Learn more about this pre-release platform-specific build at https://on.cypress.io/installing-cypress#Install-pre-release-version.

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/6.7.2/appveyor-7.0-release-8b7ef269c5fb0a09cc5d7b49578c0dba03090604/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on 8b7ef26 Apr 5, 2021

Choose a reason for hiding this comment

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

AppVeyor has built the win32 x64 version of the Test Runner.

Learn more about this pre-release platform-specific build at https://on.cypress.io/installing-cypress#Install-pre-release-version.

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/6.7.2/appveyor-7.0-release-8b7ef269c5fb0a09cc5d7b49578c0dba03090604/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on 8b7ef26 Apr 5, 2021

Choose a reason for hiding this comment

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

Circle has built the darwin x64 version of the Test Runner.

Learn more about this pre-release platform-specific build at https://on.cypress.io/installing-cypress#Install-pre-release-version.

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/6.9.0/circle-7.0-release-8b7ef269c5fb0a09cc5d7b49578c0dba03090604/cypress.tgz

Please sign in to comment.