mirror of
https://github.com/cypress-io/cypress.git
synced 2026-04-28 02:39:31 -05:00
Merge branch 'master' into driver-refactor-merge
# Conflicts: # circle.yml # docs/readme.md # packages/driver/src/cy/commands/actions/text.coffee # packages/driver/src/cy/commands/fixtures.coffee # packages/driver/src/cypress/dom.coffee # packages/driver/src/cypress/snapshot.coffee # packages/driver/test/unit/cy/commands/actions/select_spec.coffee # packages/driver/test/unit/cy/commands/fixtures_spec.coffee # packages/driver/test/unit/cy/snapshot_spec.coffee # packages/runner/src/lib/event-manager.js # packages/server/timers/child.js # packages/server/timers/parent.js
This commit is contained in:
@@ -28,6 +28,7 @@ packages/example/cypress/fixtures/users.json
|
||||
|
||||
# from server
|
||||
packages/server/.cy
|
||||
packages/server/.projects
|
||||
packages/server/support
|
||||
|
||||
# from docs
|
||||
|
||||
+305
@@ -0,0 +1,305 @@
|
||||
# Contributing to Cypress
|
||||
|
||||
Thanks for taking the time to contribute! :smile:
|
||||
|
||||
**Once you learn how to use Cypress, you can contribute in many ways:**
|
||||
|
||||
- Join the [Cypress Gitter chat](https://gitter.im/cypress-io/cypress) and answer questions. Teaching others how to use Cypress is a great way to learn more about how it works.
|
||||
- Blog about Cypress. We display blogs featuring Cypress on our [Examples](https://on.cypress.io/examples) page. If you'd like your blog featured, [contact us](mailto:support@cypress.io).
|
||||
- Write some documentation or improve our existing docs. Know another language? You can help us translate them. See our [guide to contributing to our docs](./docs/contributing.md).
|
||||
- Give a talk about Cypress. [Contact us](mailto:support@cypress.io) ahead of time and we'll send you some swag.
|
||||
|
||||
**Want to dive deeper into how Cypress works? There are several ways you can help with the development of Cypress:**
|
||||
|
||||
- [Report bugs](https://github.com/cypress-io/cypress/issues/new) by opening an issue.
|
||||
- [Request features](https://github.com/cypress-io/cypress/issues/new) by opening an issue.
|
||||
- Write Code for one of our core packages. [Please thoroughly read our writing code guide](#writing-code).
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Code of Conduct](#code-of-conduct)
|
||||
- [Contributing Bug Reports & Feature Requests](#contributing-bug-reports--feature-requests)
|
||||
- [Bug Reports](#bug-reports)
|
||||
- [Feature Requests](#feature-requests)
|
||||
- [Writing Code](#writing-code)
|
||||
- [What you need to know before getting started](#what-you-need-to-know-before-getting-started)
|
||||
- [Requirements](#requirements)
|
||||
- [Getting Started](#getting-started)
|
||||
- [Coding Style](#coding-style)
|
||||
- [Tests](#tests)
|
||||
- [Writing Documentation](#writing-documentation)
|
||||
- [Committing Code](#committing-code)
|
||||
- [Pull Requests](#pull-requests)
|
||||
- [Deployment](#deployment)
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
All contributors are expecting to abide by our [Code of Conduct](https://github.com/cypress-io/cypress/wiki/code-of-conduct).
|
||||
|
||||
## Contributing Bug Reports & Feature Requests
|
||||
|
||||
### Bug Reports
|
||||
|
||||
This article describes how to open an effective bug report so we can get your issue fixed or help you work around it.
|
||||
|
||||
**The most important things to do are:**
|
||||
|
||||
- Search existing [issues](https://github.com/cypress-io/cypress/issues) for your problem
|
||||
- Check the list of common fixes below
|
||||
- Make sure we support your setup
|
||||
- Gather debugging information
|
||||
- Explain how to reproduce the issue
|
||||
|
||||
If you have a feature request (not a bug), see [Feature Requests](#feature-requests).
|
||||
|
||||
### Common Fixes
|
||||
|
||||
Before filing a bug, make sure you are up to date. Your issue may have already been fixed. Even if you do not see the issue described as resolved in a newer version, a newer version may help in the process of debugging your issue by giving more helpful error messages.
|
||||
|
||||
[See our document on installing cypress](https://on.cypress.io/installing-cypress)
|
||||
|
||||
### Supported Issues
|
||||
|
||||
Before filing a bug, make sure you're filing an issue against something we support. See our [System Requirements](https://on.cypress.io/installing-cypress#system-requirements).
|
||||
|
||||
### Getting More Information
|
||||
|
||||
For some issues, there are places you can check for more information. This may help you resolve the issue yourself. Even if it doesn't, this information can help us figure out and resolve an issue.
|
||||
|
||||
- For issues in the web browser, check the JavaScript console and your Network tab in your DevTools
|
||||
- Click on any command in the Command Log where the failure occurred, this will log more information about the error to the JavaScript console.
|
||||
- Use Cypress' [`debug`](https://on.cypress.io/api/debug) or [`pause`](https://on.cypress.io/api/pause) commands to step through your commands.
|
||||
- Ask other Cypress users for help in our [gitter channel](https://gitter.im/cypress-io/cypress).
|
||||
|
||||
### Reproducibility
|
||||
|
||||
The most important part of your issue is instructions on how to reproduce the issue.
|
||||
- What did you do?
|
||||
- If you do it again, does it still break?
|
||||
- Does it depend on a specific order?
|
||||
|
||||
**It is nearly impossible for us to resolve many issues if we can not reproduce them. Your best chance of getting a bug looked at quickly is to provide a repository with a reproducible bug that can be cloned and run.**
|
||||
|
||||
### Open an Issue
|
||||
|
||||
If you're up to date, supported, have collected information about the problem, and have the best reproduction instructions you can come up with, you're ready to [open an issue](https://github.com/cypress-io/cypress/issues/new).
|
||||
|
||||
## Feature Requests
|
||||
|
||||
Have a feature you'd like to see in Cypress? This describes how to file an effective feature request.
|
||||
|
||||
**The most important things to do are:**
|
||||
|
||||
- Understand our [roadmap](https://on.cypress.io/roadmap)
|
||||
- Make sure your feature makes sense in the project
|
||||
- Align your expectations around timelines and priorities
|
||||
- Describe your problem, not your solution
|
||||
|
||||
### Understand our Roadmap
|
||||
|
||||
We have a cohesive vision for Cypress in the long term and a general roadmap that extends into the future. While the specifics of how we get there are flexible, many milestones are well-established.
|
||||
|
||||
Feature requests are an important part of what we plan in our roadmap, but we ultimately build only features which make sense as part of the long term plan.
|
||||
|
||||
### Setting Expectations
|
||||
|
||||
We have a lot of users and a small team. Even if your feature is something we're interested in and a good fit for where we want the product to go, it may take us a long time to get around to building it.
|
||||
|
||||
If you want a concrete timeline, you can [contact us](mailto:support@cypress.io) to pay for some control over our roadmap.
|
||||
|
||||
### Describe Problems
|
||||
|
||||
When you file a feature request, we need you to **describe the problem you're facing first**, not just your desired solution.
|
||||
|
||||
Often, your problem may have a lot in common with other similar problems. If we understand your use case we can compare it to other use cases and sometimes find a more powerful or more general solution which solves several problems at once. Understanding the root issue can let us merge and contextualize things. Sometimes there's already a way to solve your problem that might just not be obvious.
|
||||
|
||||
Also, your proposed solution may not be compatible with the direction we want to take the product, but we may be able to come up with another solution which has approximately the same effect and does fit into the product direction.
|
||||
|
||||
### Open an Issue
|
||||
|
||||
If you think your feature might be a good fit for our roadmap, has reasonable expectations about it, and has a good description of the problem you're trying to solve, you're ready to [open a feature request](https://github.com/cypress-io/cypress/issues/new).
|
||||
|
||||
## Writing code
|
||||
|
||||
Working on your first Pull Request? You can learn how from this free series [How to Contribute to an Open Source Project on GitHub](https://egghead.io/courses/how-to-contribute-to-an-open-source-project-on-github)
|
||||
|
||||
### What you need to know before getting started
|
||||
|
||||
#### Cypress and Packages
|
||||
|
||||
Cypress is a large open source project. When you want to contribute to Cypress, you may be unsure which part of the project to work within.
|
||||
|
||||
This repository is made up of various packages. They are discrete modules with different responsibilities, but each is necessary for the Cypress app and is not necessarily useful outside of the Cypress app.
|
||||
|
||||
Here is a list of the core packages in this repository with a short description, located within the [`packages`](./packages) directory:
|
||||
|
||||
Folder Name | Purpose
|
||||
----------- | -------
|
||||
[coffee]() | A centralized version of CoffeeScript used for other packages.
|
||||
[desktop-gui]() | The front-end code for the Cypress Desktop GUI.
|
||||
[driver]() | The code that is used to drive the behavior of the API commands.
|
||||
[electron]() | The Cypress implementation of Electron.
|
||||
[example]() | Our example kitchen-sink application.
|
||||
[extension]() | The Cypress Chrome browser extension
|
||||
[https-proxy]() | This does https proxy for handling http certs and traffic.
|
||||
[launcher]() | Finds and launches browsers installed on your system.
|
||||
[reporter]() | The reporter shows the running results of the tests (The Command Log UI).
|
||||
[root]() | Dummy package pointing at the root of the repository.
|
||||
[runner]() | The runner is the minimal "chrome" around the user's application under test.
|
||||
[server]() | The <3 of Cypress. This orchestrates everything. The entire backend.
|
||||
[socket]() | A wrapper around socket.io to provide common libraries.
|
||||
[static]() | Serves static assets used in the Cypress GUI.
|
||||
[ts]() | A centralized version of typescript.
|
||||
|
||||
### Requirements
|
||||
|
||||
You must have [`node`](https://nodejs.org/en/) and [`npm`](https://www.npmjs.com/) installed to run the project.
|
||||
|
||||
### Getting Started
|
||||
|
||||
**Install all dependencies:**
|
||||
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
This will install this repo's direct dependencies as well as the dependencies for every individual package.
|
||||
|
||||
**Then, build all the packages and start the app:**
|
||||
|
||||
```bash
|
||||
npm run all build
|
||||
npm start
|
||||
```
|
||||
|
||||
If there are errors building the packages, run with `DEBUG=cypress:run ...`
|
||||
option to see more details
|
||||
|
||||
#### Tasks
|
||||
|
||||
Each package is responsible for building itself and testing itself and can do so using whatever tools are appropriate, but each conforms to a standard set of npm scripts so that building, watching, testing, etc. can be orchestrated from the root of this repo. Here are the npm scripts supported and what they mean:
|
||||
|
||||
Task | Purpose
|
||||
---- | -------
|
||||
`build-dev` | Build all assets for development
|
||||
`build-prod` | Build all assets for production
|
||||
`watch-dev` | Watch source files and build development assets when they are saved. This may also run a server for serving files and run tests related to a saved file.
|
||||
`server` | Run a server for serving files
|
||||
`clean` | Remove any assets created by `build-dev` or `build-prod`
|
||||
`clean-deps` | Remove any dependencies installed (usually by npm or bower)
|
||||
`test-all` | Run all tests in watch mode
|
||||
`test-all-once` | Run all tests
|
||||
`test-all-unit` | Run unit tests in watch mode
|
||||
`test-all-unit-once` | Run unit tests
|
||||
`test-all-integration` | Run integration tests in watch mode
|
||||
`test-all-integration-once` | Run integration tests
|
||||
`test-all-e2e` | Run end-2-end tests in watch mode
|
||||
`test-all-e2e-once` | Run end-2-end tests
|
||||
|
||||
Not every package requires or makes use of every script, so it is simply omitted from that package's `package.json` and not run.
|
||||
|
||||
You can run `npm run all <script>` from the root directory to run a script in every package that utilizes that script. Many times, you may only be working on one or two packages at a time, so it won't be necessary or desirable to run a script for every package. You can use the `--packages` option to specify in which package(s) to run the script.
|
||||
|
||||
You can even run `npm run all install` to install all npm dependencies for each package. Note that this is already done automatically for you when you run `npm install`.
|
||||
|
||||
```bash
|
||||
npm run all watch-dev -- --packages core-desktop-gui
|
||||
```
|
||||
|
||||
Separate the package names with commas to specify multiple packages:
|
||||
|
||||
```bash
|
||||
npm run all watch-dev -- --packages core-desktop-gui,core-runner
|
||||
```
|
||||
|
||||
By default, all tasks run in parallel. This is faster than running serially, but the output ends up mixed together and, if things go wrong, it can be difficult see where the error occurred. To run tasks serially, use the `--serial` flag:
|
||||
|
||||
|
||||
```bash
|
||||
npm run all build-prod -- --serial
|
||||
```
|
||||
|
||||
*build-prod* will be run sequentially for every package, so the output for each package won't be jumbled with the output of the others.
|
||||
|
||||
It is not recommended to use `--serial` with any script that is long-running, like *watch-dev* or *test*, since they need to be parallel.
|
||||
|
||||
#### Debugging
|
||||
|
||||
Some packages use [debug](https://github.com/visionmedia/debug#readme) to
|
||||
log debug messages to the console. The naming scheme should be
|
||||
`cypress:<package name>`. For example to see launcher messages during unit
|
||||
tests start it using
|
||||
|
||||
```bash
|
||||
cd packages/launcher
|
||||
DEBUG=cypress:launcher npm test
|
||||
```
|
||||
|
||||
If you want to see log messages from all Cypress projects use wild card
|
||||
|
||||
```bash
|
||||
DEBUG=cypress:* ...
|
||||
```
|
||||
|
||||
### Coding Style
|
||||
|
||||
### Tests
|
||||
|
||||
Since it is generally best to do single runs of tests serially instead of in parallel, this repo has some convenience scripts to run all the tests for all the packages sequentially:
|
||||
|
||||
```bash
|
||||
npm run test-once ## same as 'npm run all test-once -- --serial'
|
||||
npm run test-unit-once ## same as 'npm run all test-unit-once -- --serial'
|
||||
npm run test-integration-once ## same as 'npm run all test-integration-once -- --serial'
|
||||
npm run test-e2e-once ## same as 'npm run all test-e2e-once -- --serial'
|
||||
```
|
||||
|
||||
#### Docker
|
||||
|
||||
Sometimes tests pass locally, but fail on CI. Our CI environment should be
|
||||
dockerized. In order to run the same image locally, there is script
|
||||
[scripts/run-docker-local.sh](scripts/run-docker-local.sh) that assumes that you
|
||||
have pulled the image `cypress/internal:chrome58` (see
|
||||
[circle.yml](circle.yml) for the current image name).
|
||||
|
||||
The image will start and will map the root of the repository to
|
||||
`/cypress-monorepo` inside the image. Now you can modify the files using your
|
||||
favorite environment and rerun tests inside the docker environment.
|
||||
|
||||
**hint** sometimes building inside the image has problems with `node-sass`
|
||||
library
|
||||
|
||||
```bash
|
||||
Error: Missing binding /cypress-monorepo/packages/desktop-gui/node_modules/node-sass/vendor/linux-x64-48/binding.node
|
||||
Node Sass could not find a binding for your current environment: Linux 64-bit with Node.js 6.x
|
||||
|
||||
Found bindings for the following environments:
|
||||
- OS X 64-bit with Node.js 6.x
|
||||
|
||||
This usually happens because your environment has changed since running `npm install`.
|
||||
Run `npm rebuild node-sass` to build the binding for your current environment.
|
||||
```
|
||||
|
||||
From the running container, go into that project and rebuild `node-sass`
|
||||
|
||||
```bash
|
||||
$ npm run docker
|
||||
cd packages/desktop-gui
|
||||
npm rebuild node-sass
|
||||
```
|
||||
|
||||
## Writing Documentation
|
||||
|
||||
See our [Documentation Contributing Guideline](docs/CONTRIBUTING.md)
|
||||
|
||||
## Commiting Code
|
||||
|
||||
### Pull Requests
|
||||
|
||||
- When opening a PR for a specific issue already open, please use the `address #[issue number]` or `closes #[issue number]` syntax in the pull request description.
|
||||
|
||||
## Deployment
|
||||
|
||||
We will try to review and merge pull requests quickly. After merging we
|
||||
will try releasing a new version. If you want to know our build process or
|
||||
build your own Cypress binary, read [DEPLOY.md](DEPLOY.md)
|
||||
@@ -0,0 +1,35 @@
|
||||
## Deployment
|
||||
|
||||
You can only deploy Cypress application and publish NPM module `cypress` if
|
||||
you are a member of `cypress` NPM organization.
|
||||
|
||||
### Building the binary
|
||||
|
||||
First, you need to build, zip and upload application binary to Cypress server.
|
||||
You can either specify each command separately
|
||||
|
||||
```
|
||||
npm run binary-build
|
||||
npm run binary-zip
|
||||
npm run binary-upload
|
||||
```
|
||||
|
||||
or use a single command
|
||||
|
||||
```
|
||||
npm run binary-deploy
|
||||
```
|
||||
|
||||
You can pass options to each command to avoid answering questions, for example
|
||||
|
||||
```
|
||||
npm run binary-deploy -- --platform darwin --version 0.20.0
|
||||
npm run binary-upload -- --platform darwin --version 0.20.0 --zip cypress.zip
|
||||
```
|
||||
|
||||
If something goes wrong, see debug messages using `DEBUG=cypress:binary ...` environment
|
||||
variable.
|
||||
|
||||
Because we had many problems reliably zipping built binary, for now we need
|
||||
to build both Mac and Linux binary from Mac (Linux binary is built using
|
||||
a Docker container), then zip it **from Mac**, then upload it.
|
||||
@@ -38,178 +38,12 @@ https://img.shields.io/badge/cypress.io-tests-green.svg?style=flat-square
|
||||
|
||||
## Development
|
||||
|
||||
### Getting Started
|
||||
Please see our [Contributing Guideline](/CONTRIBUTING.md)
|
||||
|
||||
Install all dependencies:
|
||||
## Deployment
|
||||
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
Please see out [deployment document](DEPLOY.md)
|
||||
|
||||
This will install this repo's direct dependencies as well as the dependencies for every individual package.
|
||||
## License
|
||||
|
||||
Then, build all the packages and start the app:
|
||||
|
||||
```bash
|
||||
npm run all build
|
||||
npm start
|
||||
```
|
||||
|
||||
If there are errors building the packages, run with `DEBUG=cypress:run ...`
|
||||
option to see more details
|
||||
|
||||
### Tasks
|
||||
|
||||
Each package is responsible for building itself and testing itself and can do so using whatever tools are appropriate, but each conforms to a standard set of npm scripts so that building, watching, testing, etc. can be orchestrated from the root of this repo. Here are the npm scripts supported and what they mean:
|
||||
|
||||
**build-dev**: Build all assets for development
|
||||
|
||||
**build-prod**: Build all assets for production
|
||||
|
||||
**watch-dev**: Watch source files and build development assets when they are saved. This may also run a server for serving files and run tests related to a saved file.
|
||||
|
||||
**server**: Run a server for serving files
|
||||
|
||||
**clean**: Remove any assets created by *build-dev* or *build-prod*
|
||||
|
||||
**clean-deps**: Remove any dependencies installed (usually by npm or bower)
|
||||
|
||||
**test-all**: Run all tests in watch mode
|
||||
|
||||
**test-all-once**: Run all tests
|
||||
|
||||
**test-all-unit**: Run unit tests in watch mode
|
||||
|
||||
**test-all-unit-once**: Run unit tests
|
||||
|
||||
**test-all-integration**: Run integration tests in watch mode
|
||||
|
||||
**test-all-integration-once**: Run integration tests
|
||||
|
||||
**test-all-e2e**: Run end-2-end tests in watch mode
|
||||
|
||||
**test-all-e2e-once**: Run end-2-end tests
|
||||
|
||||
Not every package requires or makes use of every script, so it is simply omitted from that package's `package.json` and not run.
|
||||
|
||||
You can run `npm run all <script>` to run a script in every package that utilizes that script. Many times, you may only be working on one or two packages at a time, so it won't be necessary or desirable to run a script for every package. You can use the `--packages` option to specify in which package(s) to run the script.
|
||||
|
||||
You can even run `npm run all install` to install all npm dependencies for each package. Note that this is already done automatically for you when you run `npm install`.
|
||||
|
||||
```bash
|
||||
npm run all watch-dev -- --packages core-desktop-gui
|
||||
```
|
||||
|
||||
Separate the package names with commas to specify multiple packages:
|
||||
|
||||
```bash
|
||||
npm run all watch-dev -- --packages core-desktop-gui,core-runner
|
||||
```
|
||||
|
||||
By default, all tasks run in parallel. This is faster than running serially, but the output ends up mixed together and, if things go wrong, it can be difficult see where the error occurred. To run tasks serially, use the `--serial` flag:
|
||||
|
||||
|
||||
```bash
|
||||
npm run all build-prod -- --serial
|
||||
```
|
||||
|
||||
*build-prod* will be run sequentially for every package, so the output for each package won't be jumbled with the output of the others.
|
||||
|
||||
It is not recommended to use `--serial` with any script that is long-running, like *watch-dev* or *test*, since they need to be parallel.
|
||||
|
||||
Since it is generally best to do single runs of tests serially instead of in parallel, this repo has some convenience scripts to run all the tests for all the packages sequentially:
|
||||
|
||||
```bash
|
||||
npm run test-once ## same as 'npm run all test-once -- --serial'
|
||||
npm run test-unit-once ## same as 'npm run all test-unit-once -- --serial'
|
||||
npm run test-integration-once ## same as 'npm run all test-integration-once -- --serial'
|
||||
npm run test-e2e-once ## same as 'npm run all test-e2e-once -- --serial'
|
||||
```
|
||||
|
||||
### Debugging
|
||||
|
||||
Some packages use [debug](https://github.com/visionmedia/debug#readme) to
|
||||
log debug messages to the console. The naming scheme should be
|
||||
`cypress:<package name>`. For example to see launcher messages during unit
|
||||
tests start it using
|
||||
|
||||
```bash
|
||||
cd packages/launcher
|
||||
DEBUG=cypress:launcher npm test
|
||||
```
|
||||
|
||||
If you want to see log messages from all Cypress projects use wild card
|
||||
|
||||
```bash
|
||||
DEBUG=cypress:* ...
|
||||
```
|
||||
|
||||
### Docker
|
||||
|
||||
Sometimes tests pass locally, but fail on CI. Our CI environment should be
|
||||
dockerized. In order to run the same image locally, there is script
|
||||
[scripts/run-docker-local.sh](scripts/run-docker-local.sh) that assumes that you
|
||||
have pulled the image `cypress/internal:chrome58` (see
|
||||
[circle.yml](circle.yml) for the current image name).
|
||||
|
||||
The image will start and will map the root of the monorepo to
|
||||
`/cypress-monorepo` inside the image. Now you can modify the files using your
|
||||
favorite environment and rerun tests inside the docker environment.
|
||||
|
||||
***hint** sometimes building inside the image has problems with `node-sass`
|
||||
library
|
||||
|
||||
```
|
||||
Error: Missing binding /cypress-monorepo/packages/desktop-gui/node_modules/node-sass/vendor/linux-x64-48/binding.node
|
||||
Node Sass could not find a binding for your current environment: Linux 64-bit with Node.js 6.x
|
||||
|
||||
Found bindings for the following environments:
|
||||
- OS X 64-bit with Node.js 6.x
|
||||
|
||||
This usually happens because your environment has changed since running `npm install`.
|
||||
Run `npm rebuild node-sass` to build the binding for your current environment.
|
||||
```
|
||||
|
||||
From the running container, go into that project and rebuild `node-sass`
|
||||
|
||||
```
|
||||
$ npm run docker
|
||||
cd packages/desktop-gui
|
||||
npm rebuild node-sass
|
||||
```
|
||||
|
||||
## Deploy
|
||||
|
||||
You can only deploy Cypress application and publish NPM module `cypress` if
|
||||
you are a member of `cypress` NPM organization.
|
||||
|
||||
### Building the binary
|
||||
|
||||
First, you need to build, zip and upload application binary to Cypress server.
|
||||
You can either specify each command separately
|
||||
|
||||
```
|
||||
npm run binary-build
|
||||
npm run binary-zip
|
||||
npm run binary-upload
|
||||
```
|
||||
|
||||
or use a single command
|
||||
|
||||
```
|
||||
npm run binary-deploy
|
||||
```
|
||||
|
||||
You can pass options to each command to avoid answering questions, for example
|
||||
|
||||
```
|
||||
npm run binary-deploy -- --platform darwin --version 0.20.0
|
||||
npm run binary-upload -- --platform darwin --version 0.20.0 --zip cypress.zip
|
||||
```
|
||||
|
||||
If something goes wrong, see debug messages using `DEBUG=cypress:binary ...` environment
|
||||
variable.
|
||||
|
||||
Because we had many problems reliably zipping built binary, for now we need
|
||||
to build both Mac and Linux binary from Mac (Linux binary is built using
|
||||
a Docker container), then zip it **from Mac**, then upload it.
|
||||
This project is licensed under the terms of the [MIT license](/LICENSE.md).
|
||||
|
||||
+32
-12
@@ -217,16 +217,29 @@ jobs:
|
||||
steps:
|
||||
- restore_cache:
|
||||
key: cypress-monorepo-{{ .Branch }}-{{ .Revision }}
|
||||
- run: cd docs && npm run deps
|
||||
- run: cd docs && npm run build
|
||||
- run: cd docs && npm run test-e2e
|
||||
|
||||
# "driver-unit-tests":
|
||||
# <<: *defaults
|
||||
# parallelism: 2
|
||||
# steps:
|
||||
# - restore_cache:
|
||||
# key: cypress-monorepo-{{ .Branch }}-{{ .Revision }}
|
||||
# - run: chrome --version
|
||||
# - run: xvfb-run -as "-screen 0 1280x720x16" npm run all test -- --package driver
|
||||
"deploy-docs":
|
||||
<<: *defaults
|
||||
steps:
|
||||
- restore_cache:
|
||||
key: cypress-monorepo-{{ .Branch }}-{{ .Revision }}
|
||||
- run: cd docs && NODE_ENV=production npm run build
|
||||
# because we already built docs for production
|
||||
# use script that just deploys without rebuilding the production docs
|
||||
- run: cd docs && npm run deploy-prebuilt -- --environment production --scrape
|
||||
- run: cd docs && cat public/build.json
|
||||
|
||||
"driver-unit-tests":
|
||||
<<: *defaults
|
||||
parallelism: 2
|
||||
steps:
|
||||
- restore_cache:
|
||||
key: cypress-monorepo-{{ .Branch }}-{{ .Revision }}
|
||||
- run: chrome --version
|
||||
- run: xvfb-run -as "-screen 0 1280x720x16" npm run all test -- --package driver
|
||||
|
||||
"build-binary":
|
||||
<<: *defaults
|
||||
@@ -257,15 +270,22 @@ workflows:
|
||||
- server-e2e-tests:
|
||||
requires:
|
||||
- build
|
||||
- docs-tests:
|
||||
- driver-unit-tests:
|
||||
requires:
|
||||
- build
|
||||
# - driver-unit-tests:
|
||||
# requires:
|
||||
# - build
|
||||
- build-binary:
|
||||
requires:
|
||||
- build
|
||||
# test and deploy documentation site
|
||||
- docs-tests:
|
||||
requires:
|
||||
- build
|
||||
- deploy-docs:
|
||||
filters:
|
||||
branches:
|
||||
only: master
|
||||
requires:
|
||||
- docs-tests
|
||||
|
||||
#
|
||||
# things to run in the 4th CI container
|
||||
|
||||
@@ -199,10 +199,9 @@ const runSmokeTest = () => {
|
||||
const differentFrom = (a) => (b) => a !== b
|
||||
|
||||
const logStart = () =>
|
||||
log(chalk.green('⧖ Verifying Cypress executable...'))
|
||||
log(chalk.yellow('⧖ Verifying Cypress executable...'))
|
||||
|
||||
const logSuccess = () => {
|
||||
log()
|
||||
log(chalk.green('✓ Successfully verified Cypress executable'))
|
||||
}
|
||||
|
||||
@@ -272,12 +271,16 @@ const verify = (options = {}) => {
|
||||
})
|
||||
.then(() => {
|
||||
const executable = getPathToExecutable()
|
||||
return fs.statAsync(executable).catch(() =>
|
||||
explainAndFail(errors.missingApp)(new Error(stripIndent`
|
||||
Cypress executable not found at
|
||||
${executable}
|
||||
`))
|
||||
)
|
||||
return fs.statAsync(executable)
|
||||
.then(() => {
|
||||
log(chalk.green('✓ Cypress executable found at:'), chalk.cyan(executable))
|
||||
})
|
||||
.catch(() =>
|
||||
explainAndFail(errors.missingApp)(new Error(stripIndent`
|
||||
Cypress executable not found at
|
||||
${executable}
|
||||
`))
|
||||
)
|
||||
})
|
||||
.then(() => {
|
||||
return maybeVerify(options)
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@
|
||||
"dependencies": {
|
||||
"@cypress/xvfb": "1.0.3",
|
||||
"bluebird": "3.5.0",
|
||||
"chalk": "1.1.3",
|
||||
"chalk": "2.0.1",
|
||||
"commander": "2.9.0",
|
||||
"common-tags": "1.4.0",
|
||||
"debug": "2.6.8",
|
||||
|
||||
@@ -18,7 +18,7 @@ const distDir = path.join(__dirname, '../../dist')
|
||||
const infoFilePath = path.join(distDir, 'info.json')
|
||||
|
||||
describe('utils', function () {
|
||||
const verifyingMessage = chalk.green('⧖ Verifying Cypress executable...')
|
||||
const verifyingMessage = chalk.yellow('⧖ Verifying Cypress executable...')
|
||||
|
||||
beforeEach(function () {
|
||||
this.sandbox.stub(process, 'exit')
|
||||
@@ -242,6 +242,16 @@ describe('utils', function () {
|
||||
return fs.outputJsonAsync(infoFilePath, { version: packageVersion, verifiedVersion: packageVersion })
|
||||
})
|
||||
|
||||
it('shows full path to executable when verifying', function () {
|
||||
const executable = utils.getPathToExecutable()
|
||||
const message1 = chalk.green('✓ Cypress executable found at:')
|
||||
const message2 = chalk.cyan(executable)
|
||||
return utils.verify({ force: true })
|
||||
.then(() => {
|
||||
expect(this.log).to.be.calledWith(message1, message2)
|
||||
})
|
||||
})
|
||||
|
||||
it('runs smoke test even if version already verified', function () {
|
||||
return utils.verify({ force: true })
|
||||
.then(() => {
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
test/**/*_spec.js
|
||||
--timeout 4000
|
||||
--timeout 10000
|
||||
--reporter spec
|
||||
--recursive
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
# Contributing to Cypress Documentation
|
||||
|
||||
Thanks for taking the time to contribute! :smile:
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Code of Conduct](#code-of-conduct)
|
||||
- [Getting Started](#getting-started)
|
||||
- [Writing Documentation](#writing-documentation)
|
||||
- [Committing Code](#committing-code)
|
||||
- [Pull Requests](#pull-requests)
|
||||
- [Deployment](#deployment)
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
All contributors are expecting to abide by our [Code of Conduct](https://github.com/cypress-io/cypress/wiki/code-of-conduct).
|
||||
|
||||
## Getting Started
|
||||
|
||||
If you need to work on documentation, ensure you are in the `docs` directory within the main cypress repository.
|
||||
|
||||
The documentation in this repo are generated using [Hexo](https://hexo.io/). You should
|
||||
be able to install tools, build and start local `hexo` site.
|
||||
|
||||
From the `docs` directory:
|
||||
|
||||
**Install dependencies:**
|
||||
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
This will install this repo's direct dependencies.
|
||||
|
||||
**Then, build the `public` directory and start the app:**
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
npm start
|
||||
```
|
||||
|
||||
Visit [http://localhost:2222/](http://localhost:2222/)
|
||||
|
||||
## Writing Documentation
|
||||
|
||||
### Links
|
||||
|
||||
Links are all handled through our [cypress.on](https://github.com/cypress-io/cypress-on) api.
|
||||
|
||||
Link all pages but their name (property key) in `source/_data/sidebar.yml`
|
||||
|
||||
- https://on.cypress.io/NAME_OF_PAGE
|
||||
- https://on.cypress.io/and
|
||||
- https://on.cypress.io/visit
|
||||
- https://on.cypress.io/unit-testing-recipe
|
||||
- https://on.cypress.io/introduction-to-cypress
|
||||
- https://on.cypress.io/writing-your-first-test
|
||||
- https://on.cypress.io/general-questions-faq
|
||||
|
||||
### Adding Examples
|
||||
|
||||
The documents outlining examples are within the [`source/examples`](/source/examples) directory. Each document is written in markdown with a little bit of [Hexo flair](https://hexo.io/docs/tag-plugins.html). To add an example to a document, just try to follow the formatting of any previous examples in the markdown file.
|
||||
|
||||
Add an associating image with the example within the [`source/img/examples`](/source/img/examples) directory. Each image should be resolution **715w x 480h**. Reference the image in the markdown document as follows:
|
||||
|
||||
```md
|
||||
{% img /img/examples/name-of-file.jpg "alt text describing img" %}
|
||||
```
|
||||
|
||||
## Commiting Code
|
||||
|
||||
### Pull Requests
|
||||
|
||||
- When opening a PR for a specific issue already open, please use the `address #[issue number]` or `closes #[issue number]` syntax in the pull request description.
|
||||
|
||||
## Deployment
|
||||
|
||||
We will try to review and merge pull requests quickly. After merging we
|
||||
will try releasing the documentation. If you want to know our deploy process, read [DEPLOY.md](DEPLOY.md)
|
||||
@@ -0,0 +1,50 @@
|
||||
# Deploy Cypress Documentation
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Automatic Deployment](#automatic-deployment)
|
||||
- [Manual Deployment](#manual-deployment)
|
||||
- [Check Latest Deployed Version](#check-latest-deployed-version)
|
||||
|
||||
## Automatic Deployment
|
||||
|
||||
Any commits or PR requests merged that touch any file in the `docs` directory
|
||||
will trigger a deployment, once the commit lands in the `master` branch.
|
||||
But this deployment will only happen if the job `docs-tests` succeeds.
|
||||
See [circle.yml](circle.yml) job definition file for up-to-date information.
|
||||
|
||||
## Manual Deployment
|
||||
|
||||
You can only deploy the Cypress documentation manually if
|
||||
you are a member of the Cypress organization.
|
||||
|
||||
```shell
|
||||
npm run deploy
|
||||
```
|
||||
|
||||
You can specify all options for deploying via command line arguments.
|
||||
For example to deploy to production and scrape the docs
|
||||
|
||||
```shell
|
||||
npm run deploy -- --environment production --scrape
|
||||
```
|
||||
|
||||
By default, only deploying from `master` branch is set, but you can force
|
||||
deployment by using `--force` option.
|
||||
|
||||
To debug deployment actions, run with `DEBUG=deploy ...` environment variable.
|
||||
|
||||
**note**
|
||||
|
||||
on CI, the deployment and scraping configuration are passed via environment
|
||||
variables `support__aws_credentials_json` and `support__circle_credentials_json`
|
||||
which are just JSON files as strings.
|
||||
|
||||
```shell
|
||||
cat support/.circle-credentials.json | pbcopy
|
||||
```
|
||||
|
||||
## Check Latest Deployed Version
|
||||
|
||||
You can see the latest deployed version, including deployment date at
|
||||
[https://docs.cypress.io/build.json](https://docs.cypress.io/build.json)
|
||||
+38
-3
@@ -17,7 +17,7 @@ permalink: :year/:month/:day/:title/
|
||||
permalink_defaults:
|
||||
|
||||
logo: img/logo.png
|
||||
github: cypress-io/cypress-documentation
|
||||
github: cypress-io/cypress
|
||||
|
||||
# Directory
|
||||
source_dir: source
|
||||
@@ -28,6 +28,9 @@ skip_render:
|
||||
# Generate alias pages for redirecting to posts, pages or URL
|
||||
# You must run `npm run build` for changes to take effect
|
||||
# https://github.com/hexojs/hexo-generator-alias
|
||||
# to redirect a directory, make sure it has a trailing slash (/docs/bob > /docs/bob/)
|
||||
# can also do outside page links here
|
||||
# plugins/index.html: https://github.com/cypress/cypress/wiki/Plugins
|
||||
alias:
|
||||
index.html: guides/getting-started/why-cypress.html
|
||||
guides/index.html: guides/getting-started/why-cypress.html
|
||||
@@ -35,8 +38,40 @@ alias:
|
||||
examples/index.html: examples/recipes/unit-testing-recipe.html
|
||||
dashboard/index.html: dashboard/overview/features-dashboard.html
|
||||
faq/index.html: faq/questions/general-questions-faq.html
|
||||
# can also do outside page links here
|
||||
# plugins/index.html: https://github.com/cypress/cypress/wiki/Plugins
|
||||
docs/known-issues/: guides/references/known-issues.html
|
||||
docs/finding-elements/: guides/core-concepts/introduction-to-cypress.html#Querying-Elements
|
||||
|
||||
#from Google Search Console/Webmaster Tools since 7/3/2017 - https://docs.google.com/spreadsheets/d/1SHhjytMEzIxtdzFNXJHn_Q6p3xFFv_lFw0x_JtNtxt0/edit#gid=1581503717
|
||||
v1.0/docs/promise/: api/utilities/promise.html
|
||||
v1.0/docs/getcookie/: api/commands/getcookie.html
|
||||
v1.0/docs/getcookies/: api/commands/getcookies.html
|
||||
v1.0/docs/nextall/: api/commands/nextall.html
|
||||
v1.0/docs/prevuntil/: api/commands/prevuntil.html
|
||||
v1.0/docs/siblings/: api/commands/siblings.html
|
||||
guides/issuing-commands/: guides/core-concepts/introduction-to-cypress.html#Subject-Management
|
||||
v1.0/docs/clearcookie/: api/commands/clearcookie.html
|
||||
api/commands/api-server.html: api/cypress-api/cypress-server.html
|
||||
guides/installing-and-running/: guides/getting-started/installing-cypress.html
|
||||
docs/screenshots-and-videos/: guides/core-concepts/screenshots-and-videos.html
|
||||
docs/projects/: dashboard/overview/projects-dashboard.html
|
||||
docs/network-requests-xhr/: guides/guides/network-requests.html
|
||||
docs/environment-variables/: guides/guides/environment-variables.html
|
||||
api/commands/cypress-blob.html: api/utilities/blob.html
|
||||
docs/installing-and-running/: guides/getting-started/installing-cypress.html
|
||||
docs/configuration/: guides/references/configuration.html
|
||||
v1.0/docs/exec/: api/commands/exec.html
|
||||
v1.0/docs/setcookie/: api/commands/setcookie.html
|
||||
v1.0/docs/clear/: api/commands/clear.html
|
||||
v1.0/docs/cypress-minimatch/: api/utilities/minimatch.html
|
||||
v1.0/docs/first/: /api/commands/first.html
|
||||
docs/cli/: guides/guides/command-line.html
|
||||
docs/wait/: api/commands/wait.html
|
||||
why-cypress/: guides/getting-started/why-cypress.html
|
||||
guides.html/: guides/getting-started/why-cypress.html
|
||||
guides/references/type/: api/commands/type.html
|
||||
docs/launching-browsers/: guides/core-concepts/launching-browsers.html
|
||||
guides/integrating-cypress/features.html/: guides/guides/continuous-integration.html
|
||||
guides/network-requests-xhr/: guides/guides/network-requests.html
|
||||
|
||||
# Include/Exclude Files/Folders
|
||||
include:
|
||||
|
||||
+112
-52
@@ -10,28 +10,31 @@ const Promise = require('bluebird')
|
||||
const inquirer = require('inquirer')
|
||||
const awspublish = require('gulp-awspublish')
|
||||
const parallelize = require('concurrent-transform')
|
||||
const minimist = require('minimist')
|
||||
const questionsRemain = require('@cypress/questions-remain')
|
||||
const scrape = require('./scrape')
|
||||
const shouldDeploy = require('./should-deploy')
|
||||
const { configFromEnvOrJsonFile } = require('@cypress/env-or-json-file')
|
||||
const R = require('ramda')
|
||||
const la = require('lazy-ass')
|
||||
const is = require('check-more-types')
|
||||
|
||||
const distDir = path.resolve('public')
|
||||
|
||||
const fs = Promise.promisifyAll(require('fs-extra'))
|
||||
const isValidEnvironment = is.oneOf(['production', 'staging'])
|
||||
|
||||
// initialize on existing repo
|
||||
const repo = Promise.promisifyAll(gift(path.resolve('..')))
|
||||
|
||||
console.log()
|
||||
console.log(chalk.yellow('Cypress Docs Deployinator'))
|
||||
console.log(chalk.yellow('==============================\n'))
|
||||
|
||||
function getS3Credentials () {
|
||||
const pathToAwsCreds = path.resolve('support', '.aws-credentials.json')
|
||||
|
||||
return fs.readJsonAsync(pathToAwsCreds)
|
||||
.catch({ code: 'ENOENT' }, (err) => {
|
||||
console.log(chalk.red(`Cannot deploy.\n\nYou are missing your AWS credentials.\n\nPlease add your credentials here: ${pathToAwsCreds}\n`))
|
||||
|
||||
throw err
|
||||
})
|
||||
const key = path.join('support', '.aws-credentials.json')
|
||||
const config = configFromEnvOrJsonFile(key)
|
||||
if (!config) {
|
||||
console.error('⛔️ Cannot find AWS credentials')
|
||||
console.error('Using @cypress/env-or-json-file module')
|
||||
console.error('and key', key)
|
||||
throw new Error('AWS config not found')
|
||||
}
|
||||
return config
|
||||
}
|
||||
|
||||
function getCurrentBranch () {
|
||||
@@ -52,6 +55,18 @@ function promptForDeployEnvironment () {
|
||||
.get('strategy')
|
||||
}
|
||||
|
||||
function cliOrAsk (property, ask, minimistOptions) {
|
||||
// for now isolate the CLI/question logic
|
||||
const askRemaining = questionsRemain({
|
||||
[property]: ask,
|
||||
})
|
||||
const options = minimist(process.argv.slice(2), minimistOptions)
|
||||
return askRemaining(options).then(R.prop(property))
|
||||
}
|
||||
|
||||
const getDeployEnvironment = R.partial(cliOrAsk,
|
||||
['environment', promptForDeployEnvironment])
|
||||
|
||||
function ensureCleanWorkingDirectory () {
|
||||
return repo.statusAsync()
|
||||
.then((status) => {
|
||||
@@ -104,18 +119,20 @@ function publishToS3 (publisher) {
|
||||
}
|
||||
|
||||
function uploadToS3 (env) {
|
||||
return getS3Credentials()
|
||||
la(isValidEnvironment(env), 'invalid environment', env)
|
||||
const bucketName = `bucket-${env}`
|
||||
return Promise.resolve()
|
||||
.then(getS3Credentials)
|
||||
.then((json) => {
|
||||
const bucket = json[`bucket-${env}`] || (function () {
|
||||
throw new Error(`Could not find a bucket for environment: ${env}`)
|
||||
})()
|
||||
la(is.object(json), 'missing S3 credentials object for environment', env)
|
||||
const bucket = json[bucketName]
|
||||
la(is.unemptyString(bucket), 'Could not find a bucket for environment', env)
|
||||
|
||||
console.log('\n', 'Deploying to:', chalk.green(bucket), '\n')
|
||||
|
||||
const publisher = getPublisher(bucket, json.key, json.secret)
|
||||
|
||||
return publishToS3(publisher)
|
||||
return publisher
|
||||
})
|
||||
.then(publishToS3)
|
||||
}
|
||||
|
||||
function prompt (questions) {
|
||||
@@ -143,6 +160,22 @@ function commitMessage (env, branch) {
|
||||
})
|
||||
}
|
||||
|
||||
function prompToScrape () {
|
||||
return prompt({
|
||||
type: 'list',
|
||||
name: 'scrape',
|
||||
message: 'Would you like to scrape the docs? (You only need to do this if they have changed on this deployment)',
|
||||
choices: [
|
||||
{ name: 'Yes', value: true },
|
||||
{ name: 'No', value: false },
|
||||
],
|
||||
})
|
||||
.get('scrape')
|
||||
}
|
||||
|
||||
const getScrapeDocs = R.partial(cliOrAsk,
|
||||
['scrape', prompToScrape, { boolean: 'scrape' }])
|
||||
|
||||
function scrapeDocs (env, branch) {
|
||||
console.log('')
|
||||
|
||||
@@ -155,21 +188,12 @@ function scrapeDocs (env, branch) {
|
||||
|
||||
// if we arent deploying to production return
|
||||
if (env !== 'production') {
|
||||
console.log('Skipping doc scraping because you deployed to:', chalk.cyan('production'))
|
||||
|
||||
console.log('Skipping doc scraping because you deployed to:', chalk.cyan(env))
|
||||
console.log('Only scraping production deploy')
|
||||
return
|
||||
}
|
||||
|
||||
return prompt({
|
||||
type: 'list',
|
||||
name: 'scrape',
|
||||
message: 'Would you like to scrape the docs? (You only need to do this if they have changed on this deployment)',
|
||||
choices: [
|
||||
{ name: 'Yes', value: true },
|
||||
{ name: 'No', value: false },
|
||||
],
|
||||
})
|
||||
.get('scrape')
|
||||
return getScrapeDocs()
|
||||
.then((bool) => {
|
||||
if (bool) {
|
||||
return scrape()
|
||||
@@ -178,13 +202,13 @@ function scrapeDocs (env, branch) {
|
||||
|
||||
}
|
||||
|
||||
getS3Credentials()
|
||||
.then(getCurrentBranch)
|
||||
.then((branch) => {
|
||||
console.log('On branch:', chalk.green(branch), '\n')
|
||||
function deployEnvironmentBranch (env, branch) {
|
||||
la(is.unemptyString(branch), 'missing branch to deploy', branch)
|
||||
la(isValidEnvironment(env), 'invalid deploy environment', env)
|
||||
|
||||
return promptForDeployEnvironment()
|
||||
.then((env) => {
|
||||
const cleanup = () => {
|
||||
console.log('Target environment:', chalk.green(env))
|
||||
console.log('On branch:', chalk.green(branch), '\n')
|
||||
if (env === 'staging') {
|
||||
return env
|
||||
}
|
||||
@@ -195,23 +219,59 @@ getS3Credentials()
|
||||
}
|
||||
|
||||
return ensureCleanWorkingDirectory()
|
||||
.return(env)
|
||||
} else {
|
||||
throw new Error(`Unknown environment: ${env}`)
|
||||
}
|
||||
}
|
||||
|
||||
const uploadEnvToS3 = _.partial(uploadToS3, env)
|
||||
const maybeCommit = () =>
|
||||
commitMessage(env, branch)
|
||||
.catch((err) => {
|
||||
// ignore commit error - do we really need it?
|
||||
console.error('could not make a doc commit')
|
||||
console.error(err.message)
|
||||
})
|
||||
|
||||
return Promise.resolve()
|
||||
.then(cleanup)
|
||||
.then(uploadEnvToS3)
|
||||
.then(maybeCommit)
|
||||
.then(() => scrapeDocs(env, branch))
|
||||
.then(() => {
|
||||
console.log(chalk.yellow('\n==============================\n'))
|
||||
console.log(chalk.bgGreen(chalk.black(' Done Deploying ')))
|
||||
console.log('')
|
||||
})
|
||||
}
|
||||
|
||||
function doDeploy (env) {
|
||||
la(isValidEnvironment(env), 'invalid deploy environment', env)
|
||||
return getCurrentBranch()
|
||||
.then((branch) => deployEnvironmentBranch(env, branch))
|
||||
}
|
||||
|
||||
function deploy () {
|
||||
console.log()
|
||||
console.log(chalk.yellow('Cypress Docs Deployinator'))
|
||||
console.log(chalk.yellow('==============================\n'))
|
||||
|
||||
return getDeployEnvironment()
|
||||
.then((env) => {
|
||||
return uploadToS3(env)
|
||||
.then(() => {
|
||||
return commitMessage(env, branch)
|
||||
})
|
||||
.then(() => {
|
||||
return scrapeDocs(env, branch)
|
||||
return shouldDeploy(env)
|
||||
.then((should) => {
|
||||
if (!should) {
|
||||
console.log('nothing to deploy for environment %s', env)
|
||||
return false
|
||||
}
|
||||
return doDeploy(env)
|
||||
})
|
||||
})
|
||||
})
|
||||
.then(() => {
|
||||
console.log(chalk.yellow('\n==============================\n'))
|
||||
console.log(chalk.bgGreen(chalk.black(' Done Deploying ')))
|
||||
console.log('')
|
||||
})
|
||||
}
|
||||
|
||||
deploy()
|
||||
.catch((err) => {
|
||||
console.error('🔥 deploy failed')
|
||||
console.error(err)
|
||||
process.exit(-1)
|
||||
})
|
||||
|
||||
+34
-28
@@ -2,42 +2,48 @@
|
||||
|
||||
const path = require('path')
|
||||
const chalk = require('chalk')
|
||||
const request = require('request-promise')
|
||||
const Promise = require('bluebird')
|
||||
const request = require('request-promise')
|
||||
const { configFromEnvOrJsonFile } = require('@cypress/env-or-json-file')
|
||||
const { stripIndent } = require('common-tags')
|
||||
const R = require('ramda')
|
||||
|
||||
const fs = Promise.promisifyAll(require('fs-extra'))
|
||||
function checkToken (token) {
|
||||
if (!token) {
|
||||
const example = JSON.stringify({
|
||||
token: 'foobarbaz',
|
||||
})
|
||||
|
||||
console.log(chalk.red(stripIndent`Cannot scrape docs.
|
||||
You are missing your Circle CI API token.
|
||||
It should look like this:
|
||||
|
||||
${example}
|
||||
`))
|
||||
throw new Error('missing token')
|
||||
}
|
||||
}
|
||||
|
||||
function getCircleCredentials () {
|
||||
const pathToCircleCreds = path.resolve('support', '.circle-credentials.json')
|
||||
|
||||
const example = JSON.stringify({
|
||||
token: 'foobarbaz',
|
||||
}, null, 2)
|
||||
|
||||
return fs.readJsonAsync(pathToCircleCreds)
|
||||
.catch({ code: 'ENOENT' }, () => {
|
||||
return {}
|
||||
})
|
||||
.then((json) => {
|
||||
const token = json.token
|
||||
|
||||
if (!token) {
|
||||
console.log(chalk.red(`Cannot scrape docs.\n\nYou are missing your Circle CI token.\n\nPlease add your token here: ${pathToCircleCreds}\n\nIt should look like this:\n\n${example}\n`))
|
||||
}
|
||||
|
||||
return token
|
||||
})
|
||||
const key = path.join('support', '.circle-credentials.json')
|
||||
const config = configFromEnvOrJsonFile(key)
|
||||
if (!config) {
|
||||
console.error('⛔️ Cannot find CircleCI credentials')
|
||||
console.error('Using @cypress/env-or-json-file module')
|
||||
console.error('and key', key)
|
||||
throw new Error('Cannot load CircleCI credentials')
|
||||
}
|
||||
return config.token
|
||||
}
|
||||
|
||||
function scrape () {
|
||||
return getCircleCredentials()
|
||||
return Promise.resolve()
|
||||
.then(getCircleCredentials)
|
||||
.then(R.tap(checkToken))
|
||||
.then((token) => {
|
||||
// bail if we dont have a token
|
||||
if (!token) {
|
||||
console.log('After fixing this problem you can run scraping by itself with this command:', chalk.yellow('npm run scrape'), '\n')
|
||||
return
|
||||
}
|
||||
|
||||
// hmm, how do we trigger workflow?
|
||||
// seems this is not supported yet as of July 10th 2017
|
||||
// https://discuss.circleci.com/t/trigger-workflow-through-rest-api/13931
|
||||
return request({
|
||||
url: 'https://circleci.com/api/v1.1/project/github/cypress-io/docsearch-scraper/',
|
||||
method: 'POST',
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
const got = require('got')
|
||||
const git = require('ggit')
|
||||
const pluralize = require('pluralize')
|
||||
const debug = require('debug')('deploy')
|
||||
const { isEmpty, complement, filter, test, tap, path, all, equals, T } = require('ramda')
|
||||
const la = require('lazy-ass')
|
||||
const is = require('check-more-types')
|
||||
|
||||
const justDocs = filter(test(/^docs\//))
|
||||
const docsChanged = complement(isEmpty)
|
||||
|
||||
const isForced = process.argv.some(equals('--force'))
|
||||
|
||||
/* eslint-disable no-console */
|
||||
/* global Promise */
|
||||
|
||||
const expectedBranch = 'master'
|
||||
|
||||
function isRightBranch () {
|
||||
return git.branchName()
|
||||
.then(tap((name) => {
|
||||
console.log('branch name', name)
|
||||
}))
|
||||
.then(equals(expectedBranch))
|
||||
.then(tap((rightBranch) => {
|
||||
console.log('is right branch?', rightBranch)
|
||||
}))
|
||||
}
|
||||
|
||||
function buildUrlForEnvironment (env) {
|
||||
const urls = {
|
||||
staging: 'https://docs-staging.cypress.io/build.json',
|
||||
production: 'https://docs.cypress.io/build.json',
|
||||
}
|
||||
const url = urls[env]
|
||||
la(url, 'invalid build url for environment', env, url)
|
||||
return url
|
||||
}
|
||||
|
||||
function lastDeployedCommit (env) {
|
||||
const url = buildUrlForEnvironment(env)
|
||||
la(url, 'could not get build url for environment', env)
|
||||
|
||||
debug('checking last deploy info using url %s', url)
|
||||
return got(url, { json: true })
|
||||
.then(path(['body', 'id']))
|
||||
.then(tap((id) => {
|
||||
console.log('docs last deployed for commit', id)
|
||||
}))
|
||||
}
|
||||
|
||||
function changedFilesSince (sha) {
|
||||
return git.changedFilesAfter(sha, expectedBranch)
|
||||
.then(tap((list) => {
|
||||
debug('%s changed since last docs deploy',
|
||||
pluralize('file', list.length, true))
|
||||
debug(list.join('\n'))
|
||||
}))
|
||||
}
|
||||
|
||||
function docsFilesChangedSinceLastDeploy (env) {
|
||||
return lastDeployedCommit(env)
|
||||
.then(changedFilesSince)
|
||||
.then(justDocs)
|
||||
.then(tap((list) => {
|
||||
console.log('%d documentation %s changed since last doc deploy',
|
||||
list.length, pluralize('file', list.length))
|
||||
console.log(list.join('\n'))
|
||||
}))
|
||||
.then(docsChanged)
|
||||
.then(tap((hasDocumentChanges) => {
|
||||
console.log('has document changes?', hasDocumentChanges)
|
||||
}))
|
||||
.catch(T)
|
||||
// if cannot fetch last build, or some other exception
|
||||
// then should deploy!
|
||||
}
|
||||
|
||||
// resolves with boolean true/false
|
||||
function shouldDeploy (env = 'production') {
|
||||
la(is.unemptyString(env), 'missing deploy check environment')
|
||||
|
||||
const questions = [
|
||||
isRightBranch(),
|
||||
docsFilesChangedSinceLastDeploy(env),
|
||||
]
|
||||
return Promise.all(questions)
|
||||
.then(all(equals(true)))
|
||||
.then(Boolean)
|
||||
.then((result) => {
|
||||
if (isForced) {
|
||||
console.log('should deploy is forced!')
|
||||
return isForced
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = shouldDeploy
|
||||
|
||||
if (!module.parent) {
|
||||
// see list of changed files since last docs deploy
|
||||
// lastDeployedCommit()
|
||||
// .then(changedFilesSince)
|
||||
// .then(console.log)
|
||||
// .catch(console.error)
|
||||
|
||||
shouldDeploy()
|
||||
.then((should) => {
|
||||
console.log('should deploy?', should)
|
||||
})
|
||||
.catch(console.error)
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
YAML = require('yamljs')
|
||||
_ = require('lodash')
|
||||
|
||||
DASHBOARD_PATH = "/dashboard/overview/features"
|
||||
DASHBOARD_PATH = "/dashboard/overview/features-dashboard"
|
||||
|
||||
describe "Dashboard", ->
|
||||
beforeEach ->
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
YAML = require('yamljs')
|
||||
_ = require('lodash')
|
||||
|
||||
EXAMPLES_PATH = "/examples/recipes/unit-testing"
|
||||
EXAMPLES_PATH = "/examples/recipes/unit-testing-recipe"
|
||||
|
||||
describe "Examples", ->
|
||||
beforeEach ->
|
||||
|
||||
@@ -3,8 +3,8 @@ _ = require('lodash')
|
||||
|
||||
GUIDES_PATH = "/guides/getting-started/why-cypress"
|
||||
API_PATH = "/api/introduction/api"
|
||||
EXAMPLES_PATH = "/examples/recipes/unit-testing"
|
||||
DASHBOARD_PATH = "/dashboard/overview/features"
|
||||
EXAMPLES_PATH = "/examples/recipes/unit-testing-recipe"
|
||||
DASHBOARD_PATH = "/dashboard/overview/features-dashboard"
|
||||
FAQ_PATH = "/faq/questions/general-questions-faq"
|
||||
|
||||
describe "Documentation", ->
|
||||
|
||||
+12
-3
@@ -1,7 +1,7 @@
|
||||
const util = require('hexo-util')
|
||||
|
||||
function issue (hexo, args) {
|
||||
// {% issue 74 'not currently supported' %}
|
||||
// {% issue 74 "not currently supported" %}
|
||||
|
||||
const num = args[0]
|
||||
|
||||
@@ -10,9 +10,18 @@ function issue (hexo, args) {
|
||||
target: '_blank',
|
||||
}
|
||||
|
||||
const text = args[1] || `issue #${num}`
|
||||
const text = args[1] || `#${num}`
|
||||
|
||||
return util.htmlTag('a', attrs, text)
|
||||
return hexo.render.render({ text: text, engine: 'markdown' })
|
||||
.then((markdown) => {
|
||||
// remove <p> and </p> and \n
|
||||
markdown = markdown
|
||||
.split('<p>').join('')
|
||||
.split('</p>').join('')
|
||||
.split('\n').join('')
|
||||
|
||||
return util.htmlTag('a', attrs, markdown)
|
||||
})
|
||||
}
|
||||
|
||||
function openAnIssue (hexo, args) {
|
||||
|
||||
@@ -1,772 +0,0 @@
|
||||
<div id="landing-page">
|
||||
<div class="row landing-heading">
|
||||
<div class="col-xs-12">
|
||||
<h1>Cypress Documentation</h1>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row highlight-boxes">
|
||||
<div class="col-xs-4">
|
||||
<a href="https://on.cypress.io/guides" class="highlight-box">
|
||||
<i class="fa fa-graduation-cap"></i>
|
||||
<h3>
|
||||
Guides
|
||||
</h3>
|
||||
<p>Walkthroughs and tutorials on the core concepts of testing in Cypress.</p>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-xs-4">
|
||||
<a href="https://on.cypress.io/guides/all-example-apps" class="highlight-box">
|
||||
<i class="fa fa-file-code-o"></i>
|
||||
<h3>
|
||||
Examples
|
||||
</h3>
|
||||
<p>See real applications using Cypress with code examples.</p>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-xs-4">
|
||||
<a href="https://on.cypress.io/api" class="highlight-box">
|
||||
<i class="fa fa-code"></i>
|
||||
<h3>
|
||||
API Commands
|
||||
</h3>
|
||||
<p>Our API provides commands and utilities to drive your tests in the browser.</p>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row highlight-boxes chat">
|
||||
|
||||
<div class="col-xs-12">
|
||||
<a href="https://gitter.im/cypress-io/cypress" class="highlight-box">
|
||||
<i class="fa fa-comments-o"></i>
|
||||
<h3>
|
||||
Chat
|
||||
</h3>
|
||||
<p>Ask questions and get support directly from our team.</p>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<h2>Guides</h2>
|
||||
<p>Walkthroughs and tutorials on the core concepts of testing in Cypress.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-4">
|
||||
<h4>
|
||||
<i class="fa fa-list"></i>
|
||||
Welcome
|
||||
</h4>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/guides/guides">
|
||||
<span>Guides</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/guides/our-goals">
|
||||
<span>Our Goals</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-xs-4">
|
||||
<h4>
|
||||
<i class="fa fa-download"></i>
|
||||
Getting Started
|
||||
</h4>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/guides/installing-and-running">
|
||||
<span>Installing and Running</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/guides/writing-your-first-test">
|
||||
<span>Writing Your First Test</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/guides/using-the-cypress-runner">
|
||||
<span>Using the Cypress Runner</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-xs-4">
|
||||
<h4>
|
||||
<i class="fa fa-folder-open-o"></i>
|
||||
Examples
|
||||
</h4>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/guides/all-example-apps">
|
||||
<span>List of Example Apps</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://github.com/cypress-io/cypress-example-recipes">
|
||||
<span>List of Example Recipes</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-4">
|
||||
<h4>
|
||||
<i class="fa fa-tachometer"></i>
|
||||
Dashboard
|
||||
</h4>
|
||||
</div>
|
||||
<div class="col-xs-4">
|
||||
<h4>
|
||||
<i class="fa fa-graduation-cap"></i>
|
||||
Guides
|
||||
</h4>
|
||||
</div>
|
||||
<div class="col-xs-4">
|
||||
<h4>
|
||||
<i class="fa fa-book"></i>
|
||||
References
|
||||
</h4>
|
||||
</div>
|
||||
<div class="col-xs-4">
|
||||
<ul>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/guides/dashboard-features">
|
||||
<span>Features</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/guides/projects">
|
||||
<span>Projects</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/guides/runs">
|
||||
<span>Runs</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/guides/organizations">
|
||||
<span>Organizations</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-xs-4">
|
||||
<ul>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/guides/configuration">
|
||||
<span>Configuration</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/guides/issuing-commands">
|
||||
<span>Issuing Commands</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/guides/finding-elements">
|
||||
<span>Finding Elements</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/guides/making-assertions">
|
||||
<span>Making Assertions</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/guides/network-requests-xhr">
|
||||
<span>Network Requests</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/guides/continuous-integration">
|
||||
<span>Continuous Integration</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/guides/environment-variables">
|
||||
<span>Environment Variables</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/guides/stubs-spies-clocks">
|
||||
<span>Stubs, Spies, and Clocks</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/guides/using-aliases">
|
||||
<span>Using Aliases</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/guides/creating-fixtures">
|
||||
<span>Creating Fixtures</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/guides/screenshots-and-videos">
|
||||
<span>Screenshots and Videos</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/guides/bundled-tools">
|
||||
<span>Bundled Tools</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/guides/reporters">
|
||||
<span>Reporters</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-xs-4">
|
||||
<ul>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/guides/cli">
|
||||
<span>CLI Tool</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/guides/errors">
|
||||
<span>Error Messages</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/guides/anti-patterns">
|
||||
<span>Anti-patterns</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/guides/known-issues">
|
||||
<span>Known Issues</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/guides/web-security">
|
||||
<span>Web Security</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/guides/browser-management">
|
||||
<span>Browser Management</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/guides/userland-extensions">
|
||||
<span>Userland Extensions</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<h2>API</h2>
|
||||
<p>Our API provides commands and utilities to drive your tests in the browser. </p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<h4>
|
||||
<i class="fa fa-list"></i>
|
||||
Welcome
|
||||
</h4>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/api">
|
||||
<span>Overview</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<h4>
|
||||
<i class="fa fa-code"></i>
|
||||
API
|
||||
</h4>
|
||||
</div>
|
||||
<div class="col-xs-4">
|
||||
<ul>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/and">
|
||||
<span>and</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/as">
|
||||
<span>as</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/blur">
|
||||
<span>blur</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/check">
|
||||
<span>check</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/children">
|
||||
<span>children</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/clear">
|
||||
<span>clear</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/clearcookie">
|
||||
<span>clearCookie</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/clearcookies">
|
||||
<span>clearCookies</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/clearlocalstorage">
|
||||
<span>clearLocalStorage</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/click">
|
||||
<span>click</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/clock">
|
||||
<span>clock</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/closest">
|
||||
<span>closest</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/contains">
|
||||
<span>contains</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/dblclick">
|
||||
<span>dblclick</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/debug">
|
||||
<span>debug</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/document">
|
||||
<span>document</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/each">
|
||||
<span>each</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/end">
|
||||
<span>end</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/eq">
|
||||
<span>eq</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/exec">
|
||||
<span>exec</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/filter">
|
||||
<span>filter</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/find">
|
||||
<span>find</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/first">
|
||||
<span>first</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/fixture">
|
||||
<span>fixture</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/focus">
|
||||
<span>focus</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/focused">
|
||||
<span>focused</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-xs-4">
|
||||
<ul>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/get">
|
||||
<span>get</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/getcookie">
|
||||
<span>getCookie</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/getcookies">
|
||||
<span>getCookies</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/go">
|
||||
<span>go</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/hash">
|
||||
<span>hash</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/hover">
|
||||
<span>hover</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/invoke">
|
||||
<span>invoke</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/its">
|
||||
<span>its</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/last">
|
||||
<span>last</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/location">
|
||||
<span>location</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/log">
|
||||
<span>log</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/next">
|
||||
<span>next</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/nextall">
|
||||
<span>nextAll</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/nextuntil">
|
||||
<span>nextUntil</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/not">
|
||||
<span>not</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/parent">
|
||||
<span>parent</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/parents">
|
||||
<span>parents</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/parentsuntil">
|
||||
<span>parentsUntil</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/pause">
|
||||
<span>pause</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/prev">
|
||||
<span>prev</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/prevall">
|
||||
<span>prevAll</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/prevuntil">
|
||||
<span>prevUntil</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/readfile">
|
||||
<span>readFile</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/reload">
|
||||
<span>reload</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/request">
|
||||
<span>request</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-xs-4">
|
||||
<ul>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/root">
|
||||
<span>root</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/route">
|
||||
<span>route</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/screenshot">
|
||||
<span>screenshot</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/select">
|
||||
<span>select</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/server">
|
||||
<span>server</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/setcookie">
|
||||
<span>setCookie</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/should">
|
||||
<span>should</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/siblings">
|
||||
<span>siblings</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/spread">
|
||||
<span>spread</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/spy">
|
||||
<span>spy</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/stub">
|
||||
<span>stub</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/submit">
|
||||
<span>submit</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/then">
|
||||
<span>then</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/tick">
|
||||
<span>tick</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/title">
|
||||
<span>title</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/type">
|
||||
<span>type</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/uncheck">
|
||||
<span>uncheck</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/url">
|
||||
<span>url</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/viewport">
|
||||
<span>viewport</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/visit">
|
||||
<span>visit</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/wait">
|
||||
<span>wait</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/window">
|
||||
<span>window</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/within">
|
||||
<span>within</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/writefile">
|
||||
<span>writeFile</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/wrap">
|
||||
<span>wrap</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-4">
|
||||
<h4>
|
||||
<i class="fa fa-code"></i>
|
||||
Utilities
|
||||
</h4>
|
||||
</div>
|
||||
<div class="col-xs-4">
|
||||
<h4>
|
||||
<i class="fa fa-code"></i>
|
||||
Cypress API
|
||||
</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-4">
|
||||
<ul>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/cypress-underscore">
|
||||
<span>_</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/cypress-jquery">
|
||||
<span>$</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/cypress-minimatch">
|
||||
<span>minimatch</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/cypress-moment">
|
||||
<span>moment</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/cypress-blob">
|
||||
<span>Blob</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/cypress-promise">
|
||||
<span>Promise</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-xs-4">
|
||||
<ul>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/config">
|
||||
<span>config</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/env">
|
||||
<span>env</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/commands">
|
||||
<span>Commands</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/cookies">
|
||||
<span>Cookies</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/dom">
|
||||
<span>Dom</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://on.cypress.io/api/api-server">
|
||||
<span>Server</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,353 +0,0 @@
|
||||
body {
|
||||
-webkit-text-size-adjust: 100%;
|
||||
text-size-adjust: 100%;
|
||||
color: #333;
|
||||
font-family: "Helvetica Neue", Helvetica, "Segoe UI", Arial, freesans, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
|
||||
font-size: 16px;
|
||||
line-height: 1.6;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.landing-heading {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#jumbotron {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.highlight-boxes {
|
||||
}
|
||||
|
||||
#landing-page .highlight-boxes>div>.highlight-box {
|
||||
margin-top: 40px;
|
||||
display: block;
|
||||
border: 2px solid #ddd;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
color: #ec4f5c;
|
||||
}
|
||||
|
||||
#landing-page .highlight-boxes>div>.highlight-box:hover {
|
||||
color: #d32f3d;
|
||||
border: 2px solid #ccc;
|
||||
}
|
||||
|
||||
#landing-page .highlight-boxes>div:nth-child(2)>.highlight-box {
|
||||
color: #08bf88;
|
||||
}
|
||||
|
||||
#landing-page .highlight-boxes>div:nth-child(2)>.highlight-box:hover {
|
||||
color: #069167;
|
||||
}
|
||||
|
||||
#landing-page .highlight-boxes>div:nth-child(3)>.highlight-box {
|
||||
color: #f5a327;
|
||||
}
|
||||
|
||||
#landing-page .highlight-boxes>div:nth-child(3)>.highlight-box:hover {
|
||||
color: #ce810d;
|
||||
}
|
||||
|
||||
|
||||
#landing-page .highlight-boxes.chat>div>.highlight-box {
|
||||
color: #0094ff;
|
||||
}
|
||||
|
||||
#landing-page .highlight-boxes.chat>div>.highlight-box:hover {
|
||||
color: #428bca;
|
||||
}
|
||||
|
||||
#landing-page .highlight-boxes.chat>div>.highlight-box i, #landing-page .highlight-boxes.chat>div>.highlight-box h3{
|
||||
display: inline;
|
||||
}
|
||||
|
||||
#landing-page .highlight-boxes>div>.highlight-box:hover>p {
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.highlight-box i {
|
||||
display: block;
|
||||
font-size: 40px;
|
||||
}
|
||||
|
||||
#landing-page h1 {
|
||||
font-size: 2em;
|
||||
font-weight: bold;
|
||||
margin-top: 2em;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
#landing-page h2 {
|
||||
margin-top: 2em;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
#landing-page p {
|
||||
color: #888;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
#landing-page h4 {
|
||||
border-bottom: 1px solid #ddd;
|
||||
font-weight: bold;
|
||||
padding-bottom: 10px;
|
||||
margin-top: 2em;
|
||||
}
|
||||
|
||||
#landing-page ul {
|
||||
list-style: none;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
#landing-page ul>li {
|
||||
line-height: 2em; ;
|
||||
}
|
||||
|
||||
#landing-page .row>.col-xs-6:first-child {
|
||||
border-right: 1px solid #ddd;
|
||||
}
|
||||
|
||||
/*-------Tables---------*/
|
||||
|
||||
.docs-content th {
|
||||
border-right: 0;
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
.docs-content td {
|
||||
border-bottom: 0;
|
||||
border-right: 0;
|
||||
}
|
||||
|
||||
.docs-content tbody>tr {
|
||||
border-top: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.docs-content tr:nth-child(even) {
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
|
||||
.docs-content table {
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
|
||||
thead>tr>th {
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
|
||||
thead>tr>th:empty {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/*-------Nav---------*/
|
||||
|
||||
.header .searchbox {
|
||||
top: 11px;
|
||||
}
|
||||
|
||||
/*-------Headers---------*/
|
||||
|
||||
.theme-solid .docs-header h1 {
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
.docs-content [marked] h1 {
|
||||
font-size: 1.6em;
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
.blocks-parent .block>div>div [marked] h1:first-child {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
.docs-content [marked] h2 {
|
||||
font-size: 1.2em;
|
||||
text-transform: none;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.theme-solid .sidebar-nav h4 {
|
||||
font-weight: bold;
|
||||
font-size: 0.75em;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
/*-------Contents UL---------*/
|
||||
|
||||
.blocks-parent .block>div>div [marked] h1:first-child + ul {
|
||||
list-style-type: none;
|
||||
padding-left: 0.25em;
|
||||
}
|
||||
|
||||
.blocks-parent .block>div>div [marked] h1:first-child + ul li {
|
||||
position: relative;
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
.blocks-parent .block>div>div [marked] h1:first-child + ul li i {
|
||||
color: #0094ff;
|
||||
margin-right: 0.25em;
|
||||
position: absolute;
|
||||
top: 3px;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.blocks-parent .block>div>div [marked] h1:first-child + ul>li i.fa-minus {
|
||||
font-size: 0.7em;
|
||||
top: 7px;
|
||||
}
|
||||
|
||||
.blocks-parent .block>div>div [marked] h1:first-child + ul>li>ul {
|
||||
list-style-type: disc;
|
||||
padding-left: 1.5em;
|
||||
font-size: 0.95em;
|
||||
color: #0094ff;
|
||||
}
|
||||
|
||||
.blocks-parent .block>div>div [marked] h1:first-child + ul>li>ul>li {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
/*-------Images---------*/
|
||||
|
||||
.docs-content [marked] p img {
|
||||
border: 1px solid #ddd;
|
||||
box-shadow: 0 2px 2px #eee;
|
||||
margin-top: 1em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
/*-------HR---------*/
|
||||
|
||||
.docs-content .docs-body hr {
|
||||
margin-top: 3em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
/*-------Code tags/variable colors---------*/
|
||||
|
||||
.cm-s-neo .cm-variable {
|
||||
color: #333;
|
||||
}
|
||||
.cm-s-neo .cm-property {
|
||||
color: #795da3;
|
||||
}
|
||||
|
||||
.cm-s-new .cm-string {
|
||||
color: #183691;
|
||||
}
|
||||
|
||||
.cm-s-neo .cm-comment {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.cm-tag {
|
||||
color: #63a35c;
|
||||
}
|
||||
|
||||
.cm-s-neo .cm-string {
|
||||
color: #183691;
|
||||
}
|
||||
|
||||
.cm-s-neo .cm-qualifier {
|
||||
color: #795da3;
|
||||
}
|
||||
|
||||
.cm-tag.cm-bracket {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
/*-------Code styles---------*/
|
||||
|
||||
.block-show-code {
|
||||
padding: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.block-show-code .code-tabs a {
|
||||
color: #aaa;
|
||||
font-size: 0.90em;
|
||||
}
|
||||
|
||||
.block-show-code .code-tabs a:hover {
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
.block-show-code pre {
|
||||
margin-top: 0;
|
||||
margin-bottom: 16px;
|
||||
font: 12px Consolas, "Liberation Mono", Menlo, Courier, monospace;
|
||||
overflow: auto;
|
||||
background-color: #f7f7f7;
|
||||
box-shadow: 0 0 0 0 #fff;
|
||||
padding: 15px;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
.block-show-code pre>code {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-size: 1em;
|
||||
word-break: normal;
|
||||
white-space: pre;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
a>code {
|
||||
color: #0094ff;
|
||||
}
|
||||
|
||||
.theme-solid .sidebar-nav ul {
|
||||
font-size: 0.90em;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace;
|
||||
font-size: 12px;
|
||||
padding: 0;
|
||||
padding-top: 0.2em;
|
||||
padding-bottom: 0.2em;
|
||||
margin: 0;
|
||||
font-size: 0.85em;
|
||||
background-color: rgba(0,0,0,0.04);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
code:before,
|
||||
code:after {
|
||||
letter-spacing: -0.2em;
|
||||
content: "\00a0";
|
||||
}
|
||||
|
||||
.block-show-code pre {
|
||||
padding: 16px;
|
||||
overflow: auto;
|
||||
font-size: 0.85em;
|
||||
line-height: 1.45;
|
||||
background-color: #f7f7f7;
|
||||
border-radius: 3px;
|
||||
border: 0;
|
||||
margin-bottom: 0;
|
||||
word-break: normal;
|
||||
word-wrap: normal;
|
||||
}
|
||||
|
||||
|
||||
.block-show-code pre code {
|
||||
display: inline;
|
||||
max-width: initial;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
overflow: initial;
|
||||
line-height: inherit;
|
||||
word-wrap: normal;
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.block-show-code pre code:before,
|
||||
.block-show-code pre code:after {
|
||||
content: normal;
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
slug: guides
|
||||
excerpt: Walkthroughs and tutorials on the core concepts of testing in Cypress.
|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "info",
|
||||
"body": "[Explore talks, blogs, and podcasts about testing in Cypress.](https://www.cypress.io/explore)",
|
||||
"title": "Want to see Cypress in action?"
|
||||
}
|
||||
[/block]
|
||||
|
||||
---
|
||||
|
||||
| Getting Started | |
|
||||
| -------------------- | -- |
|
||||
| [Installing & Running](https://on.cypress.io/guides/installing-and-running) | Get Cypress downloaded and in your project |
|
||||
| [Writing your First Test](https://on.cypress.io/guides/writing-your-first-test) | Write a test in Cypress |
|
||||
| [Using the Cypress Running](https://on.cypress.io/guides/using-the-cypress-runner) | Review test commands, instrumentation and your application under test. |
|
||||
|
||||
| Examples | |
|
||||
| -------------------- | -- |
|
||||
| [List of Example Apps](https://on.cypress.io/guides/all-example-apps) | See Cypress being used in existing projects. |
|
||||
| [List of Example Recipes](https://github.com/cypress-io/cypress-example-recipes) | Recipes for testing common scenarios in Cypress. |
|
||||
|
||||
| Dashboard | |
|
||||
| -------------------- | -- |
|
||||
| [Features](https://on.cypress.io/guides/dashboard-features) | An overview of our Dashboard. |
|
||||
| [Projects](https://on.cypress.io/guides/projects) | Manage your Projects and configure them to record runs. |
|
||||
| [Runs](https://on.cypress.io/guides/runs) | View your Recorded Runs. |
|
||||
| [Organizations](https://on.cypress.io/guides/organizations) | Manage your Organizations. |
|
||||
|
||||
| Guides | |
|
||||
| -------------------- | -- |
|
||||
| [Configuration](https://on.cypress.io/guides/configuration) | Configure global, network, directory, viewport and animation options |
|
||||
| [Issuing Commands](https://on.cypress.io/guides/issuing-commands) | Issue actions to be performed in your test |
|
||||
| [Finding Elements](https://on.cypress.io/guides/finding-elements) | Traverse the DOM, find elements, make assertions |
|
||||
| [Making Assertions](https://on.cypress.io/guides/making-assertions) | Set expectations for the behavior |
|
||||
| [Network Requests](https://on.cypress.io/guides/network-requests-xhr) | Handle AJAX/XHR Requests |
|
||||
| [Continuous Integration](https://on.cypress.io/guides/continuous-integration) | Integrate with CI Providers |
|
||||
| [Environment Variables](https://on.cypress.io/guides/environment-variables) | Set environment variables |
|
||||
| [Stubs, Spies and Clocks](https://on.cypress.io/guides/stubs-spies-clocks) | Learn about when and why to use stubs, spies, and control clock time |
|
||||
| [Using Aliases](https://on.cypress.io/guides/using-aliases) | Represent an object as alias |
|
||||
| [Creating Fixtures](https://on.cypress.io/guides/creating-fixtures) | Mock data in fixtures |
|
||||
| [Screenshots and Videos](https://on.cypress.io/guides/screenshots-and-videos) | Capture screenshots and videos of your test run |
|
||||
| [Bundled Tools](https://on.cypress.io/guides/bundled-tools) | What is Cypress built on |
|
||||
| [Reporters](https://on.cypress.io/guides/reporters) | Customize test results with reporters |
|
||||
|
||||
| References | |
|
||||
| -------------------- | -- |
|
||||
| [CLI Tool](https://on.cypress.io/guides/cli) | Cypress CLI Tool for programmatically interacting with the Desktop Application |
|
||||
| [Error Messages](https://on.cypress.io/guides/errors) | Error messages |
|
||||
| [Anti-patterns](https://on.cypress.io/guides/anti-patterns) | Anti-patterns |
|
||||
| [Known Issues](https://on.cypress.io/guides/known-issues) | Known Issues |
|
||||
| [Web Security](https://on.cypress.io/guides/web-security) | Web Security |
|
||||
| [Browser Management](https://on.cypress.io/guides/browser-management) | Browser Management |
|
||||
| [Userland Extensions](https://on.cypress.io/guides/userland-extensions) | Userland Extensions |
|
||||
@@ -1,54 +0,0 @@
|
||||
slug: our-goals
|
||||
excerpt: What we believe and why
|
||||
|
||||
# Contents
|
||||
|
||||
- :fa-angle-right: [Problems with current front-end testing tools](#section-problems-with-current-front-end-testing-tools)
|
||||
- :fa-angle-right: [Goals of Cypress](#section-goals-of-cypress)
|
||||
|
||||
***
|
||||
|
||||
# Problems with current front-end testing tools
|
||||
|
||||
There are clear benefits to testing code. But many *web applications* are not fully covered in tests. Why? There are many testing tools for the front end, but most (if not all) suffer from some problems.
|
||||
|
||||
* **Poor Setup:** Testing environments take too long to setup.
|
||||
* **Unreliable:** Tests are often brittle, and randomly fail.
|
||||
* **Time consuming:** Running an entire test suite takes too long (sometimes hours).
|
||||
* **No Cross Browser Support:** Debugging across browsers and browser versions is very time consuming.
|
||||
* **Obscure Error Messages:** Error messages are obtuse, indirect, and increase the time it takes to debug.
|
||||
* **Coupled dependencies:** Integration tests are often coupled directly to the server.
|
||||
* **Lacking fixture support:** Handling mock data, or fixtures, is difficult.
|
||||
* **Don't encourage TDD:** Testing often occurs *after* features are built because there isn't an apparent test-driven development (TDD) flow.
|
||||
* **Lack of integration testing:** Even if unit testing JavaScript is reasonably simple, unit testing alone does not verify your application is fully functioning.
|
||||
* **Async hell:** Handling complicated asynchronous logic that is found on most modern single-page JavaScript applications is impossible.
|
||||
* **No Visibility:** Testing through a console is obscure and doesn't give full visibility on the problems your users will face using your web application.
|
||||
* **Buggy:** Selenium drivers differ in implementation details which means code breaks across different browsers and browser versions.
|
||||
|
||||
These are just a few reasons why it's often difficult to test modern web applications. Often it takes longer to write a passing test for a feature, than to actually build the feature.
|
||||
|
||||
There is a lot of mental friction when writing tests. Testing just becomes another layer to cut through. Because testing is often brittle, we lose confidence that our tests are delivering any additional value.
|
||||
|
||||
Because of this complexity, most organizations have an entire Quality Assurance (QA) department dedicated to these tasks.
|
||||
|
||||
> Cypress aims to solve the biggest difficulties when it comes to testing web applications. It aims to reduce the mental effort required to write tests. Cypress works in your typical development workflow (in the browser) and allows you to see your application while it's being tested. This enables you to practice TDD since there is no context shift between testing and development.
|
||||
|
||||
***
|
||||
|
||||
# Goals of Cypress
|
||||
|
||||
* **Easy Setup:** Allow you to write your first test in less than 5 minutes.
|
||||
* **Automated:** Allow you to drive your application with tests instead of manual interactions.
|
||||
* **Easy Integration:** Require zero code changes to your existing application.
|
||||
* **In-Browser Testing:** Integrate testing within your normal development process.
|
||||
* **JavaScript:** Allow you to write your test suite in JavaScript.
|
||||
* **Enjoyable:** Make writing tests an enjoyable, fun experience.
|
||||
* **Opinionated:** Encourage writing good tests.
|
||||
* **Clear error messages:** Provide **clear**, debuggable error messages.
|
||||
* **Intelligent Network Requests:** Make dealing with `AJAX/XHR` ridiculously simple.
|
||||
* **Cross-browser support:** Provide cross-browser testing and debugging **without** leaving Google Chrome and work in all modern browsers (`IE11+`)
|
||||
* **CI Integration:** Instantly integrate any Continuous Integration provider.
|
||||
* **Accessible:** Work with any JavaScript framework (current and future).
|
||||
* **Flexible:** Replace server side testing tools like `Capybara`.
|
||||
* **Integrate with Server:** Allow you to communicate directly to a backend server for seeding / querying.
|
||||
* **Elimate Selenium:** Eliminate the need to code or deal with `Selenium`.
|
||||
@@ -1,153 +0,0 @@
|
||||
slug: installing-and-running
|
||||
excerpt: Quick start guide for using Cypress
|
||||
|
||||
# Contents
|
||||
|
||||
- :fa-angle-right: [System Requirements](#section-system-requirements)
|
||||
- :fa-angle-right: [Installing](#section-installing)
|
||||
- [Command Line](#section-command-line-tool)
|
||||
- [Direct Download](#section-direct-download)
|
||||
- :fa-angle-right: [Logging In](#section-logging-in)
|
||||
- :fa-angle-right: [Adding Projects](#section-adding-projects)
|
||||
- :fa-angle-right: [Running Headlessly](#section-running-headlessly)
|
||||
|
||||
***
|
||||
|
||||
# System Requirements
|
||||
|
||||
Cypress is a desktop application. This desktop application is the equivalent replacement of Selenium Server and must be running to test in Cypress.
|
||||
|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "info",
|
||||
"body": "The desktop application manages your local projects. The actual testing will be done in a **browser**, not the desktop application"
|
||||
}
|
||||
[/block]
|
||||
|
||||
The desktop application can be installed in the following operating systems:
|
||||
|
||||
| Operating System |
|
||||
| ------ |
|
||||
| Linux |
|
||||
| OSX |
|
||||
|
||||
Windows is [(not yet working)](https://github.com/cypress-io/cypress/issues/74).
|
||||
|
||||
There are no dependencies to install the Desktop Application, although if you want to [use Cypress from the Command Line](https://github.com/cypress-io/cypress-cli) you will need to have `node` installed.
|
||||
|
||||
***
|
||||
|
||||
# Installing
|
||||
|
||||
You can install Cypress in 2 different ways:
|
||||
* [Cypress CLI Tool](https://github.com/cypress-io/cypress-cli)
|
||||
* [Direct Download](#section-direct-download)
|
||||
|
||||
## Command Line Tool
|
||||
|
||||
```shell
|
||||
## install the Cypress CLI tool
|
||||
npm install -g cypress-cli
|
||||
|
||||
## install the Desktop Cypress app
|
||||
cypress install
|
||||
```
|
||||
|
||||

|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "info",
|
||||
"body": "The Cypress CLI Tool contains many additional options such as installing a specific Cypress version.\n\nSee the [Cypress CLI Docs](https://github.com/cypress-io/cypress-cli#installation).",
|
||||
"title": "Cypress CLI"
|
||||
}
|
||||
[/block]
|
||||
|
||||
## Direct Download
|
||||
|
||||
You can download Cypress directly [here.](http://download.cypress.io/desktop)
|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "danger",
|
||||
"body": "The vast majority of the time, Cypress will install correctly. But if you're on Linux you [might have to install some other dependencies](https://on.cypress.io/guides/continuous-integration#section-dependencies).",
|
||||
"title": "Woops, I got an error installing"
|
||||
}
|
||||
[/block]
|
||||
|
||||
***
|
||||
|
||||
# Logging In
|
||||
|
||||
After installing, you will need to login to Cypress. Login currently requires a [Github](https://github.com/) account, if you do not have an account, you will have to [create one](https://github.com/join) to use Cypress.
|
||||
|
||||
**To Login:**
|
||||
|
||||
- Open the Cypress App -- just double click the app from your OS application's folder.
|
||||
- Click "Log In with GitHub".
|
||||
- Authorize GitHub access to your account.
|
||||
|
||||

|
||||
|
||||
## Your email: `jane.doe@gmail.com` has not been authorized.
|
||||
|
||||
While in beta, the Cypress team has to whitelist the email address associated with your GitHub account in order for you to use Cypress.
|
||||
|
||||
- If you received this error and have never filled out our [Early Adopter Access](http://goo.gl/forms/4vEMwj8LNT) form, fill out this form with the email in the error so we can whitelist it. You will receive an invite during one of our future Beta invites.
|
||||
- If you received this error after receiving a Beta invite email from Cypress, please send an email to **support@cypress.io** telling us the email in the error so we can whitelist it.
|
||||
|
||||
***
|
||||
|
||||
# Adding Projects
|
||||
|
||||
After successfully logging in, you will need to add the project(s) you want to write Cypress tests in.
|
||||
|
||||
- Click :fa-plus: Add Project.
|
||||
|
||||

|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "info",
|
||||
"body": "Projects added in our Desktop Application are strictly local to your computer. They are not tracked in any way by Cypress servers and do not communicate with us until they are [setup to be recorded](https://on.cypress.io/guides/projects#section-recording-runs)."
|
||||
}
|
||||
[/block]
|
||||
|
||||
***
|
||||
|
||||
# Running Tests from the GUI
|
||||
|
||||
To run tests:
|
||||
|
||||
- Click on the project.
|
||||
- You will then come to a page listing all files in your project's `cypress/integration` folder. If it's a new project, you'll see a message about the folder structure generated for you and also an `example_spec.js` file.
|
||||
- Click on the test file you want to run or click "Run All Tests".
|
||||
- After opening your project in Cypress, Cypress will generate a `cypress.json` file in your project:
|
||||
|
||||
```text
|
||||
<your project>/cypress.json
|
||||
```
|
||||
|
||||
This file contains a unique `projectId` and allows for specific Cypress [configuration](https://on.cypress.io/guides/configuration). It is okay to commit this file to `git`.
|
||||
|
||||
***
|
||||
|
||||
# Running Headlessly
|
||||
|
||||
While you'll find yourself working primarily in the GUI, it is helpful to be able to run your tests headlessly.
|
||||
|
||||
Once you have the [Cypress CLI Tool](https://github.com/cypress-io/cypress-cli) installed, you can simply execute:
|
||||
|
||||
```shell
|
||||
cypress run
|
||||
```
|
||||
|
||||
Additionally you can specify:
|
||||
|
||||
- a single test file
|
||||
- [a specific reporter and reporter options](https://on.cypress.io/guides/reporters)
|
||||
- a different port
|
||||
- environment variables
|
||||
|
||||
You can [read about all of these options](https://github.com/cypress-io/cypress-cli#cypress-run-1) which are documented on the Cypress CLI tool.
|
||||
@@ -1,255 +0,0 @@
|
||||
slug: writing-your-first-test
|
||||
excerpt: Walkthrough writing your first test
|
||||
|
||||
# Contents
|
||||
|
||||
- :fa-angle-right: [Folder Structure](#section-folder-structure)
|
||||
- :fa-angle-right: [Test Files](#section-test-files)
|
||||
- :fa-angle-right: [How to Write Tests](#section-how-to-write-tests)
|
||||
- [BDD Interface](#section-bdd-interface)
|
||||
- [Hooks](#section-hooks)
|
||||
- [Excluding and Including Tests](#section-excluding-and-including-tests)
|
||||
- [Dynamically Generate Tests](#section-dynamically-generate-tests)
|
||||
|
||||
***
|
||||
|
||||
# Folder Structure
|
||||
|
||||
After adding a new project, Cypress will automatically scaffold out a suggested folder structure. By default it will create:
|
||||
|
||||
```text
|
||||
/cypress
|
||||
/cypress/fixtures
|
||||
/cypress/integration
|
||||
/cypress/support
|
||||
```
|
||||
|
||||
Cypress also adds placeholder files to help get you started with examples in each folder.
|
||||
|
||||
**Example JSON fixture**
|
||||
```text
|
||||
/cypress/fixtures/example.json
|
||||
```
|
||||
|
||||
**Example Integration Test**
|
||||
```text
|
||||
/cypress/integration/example_spec.js
|
||||
```
|
||||
|
||||
**Example JavaScript Support Files**
|
||||
```text
|
||||
/cypress/support/commands.js
|
||||
/cypress/support/defaults.js
|
||||
/cypress/support/index.js
|
||||
```
|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "info",
|
||||
"body": "[Check out our example recipe using support files to import common utilities](https://github.com/cypress-io/cypress-example-recipes/blob/master/cypress/integration/es2015_commonjs_modules_spec.js)",
|
||||
"title": "Using Support files for common functionality"
|
||||
}
|
||||
[/block]
|
||||
|
||||
**Configuring Folder Structure**
|
||||
|
||||
While Cypress allows for configuration of where your tests, fixtures, and support files are located, if you're starting your first project, we recommend you use the above structure.
|
||||
|
||||
You can modify the folder configuration in your `cypress.json`. See [configuration](https://on.cypress.io/guides/configuration) for more detail.
|
||||
|
||||
***
|
||||
|
||||
# Test Files
|
||||
|
||||
Test files may be written as `.js`, `.jsx`, `.coffee`, or `cjsx` files.
|
||||
|
||||
Cypress supports ES2015, ES2016, ES2017, and JSX. ES2015 modules and CommonJS modules are also supported, so you can `import` or `require` both npm packages and local modules.
|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "info",
|
||||
"body": "[Check out our example recipe using ES2015 and CommonJS modules](https://github.com/cypress-io/cypress-example-recipes/blob/master/cypress/integration/es2015_commonjs_modules_spec.js)",
|
||||
"title": "Importing ES2015 or CommonJS modules"
|
||||
}
|
||||
[/block]
|
||||
|
||||
To see an example of every command used in Cypress, open the [`example_spec.js`](https://github.com/cypress-io/cypress-example-kitchensink/blob/master/cypress/integration/example_spec.js) within your `cypress/integration` folder.
|
||||
|
||||
To start writing tests for your app, simply create a new file like `app_spec.js` within your `cypress/integration` folder. Refresh your tests list in the Cypress GUI and your new file should have appeared in the list.
|
||||
|
||||
***
|
||||
|
||||
# How to Write Tests
|
||||
|
||||
Cypress is built on top of [Mocha](https://on.cypress.io/guides/bundled-tools#section-mocha) and uses its `bdd` interface. Tests you write in Cypress will mostly adhere to this style.
|
||||
|
||||
If you're familiar with writing tests in JavaScript, then writing tests in Cypress will be a breeze.
|
||||
|
||||
We're still working on introductory docs and videos. If you want to see Cypress in action, [check out some examples](https://on.cypress.io/guides/all-example-apps) of applications using Cypress tests and [check out some example recipes we've written](https://github.com/cypress-io/cypress-example-recipes) for special use cases.
|
||||
|
||||
## BDD Interface
|
||||
|
||||
The BDD interface borrowed from [Mocha](https://on.cypress.io/guides/bundled-tools#section-mocha) provides `describe()`, `context()`, `it()` and `specify()`.
|
||||
|
||||
`context()` is identical to `describe()` and `specify()` is identical to `it()`, so choose whatever terminology works best for you.
|
||||
|
||||
```javascript
|
||||
// -- Start: Our Application Code --
|
||||
function add (a, b) {
|
||||
return a + b
|
||||
}
|
||||
|
||||
function subtract (a, b) {
|
||||
return a - b
|
||||
}
|
||||
|
||||
function divide (a, b) {
|
||||
return a / b
|
||||
}
|
||||
|
||||
function multiply (a, b) {
|
||||
return a * b
|
||||
}
|
||||
// -- End: Our Application Code --
|
||||
|
||||
// -- Start: Our Cypress Tests --
|
||||
describe('Unit test our math functions', function() {
|
||||
context('math', function() {
|
||||
it('can add numbers', function() {
|
||||
expect(add(1, 2)).to.eq(3)
|
||||
})
|
||||
|
||||
it('can subtract numbers', function() {
|
||||
expect(subtract(5, 12)).to.eq(-7)
|
||||
})
|
||||
|
||||
specify('can divide numbers', function() {
|
||||
expect(divide(27, 9)).to.eq(3)
|
||||
})
|
||||
|
||||
specify('can muliple numbers', function() {
|
||||
expect(multiply(5, 4)).to.eq(20)
|
||||
})
|
||||
})
|
||||
})
|
||||
// -- End: Our Cypress Tests --
|
||||
|
||||
```
|
||||
|
||||
## Hooks
|
||||
|
||||
Cypress also provides hooks (borrowed from [Mocha](https://on.cypress.io/guides/bundled-tools#section-mocha)).
|
||||
|
||||
These are helpful to set conditions that you want run before a set of tests or before each test. They're also helpful to clean up conditions after a set of tests or after each test.
|
||||
|
||||
```javascript
|
||||
describe('Hooks', function() {
|
||||
before(function() {
|
||||
// runs before all tests in this block
|
||||
})
|
||||
|
||||
after(function() {
|
||||
// runs after all tests in this block
|
||||
})
|
||||
|
||||
beforeEach(function() {
|
||||
// runs before each test in this block
|
||||
})
|
||||
|
||||
afterEach(function() {
|
||||
// runs after each test in this block
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
**The order of hook and test execution is as follows:**
|
||||
|
||||
- All `before()` hooks run (once)
|
||||
- Any `beforeEach()` hooks run
|
||||
- Tests run
|
||||
- Any `afterEach()` hooks run
|
||||
- All `after()` hooks run (once)
|
||||
|
||||
## Excluding and Including Tests
|
||||
|
||||
To run a specified suite or test simply append `.only()` to the function. All nested suites will also be executed.
|
||||
|
||||
```javascript
|
||||
// -- Start: Our Application Code --
|
||||
function fizzbuzz (num) {
|
||||
if (num % 3 === 0 && num % 5 === 0) {
|
||||
return "fizzbuzz"
|
||||
}
|
||||
|
||||
if (num % 3 === 0) {
|
||||
return "fizz"
|
||||
}
|
||||
|
||||
if (num % 5 === 0) {
|
||||
return "buzz"
|
||||
}
|
||||
}
|
||||
// -- End: Our Application Code --
|
||||
|
||||
// -- Start: Our Cypress Tests --
|
||||
describe('Unit Test FizzBuzz', function(){
|
||||
beforeEach(function(){
|
||||
this.numsExpectedToEq = (arr, expected) =>
|
||||
arr.forEach((num) => {
|
||||
expect(fizzbuzz(num)).to.eq(expected)
|
||||
})
|
||||
})
|
||||
|
||||
// Only this test and it's beforeEach code would run
|
||||
it.only('returns "fizz" when number is multiple of 3', function(){
|
||||
this.numsExpectedToEq([9, 12, 18], "fizz")
|
||||
})
|
||||
|
||||
it('returns "buzz" when number is multiple of 5', function(){
|
||||
this.numsExpectedToEq([10, 20, 25], "buzz")
|
||||
})
|
||||
|
||||
it('returns "fizzbuzz" when number is multiple of both 3 and 5', function(){
|
||||
this.numsExpectedToEq([15, 30, 60], "fizzbuzz")
|
||||
})
|
||||
})
|
||||
|
||||
```
|
||||
|
||||
To skip a specified suite or test simply append `.skip()` to the function. All nested suites will also be skipped.
|
||||
|
||||
```javascript
|
||||
it.skip('returns "fizz" when number is multiple of 3', function(){
|
||||
this.numsExpectedToEq([9, 12, 18], "fizz")
|
||||
})
|
||||
```
|
||||
|
||||
## Dynamically Generate Tests
|
||||
|
||||
You can dynamically generate tests using JavaScript.
|
||||
|
||||
```javascript
|
||||
describe('if your app uses jQuery', function(){
|
||||
['mouseover', 'mouseout', 'mouseenter', 'mouseleave'].forEach((event) => {
|
||||
it('triggers event: ' + event, function(){
|
||||
// if your app uses jQuery, then we can trigger a jQuery
|
||||
// event that causes the event callback to fire
|
||||
cy
|
||||
.get('#with-jquery').invoke('trigger', event)
|
||||
.get('#messages').should('contain', 'the event ' + event + 'was fired')
|
||||
})
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
The code above will produce a suite with 4 tests:
|
||||
|
||||
```bash
|
||||
> if your app uses jQuery
|
||||
> triggers event: 'mouseover'
|
||||
> triggers event: 'mouseout'
|
||||
> triggers event: 'mouseenter'
|
||||
> triggers event: 'mouseleave'
|
||||
```
|
||||
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
slug: using-the-cypress-runner
|
||||
excerpt: Review test commands, instrumentation and your application under test.
|
||||
|
||||
# Contents
|
||||
|
||||
- :fa-angle-right: [Overview](#section-overview)
|
||||
- :fa-angle-right: [Test Runner Components](#section-test-runner-components)
|
||||
- [Command Log](#section-command-log)
|
||||
- [Instrument Panel](#section-instrument-panel)
|
||||
- [Application Under Test](#section-application-under-test)
|
||||
|
||||
***
|
||||
|
||||
# Overview
|
||||
|
||||
Cypress runs tests in a unique interactive runner that allows you to see commands as they execute while also viewing the application under test.
|
||||
|
||||

|
||||
|
||||
***
|
||||
|
||||
# Test Runner Components:
|
||||
|
||||
## Command Log
|
||||
|
||||
The lefthand side of the test runner is a visual representation of your test suite. Each test block is properly nested and each test, when clicked, displays every Cypress command and assertion executed within the test's block as well as any command or assertion executed in relevant `before`, `beforeEach`, `afterEach`, and `after` hooks.
|
||||
|
||||
<img width="436" alt="screen shot 2017-03-06 at 2 03 49 pm" src="https://cloud.githubusercontent.com/assets/1271364/23626797/1a6a59f6-027c-11e7-9ca5-7451b97557a9.png">
|
||||
|
||||
**Hovering on Commands**
|
||||
|
||||
Each command and assertion, when hovered over, restores the Application Under Test (righthand side) to the state it was in when that command executed. This allows you to 'time-travel' back to previous states of your application when testing.
|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "info",
|
||||
"body": "By default, Cypress keeps 50 tests worth of snapshots and command data for time traveling. If you are seeing extremely high memory consumption in your browser, you may want to lower the `numTestsKeptInMemory` in your [configuration](https://on.cypress.io/guides/configuration#section-global)."
|
||||
}
|
||||
[/block]
|
||||
|
||||
|
||||
**Clicking on Commands**
|
||||
|
||||
Each command, assertion, or error, when clicked on, displays extra information in the dev tools console. Clicking also 'pins' the Application Under Test (righthand side) to it's previous state when the command executed.
|
||||
|
||||

|
||||
|
||||
|
||||
***
|
||||
|
||||
## Instrument Panel
|
||||
|
||||
For certain commands like [`cy.route()`](https://on.cypress.io/api/route), [`cy.stub()`](https://on.cypress.io/api/stub), and [`cy.spy()`](https://on.cypress.io/api/spy), an extra instrument panel is displayed above the test to give more information about the state of your tests.
|
||||
|
||||
*Routes*
|
||||
|
||||

|
||||
|
||||
*Stubs*
|
||||
|
||||

|
||||
|
||||
*Spies*
|
||||
|
||||

|
||||
|
||||
## Application Under Test
|
||||
|
||||
The righthand side of the test runner is used to display the Application Under Test (AUT: the application that was navigated to using a [`cy.visit()`](https://on.cypress.io/api/visit) or any subsequent routing calls made from the visited application.
|
||||
|
||||
In the example below, we wrote the following code in our test file:
|
||||
|
||||
```javascript
|
||||
cy.visit('https://example.cypress.io')
|
||||
|
||||
cy.title().should('include', 'Kitchen Sink')
|
||||
```
|
||||
|
||||
In the corresponding Application Preview below, you can see `https://example.cypress.io` is being displayed in the righthand side. Not only is the application visible, but it is fully interactable. You can open your developer tools to inspect elements as you would your normal application. The DOM is completely available for debugging.
|
||||
|
||||

|
||||
|
||||
The AUT also displays in the size and orientation specified in your tests. You can change the size or orientation with the [`cy.viewport()`](https://on.cypress.io/api/viewport) command or in your [Cypress configuration](https://docs.cypress.io/docs/configuration#section-viewport). If the AUT does not fit within the current browser window, it is scaled appropriately to fit within the window.
|
||||
|
||||
The current size and scale of the AUT is displayed in the top right corner of the window.
|
||||
|
||||
The image below shows that our application is displaying at `1000px` width, `660px` height and scaled to `100%`.
|
||||
|
||||

|
||||
|
||||
|
||||
*Note: The righthand side may also be used to display syntax errors in your test file that prevent the tests from running.*
|
||||
|
||||

|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
slug: all-example-apps
|
||||
excerpt: Applications using Cypress
|
||||
|
||||
Name | JS | Server | CI
|
||||
--- | --- | --- |
|
||||
[Kitchen Sink](https://github.com/cypress-io/cypress-example-kitchensink) | jQuery | Node | TravisCI, CircleCI, Codeship
|
||||
[TodoMVC](https://github.com/cypress-io/cypress-example-todomvc) | React | Node | TravisCI, CircleCI
|
||||
[PieChopper](https://github.com/cypress-io/cypress-example-piechopper) | Angular | Node | TravisCI, CircleCI
|
||||
|
||||
***
|
||||
|
||||
# [Kitchen Sink](https://github.com/cypress-io/cypress-example-kitchensink)
|
||||
|
||||
This is an example app is used to showcase every command available in Cypress. The [tests](https://github.com/cypress-io/examples-kitchen-sink/blob/master/cypress/integration/example_spec.js) cover all of the following features:
|
||||
|
||||

|
||||
|
||||
**Features:**
|
||||
|
||||
- Querying
|
||||
- Traversal
|
||||
- Actions
|
||||
- Viewport
|
||||
- Navigation
|
||||
- Aliasing
|
||||
- Waiting
|
||||
- Network Requests
|
||||
- Fixtures
|
||||
- Local Storage
|
||||
- Cookies
|
||||
|
||||
***
|
||||
|
||||
# [TodoMVC](https://github.com/cypress-io/cypress-example-todomvc)
|
||||
|
||||
This repo compares [Cypress Tests](https://github.com/cypress-io/cypress-example-todomvc/blob/master/cypress/integration/app_spec.js) to [official TodoMVC Tests](https://github.com/tastejs/todomvc/blob/master/tests/test.js). This gives you a good comparison of writing and running tests in Cypress versus vanilla Selenium.
|
||||
|
||||

|
||||
|
||||
**Features:**
|
||||
|
||||
- Querying
|
||||
- Custom Commands
|
||||
- Aliasing
|
||||
- Navigation
|
||||
|
||||
***
|
||||
|
||||
# [PieChopper](https://github.com/cypress-io/cypress-example-piechopper)
|
||||
|
||||
This is a single page application with a decent amount of features. The [tests](https://github.com/cypress-io/cypress-example-piechopper/blob/master/cypress/integration/app_spec.js) involve a lot of form submissions.
|
||||
|
||||

|
||||
|
||||
**Features:**
|
||||
|
||||
- Querying
|
||||
- Forms
|
||||
- Scroll Tests
|
||||
- Network Requests
|
||||
- XHR Stubbing
|
||||
- Dialogs
|
||||
- Responsive Tests
|
||||
@@ -1,99 +0,0 @@
|
||||
slug: dashboard-features
|
||||
excerpt: An overview of our Dashboard
|
||||
|
||||
# Contents
|
||||
|
||||
- :fa-angle-right: [What is the Dashboard?](#section-what-is-the-dashboard-)
|
||||
- [Example Projects](#section-example-projects)
|
||||
- :fa-angle-right: [Frequently Asked Questions](#section-frequently-asked-questions)
|
||||
- [How do I record my tests?](#section-how-do-i-record-my-tests)
|
||||
- [How is this different than CI?](#section-how-is-this-different-than-ci-)
|
||||
- [How much does this cost?](#section-how-much-does-this-cost-)
|
||||
- [Can I host this myself?](#section-can-i-host-this-myself-)
|
||||
- [Can I choose not to use it?](#section-can-i-choose-not-to-use-it-)
|
||||
|
||||
***
|
||||
|
||||
# What is the Dashboard?
|
||||
|
||||

|
||||
|
||||
[The Dashboard](https://on.cypress.io/dashboard) is a Cypress service that gives you access to tests you've recorded - typically when running Cypress tests from your CI provider. The Dashboard provides you insight into what happened during your run.
|
||||
|
||||
**The Dashboard allows you to:**
|
||||
|
||||
- See the number of failed, pending and passing tests
|
||||
- Get the entire stack trace of failed tests
|
||||
- View screenshots taken when tests fail and when using [`cy.screenshot`](https://on.cypress.io/api/screenshot)
|
||||
- Watch a video of your entire test run or a clip at the point of test failure.
|
||||
- Manage who has access to your run data
|
||||
|
||||
Additionally we've integrated the dashboard into the Cypress [Desktop Application](https://on.cypress.io/guides/installing-and-running). This means you'll see the test runs in the Tunes tab from within every project.
|
||||
|
||||

|
||||
|
||||
***
|
||||
|
||||
## Example Projects
|
||||
|
||||
Once you're logged into the [Dashboard](https://on.cypress.io/dashboard) you can view any [public project](https://on.cypress.io/what-is-project-access).
|
||||
|
||||
Here are some of our own public projects you can look at:
|
||||
|
||||
- [cypress-core-desktop-gui](https://dashboard.cypress.io/#/projects/fas5qd)
|
||||
- [cypress-example-recipes](https://dashboard.cypress.io/#/projects/6p53jw)
|
||||
- [cypress-example-kitchensink](https://dashboard.cypress.io/#/projects/4b7344)
|
||||
- [cypress-example-todomvc](https://dashboard.cypress.io/#/projects/245obj)
|
||||
- [cypress-example-piechopper](https://dashboard.cypress.io/#/projects/fuduzp)
|
||||
|
||||
***
|
||||
|
||||
# Frequently Asked Questions
|
||||
|
||||
## How do I record my tests?
|
||||
|
||||
1. First [setup your project to record](https://on.cypress.io/recording-project-runs).
|
||||
2. Then [record your runs](https://on.cypress.io/how-do-i-record-runs).
|
||||
|
||||
After recording your tests, you will see them in the Dashboard and in the Desktop Application.
|
||||
|
||||
***
|
||||
|
||||
## How is this different than CI?
|
||||
|
||||
Cypress is **complimentary** to your CI provider, and plays a completely different role.
|
||||
|
||||
It doesn't replace nor change anything related to CI. You will simply run Cypress tests in your CI provider.
|
||||
|
||||
The difference is that your CI provider has no idea what is going on inside of the Cypress process. It's simply programmed to know whether or not a process failed - based on whether it had an exit code greater than `0`.
|
||||
|
||||
Our dashboard provides you with the low level details of *what* happened during your run. Using both your CI provider + Cypress together gives the insight required to debug your test runs.
|
||||
|
||||
When a run happens and a test fails - instead of going and inspecting your CI provider's `stdout` output, you can log into the [Dashboard](https://on.cypress.io/dashboard) and see all of the test run results. It should be instantly clear what the problem was.
|
||||
|
||||
***
|
||||
|
||||
## How much does this cost?
|
||||
|
||||
Everything is free while we are in Beta.
|
||||
|
||||
In the future, we will charge per month for private projects.
|
||||
|
||||
Public projects will be free but will likely have a monthly usage cap on them.
|
||||
|
||||
We will offer similar pricing models of other Developer Tools you are familiar with using.
|
||||
|
||||
Plans will likely start around the $99/month level.
|
||||
***
|
||||
|
||||
## Can I host this myself?
|
||||
|
||||
No, although we are looking to build an on-premise version of the Dashboard for use in private clouds. If you're interested in our on-premise version, [let us know](mailto:hello@cypress.io)!
|
||||
|
||||
***
|
||||
|
||||
## Can I choose not to use it?
|
||||
|
||||
Of course. The dashboard is a separate service from the Desktop Application and will always remain optional. We hope you'll find a tremendous amount of value out of it, but it is not coupled to being able to run your tests.
|
||||
|
||||
You can simply always run your tests in CI using `cypress run` without the `--record` flag which does not communicate with our external servers and will not record any test results.
|
||||
@@ -1,208 +0,0 @@
|
||||
slug: projects
|
||||
excerpt: Manage your Projects and configure them to record runs
|
||||
|
||||
# Contents
|
||||
|
||||
- :fa-angle-right: [What Are Projects?](#section-what-are-projects-)
|
||||
- :fa-angle-right: [Adding a New Project](#section-adding-a-new-project)
|
||||
- :fa-angle-right: [Setting up a Project to Record](#section-setting-up-a-project-to-record)
|
||||
- [How do I record runs?](#section-how-do-i-record-runs-)
|
||||
- [What is a projectId?](#section-what-is-a-projectid-)
|
||||
- [What is a Record Key?](#section-what-is-a-record-key-)
|
||||
- [How do a projectId and Record Key work together?](#how-do-a-projectid-and-record-key-work-together-)
|
||||
- [What is the difference between public and private projects?](#section-what-is-the-difference-between-public-and-private-projects-)
|
||||
- :fa-angle-right: [Transferring Ownership of a Project](#section-transferring-ownership-of-a-project)
|
||||
- :fa-angle-right: [Deleting a Project](#section-deleting-a-project)
|
||||
|
||||
***
|
||||
|
||||
# What are Projects?
|
||||
|
||||
A Cypress project represents the directory of files and folders that make up your tests.
|
||||
|
||||
This is often the same repository as your code, but can also be a subfolder or a separate repository altogether.
|
||||
|
||||
***
|
||||
|
||||
# Adding a new Project
|
||||
|
||||
Projects can **only** be added to Cypress through our [Desktop Application](https://on.cypress.io/guides/installing-and-running).
|
||||
|
||||
1. Click :fa-plus: Add Project.
|
||||
|
||||

|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "info",
|
||||
"body": "Projects added in our Desktop Application are strictly local to your computer. They are not tracked in any way by Cypress servers and do not communicate with us until they are [setup to be recorded](#section-recording-runs)."
|
||||
}
|
||||
[/block]
|
||||
|
||||
***
|
||||
|
||||
# Setting up a Project to Record
|
||||
|
||||
You can also setup your project to have its test runs recorded and displayed in both the Desktop Application and the Dashboard.
|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "info",
|
||||
"body": "Not sure what the Dashboard is? [Its our service which displays all of your recorded runs](https://on.cypress.io/guides/dashboard-features)."
|
||||
}
|
||||
[/block]
|
||||
|
||||
During a run we record all failing tests, logs, screenshots, and videos.
|
||||
|
||||
**To setup a project:**
|
||||
|
||||
1. Click on the "Runs" tab of your project, then click "Setup Project to Record".
|
||||
|
||||

|
||||
|
||||
2. Fill in the name of your project (this is only for display purposes and can be changed later).
|
||||
|
||||

|
||||
|
||||
3. Choose who owns the project. You can personally own it or select an organization you've created. Organizations work just like they do in Github. They enable you to seperate your personal and work projects. [Read more about Organizations.](https://on.cypress.io/guides/organizations)
|
||||
|
||||

|
||||
|
||||
4. Choose whether this project is Public or Private.
|
||||
|
||||
**A public project** can have its recordings and runs seen by *anyone*. Typically these are open source projects.
|
||||
|
||||
**A private project** restricts its access to *only users you invite* to see your Organization or your own projects.
|
||||
|
||||

|
||||
|
||||
5. Click "Setup Project".
|
||||
|
||||

|
||||
|
||||
You are now ready to record your runs. Typically you would record your runs when running in [Continuous Integration](https://on.cypress.io/guides/continuous-integration) but you can also record your runs from your local computer.
|
||||
|
||||
***
|
||||
|
||||
## How do I record runs?
|
||||
|
||||
Now that your project is setup, Cypress has inserted your unique [projectId](#section-what-is-a-projectid-) into `cypress.json`.
|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "info",
|
||||
"body": "Be sure to check your `cypress.json` into source control."
|
||||
}
|
||||
[/block]
|
||||
|
||||
In order to record we also require you provide us your [Record Key](#section-what-is-a-record-key-). The record key along with your projectId uniquely identifies your project.
|
||||
|
||||
You can provide the Record Key when running this command:
|
||||
|
||||
```shell
|
||||
cypress run --record --key <record_key>
|
||||
```
|
||||
|
||||
Or you can also set an environment variable and we will automatically look for that.
|
||||
|
||||
```shell
|
||||
## you'd typically set this in your CI provider
|
||||
export CYPRESS_RECORD_KEY=abc-key-123
|
||||
|
||||
## we will automatically search and apply the key
|
||||
cypress run --record
|
||||
```
|
||||
|
||||
Once tests run, you will see them show up in the [Dashboard](https://on.cypress.io/dashboard) and in the Desktop Application.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
***
|
||||
|
||||
## What is a projectId?
|
||||
|
||||
Once you setup your project to record, we generate a unique `projectId` for your project, and automatically insert it into your `cypress.json` file.
|
||||
|
||||
**The `projectId` is a 6 character string in your cypress.json:**
|
||||
|
||||
```javascript
|
||||
// cypress.json
|
||||
{
|
||||
"projectId": "a7bq2k"
|
||||
}
|
||||
```
|
||||
|
||||
This is how we uniquely identify your project. If you manually alter this, **Cypress will no longer be able to identify your project or find the recorded builds for it**. We recommend that you check your `cypress.json` including the `projectId` into source control.
|
||||
|
||||
***
|
||||
|
||||
## What is a Record Key?
|
||||
|
||||
Once you're setup to record test runs, we automatically generate a **Record Key** for the project.
|
||||
|
||||
**A record key is a GUID that looks like this:**
|
||||
|
||||
```shell
|
||||
f4466038-70c2-4688-9ed9-106bf013cd73
|
||||
```
|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "info",
|
||||
"body": "You can create multiple Record Keys for a project, or delete existing ones from our [Dashboard](https://on.cypress.io/dashboard)."
|
||||
}
|
||||
[/block]
|
||||
|
||||
You can also find your Record Key inside of the **Settings** tab.
|
||||
|
||||
|
||||

|
||||
|
||||
***
|
||||
|
||||
## How do a projectId and Record Key work together?
|
||||
|
||||
Cypress uses your `projectId` and **Record Key** together to uniquely identify projects.
|
||||
|
||||

|
||||
|
||||
The record key is used to authenticate that your project is *allowed* to record. As long as your record key stays *private*, nobody will be able to record test runs for your project - even if they have your `projectId`.
|
||||
|
||||
If you have a public project you should *still* keep your record key secret. If someone knows both your record key and your `projectId`, they could record test runs for your project - which would mix up all of your results!
|
||||
|
||||
Think of your record key as the key that enables you to **write and create** builds. However, it has nothing to do with being able to **read or see** builds once they are created.
|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "warning",
|
||||
"body": "If your Record Key is accidentally exposed, you simply need to remove it and generate a new one from our [Dashboard](https://on.cypress.io/dashboard)."
|
||||
}
|
||||
[/block]
|
||||
|
||||
***
|
||||
|
||||
## What is the difference between public and private projects?
|
||||
|
||||
**A public project** means that anyone can see the recorded runs for it. It's similar to how public projects on Github, Travis, or Circle are handled. Anyone who knows your `projectId` will be able to see the recorded runs for public projects.
|
||||
|
||||
**A private project** means that only [users](https://on.cypress.io/guides/organizations#section-inviting-users) you explicitly invite to your [organization](https://on.cypress.io/guides/organizations) can see its recorded runs. Even if someone knows your `projectId`, they will not have access to your runs unless you have invited them.
|
||||
|
||||
A Record Key has nothing to do with **viewing** build data - it's a "write only" key. Even if it is accidentally leaked, it will not affect who can "see" your builds.
|
||||
|
||||
***
|
||||
|
||||
# Transferring Ownership of a Project
|
||||
|
||||
You can transfer projects that you own to another organization you are a part of or to another user in the organization. This functionality only exists in our [Dashboard](https://on.cypress.io/dashboard).
|
||||
|
||||

|
||||
|
||||
***
|
||||
|
||||
# Deleting a Project
|
||||
|
||||
You can delete projects you own. This will also delete all of their recorded runs. This functionality only exists in our [Dashboard](https://on.cypress.io/dashboard).
|
||||
|
||||

|
||||
@@ -1,83 +0,0 @@
|
||||
slug: runs
|
||||
excerpt: View your Recorded Runs
|
||||
|
||||
# Contents
|
||||
|
||||
- :fa-angle-right: [What are Recorded Runs?](#section-what-are-recorded-runs)
|
||||
- :fa-angle-right: [What is recorded during a run?](#section-what-is-recorded-during-a-run-)
|
||||
- [Standard Output](#section-standard-output)
|
||||
- [Test Failures](#section-test-failures)
|
||||
- [Screenshots](#section-screenshots)
|
||||
- [Videos](#section-videos)
|
||||
|
||||
***
|
||||
|
||||
# What are Recorded Runs?
|
||||
|
||||
Recorded runs are the results and artifacts captured from your test runs.
|
||||
|
||||
To record your runs:
|
||||
|
||||
1. First [setup your project to record](https://on.cypress.io/recording-project-runs)
|
||||
2. Then [run the command](https://on.cypress.io/how-do-i-record-runs) `cypress run --record --key <record_key>`
|
||||
|
||||
***
|
||||
|
||||
# What is recorded during a run?
|
||||
|
||||
We capture the following:
|
||||
|
||||
- [Standard Output](#section-standard-output)
|
||||
- [Test Failures](#section-test-failures)
|
||||
- [Screenshots](#section-screenshots)
|
||||
- [Video](#section-video)
|
||||
|
||||
We have already begun the implementation for capturing even more things from your run such as:
|
||||
|
||||
- Commands
|
||||
- Network Traffic
|
||||
- Browser Console Logs
|
||||
|
||||
These will be added in subsequent releases.
|
||||
|
||||
***
|
||||
|
||||
## Standard Output
|
||||
|
||||
Standard output includes details and summaries of your tests based on the [reporter](https://on.cypress.io/guides/reporters) you have set. By default it is the `spec` reporter.
|
||||
|
||||

|
||||
|
||||
You will also see a summary at the bottom indicating the files we've recorded.
|
||||
|
||||
***
|
||||
|
||||
## Test Failures
|
||||
|
||||
Any tests that fail during a run can be found under the **Failures** tab. Each failure is listed under it's nested test title.
|
||||
|
||||
**Each failure displays the failure's:**
|
||||
|
||||
- **Error:** The stack trace of the error.
|
||||
- **Video:** The recorded video scrubbed to the point of failure during the test.
|
||||
- **Screenshot:** Any screenshots taken during the test.
|
||||
|
||||

|
||||
|
||||
***
|
||||
|
||||
## Screenshots
|
||||
|
||||
All screenshots taken during the entire run can be found under the **Screenshots** tab. Both screenshots taken during failures and screenshots taken using the [`cy.screenshot`](https://on.cypress.io/api/screenshot) command are located here. Each screenshot displays the application as well as the Cypress Command Log.
|
||||
|
||||
Each screenshot will display under the test title it was taken in.
|
||||
|
||||

|
||||
|
||||
***
|
||||
|
||||
## Videos
|
||||
|
||||
Any videos taken during the run can be found under the **Videos** tab. You can also download the video of a run.
|
||||
|
||||

|
||||
@@ -1,114 +0,0 @@
|
||||
slug: organizations
|
||||
excerpt: Manage your Organizations
|
||||
|
||||
# Contents
|
||||
|
||||
- :fa-angle-right: [What are Organizations?](#section-what-are-organizations-)
|
||||
- [Personal Organization](#section-personal-organization)
|
||||
- :fa-angle-right: [Creating an Organization](#section-creating-an-organization)
|
||||
- [Inviting Users](#section-inviting-users)
|
||||
- [User Roles](#section-user-roles)
|
||||
- [User Requests](#section-user-requests)
|
||||
- [Transferring Projects](#section-transferring-projects)
|
||||
- :fa-angle-right: [Deleting an Organization](#section-deleting-an-organization)
|
||||
|
||||
***
|
||||
|
||||
# What are Organizations?
|
||||
|
||||
Organizations are used to group projects and to manage permissions for who can access those projects.
|
||||
|
||||

|
||||
|
||||
With organizations you can:
|
||||
|
||||
- Create projects
|
||||
- Invite users
|
||||
|
||||
Once out of beta, organizations will also handle billing.
|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "info",
|
||||
"body": "Cypress Organizations are meant to work similar to GitHub Organizations."
|
||||
}
|
||||
[/block]
|
||||
|
||||
***
|
||||
|
||||
## Personal Organization
|
||||
|
||||
By default, every user of Cypress is given a personal organization - named after you.
|
||||
|
||||
You cannot delete or edit the name of this organization.
|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "info",
|
||||
"body": "All existing Cypress projects prior to version 0.19.0 were automatically added to your personal organization."
|
||||
}
|
||||
[/block]
|
||||
|
||||
***
|
||||
|
||||
# Creating an Organization
|
||||
|
||||

|
||||
|
||||
***
|
||||
|
||||
## Inviting Users
|
||||
|
||||
You can invite users to Cypress from our [Dashboard](https://on.cypress.io/dashboard). Invited users will see the projects and runs for your organization.
|
||||
|
||||
Even though we are in a **private beta**, any user you invite will automatically be whitelisted to use Cypress. This means you can freely invite your team members without needing to talk to us!
|
||||
|
||||
*To invite a user to an organization:*
|
||||
|
||||
1. Click the :fa-cog: beside the Projects you want to give the user access to.
|
||||
2. Click 'Invite User'. Note: you must have the role of 'owner' or 'admin' to invite users.
|
||||
3. Fill in the email and select the role for the user and click 'Invite User' Note: only 'owner's can give other user's 'owner' access.
|
||||
4. The user will recieve an invite email with a link to accept the invitation.
|
||||
|
||||

|
||||
|
||||
***
|
||||
|
||||
## User Roles
|
||||
|
||||
User's can be assigned roles that affect their access to certain features.
|
||||
|
||||
- **Member:** Can see the projects, runs, and keys.
|
||||
- **Admin:** Can also invite, edit and delete users.
|
||||
- **Owner:** Can also transfer or delete projects. Can delete and edit the organization.
|
||||
|
||||
***
|
||||
|
||||
## User Requests
|
||||
|
||||
We have also built Cypress with the ability for users to "Request" access to a given organization. This makes for a very natural flow.
|
||||
|
||||
If a developer on your team has access to Cypress and your project's source code - they can request to be given access to your Organization.
|
||||
|
||||
This means instead of you having to invite team members up front, they can simply request access and you can choose to accept or deny them.
|
||||
|
||||

|
||||
|
||||
***
|
||||
|
||||
## Transferring Projects
|
||||
|
||||
You can transfer projects that you own to another organization or another user.
|
||||
|
||||
This functionality only exists in our [Dashboard](https://on.cypress.io/dashboard).
|
||||
|
||||

|
||||
|
||||
|
||||
***
|
||||
|
||||
# Deleting an Organization
|
||||
|
||||
You can delete organizations that you own as long as they don't have any projects. You must first transfer ownership of your projects to another organization before you can delete them.
|
||||
|
||||

|
||||
@@ -1,201 +0,0 @@
|
||||
slug: configuration
|
||||
excerpt: Configure global, network, folder, viewport, and animation options
|
||||
|
||||
# Contents
|
||||
|
||||
- :fa-angle-right: [Configuration Options](#section-configuration-options)
|
||||
- [Global](#section-global)
|
||||
- [Timeouts](#section-timeouts)
|
||||
- [Folders](#section-folders)
|
||||
- [Screenshots](#section-screenshots)
|
||||
- [Videos](#section-videos)
|
||||
- [Browser](#section-browser)
|
||||
- [Web Server](#section-web-server)
|
||||
- [Viewport](#section-viewport)
|
||||
- [Animation](#section-animation)
|
||||
- :fa-angle-right: [Overriding Options](#section-overriding-options)
|
||||
- [Command Line](#section-command-line)
|
||||
- [Environment Variables](#section-environment-variables)
|
||||
- :fa-angle-right: [Resolved Configuration](#section-resolved-configuration)
|
||||
|
||||
***
|
||||
|
||||
# Configuration Options
|
||||
|
||||
When a project is added to Cypress, a `cypress.json` file is created in your project. This file contains your `projectId` and any configuration values you supply.
|
||||
|
||||
```json
|
||||
{
|
||||
"projectId": "128076ed-9868-4e98-9cef-98dd8b705d75"
|
||||
}
|
||||
```
|
||||
|
||||
By modifying the following values you can change the default behavior of Cypress.
|
||||
|
||||
Here is a list of available options and their default values.
|
||||
|
||||
## Global
|
||||
|
||||
Option | Default | Description
|
||||
----- | ---- | ----
|
||||
`baseUrl` | `null` | Base url to prefix to [`cy.visit`](https://on.cypress.io/api/visit) or [`cy.request`](https://on.cypress.io/api/request) command
|
||||
`env` | `{}` | [Environment Variables](https://on.cypress.io/guides/environment-variables)
|
||||
`ignoreTestFiles` | `*.hot-update.js` | A string or array of glob patterns for ignoring test files that would otherwise be shown in your tests list. Under the hood Cypress is using `minimatch` with the options: `{dot: true, matchBase: true}`. We suggest you using [http://globtester.com](http://globtester.com) to test what files would match.
|
||||
`port` | | Port to use for Cypress
|
||||
`numTestsKeptInMemory` | `50` | The number of tests for which snapshots and command data are kept in memory. Reduce this number if you are seeing extremely high memory consumption in your browser.
|
||||
`reporter` | `spec` | The [reporter](https://on.cypress.io/guides/reporters) used during headless or CI runs
|
||||
`reporterOptions` | `null` | The [reporter options](https://on.cypress.io/guides/reporters#section-reporter-options) used. Supported options depend on the reporter.
|
||||
`screenshotOnHeadlessFailure` | `true` | Whether to take a screenshot automatically on test failure when running headlessly or in CI
|
||||
`watchForFileChanges` | `true` | Whether Cypress will watch and restart tests on file changes
|
||||
|
||||
***
|
||||
|
||||
## Timeouts
|
||||
|
||||
Option | Default | Description
|
||||
----- | ---- | ----
|
||||
`defaultCommandTimeout` | `4000` | Time, in milliseconds, to wait until most DOM based commands are considered timed out
|
||||
`execTimeout` | `60000` | Time, in milliseconds, to wait for a system command to finish executing during [`cy.exec`](https://on.cypress.io/api/exec) command
|
||||
`pageLoadTimeout` | `60000` | Time, in milliseconds, to wait until [`cy.visit`](https://on.cypress.io/api/visit), [`cy.go`](https://on.cypress.io/api/go), [`cy.reload`](https://on.cypress.io/api/reload), or a page load times out
|
||||
`requestTimeout` | `5000` | Time, in milliseconds, to wait for an XHR request during [`cy.wait`](wait) command
|
||||
`responseTimeout` | `30000` | Time, in milliseconds, to wait until a response for [`cy.request`](https://on.cypress.io/api/request), [`cy.wait`](https://on.cypress.io/api/wait), [`cy.fixture`](https://on.cypress.io/api/fixture), [`cy.getCookie`](https://on.cypress.io/api/getcookie), [`cy.getCookies`](https://on.cypress.io/api/getcookies), [`cy.setCookie`](https://on.cypress.io/api/setcookie), [`cy.clearCookie`](https://on.cypress.io/api/clearcookie) and [`cy.clearCookies`](https://on.cypress.io/api/clearcookies), and [`cy.screenshot`](https://on.cypress.io/api/screenshot) commands
|
||||
|
||||
***
|
||||
|
||||
## Folders
|
||||
|
||||
To turn off the use of `fixture` folders, pass `false` into the configuration option.
|
||||
|
||||
Option | Default | Description
|
||||
----- | ---- | ----
|
||||
`fixturesFolder` | `cypress/fixtures` | Where Cypress will look for fixture files
|
||||
`integrationFolder` | `cypress/integration` | Where Cypress will look for integration test files
|
||||
`supportFile` | `cypress/support` | Path to a file to load before your test files. File is compiled and bundled as test files are. Pass `false` to turn off.
|
||||
`screenshotsFolder` | `cypress/screenshots` | Where Cypress will automatically save screenshots from [`cy.screenshot()`](https://on.cypress.io/api/screenshot) or during test failures when running headlessly.
|
||||
`videosFolder` | `cypress/videos` | Where Cypress will automatically save the video of the test run when running headlessly.
|
||||
|
||||
***
|
||||
|
||||
## Screenshots
|
||||
|
||||
Option | Default | Description
|
||||
----- | ---- | ----
|
||||
`screenshotsFolder` | `cypress/screenshots` | Where Cypress will automatically save screenshots from [`cy.screenshot()`](https://on.cypress.io/api/screenshot) or during test failures when running headlessly.
|
||||
`screenshotOnHeadlessFailure` | `true` | Whether Cypress will automatically take a screenshot when a test fails when running tests headlessly or in CI.
|
||||
`trashAssetsBeforeHeadlessRuns` | `true` | Whether Cypress will trash assets within the `screenshotsFolder` and `videosFolder` before headless test runs.
|
||||
|
||||
***
|
||||
|
||||
## Videos
|
||||
|
||||
Option | Default | Description
|
||||
----- | ---- | ----
|
||||
`videoRecording` | `true` | Whether Cypress will record a video of the test run when running headlessly.
|
||||
`videoCompression` | `32` | The quality setting for the video compression, in Constant Rate Factor (CRF). The value can be `false` to disable compression or a value between `0` and `51`, where a lower value results in better quality (at the expense of a higher file size).
|
||||
`videosFolder` | `cypress/videos` | Where Cypress will automatically save the video of the test run when running headlessly.
|
||||
`trashAssetsBeforeHeadlessRuns` | `true` | Whether Cypress will trash assets within the `screenshotsFolder` and `videosFolder` before headless test runs.
|
||||
|
||||
***
|
||||
|
||||
## Browser
|
||||
|
||||
Option | Default | Description
|
||||
----- | ---- | ----
|
||||
`chromeWebSecurity` | true | Whether Chome Web Security for `same-origin policy` and `insecure mixed content` is enabled. [Read more about this here.](https://on.cypress.io/guides/web-security)
|
||||
|
||||
***
|
||||
|
||||
## Web Server
|
||||
|
||||
Option | Default | Description
|
||||
----- | ---- | ----
|
||||
`fileServerFolder` | root project folder | Where Cypress will attempt to serve your application files
|
||||
|
||||
***
|
||||
|
||||
## Viewport
|
||||
|
||||
Option | Default | Description
|
||||
----- | ---- | ----
|
||||
`viewportWidth` | `1000` | Default width in pixels for [`cy.viewport`](https://on.cypress.io/api/viewport)
|
||||
`viewportHeight` | `660` | Default height in pixels for [`cy.viewport`](https://on.cypress.io/api/viewport)
|
||||
|
||||
***
|
||||
|
||||
## Animations
|
||||
|
||||
Option | Default | Description
|
||||
----- | ---- | ----
|
||||
`waitForAnimations` | `true` | Whether to wait for elements to finish animating before applying commands
|
||||
`animationDistanceThreshold` | `5` | The distance in pixels an element must exceed to be considered animating
|
||||
|
||||
***
|
||||
|
||||
# Overriding Options
|
||||
|
||||
Besides modifying your `cypress.json` you can also change configuration options by the **Command Line** or from your system **Environment Variabes**.
|
||||
|
||||
## Command Line
|
||||
|
||||
When [running Cypress from the Command Line](https://github.com/cypress-io/cypress-cli#cypress-open-1) you can pass the `--config` flag.
|
||||
|
||||
Be sure to separate multiple values with a **comma**.
|
||||
|
||||
Example:
|
||||
|
||||
```shell
|
||||
## you can pass --config to cypress open or cypress run
|
||||
cypress open --config watchForFileChanges=false,waitForAnimations=false
|
||||
|
||||
cypress run --config integrationFolder=tests,fixturesFolder=false
|
||||
|
||||
cypress run --record --config viewportWidth=1280,viewportHeight=720
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## Environment Variables
|
||||
|
||||
You can also use environment variables to override configuration values. This is especially useful in CI or when working locally. This gives you the ability to change configuration options without modifying any code or build scripts.
|
||||
|
||||
By default, any environment variable that matches a cooresponding configuration key will override its default value.
|
||||
|
||||
```shell
|
||||
## change the viewport width + height with environment variables
|
||||
export CYPRESS_VIEWPORT_WIDTH=800
|
||||
export CYPRESS_VIEWPORT_HEIGHT=600
|
||||
```
|
||||
|
||||
We automatically normalize both the key and the value.
|
||||
|
||||
Keys are automatically camel cased, and we will automatically convert values into `Number` and `Boolean`.
|
||||
|
||||
```shell
|
||||
## both of these are valid
|
||||
export CYPRESS_pageLoadTimeout=100000
|
||||
export CYPRESS_PAGE_LOAD_TIMEOUT=100000
|
||||
```
|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "info",
|
||||
"body": "Make sure to prefix your environment variables with **CYPRESS_** else they will be ignored."
|
||||
}
|
||||
[/block]
|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "warning",
|
||||
"body": "Environment variables which do not match configuration keys will instead be set as regular environment variables for use in your tests with `Cypress.env()`.\n\nYou can [read more about Environment Variables](https://on.cypress.io/environment-variables)."
|
||||
}
|
||||
[/block]
|
||||
|
||||
***
|
||||
|
||||
# Resolved Configuration
|
||||
|
||||
When you open a Cypress project, we will display the resolved configuration to you.
|
||||
|
||||
This makes it easy to understand and see where different values came from.
|
||||
|
||||

|
||||
@@ -1,196 +0,0 @@
|
||||
slug: issuing-commands
|
||||
excerpt: Traverse the dom and perform actions with commands
|
||||
|
||||
# Contents
|
||||
|
||||
- :fa-angle-right: [Commands are Async](#section-commands-are-async)
|
||||
- :fa-angle-right: [Subjects](#section-subjects)
|
||||
- :fa-angle-right: [Chaining](#section-chaining)
|
||||
- [Parent Commands](#section-parent-commands)
|
||||
- [Child Commands](#section-child-commands)
|
||||
- [Dual Commands](#section-dual-commands)
|
||||
|
||||
***
|
||||
|
||||
# Commands are Async
|
||||
|
||||
When writing integration tests, you will need to traverse the DOM and perform user actions. In traditional server-side rendered views, it's unlikely you be deal with many application state changes. However, if you're writing an application using a modern JavaScript framework, you'll likely need to navigate through highly complex, dynamic states.
|
||||
|
||||
> Cypress is designed to handle *both* traditional server-side rendered views & modern JavaScript framework applications.
|
||||
|
||||
The architecture of Cypress focuses around asynchronicity. The DOM is a highly complex, mutable object; to handle the indeterminate state of the DOM, we've designed our commands to be asynchronous.
|
||||
|
||||
**Because all commands are async, this offers many advantages.**
|
||||
|
||||
1. All commands can retry until a certain specified condition is met.
|
||||
2. Commands can be replayed, inserted between others, or even conditionally run.
|
||||
3. Cypress can look ahead (or look behind) at commands which have yet to run, and alter its behavior.
|
||||
|
||||
Cypress is designed to also handle the disadvantages of async. One example is [aliasing](https://on.cypress.io/guides/using-aliases). This works around the need to assign values to variables.
|
||||
|
||||
The [Command API](https://on.cypress.io/api) attempts to balance readability, terseness, flexibility, and capability all while being designed similar to familiar APIs. The [Command API](https://on.cypress.io/api) is also fluent - that is, you chain all commands together - similar to how jQuery's API is designed.
|
||||
|
||||
***
|
||||
|
||||
# Subjects
|
||||
|
||||
Commands work just like JavaScript Promises. That is, the resolved value of one command becomes the `subject` of the next command - just like a waterfall.
|
||||
|
||||
```javascript
|
||||
cy
|
||||
// when 'get' resolves, the subject
|
||||
// becomes the DOM element <div#main>
|
||||
.get("#main")
|
||||
|
||||
// after 'get' resolves, the find command
|
||||
// runs and is passed <div#main> as its subject.
|
||||
// <div#main> becomes the subject that we 'find' on.
|
||||
.find("button")
|
||||
|
||||
// after 'find' resolves, <button> becomes the new subject.
|
||||
// <button> is what the click operates on.
|
||||
// this is functionally equivalent to
|
||||
// writing $("#main").find("button").click() in jQuery.
|
||||
.click()
|
||||
```
|
||||
|
||||
The difference between the Cypress API and jQuery's API is that *all* commands are async. **No command returns an actual assignable value.** That is because every command is queued, ran, and retried until it resolves. Some commands may not resolve until several seconds after they are run.
|
||||
|
||||
For instance you **cannot** do this:
|
||||
|
||||
```javascript
|
||||
// THIS WILL NOT WORK!
|
||||
button = cy.get("#main").find("button")
|
||||
```
|
||||
|
||||
Just like Promises, the value that async commands return can only be yielded in a callback function.
|
||||
|
||||
If you want to retrieve the resolved value (the subject) of a command, use a [`then`](https://on.cypress.io/api/then) command, the same way you would with Promises.
|
||||
|
||||
```javascript
|
||||
cy.get("#main").find("button").then(function($button){
|
||||
// when the 'find' command resolves, we can yield its
|
||||
// resolved value (the new subject) in a callback function.
|
||||
// now we can work with this value directly.
|
||||
$button.trigger("click")
|
||||
})
|
||||
```
|
||||
|
||||
When chaining together multiple commands you rarely should need to yield the subject via a [`cy.then`](https://on.cypress.io/api/then) command. Cypress favors readability and terseness, and even [assertions](https://on.cypress.io/guides/making-assertions) can be implicitly run without having to use a [`cy.then`](https://on.cypress.io/api/then) command.
|
||||
|
||||
```javascript
|
||||
// we're testing that an 'active' class is
|
||||
// applied to our button after being clicked
|
||||
cy.get("#main").find("button").click().should("have.class", "active")
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Chaining
|
||||
|
||||
Because Cypress implements a fluent API, all commands are linked together. Cypress has a small, but powerful, set of rules to know how to process the chain of commands.
|
||||
|
||||
**There are 3 types of commands:**
|
||||
|
||||
- [Parent Commands](#section-parent-commands)
|
||||
- [Child Commands](#section-child-commands)
|
||||
- [Dual Commands](#section-dual-commands)
|
||||
|
||||
***
|
||||
|
||||
## Parent Commands
|
||||
|
||||
Parent commands always *begin* a new chain of commands. Even if you've written a previous chain, parent commands will always start a new chain, and ignore previous chains. Parent commands should be written off the `cy` object:
|
||||
|
||||
**Examples of parent commands:**
|
||||
|
||||
- [`visit`](https://on.cypress.io/api/visit)
|
||||
- [`server`](https://on.cypress.io/api/server)
|
||||
- [`get`](https://on.cypress.io/api/get)
|
||||
- [`root`](https://on.cypress.io/api/root)
|
||||
|
||||
```javascript
|
||||
cy
|
||||
// visit is a parent command which is initially called off the cy object
|
||||
.visit("http://localhost:8000")
|
||||
|
||||
// get ignores previously run commands and will
|
||||
// query (by default) from the root document
|
||||
.get("#main").find("button").click().should("have.class", "active")
|
||||
|
||||
// because get is a parent command, when we use it a 2nd time in a chain
|
||||
// the previous chain is ignored and we query from the root document
|
||||
.get("input").type("foobarbaz")
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## Child Commands
|
||||
|
||||
Child commands are always chained off of a **parent** command, or another **child** command.
|
||||
|
||||
**Examples of child commands:**
|
||||
|
||||
- [`find`](https://on.cypress.io/api/find)
|
||||
- [`click`](https://on.cypress.io/api/click)
|
||||
- [`type`](https://on.cypress.io/api/type)
|
||||
- [`children`](https://on.cypress.io/api/children)
|
||||
- [`should`](https://on.cypress.io/api/should)
|
||||
|
||||
Each of the above commands require an existing subject. It wouldn't make sense to [`click`](https://on.cypress.io/api/click) onto nothing, nor would it make sense to [`type`](https://on.cypress.io/api/type) or query for [`children`](https://on.cypress.io/api/children) of nothing.
|
||||
|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "info",
|
||||
"body": "If child commands have specific rules which are not met, they will throw a very explicit error telling you why they can't be invoked at that time."
|
||||
}
|
||||
[/block]
|
||||
|
||||
Looking at our previous example:
|
||||
|
||||
```javascript
|
||||
cy
|
||||
.visit("http://localhost:8000")
|
||||
|
||||
.get("#main")
|
||||
// we find the button within the existing DOM subject <div#main>
|
||||
// our subject now becomes the <button> element
|
||||
.find("button")
|
||||
|
||||
// then we click the current subject, <button>
|
||||
.click()
|
||||
|
||||
// the click command does not change the subject
|
||||
// it returns the existing <button> subject
|
||||
// we can now assert that the <button> has the class 'active'
|
||||
.should("have.class", "active")
|
||||
|
||||
.get("input")
|
||||
|
||||
// we type into an existing DOM <input /> subject
|
||||
.type("foobarbaz")
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## Dual Commands
|
||||
|
||||
While parent commands always start a new chain of commands and child commands require being chained off a parent command, dual commands can behave as parent or child command. That is, they can **start** a new chain, or be chained off of an **existing** chain.
|
||||
|
||||
**Examples of dual commands:**
|
||||
|
||||
- [`contains`](https://on.cypress.io/api/contains)
|
||||
- [`wait`](https://on.cypress.io/api/wait)
|
||||
|
||||
```javascript
|
||||
cy
|
||||
// contains acts as a parent command, starting a chain of commands
|
||||
// contains will query from the root document (the default subject)
|
||||
.contains("Jane Lane").click()
|
||||
|
||||
// contains can also act as a child command, using it's parent command's subject, <form>
|
||||
// contains only searches for content inside of the <form> element
|
||||
.get("form").contains("Submit!").click()
|
||||
|
||||
```
|
||||
@@ -1,37 +0,0 @@
|
||||
slug: screenshots-and-videos
|
||||
excerpt: Capture screenshots and videos of your test run
|
||||
|
||||
# Contents
|
||||
|
||||
- :fa-angle-right: [Screenshots](#section-screenshots)
|
||||
- :fa-angle-right: [Videos](#section-videos)
|
||||
|
||||
***
|
||||
|
||||
# Screenshots
|
||||
|
||||
Cypress comes with the ability to take screenshots whether you are running in a real headed browser (such as Chrome) or when you are running headlessly or in CI.
|
||||
|
||||
To take a manual screenshot just use the [`cy.screenshot`](https://on.cypress.io/api/screenshot) command.
|
||||
|
||||
Additionally, Cypress will automatically capture screenshots when a failure happens but only during a headless run.
|
||||
|
||||
This behavior can be turned off by setting [`screenshotOnHeadlessFailure`](https://on.cypress.io/configuration#section-screenshots) to `false`.
|
||||
|
||||
Screenshots are stored in the [`screenshotsFolder`](https://on.cypress.io/configuration#section-screenshots) which is set to `cypress/screenshots` by default.
|
||||
|
||||
By default, Cypress trashes the previous screenshots before a headless run. If don't want to clear your screenshots folder before a headless run, you can set [`trashAssetsBeforeHeadlessRun`](https://on.cypress.io/configuration#section-screenshots) to `false`.
|
||||
|
||||
***
|
||||
|
||||
# Videos
|
||||
|
||||
Cypress also records videos when running headlessly.
|
||||
|
||||
This behavior can be turned off by setting [`videoRecording`](https://on.cypress.io/configuration#section-videos) to `false`.
|
||||
|
||||
Videos are stored in the [`videosFolder`](https://on.cypress.io/configuration#section-videos) which is set to `cypress/videos` by default.
|
||||
|
||||
After a headless run completes, Cypress will automatically compress the video to save on file size. By default it compresses to a `32 CRF` but this is configurable with the [`videoCompression`](https://on.cypress.io/configuration#section-videos) property.
|
||||
|
||||
By default, Cypress trashes the previous videos before a headless run. If don't want to clear your videos folder before a headless run, you can set [`trashAssetsBeforeHeadlessRun`](https://on.cypress.io/configuration#section-videos) to `false`.
|
||||
@@ -1,101 +0,0 @@
|
||||
slug: bundled-tools
|
||||
excerpt: Cypress bundles together a familiar set of tools and builds heavily on them.
|
||||
|
||||
# Contents
|
||||
|
||||
- :fa-angle-right: [Mocha](#section-mocha)
|
||||
- :fa-angle-right: [Chai](#section-chai)
|
||||
- :fa-angle-right: [Chai-jQuery](#section-chai-jquery)
|
||||
- :fa-angle-right: [Sinon](#section-sinon)
|
||||
- :fa-angle-right: [Sinon-Chai](#section-sinon-chai)
|
||||
- :fa-angle-right: [Sinon-as-Promised](#section-sinon-as-romised)
|
||||
- :fa-angle-right: [Utilities](#section-utilies)
|
||||
|
||||
***
|
||||
|
||||
# Mocha
|
||||
|
||||
[Mocha docs](http://mochajs.org/)
|
||||
|
||||
Cypress has adopted Mocha's `bdd` syntax, which fits perfectly with both integration and unit testing. All of the tests you'll be writing sit on the fundamental harness Mocha provides, namely:
|
||||
|
||||
* [describe()](https://mochajs.org/#bdd)
|
||||
* [context()](https://mochajs.org/#bdd)
|
||||
* [it()](https://mochajs.org/#bdd)
|
||||
* [before()](https://mochajs.org/#hooks)
|
||||
* [beforeEach()](https://mochajs.org/#hooks)
|
||||
* [afterEach()](https://mochajs.org/#hooks)
|
||||
* [after()](https://mochajs.org/#hooks)
|
||||
* [.only()](https://mochajs.org/#exclusive-tests)
|
||||
* [.skip()](https://mochajs.org/#inclusive-tests)
|
||||
|
||||
Additionally, Mocha gives us excellent [`async` support](https://mochajs.org/#asynchronous-code). Cypress has extended Mocha, sanding off the rough edges, weird edge cases, bugs, and error messages. These fixes are all completely transparent.
|
||||
|
||||
***
|
||||
|
||||
# Chai
|
||||
|
||||
[Chai docs](http://chaijs.com/)
|
||||
|
||||
While Mocha provides us a framework to structure our tests, Chai gives us the ability to easily write assertions. Chai gives us readable assertions with excellent error messages. Cypress extends this, fixes several common pitfalls, and wraps Chai's DSL using [subjects](https://on.cypress.io/guides/making-assertions) and the [cy.should](https://on.cypress.io/api/should) command.
|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "info",
|
||||
"body": "[Check out our example recipe to see how to extend chai yourself](https://github.com/cypress-io/cypress-example-recipes/blob/master/cypress/integration/extending_chai_assertion_plugins_spec.js)",
|
||||
"title": "Extending chai to use assertion plugins"
|
||||
}
|
||||
[/block]
|
||||
|
||||
***
|
||||
|
||||
# Chai-jQuery
|
||||
|
||||
[Chai-jQuery docs](https://github.com/chaijs/chai-jquery)
|
||||
|
||||
When writing integration tests, you will likely work a lot with the DOM. Cypress brings in Chai-jQuery, which automatically extends Chai with specific jQuery chainer methods.
|
||||
|
||||
***
|
||||
|
||||
# Sinon
|
||||
|
||||
[Sinon docs](http://sinonjs.org/)
|
||||
|
||||
When writing unit tests, or even in integration-like tests, you often need to ability to [stub](http://sinonjs.org/docs/#stubs) and [spy](http://sinonjs.org/docs/#spies) methods. Cypress includes two methods, [`cy.stub`](https://on.cypress.io/api/stub) and [`cy.spy`](https://on.cypress.io/api/spy) that return Sinon stubs and spies, respectively.
|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "info",
|
||||
"body": "[Check out our example recipe for stubbing dependencies in unit tests](https://github.com/cypress-io/cypress-example-recipes/blob/master/cypress/integration/unit_test_stubbing_dependencies_spec.js)",
|
||||
"title": "Stubbing Dependencies when Unit Testing"
|
||||
}
|
||||
[/block]
|
||||
|
||||
***
|
||||
|
||||
# Sinon-Chai
|
||||
|
||||
[Sinon-Chai docs](https://github.com/domenic/sinon-chai)
|
||||
|
||||
When working with `stubs` or `spies` you'll regularly want to use those when writing Chai assertions. Cypress bundles in Sinon-Chai which extends Chai allowing you to [write assertions](https://github.com/domenic/sinon-chai#assertions) about `stubs` and `spies`.
|
||||
|
||||
***
|
||||
|
||||
# Sinon-As-Promised
|
||||
|
||||
[Sinon-As-Promised docs](https://github.com/bendrucker/sinon-as-promised)
|
||||
|
||||
Sinon-as-Promised gives you the ability to stub methods that return Promises. To fulfill the async contract of these methods, you would use Sinon-as-Promised to force these methods to easily [`resolve`](https://github.com/bendrucker/sinon-as-promised#stubresolvesvalue---stub) or [`reject`](https://github.com/bendrucker/sinon-as-promised#stubrejectserr---stub) at your discretion.
|
||||
|
||||
***
|
||||
|
||||
# Other Library Utilities
|
||||
|
||||
Cypress also bundles the following tools on the `Cypress` object. These can be used anywhere inside of your tests.
|
||||
|
||||
- [Cypress._](https://on.cypress.io/api/cypress-underscore) (Underscore)
|
||||
- [Cypress.$](https://on.cypress.io/api/cypress-jquery) (jQuery)
|
||||
- [Cypress.minimatch](https://on.cypress.io/api/cypress-minimatch) (minimatch.js)
|
||||
- [Cypress.moment](https://on.cypress.io/api/cypress-moment) (moment.js)
|
||||
- [Cypress.Blob](https://on.cypress.io/api/cypress-blob) (blob utils)
|
||||
- [Cypress.Promise](https://on.cypress.io/api/cypress-promise) (Bluebird)
|
||||
@@ -1,127 +0,0 @@
|
||||
slug: reporters
|
||||
excerpt: Customize test results with reporters
|
||||
|
||||
# Contents
|
||||
|
||||
- :fa-angle-right: [Reporters](#section-reporters)
|
||||
- :fa-angle-right: [Supported Reporters](#section-supported-reporters)
|
||||
- :fa-angle-right: [Custom Reporters](#section-custom-reporters)
|
||||
- [Local Reporters](#section-local-reporters)
|
||||
- [npm Reporters](#section-npm-reporters)
|
||||
- :fa-angle-right: [Reporter Options](#section-reporter-options)
|
||||
|
||||
# Reporters
|
||||
|
||||
When running Cypress headlessly or in CI, you can specify which Mocha reporter Cypress uses through your `cypress.json` or via the command line:
|
||||
|
||||
`cypress.json`:
|
||||
|
||||
```javascript
|
||||
{
|
||||
"reporter": "nyan"
|
||||
}
|
||||
```
|
||||
|
||||
Command line:
|
||||
|
||||
```shell
|
||||
cypress run --reporter json
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Supported Reporters
|
||||
|
||||
Cypress supports the following reporters:
|
||||
|
||||
* [Mocha's built-in reporters](https://mochajs.org/#reporters).
|
||||
* [`teamcity`](https://github.com/cypress-io/mocha-teamcity-reporter)
|
||||
* [`junit`](https://github.com/michaelleeallen/mocha-junit-reporter)
|
||||
* Custom reporters ([see below](#section-custom-reporters))
|
||||
|
||||
***
|
||||
|
||||
# Custom Reporters
|
||||
|
||||
Cypress supports custom reporters, whether local to your project or installed through npm.
|
||||
|
||||
## Local Reporters
|
||||
|
||||
Say you have the following directory structure:
|
||||
|
||||
```txt
|
||||
> my-project
|
||||
> cypress
|
||||
> src
|
||||
> reporters
|
||||
- custom.js
|
||||
```
|
||||
|
||||
Specify the path to your custom reporter to use it:
|
||||
|
||||
`cypress.json`:
|
||||
|
||||
```javascript
|
||||
{
|
||||
"reporter": "reporters/custom.js"
|
||||
}
|
||||
```
|
||||
|
||||
Command line:
|
||||
|
||||
```shell
|
||||
cypress run --reporter reporters/custom.js
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## npm Reporters
|
||||
|
||||
If you have installed a custom reporter through npm, specify the package name:
|
||||
|
||||
```javascript
|
||||
// cypress.json
|
||||
{
|
||||
"reporter": "mochawesome"
|
||||
}
|
||||
```
|
||||
|
||||
Command line:
|
||||
|
||||
```shell
|
||||
cypress run --reporter mochawesome
|
||||
```
|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "info",
|
||||
"title": "Peer Dependencies",
|
||||
"body": "You need to install any peer dependencies the reporter requires, even if they're bundled with Cypress. For example, [mochawesome](https://github.com/adamgruber/mochawesome) requires `mocha` as a peer dependency. You will need to install mocha as a dev dependency of your own project for it to work (`npm install mocha --save-dev`)."
|
||||
}
|
||||
[/block]
|
||||
|
||||
***
|
||||
|
||||
# Reporter Options
|
||||
|
||||
Some reporters accept options to customize their behavior. These can be specified in your `cypress.json` or via the command line:
|
||||
|
||||
|
||||
```javascript
|
||||
// cypress.json
|
||||
{
|
||||
"reporter": "junit",
|
||||
"reporterOptions": {
|
||||
"mochaFile": "results/my-test-output.xml",
|
||||
"toConsole": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Command line:
|
||||
|
||||
```shell
|
||||
cypress run --reporter junit --reporter-options "mochaFile=results/my-test-output.xml,toConsole=true"
|
||||
```
|
||||
|
||||
Reporter options differ depending on the reporter (and may not be supported at all). Refer to the documentation for the reporter you are using for details on which options are supported.
|
||||
@@ -1,314 +0,0 @@
|
||||
slug: finding-elements
|
||||
excerpt: Traverse the DOM, find elements, make assertions
|
||||
|
||||
# Contents
|
||||
|
||||
- :fa-angle-right: [Traversal](#section-traversing-the-dom)
|
||||
- [List of Commands](#section-list-of-commands)
|
||||
- [Starting a Query](#section-starting-a-query)
|
||||
- [CSS Selectors](#section-css-selectors)
|
||||
- :fa-angle-right: [Existence](#section-existence)
|
||||
- [Waiting for an element to exist](#section-waiting-for-an-element-to-exist)
|
||||
- [Waiting for an element to not exist](#section-waiting-for-an-element-to-not-exist)
|
||||
- :fa-angle-right: [Timeouts](#section-timeouts)
|
||||
- [How retrying works](#section-how-retrying-works)
|
||||
- [Increasing timeouts](#section-increasing-timeouts)
|
||||
- :fa-angle-right: [Assertions](#assertions)
|
||||
- [Length Assertions](#section-length-assertions)
|
||||
- [Class Assertions](#section-class-assertions)
|
||||
- [Value Assertions](#section-value-assertions)
|
||||
- [Text Content Assertions](#section-text-content-assertions)
|
||||
- [Visibility Assertions](#section-visibility-assertions)
|
||||
- [Existence Assertions](#section-existence-assertions)
|
||||
- [State Assertions](#section-state-assertions)
|
||||
|
||||
***
|
||||
|
||||
# Traversal
|
||||
|
||||
At the heart of all integration tests is the DOM. Cypress gives you a host of familiar commands to make traversing the DOM as easy as possible.
|
||||
|
||||
You'll notice many of these commands match the same behavior as their [jQuery counterparts](https://api.jquery.com/category/traversing/).
|
||||
|
||||
***
|
||||
|
||||
## List of Commands
|
||||
|
||||
- [children](https://on.cypress.io/api/children)
|
||||
- [closest](https://on.cypress.io/api/closest)
|
||||
- [contains](https://on.cypress.io/api/contains)
|
||||
- [eq](https://on.cypress.io/api/eq)
|
||||
- [find](https://on.cypress.io/api/find)
|
||||
- [filter](https://on.cypress.io/api/filter)
|
||||
- [first](https://on.cypress.io/api/first)
|
||||
- [get](https://on.cypress.io/api/get)
|
||||
- [last](https://on.cypress.io/api/last)
|
||||
- [next](https://on.cypress.io/api/next)
|
||||
- [not](https://on.cypress.io/api/not)
|
||||
- [parent](https://on.cypress.io/api/parent)
|
||||
- [parents](https://on.cypress.io/api/parents)
|
||||
- [prev](https://on.cypress.io/api/prev)
|
||||
- [siblings](https://on.cypress.io/api/siblings)
|
||||
|
||||
***
|
||||
|
||||
## Starting a Query
|
||||
|
||||
In Cypress, you will almost always start a sequence of traversal commands with [`cy.get`](https://on.cypress.io/api/get). You can think of [`cy.get`](https://on.cypress.io/api/get) as the same as jQuery's `$` for getting DOM elements.
|
||||
|
||||
**The following examples are equivalent:**
|
||||
|
||||
```javascript
|
||||
// return the element with id: 'main'
|
||||
cy.get("#main") // in cypress
|
||||
$("#main") // in jquery
|
||||
|
||||
// we can chain other traversal commands
|
||||
// using the same familiar pattern
|
||||
cy.get("#main").find("ul").children("li").first() // in cypress
|
||||
$("#main").find("ul").children("li").first() // in jquery
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## CSS Selectors
|
||||
|
||||
All DOM commands support the same CSS selectors found in the [jQuery Sizzle](https://sizzlejs.com/) engine.
|
||||
|
||||
|
||||
```javascript
|
||||
// All of the commands below are valid
|
||||
|
||||
cy.get("ul").find("li:nth-child(odd)")
|
||||
|
||||
cy.get("select[name=list] :not(:selected)")
|
||||
|
||||
cy.get(".container").children("input:disabled'")
|
||||
|
||||
cy.get("header").find("*")
|
||||
|
||||
cy.get("input[type=checkbox]").first("input:checked")
|
||||
|
||||
cy.get("span:nth-of-type(2)")
|
||||
|
||||
cy.get("input[data-js='user-name'][ng-input]")
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Existence
|
||||
|
||||
## Waiting for an element to exist
|
||||
|
||||
If you're coming to Cypress from another framework you'll likely first wonder:
|
||||
|
||||
> How do I wait until an element exists?
|
||||
|
||||
At first glance you may think you need to write this in Cypress:
|
||||
|
||||
```javascript
|
||||
cy.get("#container").should("exist")
|
||||
↲
|
||||
// this assertion is unnecessary
|
||||
```
|
||||
|
||||
In Cypress you **never have to explicitly wait for an element to exist**. By default Cypress does this for you.
|
||||
|
||||
**What's going on under the hood?**
|
||||
|
||||
Under the hood, Cypress will *automatically* retry commands which do not find elements. Cypress will continue retrying a command until it times outs.
|
||||
|
||||
*Imagine this example:*
|
||||
|
||||
```javascript
|
||||
cy.get("form").find("inpit").type("foo").parent(".row")
|
||||
↲
|
||||
// oops we have a typo here in our selector
|
||||
```
|
||||
|
||||
Cypress will continue to retry finding the `inpit` element for **4 seconds** and then time out since this element does not exist. The [`cy.type`](https://on.cypress.io/api/type) and [`cy.parent`](https://on.cypress.io/api/parent) commands are never issued because Cypress will give up after failing to find the `inpit` element.
|
||||
|
||||
Another way to look at it is to imagine there being an implied `.should("exist")` after every DOM command.
|
||||
|
||||
It's as if Cypress is writing the code below for you:
|
||||
|
||||
```javascript
|
||||
cy
|
||||
.get("form").should("exist")
|
||||
.find("input").should("exist").type("foo")
|
||||
.parent(".row").should("exist")
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## Waiting for an element not to exist
|
||||
|
||||
If you've read the previous section you may be wondering:
|
||||
|
||||
> If Cypress automatically waits for elements to exist, how do I tell Cypress to **wait for an element not to exist**?
|
||||
|
||||
The answer is quite simple - just add that assertion and Cypress will *reverse* its default behavior.
|
||||
|
||||
```javascript
|
||||
cy
|
||||
.get("button").click()
|
||||
|
||||
// now cypress will reverse its behavior.
|
||||
//
|
||||
// instead of retrying until an element DOES exist
|
||||
// it will now retry until this element DOES NOT exist
|
||||
.get("#loading-spinner").should("not.exist")
|
||||
```
|
||||
|
||||
In fact - assertions *always* tell Cypress when to resolve your DOM commands.
|
||||
|
||||
You simply describe the state of your element with assertions, and Cypress knows not to resolve your command until the element(s) in question match the assertion(s) behavior.
|
||||
|
||||
***
|
||||
|
||||
# Timeouts
|
||||
|
||||
## How retrying works
|
||||
|
||||
When you provide assertions, Cypress knows to automatically wait until those assertions pass.
|
||||
|
||||
When an assertion does not pass, Cypress will wait a brief period of time and retry again.
|
||||
|
||||
By default, all commands will retry until [`defaultCommandTimeout`](https://on.cypress.io/guides/configuration#section-timeouts) is exceeded. By default this means Cypress will wait up to **4 seconds** per DOM command + its associated assertions.
|
||||
|
||||
Imagine this example:
|
||||
|
||||
```javascript
|
||||
cy
|
||||
.get("#main") // <-- wait up to 4 seconds for '#main' to be found
|
||||
|
||||
.children("form") // <-- wait up to 4 seconds for 'form' children to be found
|
||||
|
||||
.find("input") // <-- wait up to 4 seconds for this 'input' to be found
|
||||
.should("have.value", "foo") // <-- and to have the value 'foo'
|
||||
.and("have.class", "radio") // <-- and to have the class 'radio'
|
||||
|
||||
.parents("#foo")
|
||||
.should("not.exist") // <-- wait up to 4 seconds for this element NOT to be found
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## Increasing timeouts
|
||||
|
||||
You have two ways of increasing the amount of time Cypress waits for resolving DOM commands.
|
||||
|
||||
1. Change the [`defaultCommandTimeout`](https://on.cypress.io/guides/configuration#section-timeouts) globally
|
||||
2. Override the timeout option on a specific command
|
||||
|
||||
Overriding the timeout option on a specific command looks like this:
|
||||
|
||||
```javascript
|
||||
cy
|
||||
.get("#main", {timeout: 5000}) // <-- wait up to 5 seconds for '#main' to be found
|
||||
|
||||
.children("form") // <-- wait up to 4 seconds again
|
||||
|
||||
.find("input", {timeout: 10000}) // <-- wait up to 10 seconds for this 'input' to be found
|
||||
.should("have.value", "foo") // <-- and to have the value 'foo'
|
||||
.and("have.class", "radio") // <-- and to have the class 'radio'
|
||||
|
||||
.parents("#foo", {timeout: 2000})
|
||||
.should("not.exist") // <-- wait up to 2 seconds for this element NOT to be found
|
||||
```
|
||||
|
||||
It's important to note that timeouts will automatically flow down to their cooresponding assertions.
|
||||
|
||||
**In the example we wait up to a total of 10 seconds to:**
|
||||
|
||||
1. find the `<input>`
|
||||
2. ensure it has a value of `foo`
|
||||
3. ensure it has a class of `radio`
|
||||
|
||||
```javascript
|
||||
cy.find("input", {timeout: 10000}).should("have.value", "foo").and("have.class", "radio")
|
||||
↲
|
||||
// adding the timeout here will automatically
|
||||
// flow down to the assertions, and they will
|
||||
// be retried for up to 10 seconds
|
||||
```
|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "warning",
|
||||
"body": "Assuming you have two assertions, if one passes, and one fails, Cypress will continue to retry until they **both** pass. If Cypress eventually times out you'll get a visual indicator in your Command Log to know which specific assertion failed."
|
||||
}
|
||||
[/block]
|
||||
|
||||
***
|
||||
|
||||
# Assertions
|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "info",
|
||||
"body": "Read about [Making Assertions](https://on.cypress.io/guides/making-assertions) and where they come from.",
|
||||
"title": "New to Cypress?"
|
||||
}
|
||||
[/block]
|
||||
|
||||
## Length Assertions
|
||||
|
||||
```javascript
|
||||
// retry until we find 3 matching <li.selected>
|
||||
cy.get("li.selected").should("have.length", 3)
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## Class Assertions
|
||||
|
||||
```javascript
|
||||
// retry until this input does not have class disabled
|
||||
cy.get("form").find("input").should("not.have.class", "disabled")
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## Value Assertions
|
||||
|
||||
```javascript
|
||||
// retry until this textarea has the correct value
|
||||
cy.get("textarea").should("have.value", "foo bar baz")
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## Text Content Assertions
|
||||
|
||||
```javascript
|
||||
// retry until this span does not contain 'click me'
|
||||
cy.get("a").parent("span.help").should("not.contain", "click me")
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## Visibility Assertions
|
||||
|
||||
```javascript
|
||||
// retry until this button is visible
|
||||
cy.get("button").should("be.visible")
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## Existence Assertions
|
||||
|
||||
```javascript
|
||||
// retry until loading spinner no longer exists
|
||||
cy.get("#loading").should("not.exist")
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## State Assertions
|
||||
|
||||
```javascript
|
||||
// retry until our radio is checked
|
||||
cy.get(":radio").should("be.checked")
|
||||
```
|
||||
@@ -1,488 +0,0 @@
|
||||
slug: making-assertions
|
||||
excerpt: Assertions verify an expectation.
|
||||
|
||||
# Contents
|
||||
|
||||
- :fa-angle-right: [Writing an Assertion](#section-writing-an-assertion)
|
||||
- [Implicit Subjects with `cy.should` or `cy.and`](#section-implicit-subjects-with-cy-should-or-cy-and-)
|
||||
- [Explicit Subjects with `expect` and `assert`](#section-explicit-subjects-with-expect-and-assert-)
|
||||
- :fa-angle-right: [Available Assertions](#section-available-assertions)
|
||||
- [Chai](#section-chai)
|
||||
- [Chai-jQuery](#section-chai-jquery)
|
||||
- [Chai-Sinon](#section-chai-sinon)
|
||||
- :fa-angle-right: [Using Chainers with Implicit Subjects](#section-using-chainers-with-implicit-subjects)
|
||||
- [Assertions that Change the Subject](#section-assertions-that-change-the-subject)
|
||||
- [How do I know which assertions change the subject and which keep it the same?](#section-how-do-i-know-which-assertions-change-the-subject-and-which-keep-it-the-same-)
|
||||
- :fa-angle-right: [Negating Assertions](#section-negating-assertions)
|
||||
- :fa-angle-right: [Resolving Assertions](#section-resolving-assertions)
|
||||
- :fa-angle-right: [Increasing timeouts of Assertions](#section-increasing-timeouts)
|
||||
|
||||
***
|
||||
|
||||
# Writing an Assertion
|
||||
|
||||
There are two ways to write an assertion in Cypress.
|
||||
|
||||
1. **Implicit Subjects:** Using [`cy.should`](https://on.cypress.io/api/should) or [`cy.and`](https://on.cypress.io/api/and)
|
||||
2. **Explicit Subjects:** Using `expect`
|
||||
|
||||
***
|
||||
|
||||
## Implicit Subjects with [`cy.should`](https://on.cypress.io/api/should) or [`cy.and`](https://on.cypress.io/api/and)
|
||||
|
||||
Using [`cy.should`](https://on.cypress.io/api/should) or [`cy.and`](https://on.cypress.io/api/and) commands is the preferred way of making an assertion in Cypress. The subject of the assertion is inferred from the subject of the last Cypress command, which is why this is called an **implicit subject**.
|
||||
|
||||
```javascript
|
||||
// the implicit subject here is the first <tr>
|
||||
// this asserts that the <tr> has an .active class
|
||||
cy.get("tbody tr:first").should("have.class", "active")
|
||||
```
|
||||
|
||||

|
||||
|
||||
***
|
||||
|
||||
## Explicit Subjects with `expect`
|
||||
|
||||
Using `expect` allows you to pass in a specific subject and make an assertion on the specified subject.
|
||||
|
||||
These assertions are more commonly used when writing unit tests, but can also be used when writing integration tests. Cypress comes bundled with some existing tools that handle assertions such as:
|
||||
|
||||
* [Chai](https://on.cypress.io/guides/bundled-tools#section-chai)
|
||||
* [Chai-jQuery](https://on.cypress.io/guides/bundled-tools#section-chai-jquery)
|
||||
* [Chai-Sinon](https://on.cypress.io/guides/bundled-tools#section-sinon-chai)
|
||||
|
||||
```javascript
|
||||
// the explicit subject here is the boolean: true
|
||||
expect(true).to.be.true
|
||||
```
|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "info",
|
||||
"body": "Check out our example recipes for [unit testing](https://github.com/cypress-io/cypress-example-recipes/blob/master/cypress/integration/unit_test_application_code_spec.js) and [unit testing React components](https://github.com/cypress-io/cypress-example-recipes/blob/master/cypress/integration/unit_test_react_enzyme_spec.js)",
|
||||
"title": "Unit Testing"
|
||||
}
|
||||
[/block]
|
||||
|
||||
Explicit assertions are great when you want to perform custom logic prior to making the assertion.
|
||||
|
||||
There is an [open issue](https://github.com/cypress-io/cypress/issues/101) for supporting `assert`.
|
||||
|
||||
```javascript
|
||||
cy
|
||||
.get("p")
|
||||
.should(function($p){
|
||||
// return an array of texts from all of the p's
|
||||
var texts = $p.map(function(i, el){
|
||||
return cy.$(el).text()
|
||||
})
|
||||
|
||||
// jquery map returns jquery object
|
||||
// and .get() convert this to simple array
|
||||
var texts = texts.get()
|
||||
|
||||
// array should have length of 3
|
||||
expect(texts).to.have.length(3)
|
||||
|
||||
// set this specific subject
|
||||
expect(texts).to.deep.eq([
|
||||
"Some text from first p",
|
||||
"More text from second p",
|
||||
"And even more text from third p"
|
||||
])
|
||||
})
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Available Assertions
|
||||
|
||||
An assertion is comprised of a subject, chainer methods, and an optional value.
|
||||
|
||||
```javascript
|
||||
expect({foo: "bar"}).to.have.property("foo")
|
||||
↲ ↲ ↲
|
||||
subject chainers value
|
||||
```
|
||||
|
||||
The following chainers are available for your use:
|
||||
|
||||
- [Chai](#section-chai)
|
||||
- [Chai-jQuery](#section-chai-jquery)
|
||||
- [Chai-Sinon](#section-chai-sinon)
|
||||
|
||||
***
|
||||
|
||||
## Chai
|
||||
|
||||
[Chai](http://chaijs.com/) chainers are available for assertions.
|
||||
|
||||
| Chainable getters |
|
||||
| --- |
|
||||
| to |
|
||||
| be |
|
||||
| been |
|
||||
| is |
|
||||
| that |
|
||||
| which |
|
||||
| and |
|
||||
| has |
|
||||
| have |
|
||||
| with |
|
||||
| at |
|
||||
| of |
|
||||
| same |
|
||||
|
||||
| Assertion | Example |
|
||||
| --- | --- |
|
||||
| not | `expect(foo).to.not.equal('bar')` |
|
||||
| deep | `expect(foo).to.deep.equal({ bar: 'baz' })` |
|
||||
| any | `expect(foo).to.have.any.keys('bar', 'baz')` |
|
||||
| all | `expect(foo).to.have.all.keys('bar', 'baz')` |
|
||||
| a( *type* ) | `expect('test').to.be.a('string')` |
|
||||
| an( *type* ) | `expect(undefined).to.be.an('undefined')` |
|
||||
| include( *value* ) | `expect([1,2,3]).to.include(2)` |
|
||||
| contain( *value* ) | `expect('foobar').to.contain('foo')` |
|
||||
| includes( *value* ) | `expect([1,2,3]).includes(2)` |
|
||||
| contains( *value* ) | `expect('foobar').contains('foo')` |
|
||||
| ok | `expect(undefined).to.not.be.ok` |
|
||||
| true | `expect(true).to.be.true` |
|
||||
| false | `expect(false).to.be.false` |
|
||||
| null | `expect(null).to.be.null` |
|
||||
| undefined | `expect(undefined).to.be.undefined` |
|
||||
| exist | `expect(foo).to.exist` |
|
||||
| empty | `expect([]).to.be.empty` |
|
||||
| arguments | `expect(arguments).to.be.arguments` |
|
||||
| equal( *value* ) | `expect(42).to.equal(42)` |
|
||||
| equals( *value* ) | `expect(42).equals(42)` |
|
||||
| eq( *value* ) | `expect(42).to.eq(42)` |
|
||||
| deep.equal( *value* ) | `expect({ foo: 'bar' }).to.deep.equal({ foo: 'bar' })` |
|
||||
| eql( *value* ) | `expect({ foo: 'bar' }).to.eql({ foo: 'bar' })` |
|
||||
| eqls( *value* ) | `expect([ 1, 2, 3 ]).eqls([ 1, 2, 3 ])` |
|
||||
| above( *value* ) | `expect(10).to.be.above(5)` |
|
||||
| gt( *value* ) | `expect(10).to.be.gt(5)` |
|
||||
| greaterThan( *value* ) | `expect(10).to.be.greaterThan(5)` |
|
||||
| least( *value* ) | `expect(10).to.be.at.least(10)` |
|
||||
| gte( *value* ) | `expect(10).to.be.gte(10)` |
|
||||
| below( *value* ) | `expect('foo').to.have.length.below(4)` |
|
||||
| lt( *value* ) | `expect(3).to.be.ls(4)` |
|
||||
| lessThan( *value* ) | `expect(5).to.be.lessThan(10)` |
|
||||
| most( *value* ) | `expect('foo').to.have.length.of.at.most(4)` |
|
||||
| lte( *value* ) | `expect(5).to.be.lte(5)` |
|
||||
| within( *start*, *finish* ) | `expect(7).to.be.within(5,10)` |
|
||||
| instanceof( *constructor* )| `expect([ 1, 2, 3 ]).to.be.instanceof(Array)` |
|
||||
| instanceOf( *constructor* ) | `expect([ 1, 2, 3 ]).to.be.instanceOf(Array)` |
|
||||
| property( *name*, *[value]* ) | `expect(obj).to.have.property('foo')` |
|
||||
| deep.property( *name*, *[value]* ) | `expect(deepObj).to.have.deep.property('teas[1]', 'matcha')` |
|
||||
| ownProperty( *name* ) | `expect('test').to.have.ownProperty('length')` |
|
||||
| haveOwnProperty( *name* ) | `expect('test').to.haveOwnProperty('length')` |
|
||||
| length( *value* ) | `expect('foo').to.have.length.above(2)` |
|
||||
| lengthOf( *value* ) | `expect('foo').to.have.lengthOf(3)` |
|
||||
| match( *regexp* ) | `expect('foobar').to.match(/^foo/)` |
|
||||
| string( *string* ) | `expect('foobar').to.have.string('bar')` |
|
||||
| keys( *key1*, *[key2]*, *[...]* ) | `expect({ foo: 1, bar: 2 }).to.have.key('foo')` |
|
||||
| key( *key1*, *[key2]*, *[...]* ) | `expect({ foo: 1, bar: 2 }).to.have.any.keys('foo')` |
|
||||
| throw( *constructor* ) | `expect(fn).to.throw(Error)` |
|
||||
| throws( *constructor* ) | `expect(fn).throws(ReferenceError, /bad function/)` |
|
||||
| respondTo( *method* ) | `expect(obj).to.respondTo('bar')` |
|
||||
| itself | `expect(Foo).itself.to.respondTo('bar')` |
|
||||
| satisfy( *method* ) | `expect(1).to.satisfy(function(num) { return num > 0; })` |
|
||||
| closeTo( *expected*, *delta*) | `expect(1.5).to.be.closeTo(1, 0.5)` |
|
||||
| members( *set* ) | `expect([1, 2, 3]).to.include.members([3, 2])` |
|
||||
| change( *function* ) | `expect(fn).to.change(obj, 'val')` |
|
||||
| changes( *function* ) | `expect(fn).changes(obj, 'val')` |
|
||||
| increase( *function* ) | `expect(fn).to.increase(obj, 'val')` |
|
||||
| increases( *function* ) | `expect(fn).increases(obj, 'val')` |
|
||||
| decrease( *function* ) | `expect(fn).to.decrease(obj, 'val')` |
|
||||
| decreases( *function* ) | `expect(fn).decreases(obj, 'val')` |
|
||||
|
||||
***
|
||||
|
||||
## Chai-jQuery
|
||||
|
||||
[Chai-jQuery](https://github.com/chaijs/chai-jquery) chainers are available when asserting about a DOM object.
|
||||
|
||||
| Chainers | Assertion |
|
||||
| --- | --- |
|
||||
| attr( *name*, *[value]*) | `expect($('body')).to.have.attr('foo', 'bar')` |
|
||||
| prop( *name*, *[value]*) | `expect($('body')).to.have.prop('disabled', false)` |
|
||||
| css( *name*, *[value]*) | `expect($('body')).to.have.css('background-color', 'rgb(0, 0, 0)')` |
|
||||
| data( *name*, *[value]*) | `expect($('body')).to.have.data('foo', 'bar')` |
|
||||
| class( *className* ) | `expect($('body')).to.have.class('foo')` |
|
||||
| id( *id* ) | `expect($('body')).to.have.id('foo')` |
|
||||
| html( *html*) | `expect($('#title')).to.have.html('Chai Tea')` |
|
||||
| text( *text* ) | `expect($('#title')).to.have.text('Chai Tea')` |
|
||||
| value( *value* ) | `expect($('.year')).to.have.value('2012')` |
|
||||
| visible | `expect($('.year')).to.be.visible` |
|
||||
| hidden | `expect($('.year')).to.be.hidden` |
|
||||
| selected | `expect($('option')).not.to.be.selected` |
|
||||
| checked | `expect($('input')).not.to.be.checked` |
|
||||
| enabled | `expect($('enabled')).to.be.enabled` |
|
||||
| disabled | `expect($('input')).not.to.be.disabled` |
|
||||
| empty | `expect($('body')).not.to.be.empty` |
|
||||
| exist | `expect($('#nonexistent')).not.to.exist` |
|
||||
| match( *selector* ) | `expect($('#empty')).to.match(':empty')` |
|
||||
| contain( *text* ) | `expect($('#content')).to.contain('text')` |
|
||||
| descendents( *selector* ) | `expect($('#content')).to.have.descendants('div')` |
|
||||
|
||||
You will commonly use these chainers after using DOM commands like: [`cy.get`](https://on.cypress.io/api/get), [`cy.contains`](https://on.cypress.io/api/contains), etc.
|
||||
|
||||
***
|
||||
|
||||
## Chai-Sinon
|
||||
|
||||
All Sinon assertions are available in [Sinon–Chai](https://github.com/domenic/sinon-chai).
|
||||
|
||||
| Sinon.JS property/method | Assertion |
|
||||
| -- | -- |
|
||||
| called | `expect(spy).to.be.called` |
|
||||
| callCount | `expect(spy).to.have.callCount(n)` |
|
||||
| calledOnce | `expect(spy).to.be.calledOnce` |
|
||||
| calledTwice | `expect(spy).to.be.calledTwice` |
|
||||
| calledThrice | `expect(spy).to.be.calledThrice` |
|
||||
| calledBefore | `expect(spy1).to.be.calledBefore(spy2)` |
|
||||
| calledAfter | `expect(spy1).to.be.calledAfter(spy2)` |
|
||||
| calledWithNew | `expect(spy).to.be.calledWithNew` |
|
||||
| alwaysCalledWithNew | `expect(spy).to.always.be.calledWithNew` |
|
||||
| calledOn | `expect(spy).to.be.calledOn(context)` |
|
||||
| alwaysCalledOn | `expect(spy).to.always.be.calledOn(context)` |
|
||||
| calledWith | `expect(spy).to.be.calledWith(...args)` |
|
||||
| alwaysCalledWith | `expect(spy).to.always.be.calledWith(...args)` |
|
||||
| calledWithExactly | `expect(spy).to.be.calledWithExactly(...args)` |
|
||||
| alwaysCalledWithExactly | `expect(spy).to.always.be.calledWithExactly(...args)` |
|
||||
| calledWithMatch | `expect(spy).to.be.calledWithMatch(...args)` |
|
||||
| alwaysCalledWithMatch | `expect(spy).to.always.be.calledWithMatch(...args)` |
|
||||
| returned | `expect(spy).to.have.returned(returnVal)` |
|
||||
| alwaysReturned | `expect(spy).to.have.always.returned(returnVal)` |
|
||||
| threw | `expect(spy).to.have.thrown(errorObjOrErrorTypeStringOrNothing)` |
|
||||
| alwaysThrew | `expect(spy).to.have.always.thrown(errorObjOrErrorTypeStringOrNothing)` |
|
||||
|
||||
***
|
||||
|
||||
# Using Chainers with Implicit Subjects
|
||||
|
||||
When utilizing [`cy.should`](https://on.cypress.io/api/should) or [`cy.and`](https://on.cypress.io/api/should), instead of writing chainers as properties and methods, they are instead transformed into a string argument.
|
||||
|
||||
If we convert the previous example to use [`cy.should`](https://on.cypress.io/api/should), it would look like:
|
||||
|
||||
```javascript
|
||||
cy.wrap({foo: "bar"}).should("have.property", "foo")
|
||||
↲ ↲ ↲
|
||||
subject chainers value
|
||||
```
|
||||
|
||||
The chainers are shifted and become the first argument to [`cy.should`](https://on.cypress.io/api/should), with values simply being passed in as additional arguments.
|
||||
|
||||
```javascript
|
||||
// we can additionally continue to chain and add
|
||||
// multiple assertions about our <button> subject
|
||||
cy.get("button").should("have.class", "active").and("be.visible")
|
||||
↲ ↲ ↲ ↲
|
||||
subject chainers value chainers
|
||||
```
|
||||
|
||||
If we converted the above example to use an explicit subject, this is what it would look like:
|
||||
|
||||
```javascript
|
||||
cy.get("button").should(function($button){
|
||||
expect($button).to.have.class("active")
|
||||
expect($button).to.be.visible
|
||||
})
|
||||
```
|
||||
|
||||
This example above may be more familiar to you if you've written tests in JavaScript before.
|
||||
|
||||
If you look closely, you'll see that we've passed a callback function to the [`cy.should`](https://on.cypress.io/api/should) method. This allows us to write expectations inside of that callback function, yet still receive all of the wonderful benefits of [`cy.should`](https://on.cypress.io/api/should).
|
||||
|
||||
Read about [resolving assertions](https://on.cypress.io/guides/making-assertions#section-resolving-assertions) below to learn how [`cy.should`](https://on.cypress.io/api/should) works under the hood.
|
||||
|
||||
***
|
||||
|
||||
## Assertions that change the subject
|
||||
|
||||
Sometimes using a specific chainer will automatically change the assertion subject.
|
||||
|
||||
For instance in `chai`, the method [`have.property("...")`](http://chaijs.com/api/bdd/) will automatically change the subject.
|
||||
|
||||
Additionally in [`Chai-jQuery`](https://github.com/chaijs/chai-jquery#attrname-value), the methods: `attr`, `prop`, `css`, and `data` also change the subject.
|
||||
|
||||
This allows you to utilize other `chainer` methods such as `match` when making assertions about values.
|
||||
|
||||
```javascript
|
||||
// in this example our subject changed to the string 'sans-serif' because
|
||||
// have.css("font-family") returned a string instead of the <body> element
|
||||
cy
|
||||
// subject is <body>
|
||||
.get("body")
|
||||
|
||||
// subject changes to the string return value of 'font-family'
|
||||
.should("have.css", "font-family")
|
||||
|
||||
// use match to assert the string matches a regular expression
|
||||
.and("match", /sans-serif/)
|
||||
```
|
||||
|
||||
```javascript
|
||||
// in this example our subject changed to the string '/users' because
|
||||
// have.attr, href, /users returned a string instead of the <a> element
|
||||
cy
|
||||
// subject is <a>
|
||||
.get("a")
|
||||
|
||||
// subject changes to the string 'users'
|
||||
.should("have.attr", "href", "/users")
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## How do I know which assertions change the subject and which keep it the same?
|
||||
|
||||
The chainers that come from [Chai](https://on.cypress.io/guides/bundled-tools#section-chai) or [Chai-jQuery](https://on.cypress.io/guides/bundled-tools#section-chai-jquery) will always document what they return.
|
||||
|
||||
Alternatively, it is very easy to use Cypress itself to figure this out.
|
||||
|
||||
You can [read more about debugging assertions](https://on.cypress.io/guides/making-assertions#debugging-assertions) here.
|
||||
|
||||
***
|
||||
|
||||
# Negating assertions
|
||||
|
||||
Every assertion can be chained with `.not` to assert the opposite behavior on a subject.
|
||||
|
||||
**Negating Assertion Examples**
|
||||
|
||||
```javascript
|
||||
cy.get("button.disabled").should("be.disabled") // assertion
|
||||
cy.get("button.active").should("not.be.disabled") // negating assertion
|
||||
|
||||
cy.title().should("eq", "My Awesome App") // assertion
|
||||
cy.title().should("not.eq", "My Not So Awesome App") // negating assertion
|
||||
|
||||
cy.get("a").should("have.attr", "popover", "Help") // assertion
|
||||
cy.get("a").should("not.have.attr", "popover", "I'm lost") // negating assertion
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Resolving Assertions
|
||||
|
||||
Knowing when and how to resolve assertions is can be challenging, made even more difficult by modern JavaScript frameworks. Yet accurately resolving assertions is the key for preventing flaky and brittle tests.
|
||||
|
||||
Cypress's API is built to consistently pass or fail every time. As part of this strategy, Cypress will automatically look downstream at assertions and modify its behavior based on upcoming assertions.
|
||||
|
||||
Internally, Cypress will retry commands which are associated to assertions, and will not continue until **all** assertions pass. Using assertions as guards enables you to specify conditions that must be resolved prior to moving on.
|
||||
|
||||
What conditions should you specify? Anything that guarantees your app is in the correct state.
|
||||
|
||||
**Here are some scenarios of using assertions as guards:**
|
||||
|
||||
* Clicking an `<a>` then verifying the url is correct after you expect your server to redirect.
|
||||
* Focusing, then blurring on an `<input>` and expecting an error message with a specific class to be visible.
|
||||
* Clicking a `<button>` and waiting for a modal to animate in.
|
||||
* Typing into a `<form>` and verifying an element should not exist or not be visible.
|
||||
|
||||
Every command that comes before a [`cy.should`](https://on.cypress.io/api/should) will not resolve until **all** of its associated assertions pass. This enables you to accurately test the following situation:
|
||||
|
||||
```html
|
||||
<!-- Our App Code -->
|
||||
<form>
|
||||
<input name="name" placeholder="What is your name?" />
|
||||
<span id="error" style="display: none;"></span>
|
||||
</form>
|
||||
```
|
||||
|
||||
```javascript
|
||||
// Our App Code
|
||||
function waitRandomlyThen(fn){
|
||||
setTimeout(fn, Math.random() * 3000)
|
||||
}
|
||||
|
||||
$("form").submit(function(e){
|
||||
e.preventDefault()
|
||||
|
||||
waitRandomlyThen(function(){
|
||||
$("#error").show()
|
||||
})
|
||||
|
||||
waitRandomlyThen(function(){
|
||||
$("#error").addClass("alert-danger")
|
||||
})
|
||||
|
||||
waitRandomlyThen(function(){
|
||||
$("#error").html("Your <strong>name</strong> is required.")
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
The above app code could can be tested with the following assertions:
|
||||
|
||||
```javascript
|
||||
cy
|
||||
.get("form").submit()
|
||||
.get("#error")
|
||||
.should("be.visible")
|
||||
.and("have.class", "alert-danger")
|
||||
.and("contain", "Your name is required.")
|
||||
```
|
||||
|
||||
Our tests' code is insulated from flaky failures because it is not coupled to any specific timing mechanism. If you look closely, our application code is written in such a way that introduces random wait times - yet Cypress will pass 100% of the time, without any explicit `wait` calls. The moment all 3 of the assertions pass, Cypress will resolve.
|
||||
|
||||

|
||||
|
||||
In modern JavaScript frameworks, and in many common web-based actions, there is usually an *indeterminate* amount of time between an action and a side effect such as:
|
||||
|
||||
* Network Requests
|
||||
* Redirects
|
||||
* Form Submissions
|
||||
* AJAX Requests
|
||||
* Websockets
|
||||
* Page Navigation
|
||||
* setTimeout's
|
||||
* DOM Events
|
||||
|
||||
Cypress makes it easy to test and make assertions about all of these.
|
||||
|
||||
***
|
||||
|
||||
## Increasing timeouts
|
||||
|
||||
You have two ways of increasing the amount of time Cypress waits for assertions to pass.
|
||||
|
||||
1. Change the [`defaultCommandTimeout`](https://on.cypress.io/guides/configuration#section-timeouts) globally
|
||||
2. Override the timeout option on a previous command before the assertion command.
|
||||
|
||||
Overriding the timeout option on a specific command looks like this:
|
||||
|
||||
```javascript
|
||||
cy
|
||||
.find("input", {timeout: 10000}) // <-- wait up to 10 seconds for this 'input' to be found
|
||||
.should("have.value", "foo") // <-- and to have the value 'foo'
|
||||
.and("have.class", "radio") // <-- and to have the class 'radio'
|
||||
|
||||
.parents("#foo", {timeout: 2000}) // <--
|
||||
.should("not.exist") // <-- wait up to 2 seconds for this element NOT to be found
|
||||
```
|
||||
|
||||
It's important to note that timeouts will automatically flow down to their cooresponding assertions.
|
||||
|
||||
**In the example we wait up to a total of 10 seconds to:**
|
||||
|
||||
1. find the `<input>`
|
||||
2. ensure it has a value of `foo`
|
||||
3. ensure it has a class of `radio`
|
||||
|
||||
```javascript
|
||||
cy.find("input", {timeout: 10000}).should("have.value", "foo").and("have.class", "radio")
|
||||
↲
|
||||
// adding the timeout here will automatically
|
||||
// flow down to the assertions, and they will
|
||||
// be retried for up to 10 seconds
|
||||
```
|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "warning",
|
||||
"body": "Assuming you have two assertions, if one passes, and one fails, Cypress will continue to retry until they **both** pass. If Cypress eventually times out you'll get a visual indicator in your Command Log to know which specific assertion failed."
|
||||
}
|
||||
[/block]
|
||||
@@ -1,309 +0,0 @@
|
||||
slug: network-requests-xhr
|
||||
excerpt: A guide to managing XHR requests
|
||||
|
||||
# Contents
|
||||
|
||||
- :fa-angle-right: [Strategy](#section-strategy)
|
||||
- [1: Don't Stub Responses](#section-1-don-1-stub-responses)
|
||||
- [2: Stub Responses](#section-2-stub-responses)
|
||||
- :fa-angle-right: [How to Stub Responses](#section-how-to-stub-responses)
|
||||
- :fa-angle-right: [Requests](#section-requests)
|
||||
- :fa-angle-right: [Server + Routing Table](#section-server-routing-table)
|
||||
- :fa-angle-right: [Fixtures](#section-fixtures)
|
||||
- :fa-angle-right: [Waiting](#section-waiting)
|
||||
- [Removing Flake](#section-removing-flake)
|
||||
- [Clear Source of Failure](#section-clear-source-of-failure)
|
||||
- [Asserting about the XHR Object](#section-asserting-about-the-xhr-object)
|
||||
|
||||
***
|
||||
|
||||
# Strategy
|
||||
|
||||
Cypress makes it easy to manage the entire lifecyle of AJAX / XHR requests within your application. Cypress provides you direct access to the XHR objects, enabling you to make assertions about its properties. Additionally you can even stub and mock a request's response.
|
||||
|
||||
**Common testing scenarios:**
|
||||
|
||||
- Asserting on a request's body
|
||||
- Asserting on a request's url
|
||||
- Asserting on a request's headers
|
||||
- Stubbing a response's body
|
||||
- Stubbing a response's status code
|
||||
- Stubbing a response's headers
|
||||
- Delaying a response
|
||||
- Waiting for a response to happen
|
||||
|
||||
Within Cypress, you have the ability to choose whether to stub responses or allow them to actually hit your server. You can also mix and match within the same test by choosing to stub certain requests, while allowing others to hit your server.
|
||||
|
||||
Let's investigate both strategies, why you would use one versus the other, and why you should regularly use both.
|
||||
|
||||
***
|
||||
|
||||
## 1: Don't Stub Responses
|
||||
|
||||
Requests that aren't stubbed will actually reach your server. By *not* stubbing your responses, you are writing true **end to end** tests. This means you are driving your application the same way a real user would.
|
||||
|
||||
> When requests are not stubbed, this gaurantees the *contract* between your client and server is working correctly.
|
||||
|
||||
In other words, you can have confidence your server is sending the correct data in the correct structure for your client to consume. It is a good idea to have **end to end** tests around your application's *critical paths*. These typically include user login, signup, and other critical paths such as billing.
|
||||
|
||||
**There are downsides to not stubbing responses you should be aware of:**
|
||||
|
||||
- Since no responses are stubbed, that means your server has to *actually send real responses*. This can be problematic because you may have to *seed a database* before every test to generate state. For instance, if you were testing **pagination**, you'd have to seed the database with every object that it takes to replicate this feature in your application.
|
||||
- Since real responses go through every single layer of your server (controllers / models / views) the tests are often *much* slower than stubbed responses.
|
||||
|
||||
If you are writing a traditional server-side application where most of the responses are `HTML` you will likely have few stubbed responses. However, most modern applications that serve `JSON` can take advantage of stubbing.
|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "success",
|
||||
"body": "- Guaranteed to work in production\n- Test coverage around server endpoints\n- Great for traditional server-side HTML rendering",
|
||||
"title": "Benefits"
|
||||
}
|
||||
[/block]
|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "danger",
|
||||
"body": "- Requires seeding\n- Much slower\n- Hard to test edge cases",
|
||||
"title": "Downsides"
|
||||
}
|
||||
[/block]
|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "info",
|
||||
"body": "- Use sparingly\n- Great for the *critical paths* of your application\n- Helpful to have one test around the *happy path* of a feature",
|
||||
"title": "Suggestions"
|
||||
}
|
||||
[/block]
|
||||
|
||||
***
|
||||
|
||||
## 2: Stub Responses
|
||||
|
||||
Stubbing responses enables you to control every aspect of the response, including the response body, the status, headers, and even network delay. Stubbing is extremely fast, most responses will be returned in less than 20ms.
|
||||
|
||||
> Stubbing responses is a great way to control the data that is returned to your client.
|
||||
|
||||
You don't have to do any work on the server. Your application will have no idea it's requests are being stubbed, so there are **no code changes** needed. In fact, stubbed requests will show up in the Network tab of your Developer Tools, and your application will continue to work after the test is finished.
|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "success",
|
||||
"body": "- Easy control of response bodies, status, and headers\n- Force responses to take longer to simulate network delay\n- No code changes to your server or client code\n- Fast, < 20ms response times\n- Perfect for JSON API's",
|
||||
"title": "Benefits"
|
||||
}
|
||||
[/block]
|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "danger",
|
||||
"body": "- No guarantee your stubbed responses match the actual data your server sends\n- No test coverage on some server endpoints\n- Not as useful if you're using traditional server side HTML rendering",
|
||||
"title": "Downsides"
|
||||
}
|
||||
[/block]
|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "info",
|
||||
"body": "- Use for the vast majority of your testing\n- Mix and match, typically have one true end to end test, and then stub the rest",
|
||||
"title": "Suggestions"
|
||||
}
|
||||
[/block]
|
||||
|
||||
***
|
||||
|
||||
# How to stub responses
|
||||
|
||||
Cypress makes it easy to stub a response and control the `body`, `status`, `headers`, or even delay.
|
||||
|
||||
To begin stubbing responses you have to do two things.
|
||||
|
||||
1. Start a [`cy.server`](https://on.cypress.io/api/server)
|
||||
2. Provide a [`cy.route`](https://on.cypress.io/api/route)
|
||||
|
||||
These two commands work together to control the behavior of your responses within the command's options. See [`cy.server` options](https://on.cypress.io/api/server#section-options) and [`cy.route` options](https://on.cypress.io/api/route#section-options) for instructions on how to stub responses.
|
||||
|
||||
[`cy.server`](https://on.cypress.io/api/server) enables stubbing, while [`cy.route`](https://on.cypress.io/api/route) provides a routing table so Cypress understands which response should go with which request.
|
||||
|
||||
***
|
||||
|
||||
# Requests
|
||||
|
||||
Cypress will automatically indicate when an XHR request happens in your application. These are logged in the Command Log regardless of whether or not you are using stubbing. This provides you a visual indicator when a request has started and when it is finished. Additionally, Cypress will take a snapshot of the DOM when the request is made and another snapshot when the response comes back.
|
||||
|
||||
By default, Cypress is configured to *ignore* requests that are used to fetch static content like `.js` or `.html` files. This keeps the Command Log less noisy. This option can be changed in the [configuration](https://on.cypress.io/guides/configuration).
|
||||
|
||||
Cypress automatically collects the request `headers` and the request `body` and will make this available to you.
|
||||
|
||||
***
|
||||
|
||||
# Server + Routing Table
|
||||
|
||||
```javascript
|
||||
cy
|
||||
// enable response stubbing
|
||||
.server()
|
||||
|
||||
// Route all GET requests that have a
|
||||
// URL that matches the RegExp /users/
|
||||
// and force the response to be: []
|
||||
.route({
|
||||
method: "GET",
|
||||
url: /users/,
|
||||
response: []
|
||||
})
|
||||
```
|
||||
|
||||
Each [`cy.route`](https://on.cypress.io/api/route) you provide will automatically route those requests to specific responses and control their body, response headers, or even force additional network delay.
|
||||
|
||||
When you start a server and provide a routing table, Cypress will display this under "Routes" in the Command Log.
|
||||
|
||||

|
||||
|
||||
Once you start a server with [`cy.server`](https://on.cypress.io/api/server), all requests will be controllable for the remainder of the test. When a new test runs, Cypress will restore the default behavior and remove all routing and stubbing. For a complete reference of the API and options, refer to the documentation for each command.
|
||||
|
||||
- [`cy.server`](https://on.cypress.io/api/server)
|
||||
- [`cy.route`](https://on.cypress.io/api/route)
|
||||
|
||||
***
|
||||
|
||||
# Fixtures
|
||||
|
||||
When stubbing a response, you typically need to manage potentially large and complex JSON objects. Cypress has support for [fixtures](https://on.cypress.io/guides/creating-fixtures), and even allows you to integrate fixture syntax directly into responses.
|
||||
|
||||
|
||||
```javascript
|
||||
cy
|
||||
.server()
|
||||
|
||||
// we set the response to be the activites.json fixture
|
||||
.route("GET", /activities/, "fixture:activities.json")
|
||||
```
|
||||
|
||||
You can additionally reference [aliases](https://on.cypress.io/guides/using-aliases) within responses. These aliases do not have to point to fixtures, but that is a common use case. Separating out a fixture enables you to work and mutate that object prior to handing it off to a response.
|
||||
|
||||
|
||||
```javascript
|
||||
cy
|
||||
.server()
|
||||
|
||||
.fixture("activities.json").as("activitiesJSON")
|
||||
.route("GET", /activities/, "@activitiesJSON")
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Waiting
|
||||
|
||||
Whether or not you choose to stub responses, Cypress enables you to declaratively [`cy.wait`](https://on.cypress.io/api/wait) for requests and their responses.
|
||||
|
||||
```javascript
|
||||
cy
|
||||
.server()
|
||||
.route(/activities/, "fixture:activities").as("getActivities")
|
||||
.route(/messages/, "fixture:messages").as("getMessages")
|
||||
|
||||
// visit the dashboard, which should make requests that match
|
||||
// the two routes above
|
||||
.visit("http://localhost:8888/dashboard")
|
||||
|
||||
// pass an array of Route Aliases which forces Cypress to wait
|
||||
// until it sees a response for each request that matches
|
||||
// each of these aliases
|
||||
.wait(["@getActivities", "@getMessages"])
|
||||
|
||||
// these commands will not run until the wait command resolves above
|
||||
.get("h1").should("contain", "Dashboard")
|
||||
```
|
||||
|
||||
Declaratively waiting for responses has many advantages:
|
||||
- You descrease test flake
|
||||
- Source of failure is clearer
|
||||
- You can make assertions about the XHR objects
|
||||
|
||||
***
|
||||
|
||||
## Removing Flake
|
||||
|
||||
One advantage of declaratively waiting for requests is that it decreases test flake. You can think of [`cy.wait`](https://on.cypress.io/api/wait) as a guard that indicates to Cypress when you expect a request to be made that matches a specific routing alias. This prevents commands from running until responses come back and it guards against situations where your requests are initially delayed.
|
||||
|
||||
**Auto-complete Example:**
|
||||
|
||||
```javascript
|
||||
cy
|
||||
.server()
|
||||
.route(/search/, [{item: "Book 1"}, {item: "Book 2"}]).as("getSearch")
|
||||
|
||||
// our autocomplete field is throttled
|
||||
// meaning it only makes a request after
|
||||
// 500ms from the last keyPress
|
||||
.get("#autocomplete").type("Book")
|
||||
|
||||
// wait for the request + response
|
||||
// thus insulating us from the
|
||||
// throttled request
|
||||
.wait("@getSearch")
|
||||
|
||||
.get("#results")
|
||||
.should("contain", "Book 1")
|
||||
.and("contain", "Book 2")
|
||||
```
|
||||
|
||||
What makes this example above so powerful is that Cypress will automatically wait for a request that matches the `getSearch` alias. Instead of forcing Cypress to test the *side effect* of a successful request (the display of the Book results), you can test the actual *cause* of the results.
|
||||
|
||||
***
|
||||
|
||||
## Clear Source of Failure
|
||||
|
||||
In our example above, we added an assertion to the display of the search results.
|
||||
|
||||
**The search results working are coupled to a few things in our application:**
|
||||
|
||||
1. Our application making a request to the correct URL.
|
||||
2. Our application correctly processing the response.
|
||||
3. Our application inserting the results into the DOM.
|
||||
|
||||
In this example, there are many possible sources of failure. In most testing tools, if our request failed to ever go out, we would normally only ever get an error once we attempt to find the results in the DOM and see that there is no matching element. This is problematic because it's unknown *why* the results failed to be displayed. Was there a problem with our rendering code? Did we modify or change an attribute such as an `id` or `class` on an element? Perhaps our server sent us different Book items.
|
||||
|
||||
With Cypress, by adding a [`cy.wait`](https://on.cypress.io/api/wait) guard, you can more easily pinpoint your specific problem. If the request never went out, you'll receive errors like this.
|
||||
|
||||

|
||||
|
||||
Now we know exactly why our test failed. It had nothing to do with the DOM. Instead we can see that either our request never went out or a request went out to the wrong URL.
|
||||
|
||||
***
|
||||
|
||||
## Asserting about the XHR Object
|
||||
|
||||
Another benefit of using [`cy.wait`](https://on.cypress.io/api/wait) on requests is that it allows you to access the actual `XHR` object. This is useful when you want to make assertions about this object.
|
||||
|
||||
In our example above we can assert about the request object to verify that it sent data as a query string in the URL. Although we're mocking the response, we can still verify that our application sends the correct request.
|
||||
|
||||
|
||||
```javascript
|
||||
cy
|
||||
.server()
|
||||
.route(/search/, [{item: "Book 1"}, {item: "Book 2"}]).as("getSearch")
|
||||
|
||||
.get("#autocomplete").type("Book")
|
||||
|
||||
// this yields us the XHR object which includes
|
||||
// fields for request, response, url, method, etc
|
||||
.wait("@getSearch")
|
||||
.its("url").should("include", "/search?query=Book")
|
||||
|
||||
.get("#results")
|
||||
.should("contain", "Book 1")
|
||||
.and("contain", "Book 2")
|
||||
```
|
||||
|
||||
**The XHR object that [`cy.wait`](https://on.cypress.io/api/wait) yields you has everything you need to make assertions including:**
|
||||
|
||||
- URL
|
||||
- Method
|
||||
- Status Code
|
||||
- Request Body
|
||||
- Request Headers
|
||||
- Response Body
|
||||
- Response Headers
|
||||
@@ -1,312 +0,0 @@
|
||||
slug: continuous-integration
|
||||
excerpt: Run Cypress in any CI provider
|
||||
|
||||
# Contents
|
||||
|
||||
- :fa-angle-right: [What's Supported?](#section-what-s-supported-)
|
||||
- :fa-angle-right: [Running in CI](#section-running-in-ci)
|
||||
- [Your CI config file](#section-your-ci-config-file)
|
||||
- [Recording your runs](#section-recording-your-runs)
|
||||
- :fa-angle-right: [Using Environment Variables](#section-using-environment-variables)
|
||||
- [Hide your **Record Key**](#section-hide-your-record-key)
|
||||
- [Install a specific version](#section-install-a-specific-version)
|
||||
- [Modify configuration settings](#section-modify-configuration-settings)
|
||||
- [Change environment variables in your tests](#section-change-environment-variables-in-your-tests)
|
||||
- :fa-angle-right: [Optimizing CI](#section-optimizing-ci)
|
||||
- [Caching Cypress in Travis CI](#section-caching-cypress-in-travis-ci)
|
||||
- [Caching Cypress in CircleCI](#section-caching-cypress-in-circleci)
|
||||
- :fa-angle-right: [Dependencies](#section-dependencies)
|
||||
- :fa-angle-right: [Known Issues](#section-known-issues)
|
||||
- [CircleCI](#section-circleci)
|
||||
- [Jenkins](#section-jenkins)
|
||||
- [Docker](#section-docker)
|
||||
- :fa-angle-right: [Troubleshooting](#section-troubleshooting)
|
||||
- [No output](#section-no-output)
|
||||
|
||||
***
|
||||
|
||||
# What's Supported?
|
||||
|
||||
Cypress should run on **all** CI providers. We currently have seen Cypress working on the following services / providers:
|
||||
|
||||
- Jenkins (Linux)
|
||||
- TravisCI
|
||||
- CircleCI
|
||||
- CodeShip
|
||||
- GitLab
|
||||
- Docker
|
||||
|
||||
***
|
||||
|
||||
# Running in CI
|
||||
|
||||
Running Cypress in CI is just as easy as running it locally. You generally only need to do two things:
|
||||
|
||||
```shell
|
||||
## 1. install the CLI tools
|
||||
npm install -g cypress-cli
|
||||
|
||||
## 2. run cypress
|
||||
cypress run
|
||||
```
|
||||
|
||||
That's it!
|
||||
|
||||
This will automatically go out and install Cypress, and then run all your tests.
|
||||
|
||||
For a comprehensive list of all the options you can pass to `cypress run`, [refer to the CLI documentation](https://on.cypress.io/cli).
|
||||
|
||||

|
||||
|
||||
***
|
||||
|
||||
## Your CI Config File
|
||||
|
||||
Depending on which CI provider you'll need to add these two lines (above) to a config file.
|
||||
|
||||
For instance with TravisCI and CircleCI we have:
|
||||
|
||||
- `.travis.yml`
|
||||
- `circle.yml`
|
||||
|
||||
You'll want to refer to your CI providers documentation for knowing when to run those commands.
|
||||
|
||||
Here's a couple example config files:
|
||||
|
||||
```yaml
|
||||
## .travis.yml
|
||||
|
||||
before_install:
|
||||
- npm install -g cypress-cli
|
||||
|
||||
script:
|
||||
- cypress run
|
||||
```
|
||||
|
||||
```yaml
|
||||
## circle.yml
|
||||
|
||||
dependencies:
|
||||
post:
|
||||
- npm install -g cypress-cli
|
||||
|
||||
test:
|
||||
override:
|
||||
- cypress run
|
||||
```
|
||||
|
||||
For more example config files check out any of our [example apps](https://on.cypress.io/guides/all-example-apps).
|
||||
|
||||
***
|
||||
|
||||
## Recording your runs
|
||||
|
||||
You can automatically have Cypress record your runs and make them available in our Dashboard.
|
||||
|
||||
Recorded runs will contain:
|
||||
|
||||
- Standard output
|
||||
- Failing Tests
|
||||
- Screenshots
|
||||
- Videos
|
||||
|
||||
To record your runs:
|
||||
|
||||
1. [Setup your project to record](https://on.cypress.io/guides/projects)
|
||||
2. [Pass the --record flag to `cypress run`](https://on.cypress.io/how-do-i-record-runs)
|
||||
|
||||
You can [read more about the Dashboard here](https://on.cypress.io/guides/dashboard-features).
|
||||
|
||||
***
|
||||
|
||||
# Using Environment Variables
|
||||
|
||||
You can set various environment variables to modify how Cypress runs.
|
||||
|
||||
Typically you'd want to do this to:
|
||||
|
||||
- [Hide your **Record Key**](#section-hide-your-record-key)
|
||||
- [Install a specific version](#section-install-a-specific-version)
|
||||
- [Modify configuration settings](#section-modify-configuration-settings)
|
||||
- [Change environment variables in your tests](#section-change-environment-variables-in-your-tests)
|
||||
|
||||
***
|
||||
|
||||
## Hide your Record Key
|
||||
|
||||
If you are [recording your runs](#section-recording-your-runs) on a public project, you'll want to protect your Record Key. [Learn why.](https://docs.cypress.io/docs/projects#section-how-do-a-projectid-and-record-key-work-together-)
|
||||
|
||||
Instead of hard coding it into your run command like this:
|
||||
|
||||
```shell
|
||||
cypress run --record --key <record_key>
|
||||
```
|
||||
|
||||
You can set it as an environment variable and we'll automatically use that value.
|
||||
|
||||
Typically you'd set this inside of your CI provider like this:
|
||||
|
||||
**CircleCI**
|
||||

|
||||
|
||||
**TravisCI**
|
||||

|
||||
|
||||
You can now omit the `--key` flag when recording.
|
||||
|
||||
```shell
|
||||
## weeee we don't have to pass in the key here!
|
||||
cypress run --record
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## Install a specific version
|
||||
|
||||
You can install a specific version of Cypress by setting the environment variable: `CYPRESS_VERSION`.
|
||||
|
||||
**Set the version to an older Cypress**
|
||||

|
||||
|
||||
***
|
||||
|
||||
## Modify configuration settings
|
||||
|
||||
Don't forget you can also override settings in `cypress.json` to modify Cypress's behavior.
|
||||
|
||||
Typical use cases would be modifying things like:
|
||||
|
||||
- `CYPRESS_BASE_URL`
|
||||
- `CYPRESS_VIDEO_COMPRESSION`
|
||||
- `CYPRESS_REPORTER`
|
||||
|
||||
Refer to the [configuration docs](https://on.cypress.io/guides/configuration#section-environment-variables) for more examples.
|
||||
|
||||
***
|
||||
|
||||
## Change environment variables in your tests
|
||||
|
||||
Of course you can also set environment varibables for use strictly in your tests.
|
||||
|
||||
These enable your code to reference dynamic values.
|
||||
|
||||
```shell
|
||||
export "EXTERNAL_API_SERVER=https://corp.acme.co"
|
||||
```
|
||||
|
||||
And then in your tests:
|
||||
|
||||
```javascript
|
||||
cy
|
||||
.request({
|
||||
method: "POST",
|
||||
url: Cypress.env("EXTERNAL_API_SERVER") + "/users/1",
|
||||
body: {
|
||||
foo: "bar",
|
||||
baz: "quux"
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
Refer to the dedicated [Environment Variables Guide](https://on.cypress.io/guides/environment-variables) for more examples.
|
||||
|
||||
***
|
||||
|
||||
# Optimizing CI
|
||||
|
||||
Most CI providers allow caching of directories and dependencies between builds. This allows you to save the state of Cypress, therefore making the builds run faster.
|
||||
|
||||
## Caching Cypress in Travis CI
|
||||
|
||||
```yaml
|
||||
## .travis.yml
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- /home/travis/.cypress/Cypress
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## Caching Cypress in CircleCI
|
||||
|
||||
```yaml
|
||||
## circle.yml
|
||||
|
||||
## make sure you set the correct node version based on what you've installed!
|
||||
dependencies:
|
||||
cache_directories:
|
||||
- /home/ubuntu/nvm/versions/node/v6.2.2/bin/cypress
|
||||
- /home/ubuntu/nvm/versions/node/v6.2.2/lib/node_modules/cypress-cli
|
||||
- /home/ubuntu/.cypress/Cypress
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Dependencies
|
||||
|
||||
If you're using a hosted CI service such as `Travis` or `CircleCI` then you don't have to install anything.
|
||||
|
||||
For **everything else** you must install these dependencies:
|
||||
|
||||
```shell
|
||||
apt-get install xvfb libgtk2.0-0 libnotify-dev libgconf-2-4 libnss3 libxss1
|
||||
```
|
||||
|
||||
If you run `cypress run` and see no output [see this section for troubleshooting this known issue](#section-no-output).
|
||||
|
||||
***
|
||||
|
||||
# Known Issues
|
||||
|
||||
## CircleCI
|
||||
|
||||
You need to select their [`Ubuntu 12.04` image](https://circleci.com/docs/build-image-precise/).
|
||||
|
||||

|
||||
|
||||
The `Ubuntu 14.04` image does not have all of the required dependencies installed by default. You can likely install them yourself. [There is an open issue for this here.](https://github.com/cypress-io/cypress/issues/315)
|
||||
|
||||
***
|
||||
|
||||
## Jenkins
|
||||
|
||||
You need to install all of the [linux dependencies](#section-dependencies).
|
||||
|
||||
***
|
||||
|
||||
## Docker
|
||||
|
||||
We don't offer an **official** docker container, but our users have created one. [This container has all of the required dependencies installed and ready to go](https://docs.cypress.io/docs/userland-extensions#section-docker).
|
||||
|
||||
If you don't use this image you must install all of the [linux dependencies](#section-dependencies).
|
||||
|
||||
See [this issue](https://github.com/cypress-io/cypress/issues/165) for more information.
|
||||
|
||||
If you are running **long** runs on Docker, you need to set the `ipc` to `host` mode.
|
||||
|
||||
[This issue](https://github.com/cypress-io/cypress/issues/350) describes exactly what to do.
|
||||
|
||||
***
|
||||
|
||||
# Troubleshooting
|
||||
|
||||
## No Output
|
||||
|
||||
**Sympton**
|
||||
After executing `cypress run` you don't see any output. In other words: nothing happens.
|
||||
|
||||
**Problem**
|
||||
You are in 100% of the cases missing [a dependency](#section-dependencies) above. Please install all of the dependencies.
|
||||
|
||||
The reason you're not seeing any output is a longstanding issue with Cypress which [there is an open issue for](https://github.com/cypress-io/cypress/issues/317).
|
||||
|
||||
We are working on improving this experience!
|
||||
|
||||
**Seeing Errors**
|
||||
Although running `cypress run` will yield no output - you can see the actual dependency failure by invoking the Cypress binary directly.
|
||||
|
||||
```shell
|
||||
## invoke the Cypress binary directly
|
||||
/home/<user>/.cypress/Cypress/Cypress
|
||||
```
|
||||
@@ -1,242 +0,0 @@
|
||||
slug: environment-variables
|
||||
excerpt: Set up environment variables
|
||||
|
||||
# Contents
|
||||
|
||||
- :fa-angle-right: [Use Case](#section-use-case)
|
||||
- :fa-angle-right: [Setting Environment Variables](#section-setting-environment-variables)
|
||||
- [Option #1: Set in `cypress.json`](#section-option-1-set-in-cypress-json-)
|
||||
- [Option #2: Create a `cypress.env.json`](#section-option-2-create-a-cypress-env-json-)
|
||||
- [Option #3: Export as `CYPRESS_*`](#section-option-3-export-as-cypress_-)
|
||||
- [Option #4: Pass in from the CLI as `--env`](#section-option-4-pass-in-from-the-cli-as-env-)
|
||||
- :fa-angle-right: [Overriding Configuration](#section-overriding-configuration)
|
||||
|
||||
***
|
||||
|
||||
# Use Case
|
||||
|
||||
Environment variables should be used:
|
||||
- Whenever values are different across developer machines
|
||||
- Whenever values change frequently and are highly dynamic
|
||||
|
||||
The most common use case is to access custom values you've written in your `hosts` file.
|
||||
|
||||
For instance instead of hard coding this in your tests:
|
||||
|
||||
```javascript
|
||||
cy
|
||||
// this would break on other dev machines
|
||||
.visit("http://server.dev.local")
|
||||
```
|
||||
|
||||
We can move this into an environment variable.
|
||||
|
||||
```javascript
|
||||
cy
|
||||
// now this is pointing to a dynamic
|
||||
// environment variable
|
||||
.visit(Cypress.env("host"))
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Setting Environment Variables
|
||||
|
||||
There are 4 different ways to set environment variables. Each has a slightly different use case.
|
||||
|
||||
**To summarize you can:**
|
||||
|
||||
- set in `cypress.json`
|
||||
- create a `cypress.env.json`
|
||||
- export as `CYPRESS_*`
|
||||
- pass in the CLI as `--env`
|
||||
|
||||
Don't feel obligated to pick just one method. It is common to use one strategy for local development but another when running in CI.
|
||||
|
||||
When your tests are running, you can use the [`Cypress.env()`](https://on.cypress.io/api/env) function to access the values of your environment variables.
|
||||
|
||||
***
|
||||
|
||||
## Option #1: Set in `cypress.json`
|
||||
|
||||
Any key/value you set in your [`cypress.json`](https://on.cypress.io/guides/configuration) under the `env` key will become an environment variable.
|
||||
|
||||
```json
|
||||
// cypress.json
|
||||
{
|
||||
"projectId": "128076ed-9868-4e98-9cef-98dd8b705d75",
|
||||
"env": {
|
||||
"foo": "bar",
|
||||
"some": "value"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```javascript
|
||||
// in your test files
|
||||
|
||||
Cypress.env() // => {foo: "bar", some: "value"}
|
||||
Cypress.env("foo") // => "bar"
|
||||
Cypress.env("some") // => "value"
|
||||
```
|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "success",
|
||||
"body": "- Great for values that need to be checked into source control and remain the same on all machines",
|
||||
"title": "Benefits"
|
||||
}
|
||||
[/block]
|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "danger",
|
||||
"body": "- Only works for values which should be the same on across all machines",
|
||||
"title": "Downsides"
|
||||
}
|
||||
[/block]
|
||||
|
||||
***
|
||||
|
||||
## Option #2: Create a `cypress.env.json`
|
||||
|
||||
You can create your own `cypress.env.json`, which Cypress will automatically check. Values in here will overwrite conflicting values in `cypress.json`.
|
||||
|
||||
This strategy is useful because if you add `cypress.env.json` to your `.gitignore` file, the values in here can be different for each developer machine.
|
||||
|
||||
```json
|
||||
// cypress.env.json
|
||||
|
||||
{
|
||||
"host": "veronica.dev.local",
|
||||
"api_server": "http://localhost:8888/api/v1/"
|
||||
}
|
||||
```
|
||||
|
||||
```javascript
|
||||
// in your test files
|
||||
|
||||
Cypress.env() // => {host: "veronica.dev.local", api_server: "http://localhost:8888/api/v1"}
|
||||
Cypress.env("host") // => "veronica.dev.local"
|
||||
Cypress.env("api_server") // => "http://localhost:8888/api/v1/"
|
||||
```
|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "success",
|
||||
"body": "- Dedicated file just for environment variables\n- Enables you to generate this file from other build processes\n- Values can be different on each machine if not checked into source control",
|
||||
"title": "Benefits"
|
||||
}
|
||||
[/block]
|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "danger",
|
||||
"body": "- Another file you have to deal with\n- Overkill for 1 or 2 environment variables",
|
||||
"title": "Downsides"
|
||||
}
|
||||
[/block]
|
||||
|
||||
***
|
||||
|
||||
## Option #3: Export as `CYPRESS_*`
|
||||
|
||||
Any environment variable on your machine that starts with either `CYPRESS_` or `cypress_` will automatically be added and made available to you.
|
||||
|
||||
Conflicting values from this method will override `cypress.json` and `cypress.env.json` files.
|
||||
|
||||
Cypress will automatically **strip off** the `CYPRESS_` when adding your environment variables.
|
||||
|
||||
```shell
|
||||
# export cypress env variables from the command line
|
||||
export CYPRESS_HOST=laura.dev.local
|
||||
export cypress_api_server=http://localhost:8888/api/v1/
|
||||
```
|
||||
|
||||
```javascript
|
||||
// in your test files
|
||||
|
||||
Cypress.env() // => {HOST: "laura.dev.local", api_server: "http://localhost:8888/api/v1"}
|
||||
Cypress.env("HOST") // => "laura.dev.local"
|
||||
Cypress.env("api_server") // => "http://localhost:8888/api/v1/"
|
||||
```
|
||||
|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "success",
|
||||
"body": "- Quickly export some values\n- Can be stored in your `bash_profile`\n- Allows for dynamic values between different machines\n- Especially useful for CI environments",
|
||||
"title": "Benefits"
|
||||
}
|
||||
[/block]
|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "danger",
|
||||
"body": "- Not as obvious where values come from vs the other methods",
|
||||
"title": "Downsides"
|
||||
}
|
||||
[/block]
|
||||
|
||||
***
|
||||
|
||||
## Option #4: Pass in from the CLI as `--env`
|
||||
|
||||
Lastly you can also pass in environment variables as options when [using the CLI tool](https://github.com/cypress-io/cypress-cli).
|
||||
|
||||
Values here will overwrite all other conflicting environment variables.
|
||||
|
||||
You can use the `--env` option on `cypress run`.
|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "info",
|
||||
"body": "Multiple values must be separated by a comma. NOT A SPACE."
|
||||
}
|
||||
[/block]
|
||||
|
||||
```yaml
|
||||
cypress run --env host=kevin.dev.local,api_server=http://localhost:8888/api/v1
|
||||
```
|
||||
|
||||
```javascript
|
||||
// in your test files
|
||||
|
||||
Cypress.env() // => {host: "kevin.dev.local", api_server: "http://localhost:8888/api/v1"}
|
||||
Cypress.env("host") // => "kevin.dev.local"
|
||||
Cypress.env("api_server") // => "http://localhost:8888/api/v1/"
|
||||
```
|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "success",
|
||||
"body": "- Does not require any changes to files / config\n- Obvious where environment variables come from\n- Allows for dynamic values between different machines\n- Overwrites all other forms of setting env variables",
|
||||
"title": "Benefits"
|
||||
}
|
||||
[/block]
|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "danger",
|
||||
"body": "- Pain to write the `--env` options everywhere you use Cypress",
|
||||
"title": "Downsides"
|
||||
}
|
||||
[/block]
|
||||
|
||||
***
|
||||
|
||||
# Overriding Configuration
|
||||
|
||||
If your environment variables match a standard configuration key then instead of setting an `environment variable` they will instead override the configuration value.
|
||||
|
||||
```shell
|
||||
## this changes the baseUrl configuration value
|
||||
## and will not set an environment variable in Cypress.env()
|
||||
export CYPRESS_BASE_URL=http://localhost:8080
|
||||
|
||||
## the key 'foo' does not match a configuration key so
|
||||
## this just sets a regular environment variable in Cypress.env()
|
||||
export CYPRESS_FOO=bar
|
||||
```
|
||||
|
||||
You can [read more about how environment variables can change configuration here](https://on.cypress.io/configuration).
|
||||
@@ -1,235 +0,0 @@
|
||||
slug: stubs-spies-clocks
|
||||
excerpt: Learn about when and why to use stubs, spies, and control clock time
|
||||
|
||||
# Contents
|
||||
|
||||
- :fa-angle-right: [Capabilities](#section-capabilities)
|
||||
- :fa-angle-right: [Libraries and Tools](#section-libraries-and-tools)
|
||||
- :fa-angle-right: [Common Scenarios](#section-common-scenarios)
|
||||
- [Stubs](#section-stubs)
|
||||
- [Spies](#section-spies)
|
||||
- [Clock](#section-clock)
|
||||
- [Assertions](#section-assertions)
|
||||
- :fa-angle-right: [Integration and Extensions](#section-integration-and-extensions)
|
||||
|
||||
***
|
||||
|
||||
# Capabilities
|
||||
|
||||
Cypress comes built in with the ability to [`stub`](https://on.cypress.io/api/stub), [`spy`](https://on.cypress.io/api/spy) or modify your applications [`clock`](https://on.cypress.io/api/clock) - such as controlling `Date`, `setTimeout`, and `setInterval`.
|
||||
|
||||
These commands are useful when writing both **unit tests** and **integration tests**.
|
||||
|
||||
***
|
||||
|
||||
# Libraries and Tools
|
||||
|
||||
Cypress automatically bundles and wraps these libraries:
|
||||
|
||||
| Name | What it does |
|
||||
| --- | ---- |
|
||||
| [`sinon`](http://sinonjs.org) | provides the [`stub`](https://on.cypress.io/api/stub) and [`spy`](https://on.cypress.io/api/spy) API's |
|
||||
| [`lolex`](https://github.com/sinonjs/lolex) | provides the [`clock`](https://on.cypress.io/api/clock) and [`tick`](https://on.cypress.io/api/tick) API's |
|
||||
| [`sinon-as-promised`](https://github.com/bendrucker/sinon-as-promised) | makes it easy to stub `Promise` returning functions |
|
||||
| [`sinon-chai`](https://github.com/domenic/sinon-chai) | adds `chai` assertions for stubs and spies |
|
||||
|
||||
You can refer to each of these libraries documentation for more examples and explanations.
|
||||
|
||||
***
|
||||
|
||||
# Common Scenarios
|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "info",
|
||||
"body": "[Check out our example recipe testing spying, stubbing and time](https://github.com/cypress-io/cypress-example-recipes/blob/master/cypress/integration/spy_stub_clock_spec.js)",
|
||||
"title": "Example test!"
|
||||
}
|
||||
[/block]
|
||||
|
||||
## Stubs
|
||||
|
||||
A stub is a way to modify a function and force its behavior to be controlled by you (the programmer).
|
||||
|
||||
A stub is most commonly used in a unit test but is still useful during some integration / e2e tests.
|
||||
|
||||
```javascript
|
||||
// create a standalone stub (generally for use in unit test)
|
||||
cy.stub()
|
||||
|
||||
// replace obj.method() with a stubbed function
|
||||
cy.stub(obj, "method")
|
||||
|
||||
// force obj.method() to return "foo"
|
||||
cy.stub(obj, "method").returns("foo")
|
||||
|
||||
// force obj.method() when called with "bar" argument to return "foo"
|
||||
cy.stub(obj, "method").withArgs("bar").returns("foo")
|
||||
|
||||
// force obj.method() to return a promise which resolves to "foo"
|
||||
cy.stub(obj, "method").resolves("foo")
|
||||
|
||||
// force obj.method() to return a promise rejected with an error
|
||||
cy.stub(obj, "method").rejects(new Error("foo"))
|
||||
```
|
||||
|
||||
You generally stub a function when it has side effects you are trying to control.
|
||||
|
||||
**Common Scenarios:**
|
||||
|
||||
- You have a function that accepts a callback, and want to invoke the callback.
|
||||
- Your function returns a `Promise`, and you want to automatically resolve or reject it.
|
||||
- You have a function that wraps `window.location` and don't want your application to be navigated.
|
||||
- You're trying to test your applications "failure path" by forcing things to fail.
|
||||
- You're trying to test your applications "happy path" by forcing things to pass.
|
||||
- You want to "trick" your application into thinking its logged in or logged out.
|
||||
- You're using `oauth` and want to stub login methods.
|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "info",
|
||||
"body": "[Read more about how to use cy.stub](https://on.cypress.io/api/stub)",
|
||||
"title": "cy.stub()"
|
||||
}
|
||||
[/block]
|
||||
|
||||
***
|
||||
|
||||
## Spies
|
||||
|
||||
A spy gives you the ability to "spy" on a function, by being able to capture and then assert that the function was calling with the right arguments, or that the function was called a certain number of times, or even what the return value or context the function was called with.
|
||||
|
||||
A spy does **not** modify the behavior of the function - it is left perfectly intact. A spy is most useful when you are testing the contract between multiple functions and you don't care about the side effects the real function may create (if any).
|
||||
|
||||
```javascript
|
||||
cy.spy(obj, "method")
|
||||
```
|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "info",
|
||||
"body": "[Read more about how to use cy.spy](https://on.cypress.io/api/spy)",
|
||||
"title": "cy.spy()"
|
||||
}
|
||||
[/block]
|
||||
|
||||
***
|
||||
|
||||
## Clock
|
||||
|
||||
There are situations when it is useful to control your applications `date` and `time` in order to force its behavior or avoid slow tests.
|
||||
|
||||
[`cy.clock`](https://on.cypress.io/api/clock) gives you the ability to control:
|
||||
|
||||
- `Date`
|
||||
- `setTimeout`
|
||||
- `setInterval`
|
||||
|
||||
**Common Scenarios:**
|
||||
|
||||
- You're polling something in your application with `setInterval` and want to control that.
|
||||
- You have **throttled** or **debounced** functions which you want to control.
|
||||
|
||||
Once you've enabled [`cy.clock`](https://on.cypress.io/api/clock) you can then control time by **ticking** it ahead by milliseconds.
|
||||
|
||||
```javascript
|
||||
cy
|
||||
.clock()
|
||||
.visit("http://localhost:3333")
|
||||
.get("#search").type("foobarbaz")
|
||||
.tick(1000)
|
||||
```
|
||||
|
||||
[`cy.clock`](https://on.cypress.io/api/clock) is special in that it can be called **prior** to visiting your application, and we will automatically bind it to the application on the next [`cy.visit`](https://on.cypress.io/api/visit). We bind **before** any timers from your application can be invoked. This works identically to [`cy.server`](https://on.cypress.io/api/server) + [`cy.route`](https://on.cypress.io/api/route).
|
||||
|
||||
***
|
||||
|
||||
## Assertions
|
||||
|
||||
Once you have a `stub` or a `spy` in hand, you can then create assertions about them.
|
||||
|
||||
```javascript
|
||||
const user = {
|
||||
getName: function(arg){
|
||||
return arg
|
||||
},
|
||||
|
||||
updateEmail: function(arg){
|
||||
return arg
|
||||
},
|
||||
|
||||
fail: function(){
|
||||
throw new Error("fail whale")
|
||||
}
|
||||
}
|
||||
|
||||
// force user.getName() to return "Jane"
|
||||
cy.stub(user, "getName").returns("Jane Lane")
|
||||
|
||||
// spy on updateEmail but do not change its behavior
|
||||
cy.spy(user, "updateEmail")
|
||||
|
||||
// spy on fail but do not change its behavior
|
||||
cy.spy(user, "fail")
|
||||
|
||||
// invoke getName
|
||||
const name = user.getName(123)
|
||||
|
||||
// invoke updateEmail
|
||||
const email = user.updateEmail("jane@devs.com")
|
||||
|
||||
try {
|
||||
// invoke fail
|
||||
user.fail()
|
||||
} catch (e) {
|
||||
|
||||
}
|
||||
|
||||
expect(name).to.eq("Jane Lane") // true
|
||||
expect(user.getName).to.be.calledOnce // true
|
||||
expect(user.getName).not.to.be.calledTwice // true
|
||||
expect(user.getName).to.be.calledWith(123)
|
||||
expect(user.getName).to.be.calledWithExactly(123) // true
|
||||
expect(user.getName).to.be.calledOn(user) // true
|
||||
|
||||
expect(email).to.eq("jane@devs.com") // true
|
||||
expect(user.updateEmail).to.be.calledWith("jane@devs.com") // true
|
||||
expect(user.updateEmail).to.have.returned("jane@devs.com") // true
|
||||
|
||||
expect(user.fail).to.have.thrown("Error") // true
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Integration and Extensions
|
||||
|
||||
Beyond just integrating these tools together we have also extended and improved the collaboration of these tools.
|
||||
|
||||
Some examples:
|
||||
|
||||
- We replaced Sinon's argument stringifier for a much less noisy, more performant, custom version.
|
||||
- We improved the `sinon-chai` assertion output by changing what displays during a passing vs failing test.
|
||||
- We've added aliasing support to `stub` and `spy` API's.
|
||||
- We automatically restore and teardown `stub`, `spy`, and `clock` between tests.
|
||||
|
||||
We also integrated all of these API's directly into the Command Log so you can visually see what's happening in your application.
|
||||
|
||||
We visually indicate when:
|
||||
|
||||
- A `stub` is called
|
||||
- A `spy` is called
|
||||
- A `clock` is ticked
|
||||
|
||||
|
||||
When you use aliasing with the [`.as(alias)`](https://on.cypress.io/api/as) command, we also coorelate those aliases with the calls. This works identically to aliasing a [`cy.route`](https://on.cypress.io/api/route).
|
||||
|
||||
When stubs are created by calling the method `.withArgs(...)` we also visually link these together.
|
||||
|
||||
When you click on a stub or spy we also output **incredibly** helpful debugging information.
|
||||
|
||||
For instance we automatically display:
|
||||
|
||||
- The call count (and total number of calls)
|
||||
- The arguments without transforming them (they are the real arguments)
|
||||
- The return value of the function
|
||||
- The context the function was invoked with
|
||||
@@ -1,172 +0,0 @@
|
||||
slug: using-aliases
|
||||
excerpt: Reference work done in previous commands.
|
||||
|
||||
# Contents
|
||||
|
||||
- :fa-angle-right: [Async Challenges](#section-async-challenges)
|
||||
- :fa-angle-right: [Introducing Aliasing](#section-introducing-aliasing)
|
||||
- :fa-angle-right: [Aliasing DOM Elements](#section-aliasing-dom-elements)
|
||||
- :fa-angle-right: [Aliasing Routes](#section-asliasing-routes)
|
||||
|
||||
***
|
||||
|
||||
# Async Challenges
|
||||
|
||||
Because all commands in Cypress are asynchronous, it makes referencing commands challenging. Aliasing is a DSL that solves referencing work done in previous commands.
|
||||
|
||||
**Imagine the following synchronous example in jQuery:**
|
||||
|
||||
```javascript
|
||||
var body = $("body")
|
||||
|
||||
// do more work here
|
||||
|
||||
// later use this body reference
|
||||
body.find("button")
|
||||
```
|
||||
|
||||
> In Cypress, every command is asynchronous.
|
||||
|
||||
In jQuery we can assign regular values because work is performed synchronously. But in Cypress, every command is asynchronous, so there is no immediate return value. You'd have to do something like this to get access to previously resolved values:
|
||||
|
||||
```javascript
|
||||
// A not good example of referencing in Cypress
|
||||
|
||||
var _this = this
|
||||
|
||||
cy.get("body").then(function($body){
|
||||
_this.body = $body
|
||||
})
|
||||
|
||||
// more work here
|
||||
|
||||
cy.then(function(){
|
||||
cy.wrap(_this.body).find("button")
|
||||
})
|
||||
```
|
||||
|
||||
Of course this is *not good*. It's clunky and difficult to figure out what is going on. Plus, with complex JavaScript applications, the element references may no longer be in the DOM by the time you're ready to use them.
|
||||
|
||||
***
|
||||
|
||||
# Introducing Aliasing
|
||||
|
||||
**Aliasing** was designed to solve async referencing issues and DOM Element re-querying, routing requests and responses, server integration, and automated error handling. Aliasing also gives you a human readable word for a potentially complex series of events. Aliasing is prominently displayed in the Cypress Command Log making it even easier to understand relationships.
|
||||
|
||||
**Aliasing is incredibly powerful but very simple to use:**
|
||||
|
||||
* Create an alias with the [`cy.as`](https://on.cypress.io/api/as) command.
|
||||
* Reference an alias with the [`cy.get`](https://on.cypress.io/api/get) or [`cy.wait`](https://on.cypress.io/api/wait) command.
|
||||
|
||||
Every time you reference an alias, it should be prefixed with `@`. You can think of this character as "a" for alias or you can think of an alias as a pointer (like how variables point to memory).
|
||||
|
||||
# Aliasing DOM Elements
|
||||
|
||||
One use case for aliasing is for referencing a DOM Element.
|
||||
|
||||
```javascript
|
||||
// alias all of the tr's found in the table as 'rows'
|
||||
cy.get("table").find("tr").as("rows")
|
||||
```
|
||||
|
||||
Internally Cypress has made a reference to the `<tr>` collection returned as the alias "rows". To reference these same "rows" later, you can use the [`cy.get`](https://on.cypress.io/api/get) command.
|
||||
|
||||
```javascript
|
||||
// Cypress returns the reference to the <tr>'s
|
||||
// which allows us to continue to chain commands
|
||||
// finding the 1st row.
|
||||
cy.get("@rows").first().click()
|
||||
```
|
||||
|
||||
Because we've used the `@` character in [`cy.get`](https://on.cypress.io/api/get), instead of querying the DOM for elements, [`cy.get`](https://on.cypress.io/api/get) looks for an existing alias called `rows` and returns the reference (if it finds it).
|
||||
|
||||
***
|
||||
|
||||
## When alias references no longer exist in the DOM
|
||||
|
||||
Cypress automatically decides when it should reference existing elements or re-query for new elements.
|
||||
|
||||
In many single-page JavaScript applications, the DOM re-renders parts of the application constantly. If you alias DOM elements that have been removed from the DOM by the time you call [`cy.get`](https://on.cypress.io/api/get) with the alias, Cypress automatically re-querys the DOM to find these elements again.
|
||||
|
||||
```html
|
||||
<ul id="todos">
|
||||
<li>
|
||||
Walk the dog
|
||||
<button class="edit">edit</button>
|
||||
</li>
|
||||
<li>
|
||||
Feed the cat
|
||||
<button class="edit">edit</button>
|
||||
</li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
Let's imagine when we click the `.edit` button that our `<li>` is re-rendered in the DOM. Instead of displaying the edit button, it instead displays an `<input />` text field allowing you to edit the todo. The previous `<li>` has been *completely* removed from the DOM, and a new `<li>` is rendered in its place.
|
||||
|
||||
**Cypress calculates stale alias references.**
|
||||
|
||||
```javascript
|
||||
cy
|
||||
.get("#todos li").first().as("firstTodo")
|
||||
.get("@firstTodo").find(".edit").click()
|
||||
.get("@firstTodo").should("have.class", "editing")
|
||||
.find("input").type("Clean the kitchen")
|
||||
```
|
||||
|
||||
When we reference `@firstTodo`, Cypress checks to see if all elements its referencing are still in the DOM. If they are, it returns those existing elements. If they aren't, Cypress replays the commands leading up to the alias definition.
|
||||
|
||||
In our case it would re-issue the commands: `cy.get("#todos li").first()`. Everything just works because the new `<li>` is found.
|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "warning",
|
||||
"body": "*Usually* replaying previous commands will return what you expect, but not always. Cypress' calculations are complicated and we may improve this algorithm at a later time. It is recommended to not alias DOM elements very far down a chain of commands - **alias elements as soon as possible with as few commands as possible**. When in doubt, you can *always* issue a regular `cy.get` to query for the elements again."
|
||||
}
|
||||
[/block]
|
||||
|
||||
***
|
||||
|
||||
# Aliasing Routes
|
||||
|
||||
Another use case for aliasing is with routes. Using aliases with [`cy.route`](https://on.cypress.io/api/route) makes dealing with AJAX requests much easier.
|
||||
|
||||
|
||||

|
||||
|
||||
```javascript
|
||||
cy
|
||||
.server()
|
||||
// alias this route as 'postUser'
|
||||
.route("POST", /users/, {id: 123}).as("postUser")
|
||||
```
|
||||
|
||||
Once you've given a route an alias, you can use it later to indicate what you expect to have happen in your application. Imagine your application's code is as follows:
|
||||
|
||||
```javascript
|
||||
$("form").submit(function(){
|
||||
var data = $(this).serializeData()
|
||||
|
||||
// simple example of an async
|
||||
// request that only goes out
|
||||
// after an indeterminate period of time
|
||||
setTimeout(function(){
|
||||
$.post("/users", {data: data})
|
||||
}, 1000)
|
||||
})
|
||||
```
|
||||
|
||||
You can tell Cypress to wait until it sees a request that matches your aliased route using the [`cy.wait`](https://on.cypress.io/api/wait) command.
|
||||
|
||||
```javascript
|
||||
cy
|
||||
.get("form").submit()
|
||||
.wait("@postUser")
|
||||
.get(".success").contains("User successfully created!")
|
||||
```
|
||||
|
||||
**Telling Cypress to wait for an AJAX request that matches an aliased route has enormous advantages.**
|
||||
|
||||
1. Waiting for an explicit route reference is less flaky. Instead of waiting for an arbitrary period of time, waiting for a specific aliased route is much more predictable.
|
||||
2. Cypress will resolve if there's already been a request that matches the alias.
|
||||
3. The actual XHR request object will be yielded to you as the next subject.
|
||||
4. Errors are more obvious.
|
||||
@@ -1,85 +0,0 @@
|
||||
slug: creating-fixtures
|
||||
excerpt: Load fixtures
|
||||
|
||||
# Contents
|
||||
|
||||
- :fa-angle-right: [What are Fixtures](#section-what-are-fixtures)
|
||||
- :fa-angle-right: [Supported Formats](#section-supported-formats)
|
||||
- [Validation](#section-validation)
|
||||
- [Formatting](#section-formatting)
|
||||
- :fa-angle-right: [Fixtures vs Factories](#section-fixtures-vs-factories)
|
||||
- :fa-angle-right: [Routing Responses](#section-routing-responses)
|
||||
- :fa-angle-right: [Organizing Fixtures](#section-organizing-fixtures)
|
||||
|
||||
***
|
||||
|
||||
# What are Fixtures
|
||||
|
||||
A fixture in Cypress is a fixed set of data located in a file that is used as a baseline for running tests. The purpose of a test fixture is to ensure that there is a well known and fixed environment in which tests are run so that results are repeatable. Fixtures are accessed within tests by using the [`cy.fixture`](https://on.cypress.io/api/fixture) command.
|
||||
|
||||
***
|
||||
|
||||
# Supported Formats
|
||||
|
||||
| File Extension |
|
||||
| --- |
|
||||
| `.coffee` |
|
||||
| `.gif` |
|
||||
| `.html` |
|
||||
| `.jpeg` |
|
||||
| `.jpg` |
|
||||
| `.js` |
|
||||
| `.json` |
|
||||
| `.png` |
|
||||
| `.txt` |
|
||||
| `.csv` |
|
||||
| `.tif` |
|
||||
| `.tiff` |
|
||||
| `.zip` |
|
||||
|
||||
***
|
||||
|
||||
## Validation
|
||||
|
||||
Cypress will automatically validate your fixtures. If your `.json`, `.js`, or `.coffee` files contain syntax errors, they will automatically be shown in the Command Log.
|
||||
|
||||
***
|
||||
|
||||
## Formatting
|
||||
|
||||
Cypress automatically formats your fixture files. That means you can paste in a single line of `json` and the next time Cypress serves this fixture, it will format / indent the `json` which makes it easier to read and debug.
|
||||
|
||||
Image fixtures will be sent as `base64`.
|
||||
|
||||
***
|
||||
|
||||
# Fixtures vs Factories
|
||||
|
||||
***
|
||||
|
||||
# Routing Responses
|
||||
|
||||
***
|
||||
|
||||
# Organizing Fixtures
|
||||
|
||||
Cypress will automatically scaffold out a suggested folder structure for organizing your fixtures on every new project. By default it will create this structure when you boot your project:
|
||||
|
||||
```text
|
||||
// within your project's root folder
|
||||
/cypress/fixtures/example.json
|
||||
```
|
||||
|
||||
Your fixtures can be further organized within additional folders. For instance, you could create another folder called `images` and add images:
|
||||
|
||||
```text
|
||||
/cypress/fixtures/images/cats.png
|
||||
/cypress/fixtures/images/dogs.png
|
||||
/cypress/fixtures/images/birds.png
|
||||
```
|
||||
|
||||
To access the fixtures nested within the `images` folder, simply include the folder in your [`cy.fixture`](https://on.cypress.io/api/fixture) command.
|
||||
|
||||
```javascript
|
||||
cy.fixture("images/dogs.png") //returns dogs.png as Base64
|
||||
```
|
||||
@@ -1,24 +0,0 @@
|
||||
slug: debugging-tools
|
||||
excerpt: Debug Cypress
|
||||
|
||||
# DOM Events
|
||||
|
||||
# Page Events
|
||||
|
||||
Cypress will additionally log out when specific "page events" happen. These are events which alter the state of your application and can help provide insight and feedback into the logical order of what happened and when it happened.
|
||||
|
||||
For instance Cypress will log out the following:
|
||||
|
||||
* Whenever your URL changes (and the new url)
|
||||
* Whenever the submit event is detected (from a traditional `<form>` submit)
|
||||
* Whenever the page begins to load (after clicking on an `<a>` or navigating to another page)
|
||||
* Whenever the page finishes loading
|
||||
* Whenever an XHR is issued (when the `cy.server` has been started)
|
||||
|
||||
# Debugging Assertions
|
||||
|
||||
- When assertions fail
|
||||
- Determining Subject Changes
|
||||
- Clicking on an assertion for more information
|
||||
- Inspecting Objects
|
||||
- Making multiple assertions
|
||||
@@ -1,9 +0,0 @@
|
||||
slug: running-tests
|
||||
excerpt: Run your tests
|
||||
|
||||
The test runner allows Cypress tests to be run in a web browser.
|
||||
|
||||
# Command Log
|
||||
|
||||
# Working in the Console
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
slug: websocket-handling
|
||||
excerpt: Handle Websockets
|
||||
|
||||
web sockets need to be handled
|
||||
@@ -1,24 +0,0 @@
|
||||
slug: writing-tests
|
||||
excerpt: Writing Tests
|
||||
|
||||
# Only running one test
|
||||
|
||||
All tests have a `.only` method that can be used to run only one test or test suite.
|
||||
|
||||
```javascript
|
||||
// this is the only test that will run when running test suite
|
||||
it.only("has 'Welcome' in title", function(){
|
||||
cy.title().should("include", "Welcome")
|
||||
})
|
||||
``
|
||||
|
||||
# Skipping a test
|
||||
|
||||
All tests have a `.skip` method that can be used to skip a test or test suite.
|
||||
|
||||
```javascript
|
||||
// this test will not run when running test suite
|
||||
it.skip("button highlights as active", function(){
|
||||
cy.get("button").should("have.class", "active")
|
||||
})
|
||||
``
|
||||
@@ -1,220 +0,0 @@
|
||||
slug: cli
|
||||
excerpt: Cypress CLI Tool for programmatically interacting with the Desktop Application
|
||||
|
||||
# Contents
|
||||
|
||||
- :fa-angle-right: [What is the CLI Tool?](#section-what-is-the-cli-tool-)
|
||||
- :fa-angle-right: [Installation](#section-installation)
|
||||
- :fa-angle-right: [Available Commands](#section-available-commands)
|
||||
- [cypress install](#section--cypress-install-)
|
||||
- [cypress update](#section--cypress-update-)
|
||||
- [cypress run](#section--cypress-run-)
|
||||
- [cypress run --record](cli#section--cypress-run-record-)
|
||||
- [cypress open](#section--cypress-open-)
|
||||
- [cypress get:path](#section--cypress-get-path-)
|
||||
- [cypress verify](#section--cypress-verify-)
|
||||
- [cypress version](#section--cypress-version-)
|
||||
|
||||
***
|
||||
|
||||
# What is the CLI Tool?
|
||||
|
||||
The CLI Tool is an [`npm package`](https://github.com/cypress-io/cypress-cli) that wraps the Desktop Application.
|
||||
|
||||
It provides a set of commands that can be used to do things like:
|
||||
|
||||
- Install Cypress
|
||||
- Run Cypress headlessly
|
||||
- Record your test runs
|
||||
- Output the current installed version
|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "info",
|
||||
"body": "You generally install the CLI tool so you can progamatically install and run Cypress. This is commonly used when running Cypress from your CI provider."
|
||||
}
|
||||
[/block]
|
||||
|
||||
***
|
||||
|
||||
# Installation
|
||||
|
||||
```shell
|
||||
npm install -g cypress-cli
|
||||
```
|
||||
|
||||
This will make the `cypress` command globally available from your command line.
|
||||
|
||||
You can now execute the following commands:
|
||||
|
||||
***
|
||||
|
||||
# Available Commands
|
||||
|
||||
## `cypress install`
|
||||
|
||||
Installs the **Cypress Desktop Application** to the default location for each Operating System.
|
||||
|
||||
OS | Path
|
||||
:--- | :---
|
||||
Mac | `/Applications/Cypress.app`
|
||||
Linux | `/home/<user>/.cypress/Cypress`
|
||||
|
||||
```shell
|
||||
## by default will install the latest version
|
||||
cypress install
|
||||
```
|
||||
|
||||
```shell
|
||||
## install a specific version
|
||||
cypress install --cypress-version 0.13.0
|
||||
```
|
||||
|
||||
Additionally if you have a `CYPRESS_VERSION` environment variable set, it will automatically download that version. This is most useful when running Cypress in CI.
|
||||
|
||||

|
||||
|
||||
***
|
||||
|
||||
## `cypress update`
|
||||
|
||||
Updates Cypress to the latest version. This does the same thing as `cypress install`.
|
||||
|
||||
```shell
|
||||
## now we have the latest version
|
||||
cypress update
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## `cypress run`
|
||||
|
||||
Runs Cypress headlessly without spawning a browser.
|
||||
|
||||
You can use this command when working locally or when running in [Continuous Integration](https://on.cypress.io/guides/continuous-integration).
|
||||
|
||||
Cypress will first check to see that the Desktop Application is installed and will automatically install it prior to running (if necessary).
|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "success",
|
||||
"title": "Want your test runs recorded?",
|
||||
"body": "You can also have Cypress record your test runs and make them available on our [Dashboard](https://on.cypress.io/guides/dashboard-features)."
|
||||
}
|
||||
[/block]
|
||||
|
||||
```shell
|
||||
## by default will use your current path
|
||||
cypress run
|
||||
```
|
||||
|
||||
```shell
|
||||
## or you can specify a path to the project
|
||||
cypress run /users/john/projects/TodoMVC
|
||||
```
|
||||
|
||||
```shell
|
||||
## specify a port to use which overrides values in cypress.json
|
||||
cypress run --port 8080
|
||||
```
|
||||
|
||||
```shell
|
||||
## specify a mocha reporter to use
|
||||
cypress run --reporter json
|
||||
```
|
||||
|
||||
```shell
|
||||
## specify options for the mocha reporter
|
||||
cypress run --reporter-options mochaFile=result.xml,toConsole=true
|
||||
```
|
||||
|
||||
```shell
|
||||
## specify a file to run instead of running all the tests files
|
||||
cypress run --spec cypress/integration/app_spec.js
|
||||
```
|
||||
|
||||
```shell
|
||||
## specify environment variables
|
||||
cypress run --env host=api.dev.local
|
||||
```
|
||||
|
||||
```shell
|
||||
## specify configuration values to override cypress.json
|
||||
cypress run --config pageLoadTimeout=100000,watchForFileChanges=false
|
||||
```
|
||||
|
||||
You can read more about [environment variables](https://on.cypress.io/environment-variables) and [configuration](https://on.cypress.io/configuration) here.
|
||||
|
||||
***
|
||||
|
||||
## `cypress run --record`
|
||||
|
||||
You can also have your test runs recorded [once your project is setup to record](https://on.cypress.io/guides/projects).
|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "info",
|
||||
"body": "You'd typically record your runs in [Continuous Integration](https://on.cypress.io/guides/continuous-integration), but you can also record when running locally."
|
||||
}
|
||||
[/block]
|
||||
|
||||
After setting up your project you will recieve a **Record Key**.
|
||||
|
||||
```shell
|
||||
cypress run --record --key <record_key>
|
||||
```
|
||||
|
||||
If you set this **Record Key** as an environment variable you can omit the `--key` flag.
|
||||
|
||||
```shell
|
||||
## you'd typically set this in your CI provider
|
||||
export CYPRESS_RECORD_KEY=abc-key-123
|
||||
|
||||
## we can now omit --key
|
||||
cypress run --record
|
||||
```
|
||||
|
||||
You can [read more](https://on.cypress.io/how-do-i-record-runs) about recording runs here.
|
||||
|
||||
***
|
||||
|
||||
## `cypress open`
|
||||
|
||||
Opens the Cypress application. This is the same thing as double-clicking the application.
|
||||
|
||||
In Mac you'll see the **cy** icon in the dock and in Linux you'll see the Cypress application window open.
|
||||
|
||||
Arguments you pass to `cypress open` will automatically be applied to the projects you open. These persist onto all projects until you quit the Cypress Desktop Application.
|
||||
|
||||
```shell
|
||||
## specify a port to use which overrides values in cypress.json
|
||||
cypress open --port 8080
|
||||
```
|
||||
|
||||
```shell
|
||||
## specify configuration values which override cypress.json
|
||||
cypress open --config pageLoadTimeout=100000,watchForFileChanges=false
|
||||
```
|
||||
|
||||
```shell
|
||||
## specify environment variables
|
||||
cypress open --env host=api.dev.local
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## `cypress get:path`
|
||||
|
||||
Returns the path Cypress will be installed to. Additionally checks to see if Cypress already exists at that path.
|
||||
|
||||
***
|
||||
|
||||
## `cypress verify`
|
||||
|
||||
Verifies that the Cypress application is found.
|
||||
|
||||
***
|
||||
|
||||
## `cypress version`
|
||||
|
||||
Outputs both the version of the CLI Tool and the installed Cypress application.
|
||||
@@ -1,525 +0,0 @@
|
||||
slug: errors
|
||||
excerpt: Errors that require additional explanation are listed here.
|
||||
|
||||
# Contents
|
||||
|
||||
- :fa-angle-right: [Sorry, there's something wrong with this file](#section-sorry-there-s-something-wrong-with-this-file)
|
||||
- :fa-angle-right: [Oops...we found an error preparing your test file](#section-we-found-an-error-preparing-your-test-file)
|
||||
- :fa-angle-right: [Cypress cannot execute commands outside a running test](#section-cypress-cannot-execute-commands-outside-a-running-test)
|
||||
- :fa-angle-right: [cy.method() failed because the element you are chaining off of has become detached or removed from the dom](#section-cy-method-failed-because-the-element-you-are-chaining-off-of-has-become-detached-or-removed-from-the-dom)
|
||||
- :fa-angle-right: [cy.method() failed because the element cannot be interacted with](#section-cy-method-failed-because-the-element-cannot-be-interacted-with)
|
||||
- :fa-angle-right: [cy.method() failed because the element is currently animating](#section-cy-method-failed-because-the-element-is-currently-animating)
|
||||
- :fa-angle-right: [Running Cypress in CI requires a secret project key](#section-running-cypress-in-ci-requires-a-secret-project-key)
|
||||
- :fa-angle-right: [The test has finished but Cypress still has commands in its queue](#section-the-test-has-finished-but-cypress-still-has-commands-in-its-queue)
|
||||
- :fa-angle-right: [cy.visit() failed because you're attempting to visit a second unique domain](#section-cy-visit-failed-because-you-are-attempting-to-visit-a-second-unique-domain)
|
||||
- :fa-angle-right: [Cypress detected a cross origin error happened on page load](#section-cypress-detected-a-cross-origin-error-happened-on-page-load)
|
||||
- :fa-angle-right: [The supportFolder option has been removed](#section-the-supportfolder-option-has-been-removed)
|
||||
- :fa-angle-right: [The Chromium Renderer process just crashed](#section-the-chromium-renderer-process-just-crashed)
|
||||
- :fa-angle-right: [The 'cypress ci' command has been deprecated](#section-the-cypress-ci-command-has-been-deprecated)
|
||||
|
||||
***
|
||||
|
||||
# No tests found in your file
|
||||
|
||||

|
||||
|
||||
This message means that Cypress was unable to find tests in the specified file. You'll likely get this message if you have an empty test file and have not yet written any tests.
|
||||
|
||||
***
|
||||
|
||||
# We found an error preparing your test file
|
||||
|
||||
This message means that Cypress encountered an error when compiling and/or bundling your test file.
|
||||
|
||||
Cypress automatically compiles and bundles your test code so you can use ES2015, CoffeeScript, modules, etc.
|
||||
|
||||
You'll typically receive this message due to:
|
||||
|
||||
- The file missing
|
||||
- A syntax error in the file or one of its dependencies
|
||||
- A missing dependency
|
||||
|
||||
The error will be printed on the right side, usually showing the part of the code in which the error occurred.
|
||||
|
||||
When you fix the error in your code, your tests will automatically re-run.
|
||||
|
||||
***
|
||||
|
||||
# Cypress cannot execute commands outside a running test
|
||||
|
||||

|
||||
|
||||
This message means you tried to execute one or more Cypress commands outside of a currently running test. Cypress has to be able to associate commands to a specific test.
|
||||
|
||||
Typically this happens accidentally, like in the following situation.
|
||||
|
||||
```javascript
|
||||
describe("Some Tests", function(){
|
||||
it("is true", function(){
|
||||
expect(true).to.be.true // yup, fine
|
||||
})
|
||||
|
||||
it("is false", function(){
|
||||
expect(false).to.be.false // yup, also fine
|
||||
})
|
||||
|
||||
context("some nested tests", function(){
|
||||
// oops you forgot to write an it(...) here!
|
||||
// these cypress commands below
|
||||
// are run outside of a test and cypress
|
||||
// throws an error
|
||||
cy
|
||||
.visit("http://localhost:8080")
|
||||
.get("h1").should("contain", "todos")
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
Simply move those Cypress commands into an `it(...)` and everything will work correctly.
|
||||
|
||||
If you are purposefully writing commands outside of a test, there is probably a better way to accomplish whatever you're trying to do. Read through the [Example Repos](https://on.cypress.io/guides/all-example-apps), [open an issue](https://github.com/cypress-io/cypress/issues/new?body=**Description**%0A*Include%20a%20high%20level%20description%20of%20the%20error%20here%20including%20steps%20of%20how%20to%20recreate.%20Include%20any%20benefits%2C%20challenges%20or%20considerations.*%0A%0A**Code**%0A*Include%20the%20commands%20used*%0A%0A**Steps%20To%20Reproduce**%0A-%20%5B%20%5D%20Steps%0A-%20%5B%20%5D%20To%0A-%20%5B%20%5D%20Reproduce%2FFix%0A%0A**Additional%20Info**%0A*Include%20any%20images%2C%20notes%2C%20or%20whatever.*%0A), or [come talk to someone in Gitter](https://gitter.im/cypress-io/cypress).
|
||||
|
||||
***
|
||||
|
||||
# cy.method() failed because the element you are chaining off of has become detached or removed from the dom
|
||||
|
||||

|
||||
|
||||
This message means you are trying to interact with a "dead" DOM element - meaning it is either detached or completely removed from the DOM.
|
||||
|
||||
Cypress errors because it cannot operate or interact with "dead" elements - just like a real user could not do this either.
|
||||
|
||||
Understanding how this happens is very important - and it is often easy to prevent. Let's investigate.
|
||||
|
||||
```html
|
||||
<!-- your app HTML -->
|
||||
<body>
|
||||
<div id="parent">
|
||||
<button>delete</button>
|
||||
</div>
|
||||
</body>
|
||||
```
|
||||
|
||||
```javascript
|
||||
// your app code
|
||||
$("button").click(function(){
|
||||
// when the <button> is clicked
|
||||
// we remove the button from the DOM
|
||||
$(this).remove()
|
||||
})
|
||||
```
|
||||
|
||||
```javascript
|
||||
// buggy test code
|
||||
cy
|
||||
// as soon as this click event happens the <button>
|
||||
// becomes removed from the DOM
|
||||
.get("button").click()
|
||||
|
||||
// When cypress begins processing the 'parent' command
|
||||
// it will immediately detect that the current subject
|
||||
// which is the <button> is detached from the DOM and
|
||||
// will throw the error
|
||||
.parent()
|
||||
```
|
||||
|
||||
We can prevent Cypress from throwing this error by rewriting our test code:
|
||||
|
||||
```javascript
|
||||
// fixed test code
|
||||
cy
|
||||
.get("button").click()
|
||||
|
||||
// simply query for the parent directly here
|
||||
// instead of chaining off the <button> subject
|
||||
.get("#parent")
|
||||
```
|
||||
|
||||
The above example is an oversimplification. Let's look at a more complex example.
|
||||
|
||||
In modern JavaScript frameworks, DOM elements are regularly `re-rendered` - meaning that the old element is thrown away and a new one is put in its place. Because this happens so fast, it may *appear* as if nothing has visibly changed. But if you are in the middle of executing commands it's possible the element you're interacting with has become "dead". To deal with this situation you must:
|
||||
|
||||
- understand when your application re-renders
|
||||
- re-query for newly added DOM elements
|
||||
- **guard** Cypress from executing commands until a condition is met
|
||||
|
||||
When we say **guard** we mean writing commands in such a way that prevents Cypress from going on before a specific condition is met. This usually means:
|
||||
|
||||
- writing an assertion
|
||||
- waiting on an XHR
|
||||
|
||||
***
|
||||
|
||||
# cy.method() failed because the element cannot be interacted with
|
||||
|
||||
You may see a variation of this message for 4 different reasons:
|
||||
|
||||
1. the element is not visible
|
||||
2. the element is being covered by another element
|
||||
3. the element's center is hidden from view
|
||||
4. the element is disabled
|
||||
|
||||
Cypress runs several calculations to ensure an element can *actually* be interacted with like a real user would.
|
||||
|
||||
If you're seeing this error, the solution is often obvious. You may need to add **command guards** due to a timing or animation issue.
|
||||
|
||||
There have been situations where Cypress does not correctly allow you to interact with an element which should be interactive. If that's the case, [open an issue](https://github.com/cypress-io/cypress/issues/new?body=**Description**%0A*Include%20a%20high%20level%20description%20of%20the%20error%20here%20including%20steps%20of%20how%20to%20recreate.%20Include%20any%20benefits%2C%20challenges%20or%20considerations.*%0A%0A**Code**%0A*Include%20the%20commands%20used*%0A%0A**Steps%20To%20Reproduce**%0A-%20%5B%20%5D%20Steps%0A-%20%5B%20%5D%20To%0A-%20%5B%20%5D%20Reproduce%2FFix%0A%0A**Additional%20Info**%0A*Include%20any%20images%2C%20notes%2C%20or%20whatever.*%0A) or force the action to happen.
|
||||
|
||||
If you'd like to override these built-in checks, provide the `{force: true}` option to the action itself. Refer to each command for their available options, additional use cases and argument usage.
|
||||
|
||||
```javascript
|
||||
// we ignore the built in error checking
|
||||
// and force the action to happen
|
||||
// regardless of whether the button is
|
||||
// visible, disabled, or covered by another element
|
||||
cy.get("button").click({force: true}).
|
||||
```
|
||||
|
||||
*Be careful with this option. It's possible to force your tests to pass but your feature may actually be failing.*
|
||||
|
||||
***
|
||||
|
||||
# cy.method() failed because the element is currently animating
|
||||
|
||||

|
||||
|
||||
By default Cypress detects if an element you're trying to interact with is animating. This check ensures that an element is not animating too quickly for a real user to interact with the element. This also prevents some edge cases where actions such as [`type`](https://on.cypress.io/api/type) or [`click`](https://on.cypress.io/api/click) happenening too fast during a transition.
|
||||
|
||||
Cypress will continuously attempt to interact with the element until it eventually times out.
|
||||
|
||||
If you'd like to force Cypress to interact with the element there are a few options:
|
||||
|
||||
- Pass `{force: true}` and disables **all** error checking
|
||||
- Pass `{waitForAnimations: false}` to disable animation error checking only
|
||||
- Pass `{animationDistanceThreshold: 20}` to decrease the sensitivity to detecting if an element is animating too quickly for a user to interact with. By increasing the threshold this enables your element to move farther on the page without causing Cypress to continuously retry.
|
||||
|
||||
```javascript
|
||||
cy.get("#modal button").click({waitForAnimations: false})
|
||||
```
|
||||
|
||||
You can globally disable animation error checking, or increase the threshold by modifying your [`cypress.json`](https://on.cypress.io/guides/configuration).
|
||||
|
||||
```json
|
||||
// cypress.json
|
||||
{
|
||||
"waitForAnimations": false,
|
||||
"animationDistanceThreshold": 50
|
||||
}
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Running Cypress in CI requires a secret project key
|
||||
|
||||
You may receive this error when trying to run Cypress tests in Continuous Integration. This means that you did not pass a specific key to: `cypress ci` in your CI configuration file.
|
||||
|
||||
Since no key was passed, Cypress then checks for any environment variable with the name `CYPRESS_CI_KEY`, but still didn't find any.
|
||||
|
||||
You can get your project's secret key by running the terminal command: `cypress get:key`
|
||||
|
||||
Then [add the key to your config file or as an environment variable](https://on.cypress.io/guides/continuous-integration#section-acquire-a-cypress-secret-key).
|
||||
|
||||
***
|
||||
|
||||
# The test has finished but Cypress still has commands in its queue
|
||||
|
||||

|
||||
|
||||
Let's examine several different ways you may get this error message. In every situation, you'll need to change something in your code to prevent this error.
|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "warning",
|
||||
"title": "Flaky tests below!",
|
||||
"body": "Several of these tests are dependent on race conditions. You may have to run these tests multiple times before they will actually fail. You can also try tweaking some of the delays."
|
||||
}
|
||||
[/block]
|
||||
|
||||
## Simple Example
|
||||
|
||||
```javascript
|
||||
describe("simple example", function(){
|
||||
// this first test will actually pass and shows you that
|
||||
// Cypress attempts to prevent this problem in every test
|
||||
it("Cypress is smart and this does not fail", function(){
|
||||
// queue up some commands
|
||||
// without returning the cy object
|
||||
// which is ok!
|
||||
cy
|
||||
.get("body")
|
||||
.children()
|
||||
.should("not.contain", "foo")
|
||||
|
||||
// even though we return the string here
|
||||
// Cypress automatically figures out that you've
|
||||
// queued commands above and does not end the test
|
||||
// until all commands have finished
|
||||
return "foobarbaz"
|
||||
})
|
||||
|
||||
it("but you can forcibly end the test early which does fail", function(done){
|
||||
// this example will fail because you've forcibly terminated
|
||||
// the test early with mocha
|
||||
cy
|
||||
.get("body")
|
||||
.then(function(){
|
||||
// forcibly end the test
|
||||
// even though there are still
|
||||
// pending queued commands below
|
||||
done()
|
||||
})
|
||||
.children()
|
||||
.should("not.contain", "foo")
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
## Complex Async Example
|
||||
|
||||
```javascript
|
||||
describe("a complex example with async code", function(){
|
||||
it("you can cause commands to bleed into the next test", function(){
|
||||
// what's happening here is that because we have NOT told mocha this is an async test
|
||||
// this test will pass immediately and move onto the next test...
|
||||
//
|
||||
// ...then, when the setTimeout callback function runs
|
||||
// new commands will get queued on the wrong test
|
||||
//
|
||||
// Cypress will detect this and fail the next test
|
||||
setTimeout(function(){
|
||||
cy.get("body").children().should("not.contain", "foo")
|
||||
}, 10)
|
||||
|
||||
// the correct way to write the above test code would be this:
|
||||
// it("does not cause commands to bleed into the next test", function(done){
|
||||
// setTimeout(function(){
|
||||
// cy.get("body").children().should("not.contain", "foo").then(function(){
|
||||
// now all the commands are correctly processed on this test
|
||||
// and do not bleed into the next test
|
||||
// done()
|
||||
// })
|
||||
// }, 10)
|
||||
// })
|
||||
|
||||
})
|
||||
|
||||
it("this test will fail due to the previous poorly written test", function(){
|
||||
// we will get the error here that Cypress detected
|
||||
// it still had commands in its command queue
|
||||
//
|
||||
// Cypress will print the commands out which should
|
||||
// help us figure out that the previous test is
|
||||
// causing this error message
|
||||
cy.wait(10)
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
## Complex Promise Example
|
||||
|
||||
```javascript
|
||||
describe("another complex example using a forgotten 'return'", function(){
|
||||
it("forgets to return a promise", function(){
|
||||
// we forget to return the promise to our test
|
||||
// which means the test passes synchronously but
|
||||
// our promise resolves during the next test.
|
||||
//
|
||||
// this causes the commands to be queued on the
|
||||
// wrong test
|
||||
Cypress.Promise.delay(10).then(function(){
|
||||
cy.get("body").children().should("not.contain", "foo")
|
||||
})
|
||||
|
||||
// the correct way to write the above test code would be this:
|
||||
// it("does not forget to return a promise", function(){
|
||||
// return Cypress.Promise.delay(10).then(function(){
|
||||
// return cy.get("body").children().should("not.contain", "foo")
|
||||
// })
|
||||
// }
|
||||
})
|
||||
|
||||
it("this test will fail due to the previous poorly written test", function(){
|
||||
// we will get the error here that Cypress detected
|
||||
// it still had commands in its command queue
|
||||
//
|
||||
// Cypress will print the commands out which should
|
||||
// help us figure out that the previous test is
|
||||
// causing this error message
|
||||
cy.wait(10)
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# cy.visit() failed because you are attempting to visit a second unique domain
|
||||
|
||||
TBD.
|
||||
|
||||
***
|
||||
|
||||
# Cypress detected a cross origin error happened on page load
|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "info",
|
||||
"title": "This is a simple overview...",
|
||||
"body": "For a more thorough explanation of Cypress's Web Security model, [please read our dedicated guide to it](https://on.cypress.io/guides/web-security)."
|
||||
}
|
||||
[/block]
|
||||
|
||||
This error means that your application navigated to a superdomain that Cypress was not bound to.
|
||||
|
||||
Initially when you `cy.visit` Cypress changes the url to match what you are visiting. This enables Cypress to communicate with your appliation to control it, and bypasses all same-origin security policies built into the browsers.
|
||||
|
||||
When your application navigates to a superdomain outside of the current origin-policy Cypress is unable to communicate with it, and thus fails.
|
||||
|
||||
There are generally fairly simple workarounds for these common situations:
|
||||
|
||||
1. Don't click `<a>` links that navigate you outside of your apps. Likely this isn't worth testing anyway. You should ask yourself: *What's the point of clicking and going to another app?* Likely all you care about is that the `href` attribute matches what you expect. So simply make an assertion about that.
|
||||
|
||||
2. You are testing a page that uses `Single sign-on (SSO)`. In this case your webserver is likely redirecting you between superdomains, and thus you receive this error message. You can likely get around this redirect problem by using [`cy.request`](https://on.cypress.io/api/request) and manually handling the session yourself.
|
||||
|
||||
If you find yourself stuck and cannot work around these issues you can just set this in your `cypress.json` file:
|
||||
|
||||
```javascript
|
||||
// cypress.json
|
||||
{
|
||||
chromeWebSecurity: false
|
||||
}
|
||||
```
|
||||
|
||||
But before doing so you should really understand and [read about the reasoning here](https://on.cypress.io/guides/web-security).
|
||||
|
||||
***
|
||||
|
||||
# Support file missing or invalid
|
||||
|
||||
The `supportFolder` option has been removed from Cypress and has been replaced by module support and the `supportFile` option. Cypress used to automatically include any scripts in the `supportFolder` before your test files, and that was the best way to include custom Cypress commands and utility functions. However, automatically including all the files in a certain directory is somewhat magical and unintuitive, and requires creating globals for the purpose of utility functions. This behavior has been succeeded by module support and the `supportFile` option.
|
||||
|
||||
## Use modules for utility functions
|
||||
|
||||
Cypress supports both ES2015 modules and CommonJS modules. You can import/require npm modules as well as local modules:
|
||||
|
||||
```javascript
|
||||
import _ from "lodash"
|
||||
import util from "./util"
|
||||
|
||||
it("uses modules", function () {
|
||||
expect(_.kebabCase("FooBar")).to.equal("foo-bar")
|
||||
expect(util.secretCode()).to.equal("1-2-3-4")
|
||||
})
|
||||
```
|
||||
|
||||
## Use supportFile to load scripts before your test code
|
||||
|
||||
It's still useful to load a setup file before your test code. If you are setting Cypress defaults or utilizing custom Cypress commands, instead of needing to import/require those defaults/commands in every test file, you can use the `supportFile` configuration option. This works similar to the former `supportFolder` option, but is more explicit.
|
||||
|
||||
`supportFile` is a path to a file to include before your test files. By default, `supportFile` is set to look for one of the following files:
|
||||
|
||||
* `cypress/support/index.js`
|
||||
* `cypress/support/index.coffee`
|
||||
|
||||
Just like with your test files, the `supportFile` can use ES2015+ (or CoffeeScript) and modules, so you can import/require other files as needed.
|
||||
|
||||
## Migrating from supportFolder to supportFile
|
||||
|
||||
You're seeing this error because you have the `supportFolder` option explicitly set, either to a different directory or as `false`, meaning you didn't utilize the support folder functionality.
|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "info",
|
||||
"title": "I have `supportFolder` set to `false`"
|
||||
}
|
||||
[/block]
|
||||
|
||||
Set the `supportFile` option to `false` instead:
|
||||
|
||||
```javascript
|
||||
// cypress.json
|
||||
|
||||
// before
|
||||
{
|
||||
"supportFolder": false
|
||||
}
|
||||
|
||||
// after
|
||||
{
|
||||
"supportFile": false
|
||||
}
|
||||
```
|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "info",
|
||||
"title": "I have `supportFolder` set to a different directory"
|
||||
}
|
||||
[/block]
|
||||
|
||||
When you open a project with Cypress, we look for a file named `index.js` in the `supportFolder` you have set. If one is not present, we generate a file that imports all the other files in your `supportFolder`.
|
||||
|
||||
You simply need to set the `supportFile` option to point to that file, and everything should work as before.
|
||||
|
||||
If, for example, you had the `supportFolder` set to `utilities`, change its name to `supportFile` and its value to `utilities/index.js`:
|
||||
|
||||
```javascript
|
||||
// cypress.json
|
||||
|
||||
// before
|
||||
{
|
||||
"supportFolder": "utilities"
|
||||
}
|
||||
|
||||
// after
|
||||
{
|
||||
"supportFile": "utilities/index.js"
|
||||
}
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# The Chromium Renderer process just crashed
|
||||
|
||||

|
||||
|
||||
Browsers are enormously complex pieces of software, and from time to time they will inconsistently crash *for no good reason*. Crashes are just a part of running automated tests.
|
||||
|
||||
At the moment, we haven't implemented an automatic way to recover from them, but it is actually possible for us to do so. We have an [open issue documenting the steps](https://github.com/cypress-io/cypress/issues/349) we could take to restart the renderer process and continue the run. If you're seeing consistent crashes and would like this implemented, please leave a note in the issue.
|
||||
|
||||
If you are running `Docker` [there is a simple one line fix for this problem documented here](https://github.com/cypress-io/cypress/issues/350).
|
||||
|
||||
***
|
||||
|
||||
# The 'cypress ci' command has been deprecated
|
||||
|
||||
As of version `0.19.0` and CLI versions `0.13.0`, we have deprecated the `cypress ci` command.
|
||||
|
||||
We did this to make it clearer what the difference was between a **regular run** and a **recorded run**.
|
||||
|
||||
Previously to record runs runs you wrote:
|
||||
|
||||
```shell
|
||||
cypress ci <key>
|
||||
```
|
||||
|
||||
Or if you had the environment variable: `CYPRESS_CI_KEY`
|
||||
|
||||
```shell
|
||||
cypress ci
|
||||
```
|
||||
|
||||
You need to rewrite this as:
|
||||
|
||||
```shell
|
||||
cypress run --record --key <record_key>
|
||||
```
|
||||
|
||||
If you were using the environment variable `CYPRESS_CI_KEY`, rename it to`CYPRESS_RECORD_KEY`.
|
||||
|
||||
You can now run and omit the `--key` flag:
|
||||
|
||||
```shell
|
||||
cypress run --record
|
||||
```
|
||||
|
||||
We will automatically apply the record key environment variable.
|
||||
@@ -1,48 +0,0 @@
|
||||
slug: anti-patterns
|
||||
excerpt: Patterns which you should avoid
|
||||
|
||||
# Contents
|
||||
|
||||
- :fa-angle-right: [Adding unncessary waits](#section-adding-unnecessary-waits)
|
||||
|
||||
***
|
||||
|
||||
# Adding unncessary waits
|
||||
|
||||
In Cypress, you almost **never** need to `cy.wait` for an arbitrary period of time. If you are finding yourself do this, there is likely a much better, simpler way.
|
||||
|
||||
Let's imagine the following example:
|
||||
|
||||
```javascript
|
||||
cy
|
||||
.request("http://localhost:8080/db/seed")
|
||||
.wait(5000) // <--- this is unnecessary
|
||||
.visit("http://localhost/8080")
|
||||
.wait(5000) // <--- this is unnecessary
|
||||
.server()
|
||||
.route("GET", /users/, [{"name": "Maggy"}, {"name": "Joan"}])
|
||||
.get("#fetch").click()
|
||||
.wait(4000) // <--- this is unnecessary
|
||||
.get("table tr").should("have.length", 2)
|
||||
```
|
||||
|
||||
Each arbitrary wait is unnecessary in the example above.
|
||||
|
||||
1. [`cy.request`](https://on.cypress.io/api/request) - waiting for this is unnecessary because this command will not resolve **until** it receives a response from your server. Adding the wait here only adds **5 seconds** after the `cy.request` has *already* resolved.
|
||||
2. [`cy.visit`](https://on.cypress.io/api/visit) - waiting for this is unnecessary because this command will resolve once the page fires its `load` event. By that time all of your assets have been loaded including `javascript`, `stylesheets`, and `html`.
|
||||
3. [`cy.get`](https://on.cypress.io/api/route) - waiting for the `cy.get` is unncessary because `cy.get` will automatically continue to retry until the `table tr` has a length of 2. Whenever commands have an assertion they will not resolve until their associated assertions pass. This enables you to simply describe the **state** of your application without having to worry about *when* it gets there. Alternatively a better solution to this problem is by waiting explictly for an aliased route.
|
||||
|
||||
The following is the least brittle way of writing the following:
|
||||
|
||||
```javascript
|
||||
cy
|
||||
.request("http://localhost:8080/db/seed")
|
||||
.visit("http://localhost/8080")
|
||||
.server()
|
||||
.route("GET", /users/, [{"name": "Maggy"}, {"name": "Joan"}]).as("getUsers")
|
||||
.get("#fetch").click()
|
||||
.wait("@getUsers") // <--- wait explicitly for this route to finish
|
||||
.get("table tr").should("have.length", 2)
|
||||
```
|
||||
|
||||
By waiting for the `getUsers` route, Cypress is smart enough to not only wait for a request to go out, but also for a response to come back in.
|
||||
@@ -1,120 +0,0 @@
|
||||
slug: known-issues
|
||||
excerpt: Known issues which cause problems in Cypress
|
||||
|
||||
# Contents
|
||||
|
||||
- :fa-angle-right: [Missing Commands](#section-missing-dom-action-commands)
|
||||
- [Right click](#section-right-click)
|
||||
- [Hover](#section-hover)
|
||||
- :fa-angle-right: [Difficult use cases](#section-missing-dom-action-commands)
|
||||
- [Iframes](#section-iframes)
|
||||
- [OAuth](#section-oauth)
|
||||
- [window.fetch](#section-window-fetch-routing-and-stubbing)
|
||||
|
||||
***
|
||||
|
||||
# Missing Commands
|
||||
|
||||
Some commands have not been implemented in Cypress. Some commands will be implemented in the future and some do not make sense to implement in Cypress.
|
||||
|
||||
***
|
||||
|
||||
## Right click
|
||||
|
||||
[Issue #53](https://github.com/cypress-io/cypress/issues/53)
|
||||
|
||||
**Workaround**
|
||||
|
||||
Oftentimes you can use [`cy.invoke`](https://on.cypress.io/api/invoke) or [`cy.wrap`](https://on.cypress.io/api/wrap) to trigger the event or execute the action in the DOM.
|
||||
|
||||
**Example of right clicking on an element using jQuery**
|
||||
```javascript
|
||||
cy.get("#nav").first().invoke("trigger", "contextmenu")
|
||||
```
|
||||
|
||||
**Example of right clicking on an element without jQuery**
|
||||
```javascript
|
||||
// need to create the event to later dispatch
|
||||
var e = new Event('contextmenu', {bubbles: true, cancelable: true})
|
||||
// set coordinates of click
|
||||
e.clientX = 451
|
||||
e.clientY = 68
|
||||
|
||||
cy
|
||||
.get("#nav").first().then(function($el) {
|
||||
$el[0].dispatchEvent(e)
|
||||
})
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## Hover
|
||||
|
||||
[Issue #10](https://github.com/cypress-io/cypress/issues/10)
|
||||
|
||||
Sometimes an element has specific logic on hover. Maybe the element doesn't even display to be clickable until you hover over a specific element.
|
||||
|
||||
**Workaround**
|
||||
|
||||
Oftentimes you can use [`cy.invoke`](https://on.cypress.io/api/invoke) or [`cy.wrap`](https://on.cypress.io/api/wrap) to show the element before you perform the action.
|
||||
|
||||
**Example of showing an element in order to perform action**
|
||||
```javascript
|
||||
cy.get(".content").invoke("show").click()
|
||||
```
|
||||
|
||||
You can also force the action to be performed on the element regardless of whether the element is visible or not.
|
||||
|
||||
**Example of clicking on a hidden element**
|
||||
```javascript
|
||||
cy.get(".content").click({force: true})
|
||||
```
|
||||
|
||||
**Example of checking a hidden element**
|
||||
```javascript
|
||||
cy.get(".checkbox").check({force: true})
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Difficult use cases
|
||||
|
||||
Cypress does not support the following use cases.
|
||||
|
||||
## Iframes
|
||||
|
||||
[Issue #136](https://github.com/cypress-io/cypress/issues/136)
|
||||
|
||||
You cannot target elements or interact with anything in an iframe - regardless of it being a same domain or cross domain iframe.
|
||||
|
||||
This is actively being worked on in Cypress and you'll first see support for same domain iframes, followed by cross domain (they are much harder to do).
|
||||
|
||||
**Workaround**
|
||||
|
||||
Sit tight, comment on the issue so we know you care about this support, and be patient.
|
||||
|
||||
***
|
||||
|
||||
## OAuth
|
||||
|
||||
This is related to the iframe issue above, but basically `oauth` usually will not work. This is one of the hardest things for Cypress to be able to handle as there are so many different implementations and mechanisms.
|
||||
|
||||
Likely we will be able to support server side oauth redirects, but for client side popups you'll simply use `sinon` and `stub` the oauth response directly in your code. This is actually possible to do right now but we don't have any good docs or tutorials on it.
|
||||
|
||||
**Workaround**
|
||||
|
||||
[Come into Gitter](https://gitter.im/cypress-io/cypress) and talk to us about what you're trying to do. We'll tell you if you're able to mock this and how to do it.
|
||||
|
||||
***
|
||||
|
||||
## window.fetch routing and stubbing
|
||||
|
||||
[Issue #95](https://github.com/cypress-io/cypress/issues/95)
|
||||
|
||||
Support for `fetch` has not been added but it's possible to handle in the same way as we handle `XHRs`. This biggest challenge here is that you can use `fetch` in `Service Workers` outside of the global context. We'll likely have to move routing to the server and handle it in the proxy layer but it should be possible.
|
||||
|
||||
While we currently provide things like the stack trace and initiator line for XHR's we will not be able to provide that for `fetch`.
|
||||
|
||||
**Workaround**
|
||||
|
||||
Sit tight, comment on the issue so we know you care about this support, and be patient.
|
||||
@@ -1,307 +0,0 @@
|
||||
slug: web-security
|
||||
excerpt: How Cypress handles same-origin policy
|
||||
|
||||
# Contents
|
||||
|
||||
- :fa-angle-right: [Overview](#section-overview)
|
||||
- :fa-angle-right: [Limitations](#section-limitations)
|
||||
- [One Superdomain per Test](#section-one-superdomain-per-test)
|
||||
- [Cross Origin Iframes](#section-cross-origin-iframes)
|
||||
- [Insecure Content](#section-insecure-content)
|
||||
- :fa-angle-right: [Common Workarounds](#section-common-workarounds)
|
||||
- [External Navigation](#section-external-navigation)
|
||||
- [Form Submission Redirects](#section-form-submission-redirects)
|
||||
- [JavaScript Redirects](#section-javascript-redirects)
|
||||
- :fa-angle-right: [Disabling Web Security](#section-disabling-web-security)
|
||||
|
||||
***
|
||||
|
||||
# Overview
|
||||
|
||||
Browsers adhere to a strict [`same-origin policy`](https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy). This means that browsers restrict access between `<iframes>` when their origin policies do not match.
|
||||
|
||||
Because Cypress' internal architecture is different from that of Selenium, Cypress must be able to directly communicate with your remote application at all times. Unfortunately, browsers naturally try to prevent how Cypress works.
|
||||
|
||||
To get around these restrictions, Cypress implements some strategies involving `JavaScript` code, the browser's `internal APIs`, and `network proxying` to **play by the rules** of `same-origin policy`. It is our goal to fully automate your application without needing to modify any application code - and we are *mostly* able to do this.
|
||||
|
||||
**Examples of what Cypress does under the hood:**
|
||||
|
||||
- Injects [`document.domain`](https://developer.mozilla.org/en-US/docs/Web/API/Document/domain) into `text/html` pages.
|
||||
- Proxies all `HTTP`/`HTTPS` traffic.
|
||||
- Changes the hosted url to match that of the application under test.
|
||||
- Uses the browser's internal APIs for network level traffic.
|
||||
|
||||
When Cypress first loads, the internal Cypress web application is hosted on a random port: something like `http://localhost:65874/__/`.
|
||||
|
||||
After the first [`cy.visit`](https://on.cypress.io/api/visit) is issued in a test, Cypress automatically changes its URL to match the origin of your remote application, thereby solving the first major hurdle of `same-origin policy`. Your application's code executes the same as it does outside of Cypress, and everything works as expected.
|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "info",
|
||||
"title": "How is HTTPS supported?",
|
||||
"body": "Cypress does some pretty interesting things under the hood to make testing HTTPs sites work. Cypress enables you to control and stub at the network level. Therefore, Cypress must assign and manage browser certificates to be able to modify the traffic in real time. You'll notice Chrome display a warning that the 'SSL certificate does not match'. This is normal and correct. Under the hood we act as our own CA authority and issue certificates dynamically in order to intercept requests otherwise impossible to access. We only do this for the superdomain currently under test, and bypass other traffic. That's why if you open a tab in Cypress to another host, the certificates match as expected."
|
||||
}
|
||||
[/block]
|
||||
|
||||
***
|
||||
|
||||
# Limitations
|
||||
|
||||
It's important to note that although we do our *very best* to ensure your application works normally inside of Cypress, there **are** some limitations you need to be aware of.
|
||||
|
||||
## One Superdomain per Test
|
||||
|
||||
Because Cypress changes its own host URL to match that of your applications, it requires that your application remain on the same superdomain for the entirety of a single test.
|
||||
|
||||
If you attempt to visit two different superdomains, Cypress will error. Visiting subdomains works fine, but two different superdomains does not. You can visit different superdomains in *different* tests, just not the *same* test.
|
||||
|
||||
|
||||
```javascript
|
||||
cy
|
||||
.visit("https://www.cypress.io")
|
||||
.visit("https://docs.cypress.io") // yup all good
|
||||
```
|
||||
|
||||
```javascript
|
||||
cy
|
||||
.visit("https://apple.com")
|
||||
.visit("https://google.com") // bad, this will immediately error
|
||||
|
||||
```
|
||||
|
||||
Although Cypress tries to enforce this limitation, it is possible for your application to bypass Cypress's ability to detect this.
|
||||
|
||||
**Examples of test cases that will error due to superdomain limitations:**
|
||||
|
||||
1. [`cy.click`](https://on.cypress.io/api/click) an `<a>` with an `href` to a different superdomain.
|
||||
2. [`cy.submit`](https://on.cypress.io/api/submit) a `<form>` that causes your webserver to redirect to you a different superdomain.
|
||||
3. Issue a JavaScript redirect in your application, such as `window.location.href = '...'`, to a different superdomain.
|
||||
|
||||
In each of these situations, Cypress will lose the ability to automate your application and will immediately error.
|
||||
|
||||
Read on to learn about [working around these common problems](#section-common-workarounds) or even [disabling web security](#section-disabling-web-security) altogether.
|
||||
|
||||
***
|
||||
|
||||
## Cross Origin Iframes
|
||||
|
||||
If your site embeds an `<iframe>` that is a cross-origin frame, Cypress will not be able to automate or communicate with this `<iframe>`.
|
||||
|
||||
**Examples of uses for cross-origin iframes:**
|
||||
|
||||
- Embedding a `Vimeo` or `Youtube` video.
|
||||
- Displaying a credit card form from `Stripe` or `Braintree`.
|
||||
- Displaying an embedded login form from `Auth0`.
|
||||
|
||||
It's actually *possible* for Cypress to accomodate these situations the same way Selenium does, but you will never have **native** access to these iframes from inside of Cypress.
|
||||
|
||||
As a workaround, you may be able to use [`window.postMessage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage) to directly communicate with these iframes and control them (if the 3rd party iframe supports it).
|
||||
|
||||
Other than that, you'll have to wait for us to implement API's to support this (check our [open issue](https://github.com/cypress-io/cypress/issues/136)), or you can [disable web security](#section-disabling-web-security).
|
||||
|
||||
***
|
||||
|
||||
## Insecure Content
|
||||
|
||||
Because of the way Cypress is designed, if you are testing an `HTTPS` site, Cypress will error anytime you attempt to navigate back to an `HTTP` site. This behavior helps highlight a *pretty serious* security problem with your application.
|
||||
|
||||
**Example of accessing insecure content:**
|
||||
|
||||
*Test code*
|
||||
|
||||
```javascript
|
||||
cy.visit("https://app.corp.com")
|
||||
```
|
||||
|
||||
In the application code, you set `cookies` and store a session on the browser.
|
||||
|
||||
Now let's imagine you have a single `insecure` link (or JavaScript redirect) in your application code.
|
||||
|
||||
*Application code*
|
||||
|
||||
```html
|
||||
<html>
|
||||
<a href="http://app.corp.com/page2">Page 2</a>
|
||||
</html>
|
||||
```
|
||||
|
||||
Cypress will immediately fail with the following test code:
|
||||
|
||||
*Test code*
|
||||
|
||||
```javascript
|
||||
cy
|
||||
.visit("https://app.corp.com")
|
||||
.get("a").click() // will immediately fail
|
||||
```
|
||||
|
||||
Browsers refuse to display insecure content on a secure page. Because Cypress initially changed its URL to match `https://app.corp.com` when the browser followed the `href` to `http://app.corp.com/page2`, the browser will refuse to display the contents.
|
||||
|
||||
Now you may be thinking...'this sounds like a problem with Cypress because when I work with my application outside of Cypress it works just fine'.
|
||||
|
||||
However, the truth is, Cypress is exposing a **security vulnerability** in your application, and you *want* it to fail in Cypress.
|
||||
|
||||
`cookies` that do not have their `secure` flag set to `true` will be sent as clear text to the insecure URL. This leaves your application vulnerable to session hijacking.
|
||||
|
||||
This security vulnerability exists **even if** your webserver forces a `301 redirect` back to the `HTTPS` site. The original `HTTP` request was still made once, exposing insecure session information.
|
||||
|
||||
**The Solution**
|
||||
|
||||
Simply update your `HTML` or `JavaScript` code to not navigate to an insecure `HTTP` page and instead only use `HTTPS`. Additionally make sure that cookies have their `secure` flag set to `true`.
|
||||
|
||||
If you're in a situation where you don't control the code, or otherwise cannot work around this, you can bypass this restriction in Cypress by [disabling web security](#section-dislabing-web-security).
|
||||
|
||||
***
|
||||
|
||||
# Common Workarounds
|
||||
|
||||
Let's investigate how you might encounter `cross origin` errors in your test code and break down how to work around them in Cypress.
|
||||
|
||||
## External Navigation
|
||||
|
||||
The most common situation where you might encounter this error is when you click on an `<a>` that navigates to another superdomain.
|
||||
|
||||
**Application code that is served at `localhost:8080`**
|
||||
|
||||
```html
|
||||
<html>
|
||||
<a href="https://google.com">Google</a>
|
||||
</html>
|
||||
```
|
||||
|
||||
**Test code**
|
||||
|
||||
```javascript
|
||||
cy
|
||||
.visit("http://localhost:8080") // where your webserver + HTML is hosted
|
||||
.get("a").click() // browser attempts to load google.com, Cypress errors
|
||||
```
|
||||
|
||||
There is essentially never any reason to visit a site that you don't control in your tests. It's prone to error and slow.
|
||||
|
||||
Instead, all you need to test is that the `href` property is correct!
|
||||
|
||||
```javascript
|
||||
// this is much easier to do and will run considerably faster
|
||||
cy
|
||||
.visit("http://localhost:8080")
|
||||
.get("a").should("have.attr", "href", "https://google.com") // no page load!
|
||||
```
|
||||
|
||||
Okay but let's say you're worried about `google.com` serving up the right HTML content. How would you test that? Easy! Just make a [`cy.request`](https://on.cypress.io/api/request) directly to it. [`cy.request`](https://on.cypress.io/api/request) is **NOT bound to CORS or same-origin policy**.
|
||||
|
||||
```javascript
|
||||
cy
|
||||
.visit("http://localhost:8080")
|
||||
.get("a").then(function($a) {
|
||||
// pull off the fully qualified href from the <a>
|
||||
var url = $a.prop("href")
|
||||
|
||||
// make a cy.request to it
|
||||
cy.request(url).its("body").should("include", "</html>")
|
||||
})
|
||||
```
|
||||
|
||||
Still not satisfied? Do you really want to click through to another application? Okay then read about [disabling web security](#section-disabling-web-security).
|
||||
|
||||
***
|
||||
|
||||
## Form Submission Redirects
|
||||
|
||||
When you submit a regular HTML form, the browser will follow this `HTTP(s) request`.
|
||||
|
||||
**Application code that is served at `localhost:8080`**
|
||||
|
||||
```html
|
||||
<html>
|
||||
<form method="POST" action="/submit">
|
||||
<input type="text" name="email" />
|
||||
<input type="submit" value="Submit" />
|
||||
</form>
|
||||
</html>
|
||||
```
|
||||
|
||||
```javascript
|
||||
cy
|
||||
.visit("http://localhost:8080")
|
||||
.get("form").submit() // submit the form!
|
||||
```
|
||||
|
||||
If your backend server handling the `/submit` route does a `30x` redirect to a different superdomain, you will get a `cross origin` error.
|
||||
|
||||
```javascript
|
||||
// imagine this is some node / express code
|
||||
// on your localhost:8080 server
|
||||
|
||||
app.post("/submit", function(req, res) {
|
||||
// redirect the browser to google.com
|
||||
res.redirect("https://google.com")
|
||||
})
|
||||
```
|
||||
|
||||
A commone use case for this is `Single sign-on (SSO)`. In that situation you may `POST` to a different server and are redirected elsewhere (typically with the session token in the URL).
|
||||
|
||||
If that's the case, don't worry - you can work around it with [`cy.request`](https://on.cypress.io/api/request). [`cy.request`](https://on.cypress.io/api/request) is special because it is **NOT bound to CORS or same-origin policy**.
|
||||
|
||||
In fact we can likely bypass the initial visit altogether and just `POST` directly to your `SSO` server.
|
||||
|
||||
```javascript
|
||||
cy
|
||||
.request("POST", "https://sso.corp.com/auth", {username: "foo", password: "bar"})
|
||||
.then(function(response) {
|
||||
// pull out the location redirect
|
||||
var loc = response.headers["Location"]
|
||||
|
||||
// parse out the token from the url (assuming its in there)
|
||||
var token = parseOutMyToken(loc)
|
||||
|
||||
// do something with the token that your web application expects
|
||||
// likely the same behavior as what your SSO does under the hood
|
||||
// assuming it handles query string tokens like this
|
||||
cy.visit("http://localhost:8080?token=" + token)
|
||||
|
||||
// if you don't need to work with the token you can sometimes
|
||||
// just visit the location header directly
|
||||
cy.visit(loc)
|
||||
})
|
||||
```
|
||||
|
||||
Not working for you? Don't know how to set your token? If you still need to be able to be redirected to your SSO server you can read about [disabling web security](#section-disabling-web-security).
|
||||
|
||||
***
|
||||
|
||||
## JavaScript Redirects
|
||||
|
||||
When we say *JavaScript Redirects* we are talking about any kind of code that does something like this:
|
||||
|
||||
```javascript
|
||||
window.location.href = "http://some.superdomain.com"
|
||||
```
|
||||
|
||||
This is probably the hardest situation to test because it's usually happening due to another cause. You will need to figure out why your JavaScript code is redirecting. Perhaps you're not logged in, and you need to handle that setup elsewhere? Perhaps you're using a `Single sign-on (SSO)` server and you just need to read the previous section about working around that?
|
||||
|
||||
If you can't figure out why your JavaScript code is redirecting you to a different superdomain then you might want to just read about [disabling web security](#section-disabling-websecurity).
|
||||
|
||||
***
|
||||
|
||||
# Disabling Web Security
|
||||
|
||||
So if you cannot work around any of the issues using the suggested workarounds above, you may want to disable web security.
|
||||
|
||||
One last thing to consider here is that every once in a while we discover bugs in Cypress that lead to `cross origin` errors that can otherwise be fixed. If you think you're experiencing a bug, [come into gitter](https://gitter.im/cypress-io/cypress) or [open an issue](https://github.com/cypress-io/cypress/issues/new).
|
||||
|
||||
To start, you will need to understand that **not all browsers expose a way to turn off web security**. Some do, some don't. If you rely on disabling web security, you will not be able to run tests on browsers that do not support this feature.
|
||||
|
||||
Still here? That's cool, let's disable web security!
|
||||
|
||||
**Set `chromeWebSecurity` to `false` in `cypress.json` and we'll take care of the rest.**
|
||||
|
||||
```json
|
||||
{
|
||||
chromeWebSecurity: false
|
||||
}
|
||||
```
|
||||
|
||||
The browser will now display insecure content, you can now navigate to any superdomain without cross origin errors, and you can access cross origin iframes that are embedded in your application.
|
||||
|
||||
One thing you may notice though is that Cypress still enforces visiting a single superdomain with [`cy.visit`](https://on.cypress.io/api/visit). This is an artificial limitation (and one that can be removed). You should [open an issue](https://github.com/cypress-io/cypress/issues/new) and tell us what you're trying to do!
|
||||
@@ -1,135 +0,0 @@
|
||||
slug: browser-management
|
||||
excerpt: How and why Cypress manages your browser
|
||||
|
||||
# Contents
|
||||
|
||||
- :fa-angle-right: [Managing Browsers](#section-managing-browsers)
|
||||
- :fa-angle-right: [Launching Browsers](#section-launching-browsers)
|
||||
- [Electron Browser](#section-electron-browser)
|
||||
- [Unsupported Browsers](#section-unsupported-browsers)
|
||||
- :fa-angle-right: [Clean Testing Environment](#section-clean-testing-environment)
|
||||
- [Cypress Profile](#section-cypress-profile)
|
||||
- [Testing Barriers](#section-removing-testing-barriers)
|
||||
- [Tabbed Browsing](#section-tabbed-browsing)
|
||||
- :fa-angle-right: [Automation API's](#section-automation-apis)
|
||||
- [Cypress Extension](#section-cypress-extension)
|
||||
- [Browser Drivers](#section-browser-drivers)
|
||||
- [No Selenium Server](#section-no-selenium-server)
|
||||
|
||||
***
|
||||
|
||||
# Managing Browsers
|
||||
|
||||
When you're ready to start testing, Cypress launches the browser for you. It does this for two main reasons:
|
||||
|
||||
1. To create a clean, pristine testing environment.
|
||||
2. To access the exclusive browser API's for automation.
|
||||
|
||||
***
|
||||
|
||||
# Launching Browsers
|
||||
|
||||
When Cypress is initially [run from the Desktop application](https://on.cypress.io/guides/installing-and-running#section-running-tests-from-the-gui), you can choose to run Cypress in a select number of browsers including:
|
||||
|
||||
- Chrome
|
||||
- Chromium
|
||||
- Canary
|
||||
|
||||

|
||||
|
||||
We'll automatically detect available browsers based on your OS. In `Linux` we detect browsers by their executable binary, and in `OSX` we'll automatically find them even if they aren't in your `/Applications` folder.
|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "warning",
|
||||
"body": "I'm confused which is the Cypress browser and which is my own browser!"
|
||||
}
|
||||
[/block]
|
||||
|
||||
You might notice that if you already have the browser open you will see two icons of the same browser in your Dock.
|
||||
|
||||

|
||||
|
||||
We understand that when Cypress is running in its own profile it can be confusing telling the difference from your normal browser and Cypress. For this reason we recommend [downloading Chromium](https://download-chromium.appspot.com/) or [downloading Canary](https://www.google.com/chrome/browser/canary.html). These browsers both have different icons from the standard Chrome browser and it'll be much easier to tell the difference. You can also use the bundled [Electron browser](#section-electron-browser), which does not have a Dock icon.
|
||||
|
||||

|
||||
|
||||
Additionally, we've made the browsers spawned by Cypress look different than regular sessions. You'll see a darker theme around the chrome of the browser. You'll always be able to visually distinguish these.
|
||||
|
||||

|
||||
|
||||
***
|
||||
|
||||
## Electron Browser
|
||||
|
||||
In addition to the browsers found on your system, you'll notice that `Electron` is another available browser (it may be the only browser if Cypress does not detect any compatible browsers on your system). The `Electron` browser is a version of Chrome that is bundled with [Electron](https://electron.atom.io/) (the platform underlying the Cypress app). Cypress uses it when running headless via `cypress run`. It may be useful for debugging issues that only occur when running headless.
|
||||
|
||||
The `Electron` browser does not have its own Dock icon or any chrome (address bar, tabs, bookmarks, etc).
|
||||
|
||||
***
|
||||
|
||||
## Unsupported Browsers
|
||||
|
||||

|
||||
|
||||
***
|
||||
|
||||
# Clean Testing Environment
|
||||
|
||||
When we launch browsers we open them in a way that makes testing more reliable and accessible. We do this in three ways:
|
||||
|
||||
1. Creating an isolated testing profile
|
||||
2. Disabling virtually everything that gets in the way of testing
|
||||
3. Enabling access to browser automation API's
|
||||
|
||||
***
|
||||
|
||||
## Cypress Profile
|
||||
|
||||
Cypress generates its own isolated profile away from your regular browsing profile. This means things like `history` entries, `cookies`, and `3rd party extensions` from your regular browsing session will not affect Cypress.
|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "warning",
|
||||
"body": "Wait, I need my developer extensions such as React Dev Tools, Batarang, or Ember Inspector!"
|
||||
}
|
||||
[/block]
|
||||
|
||||
That's no problem - you simply have to reinstall them **once**. We'll continue to use this Cypress testing profile on subsequent launches so all of your configuration will automatically be preserved. Note that in the [Electron browser](#section-electron-browser), while it's possible to use the dev tools, it's not possible to install developer extensions.
|
||||
|
||||
***
|
||||
|
||||
## Testing Barriers
|
||||
|
||||
Cypress automatically disables certain functionality in your browser which can get in the way of automated testing.
|
||||
|
||||
For instance we will automatically:
|
||||
|
||||
- Ignore certificate errors
|
||||
- Allow blocked pop-ups
|
||||
- Disable 'Saving passwords'
|
||||
- Disable 'Autofill forms and passwords'
|
||||
- Disable asking to become your primary browser
|
||||
- Disable language translations
|
||||
- Disable restoring sessions
|
||||
- Disable a ton of background network traffic
|
||||
- Disable background and renderer throttling
|
||||
|
||||
***
|
||||
|
||||
## Tabbed Browsing
|
||||
|
||||
***
|
||||
|
||||
# Automation API's
|
||||
|
||||
## Cypress Extension
|
||||
<talk about not using the debugger protocol>
|
||||
|
||||
***
|
||||
|
||||
## Browser Drivers
|
||||
|
||||
***
|
||||
|
||||
## No Selenium Server
|
||||
@@ -1,41 +0,0 @@
|
||||
slug: userland-extensions
|
||||
excerpt: 3rd party tools, plugins, and extensions
|
||||
|
||||
# Contents
|
||||
|
||||
- :fa-angle-right: [Docker](#section-docker)
|
||||
- [Base Image](#section-base-image)
|
||||
- :fa-angle-right: [Gitlab](#section-gitlab)
|
||||
- [Multi Cypress](#section-multi-cypress)
|
||||
- :fa-angle-right: [Typescript](#section-typescript)
|
||||
- [Definitions](#section-definitions)
|
||||
|
||||
***
|
||||
|
||||
# Docker
|
||||
|
||||
## Base Image
|
||||
|
||||
Docker image with dependencies and specific version of Cypress.
|
||||
|
||||
https://hub.docker.com/r/bahmutov/cypress-image/
|
||||
|
||||
***
|
||||
|
||||
# Gitlab
|
||||
|
||||
## Multi Cypress
|
||||
|
||||
Run multiple spec files concurrently.
|
||||
|
||||
https://github.com/kensho/multi-cypress
|
||||
|
||||
***
|
||||
|
||||
# Typescript
|
||||
|
||||
## Definitions
|
||||
|
||||
Typescript typings for most Cypress API commands.
|
||||
|
||||
https://github.com/StackVista/typed-cypress
|
||||
@@ -1,32 +0,0 @@
|
||||
slug: common-assertions
|
||||
excerpt: Cheatsheet for common assertions
|
||||
|
||||
Although you'll find examples of assertions throughout the docs, we've grouped them all here.
|
||||
|
||||
**DOM**
|
||||
- [Counting number of elements](#)
|
||||
- [Element existence](#)
|
||||
- [Element non-existance](#)
|
||||
- [Element text / content](#)
|
||||
- [Element visibility](#)
|
||||
- [Element attributes](#)
|
||||
- [Element values](#)
|
||||
- [Element classes](#)
|
||||
- [Element state](#)
|
||||
|
||||
**XHR**
|
||||
|
||||
- Element existence
|
||||
- Element non-existance
|
||||
- Element text / content
|
||||
- Element Visibility
|
||||
- Counting number of elements
|
||||
- Animations and Transitions
|
||||
- Classes and Properties
|
||||
- Request URL
|
||||
- Request Headers
|
||||
- Request Body
|
||||
- Counting number of requests
|
||||
- Verifying an object deep equals another object
|
||||
- Matching a property with regular expression
|
||||
- Matching the URL with a regular expression
|
||||
@@ -1,67 +0,0 @@
|
||||
slug: deprecations
|
||||
excerpt: Deprecations that require additional explanation will be listed here.
|
||||
|
||||
# Contents
|
||||
|
||||
- :fa-angle-right: [Passing `cy.server({stub: false})` is now deprecated](#section-passing-cy-server-stub-false-is-now-deprecated)
|
||||
- :fa-angle-right: [Passing `cy.route({stub: false})` is now deprecated](#section-passing-cy-route-stub-false-is-now-deprecated)
|
||||
- :fa-angle-right: [Cypress Package Renamed](#section-cypress-package-renamed)
|
||||
|
||||
***
|
||||
|
||||
# Passing `cy.server({stub: false})` is now deprecated
|
||||
|
||||
In previous versions of Cypress, to prevent Cypress from stubbing routes you had to explicitly tell your server not to stub routes like this:
|
||||
|
||||
```javascript
|
||||
cy
|
||||
.server({stub: false})
|
||||
.route(...)
|
||||
```
|
||||
|
||||
You no longer have to do this. Whether a [cy.route](https://on.cypress.io/api/route) is stubbed or not is simply based on whether or not you specified a response in [cy.route](https://on.cypress.io/api/route).
|
||||
|
||||
***
|
||||
|
||||
# Passing `cy.route({stub: false})` is now deprecated
|
||||
|
||||
In previous versions of Cypress, [cy.route](https://on.cypress.io/api/route) would require a `response` unless you specified `stub: false` in its options.
|
||||
|
||||
You used to have to write this:
|
||||
|
||||
```javascript
|
||||
cy
|
||||
.server()
|
||||
.route({url: /posts/, stub: false})
|
||||
```
|
||||
|
||||
This is now deprecated because Cypress automatically stubs [cy.route](https://on.cypress.io/api/route) based on whether or not it has a `response` property.
|
||||
|
||||
```javascript
|
||||
cy
|
||||
.server()
|
||||
.route(/users/, [{}, {}]) // <-- stubbed because this has a response argument
|
||||
.route({url: /comments/, response: []}) // <-- stubbed because this has a response property
|
||||
.route(/posts/) // <-- not stubbed because there is no response argument or property
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Cypress Package Renamed
|
||||
|
||||
<img width="638" alt="screen shot 2016-03-26 at 2 06 48 pm" src="https://cloud.githubusercontent.com/assets/1268976/14061658/0f675e30-f35c-11e5-9765-ab0049a2653d.png">
|
||||
|
||||
In previous versions of Cypress (below `0.15.0`) we distributed our CLI Tools under the package: `cypress`.
|
||||
|
||||
As of `0.15.0` the CLI Tools have now been renamed to the package `cypress-cli`.
|
||||
|
||||
Please update your scripts to use: `npm install -g cypress-cli`
|
||||
|
||||
This change was made for two reasons:
|
||||
|
||||
- Users were confused thinking they were actually installing cypress when in fact they were installing the CLI tools.
|
||||
- Once we open source the Desktop Application it will be distributed under the `cypress` package
|
||||
|
||||
Hopefully this change will lead to less confusion over the versioning of both the `CLI Tools` and the `Cypress Desktop Application`.
|
||||
|
||||
Once `cypress` is open sourced you'll be able to set `cypress` as a `devDependency` and will not have to fuss with downloading or managing the Desktop Application.
|
||||
@@ -1,110 +0,0 @@
|
||||
slug: faq
|
||||
excerpt: Frequently Asked Questions
|
||||
|
||||
# What kinds of applications can I test with Cypress?
|
||||
Cypress was originally designed to be used in modern JavaScript applications, but Cypress works perfectly on traditional server-side HTML based applications as well.
|
||||
|
||||
The API scales well in both situations, however there are certain commands which are useful primarily in JavaScript-based applications and won't see much use in traditional server-side applications.
|
||||
|
||||
Cypress enables you to pick the best testing strategy. You may choose not to involve the server for most tests (which is a good thing) and mock all requests/responses to and from the server. You may also choose to perform full-blown integration tests, where no data is mocked, and everything goes through the server. The implementation is completely up to you.
|
||||
|
||||
# What backend servers is Cypress compatible with?
|
||||
Any and all. Ruby, Node, C#, PHP, none of that matters. You'll write your tests in Javascript, but beyond that Cypress will work everywhere. To talk directly to your backend from your tests, you will need one of our adapters. Read on for more information on that.
|
||||
|
||||
# Does Cypress require me to change any of my existing code?
|
||||
No. But if you're wanting to test parts of your application which are not easily testable, you'll need to refactor those as you would for any testing.
|
||||
|
||||
# How is this different than Mocha, Karma, Capybara, Protractor, SauceLabs, etc?
|
||||
Cypress is kind of a hybrid application/framework/service all rolled into one. It takes a little bit of each of those tools and brings them all together.
|
||||
|
||||
**Mocha** <br>
|
||||
[Mocha](http://mochajs.org/) is a testing framework for JavaScript. Mocha gives you the `it`, `describe`, `beforeEach` methods. Cypress isn't **different** from Mocha, it actually **uses** Mocha under the hood. All of your tests will be written on top of Mocha's `bdd` interface.
|
||||
|
||||
**Karma** <br>
|
||||
[Karma](http://karma-runner.github.io/) is a unit testing runner for JavaScript, which can work with either `Jasmine`, `Mocha`, or another JavaScript testing framework.
|
||||
|
||||
Karma also watches your JavaScript files, live reloads when they change, and is also the `reporter` for your tests failing / passing. It runs from the command line.
|
||||
|
||||
Cypress would essentially replace Karma because it does all of this and much more.
|
||||
|
||||
**Capybara** <br>
|
||||
Capybara is a `Ruby` specific tool which allows you to write integration tests for your web application. In the Rails world, this is the *go-to* tool for testing your application. It uses `Selenium` or another headless driver under the hood to interact with browsers. It's API consists of commands which query for DOM elements, perform user actions, navigate around, etc.
|
||||
|
||||
Cypress would essentially replace Capybara because it does all of these things, and much more. The difference is that instead of testing your application in a GUI-less console, you'd see your application at all times. You'd never have to take a screenshot to debug because all commands instantly provide you the state of your application when they run. Upon any command failing, you'll get a human-readable error explaining why it failed. There's no "guessing" when debugging.
|
||||
|
||||
In my experience, Capybara begins to melt down on complex JavaScript applications. Additionally, trying to TDD your application is virtually impossible. You often have to resort to writing your application code first (typically manually refreshing your browser after changes) until you get it working. From there you write tests, but lose the entire value of TDD. These tests often feel "tacked" on, especially when it becomes harder to write a passing test when you can just open up your browser and see that it is already working.
|
||||
|
||||
**Protractor** <br>
|
||||
Protractor is basically the `Capybara` of the JavaScript world. It provides a nice Promise-based interface on top of Selenium, which makes it easy to deal with async code. Protractor comes with all of the features of Capybara but essentially suffers from the same problems.
|
||||
|
||||
Cypress would replace Protractor because it does all of these things and much more. One major difference is that Cypress enables you to write your unit tests and integration tests in the same tool, as opposed to splitting up this work across both Karma and Protractor. Also, Protractor is very much focused on `AngularJS`, whereas Cypress works with all JavaScript frameworks. Protractor, because it's based on Selenium, is still pretty slow, and is prohibitive when trying to TDD your application. Cypress on the other hand will run at the maximum speed your browser and application are capable of serving and rendering, there is no additional bloat.
|
||||
|
||||
**SauceLabs** <br>
|
||||
[SauceLabs](https://saucelabs.com/) is a 3rd party tool which enables Selenium-based tests to be run across various browsers and operating systems. Additionally, they have a JS Unit Testing tool which isn't Selenium focused.
|
||||
|
||||
SauceLabs also has a `manual testing` mode, where you can remotely control browsers in the cloud as if they were installed on your machine.
|
||||
|
||||
Cypress does not replace SauceLabs, in fact it compliments it. We are currently working on our own manual testing mode for browsers, but it is considerably different than what SauceLabs provides. That is the only overlapping feature.
|
||||
|
||||
Cypress's API is written to be completely compatible with SauceLabs, even though our API is not Selenium based at all. At the end of the day, you'll be able to enter your SauceLabs API key directly into Cypress, and run all of your tests across various browser combinations / operating systems. The results will be made available directly in Cypress.
|
||||
|
||||
Ultimately SauceLabs and Cypress offer very different value propositions. SauceLabs doesn't help you write your tests, it takes your existing tests and runs them across different browsers and aggregates the results for you.
|
||||
|
||||
Cypress on the other hand **helps** you write your tests. You would use Cypress every day, building and testing your application, and then use SauceLabs to ensure your application works on every browser.
|
||||
|
||||
The overlapping feature, **manually testing browsers** is different from a workflow perspective.
|
||||
|
||||
Currently, without Cypress, when you get errors from SauceLabs, you would then proceed to **debug** your application in that specific browser. This is where manually testing comes in handy. You could do that from SauceLabs tool, which means you'd navigate to their service and connect to your local server. You could then make a code change, refresh, manually recreate the bug, and debug in the other browser (with the other browser's native debugging tools). This can be tedious and time consuming. After fixing the bug you'd have to rerun your tests across all of the browsers again to see if anything else broke.
|
||||
|
||||
With Cypress, you'd still get errors from SauceLabs, but instead of leaving Cypress, you could use Cypress to create a manual session with that failing browser. Once connected to the other browser, you could see the error message in Cypress and proceed to debug it. As you make code changes, all of your Cypress commands would drive the other browser. The end result is that you'd see and debug another browser from within Google Chrome, using the same interface you use while developing locally.
|
||||
|
||||
# If Cypress runs in the browser, doesn't that mean it's sandboxed?
|
||||
Yes, technically; it's sandboxed and has to follow the same rules as every other browser. That's actually a good thing because it doesn't require a browser extension, and it naturally works across all browsers (which enables cross-browser testing).
|
||||
|
||||
But Cypress is actually way beyond just a basic JavaScript application running in the browser. That's one part, but it's also a `Desktop Application`, communicates with backend web services, and has language-specific adapters which are installed in your project.
|
||||
|
||||
All of these technologies together are coordinated and enable Cypress to work, which extends its capabilities far outside of the browser sandbox. Without these, Cypress would not work at all. While it's possible limitations may arise, these will be the minority. For the vast majority of your web development, Cypress will work just fine, and already **does** work.
|
||||
|
||||
# Seriously, I know there are security restrictions for JavaScript running in the browser!
|
||||
Seriously, Cypress has already solved these issues transparently without it causing you to change anything in your application. Everything should just work. Even clearing all cookies from JavaScript.
|
||||
|
||||
# Since Cypress runs in the browser, how can it talk to my backend?
|
||||
While you'll always write your tests in JavaScript, it's often important to talk to your backend prior to a test running, or throughout a test, or even after a test finishes. Regardless of which type of application you have, or plan to build, Cypress can communicate with the backend through one of its adapters. We are currently building adapters for:
|
||||
|
||||
* Rails
|
||||
* Node
|
||||
* .NET
|
||||
* PHP
|
||||
|
||||
Each adapter will be made available in each language's package system, and will have separate repositories detailing the installation, configuration, etc. Using these adapters will allow Cypress to pass messages to the backend for things like:
|
||||
|
||||
* Querying the database
|
||||
* Seeding the database
|
||||
* Requesting specific data
|
||||
* Asking if emails were sent
|
||||
* Anything else your backend needs to provide
|
||||
|
||||
As is the philosophy of Cypress, using these adapters should be painless and simple to install. Error messages will display directly in the browser and you'll be able to diagnose / debug easily.
|
||||
|
||||
These adapters are completely optional and are only necessary if you intend to test end to end with your server. If you only write JavaScript unit tests or mock request/responses you won't need these.
|
||||
|
||||
# We use WebSockets, will Cypress work with that?
|
||||
Yes.
|
||||
|
||||
# Will Cypress work for my CI provider?
|
||||
Yes.
|
||||
|
||||
# We have the craziest most insane authentication system ever, will Cypress work with that?
|
||||
If you're using some crazy thumb-print, retinal-scan, time-based, key-changing, microphone audial decoding mechanism to log in your users, then no, Cypress won't work with that. But seriously, Cypress is a **development** tool, which makes it easy to test your web applications. If your application is doing 100x things to make it extremely difficult to access, Cypress won't magically make it any easier.
|
||||
|
||||
Because Cypress is a development tool, you can always make your application more accessible while in your development environment. If you want, simply disable crazy steps in your authentication systems while you're in your testing environment. After all, that's why we have different environments! Normally you already have a development environment, a testing environment, a staging environment, and a production environment. So simply expose the parts of your system you want accessible in each appropriate environment.
|
||||
|
||||
In doing so, Cypress may not be able to give you 100% coverage without you changing anything, but that's okay. Just use different tools to test the crazier, less accessible parts of your application, and let Cypress test the other 99%.
|
||||
|
||||
Just remember, Cypress won't make a non-testable application suddenly testable. It's on your shoulders to architect your code in an accessible manner.
|
||||
|
||||
# I have an insanely complex JavaScript application with dragging and dropping, complex virtual DOM interactions, async loading templates, two-way-data-binding, pushState routing, all on the latest and greatest JS framework which I wrote and released today... will Cypress work?
|
||||
Yes, definitely yes.
|
||||
|
||||
# Can I use Cypress to script user-actions on an external site like `gmail.com`?
|
||||
No. There are already lots of tools to do that. Using Cypress to test against a 3rd party application is not supported. It **may** work but will defeat the purpose of why it was created. You use Cypress *while* you develop **your** application, it helps you write your tests.
|
||||
@@ -1,168 +0,0 @@
|
||||
slug: api
|
||||
excerpt: Our API provides commands and utilities to drive your tests in the browser.
|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "info",
|
||||
"body": "Read through our [Guides](https://on.cypress.io/guides/guides) first.",
|
||||
"title": "New to Cypress?"
|
||||
}
|
||||
[/block]
|
||||
|
||||
# Commands
|
||||
|
||||
Commands drive your tests in the browser like a real user would. They let you perform actions like typing, clicking, xhr requests, and can also assert things like "my button should be disabled".
|
||||
|
||||
| Navigation | |
|
||||
| -------------------- | -- |
|
||||
| [go](https://on.cypress.io/api/go) | Navigate back or forward to previous or next URL in the browser's history |
|
||||
| [reload](https://on.cypress.io/api/reload) | Reload the page |
|
||||
| [visit](https://on.cypress.io/api/visit) | Visit a remote url |
|
||||
|
||||
| Querying | |
|
||||
| -------------------- | -- |
|
||||
| [contains](https://on.cypress.io/api/contains) | Get a DOM element that contains specific text |
|
||||
| [get](https://on.cypress.io/api/get) | Get DOM element(s) by selector or alias |
|
||||
| [root](https://on.cypress.io/api/root) | Set the root scope to the current subject |
|
||||
| [within](https://on.cypress.io/api/within) | Set the root scope to the current subject |
|
||||
|
||||
| Assertions | |
|
||||
| -------------------- | -- |
|
||||
| [and](https://on.cypress.io/api/and) | Chain multiple assertions together |
|
||||
| [should](https://on.cypress.io/api/should) | Make an assertion about the current subject |
|
||||
|
||||
| DOM Traversal | |
|
||||
| -------------------- | -- |
|
||||
| [children](https://on.cypress.io/api/children) | Get the children DOM elements of the DOM elements |
|
||||
| [closest](https://on.cypress.io/api/closest) | Get the closest ancestor DOM element |
|
||||
| [eq](https://on.cypress.io/api/eq) | Get a DOM element at a specific index |
|
||||
| [filter](https://on.cypress.io/api/filter) | Filter DOM elements by a selector |
|
||||
| [find](https://on.cypress.io/api/find) | Get descendants of DOM elements |
|
||||
| [first](https://on.cypress.io/api/first) | Get the first DOM element within a set of DOM elements |
|
||||
| [last](https://on.cypress.io/api/last) | Get the last DOM element |
|
||||
| [next](https://on.cypress.io/api/next) | Get the next sibling of the DOM elements |
|
||||
| [nextAll](https://on.cypress.io/api/nextall) | Get all following siblings of the DOM elements |
|
||||
| [nextUntil](https://on.cypress.io/api/nextuntil) | Get all following siblings of the DOM elements until another element |
|
||||
| [not](https://on.cypress.io/api/not) | Remove DOM elements from the set of DOM elements |
|
||||
| [parent](https://on.cypress.io/api/parent) | Get the parent DOM element of the DOM elements |
|
||||
| [parents](https://on.cypress.io/api/parents) | Get the parents DOM elements of the DOM elements |
|
||||
| [parentsUntil](https://on.cypress.io/api/parentsuntil) | Get all ancestors of the DOM elements until another element |
|
||||
| [prev](https://on.cypress.io/api/prev) | Get the previous sibling of elements |
|
||||
| [prevAll](https://on.cypress.io/api/prevall) | Get all previous siblings of the DOM elements |
|
||||
| [prevUntil](https://on.cypress.io/api/prev) | Get all previous siblings of the DOM elements until another element |
|
||||
| [siblings](https://on.cypress.io/api/siblings) | Get all siblings DOM elements of the DOM elements |
|
||||
|
||||
| Actions | |
|
||||
| -------------------- | -- |
|
||||
| [blur](https://on.cypress.io/api/blur) | Blur a DOM element |
|
||||
| [check](https://on.cypress.io/api/check) | Select a checkbox or radio |
|
||||
| [clear](https://on.cypress.io/api/clear) | Clear a value of an input or textarea |
|
||||
| [click](https://on.cypress.io/api/click) | Click a DOM element |
|
||||
| [dblclick](https://on.cypress.io/api/dblclick) | Double-click on a DOM element |
|
||||
| [focus](https://on.cypress.io/api/focus) | Focus on a DOM element |
|
||||
| [select](https://on.cypress.io/api/select) | Select an option in a select |
|
||||
| [submit](https://on.cypress.io/api/submit) | Submit a form |
|
||||
| [type](https://on.cypress.io/api/type) | Type into a DOM element |
|
||||
| [uncheck](https://on.cypress.io/api/uncheck) | Uncheck a checkbox or radio |
|
||||
|
||||
| Network Requests | |
|
||||
| -------------------- | -- |
|
||||
| [request](https://on.cypress.io/api/request) | Make HTTP request |
|
||||
| [route](https://on.cypress.io/api/route) | Route responses to matching requests |
|
||||
| [server](https://on.cypress.io/api/server) | Control the behavior of network requests and responses |
|
||||
|
||||
| Connectors | |
|
||||
| -------------------- | -- |
|
||||
| [each](https://on.cypress.io/api/each) | Iterate through each item in the current subject |
|
||||
| [its](https://on.cypress.io/api/its) | Get properties on the current subject |
|
||||
| [invoke](https://on.cypress.io/api/invoke) | Invoke the function on the current subject |
|
||||
| [spread](https://on.cypress.io/api/spread) | Spread an array as individual arguments to a callback function |
|
||||
| [then](https://on.cypress.io/api/then) | Invokes a callback function with the current subject |
|
||||
|
||||
| Location (URL) | |
|
||||
| -------------------- | -- |
|
||||
| [hash](https://on.cypress.io/api/hash) | Get the current URL hash |
|
||||
| [location](https://on.cypress.io/api/location) | Get the `window.location` |
|
||||
| [url](https://on.cypress.io/api/url) | Get the current URL |
|
||||
|
||||
| Window | |
|
||||
| -------------------- | -- |
|
||||
| [document](https://on.cypress.io/api/document) | Get the document |
|
||||
| [title](https://on.cypress.io/api/title) | Get the title of the document |
|
||||
| [window](https://on.cypress.io/api/window) | Get global window object |
|
||||
|
||||
| Waiting | |
|
||||
| -------------------- | -- |
|
||||
| [wait](https://on.cypress.io/api/wait) | Wait for a specific amount of time or resource to resolve |
|
||||
|
||||
| Aliasing | |
|
||||
| -------------------- | -- |
|
||||
| [as](https://on.cypress.io/api/as) | Alias a route or DOM element for use later. |
|
||||
|
||||
| Spies, Stubs & Clocks | |
|
||||
| -------------------- | -- |
|
||||
| [spy](https://on.cypress.io/api/spy) | Wrap a method in a spy |
|
||||
| [stub](https://on.cypress.io/api/stub) | Create a stub and/or replace a function with a stub |
|
||||
| [clock](https://on.cypress.io/api/clock) | Control time in the browser |
|
||||
| [tick](https://on.cypress.io/api/tick) | Move time in the browser |
|
||||
|
||||
| Files | |
|
||||
| -------------------- | -- |
|
||||
| [fixture](https://on.cypress.io/api/fixture) | Load a fixture file to represent data |
|
||||
| [readFile](https://on.cypress.io/api/readfile) | Read a file's contents |
|
||||
| [writeFile](https://on.cypress.io/api/writefile) | Write to a file with the specified contents |
|
||||
|
||||
| Viewport | |
|
||||
| -------------------- | -- |
|
||||
| [viewport](https://on.cypress.io/api/viewport) | Change the screen size of your application |
|
||||
|
||||
| Local Storage | |
|
||||
| -------------------- | -- |
|
||||
| [clearLocalStorage](https://on.cypress.io/api/clearlocalstorage) | Clear all data in local storage |
|
||||
|
||||
| Cookies | |
|
||||
| -------------------- | -- |
|
||||
| [clearCookie](https://on.cypress.io/api/clearcookie) | Clear a browser cookie |
|
||||
| [clearCookies](https://on.cypress.io/api/clearcookies) | Clear all browser cookies |
|
||||
| [getCookie](https://on.cypress.io/api/getcookie) | Get a browser cookie |
|
||||
| [getCookies](https://on.cypress.io/api/getcookies) | Get all browser cookies |
|
||||
| [setCookie](https://on.cypress.io/api/setcookie) | Set a browser cookie |
|
||||
|
||||
| Debugging | |
|
||||
| -------------------- | -- |
|
||||
| [debug](https://on.cypress.io/api/debug) | Set a `debugger` |
|
||||
| [pause](https://on.cypress.io/api/pause) | Pause a command |
|
||||
|
||||
| Misc | |
|
||||
| -------------------- | -- |
|
||||
| [end](https://on.cypress.io/api/end) | End the command chain |
|
||||
| [exec](https://on.cypress.io/api/exec) | Execute a system command |
|
||||
| [focused](https://on.cypress.io/api/focused) | Get the DOM element that is focused |
|
||||
| [log](https://on.cypress.io/api/log) | Print a message to the Command Log |
|
||||
| [screenshot](https://on.cypress.io/api/screenshot) | Take a screenshot |
|
||||
| [wrap](https://on.cypress.io/api/wrap) | Wrap an object |
|
||||
|
||||
# Utilities
|
||||
|
||||
Utilities give you access to methods from other commonly used libraries.
|
||||
|
||||
| Commands | |
|
||||
| -------------------- | -- |
|
||||
| [_](https://on.cypress.io/api/cypress-underscore) | Call any Underscore method |
|
||||
| [$](https://on.cypress.io/api/cypress-jquery) | Call any jQuery method |
|
||||
| [moment](https://on.cypress.io/api/cypress-moment) | Format or parse dates using moment methods |
|
||||
| [Blob](https://on.cypress.io/api/cypress-blob) | Convert base64 strings to blob objects |
|
||||
| [Promise](https://on.cypress.io/api/cypress-promise) | Instantiate a bluebird promise |
|
||||
|
||||
# Cypress API
|
||||
|
||||
The Cypress API enables you to configure the behavior of how Cypress works internally. You can do things like access Environment Variables, change configuration, create custom commands, and more.
|
||||
|
||||
| Commands | |
|
||||
| -------------------- | -- |
|
||||
| [config](https://on.cypress.io/api/config) | get and set configuration options |
|
||||
| [env](https://on.cypress.io/api/env) | get and set environment variables |
|
||||
| [Commands](https://on.cypress.io/api/commands) | Commands API |
|
||||
| [Cookies](https://on.cypress.io/api/cookies) | Manage your application's cookies |
|
||||
| [Dom](https://on.cypress.io/api/dom) | Find out whether an element is hidden |
|
||||
| [Server](https://on.cypress.io/api/api-server) | Permanently override default server options |
|
||||
@@ -1,294 +0,0 @@
|
||||
slug: and
|
||||
excerpt: Chain multiple assertions together
|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "info",
|
||||
"body": "[Read about Making Assertions first.](https://on.cypress.io/guides/making-assertions)",
|
||||
"title": "New to Cypress?"
|
||||
}
|
||||
[/block]
|
||||
|
||||
`cy.and` makes chaining together assertions easy.
|
||||
|
||||
You'd typically use `cy.and` when you are making multiple assertions about the same subject.
|
||||
|
||||
| | |
|
||||
|--- | --- |
|
||||
| **Returns** | the current subject but (in some cases) a new subject |
|
||||
| **Timeout** | the assertion will retry for the duration of the [`defaultCommandTimeout`](https://on.cypress.io/guides/configuration#section-timeouts) |
|
||||
|
||||
***
|
||||
|
||||
# [cy.and( *chainers* )](#section-chainers-usage)
|
||||
|
||||
Make an assertion about the current subject using assertion chainers.
|
||||
|
||||
***
|
||||
|
||||
# [cy.and( *chainers*, *value* )](#section-chainers-with-value-usage)
|
||||
|
||||
Make an assertion about the value of the current subject.
|
||||
|
||||
Some chai methods and chai-jQuery methods return a new (different) subject for chain-ability.
|
||||
|
||||
***
|
||||
|
||||
# [cy.and( *chainers*, *method*, *value* )](#section-chainers-with-method-and-value-usage)
|
||||
|
||||
Make an assertion about the subject by calling a method and providing a value to that method.
|
||||
|
||||
***
|
||||
|
||||
# [cy.and( *function* )](#section-function-usage)
|
||||
|
||||
Pass a function that can have any number of explicit assertions written within it.
|
||||
|
||||
Does not change the subject. Whatever was passed to the function is what is returned.
|
||||
|
||||
***
|
||||
|
||||
# Chainers Usage
|
||||
|
||||
## Chain assertions on the same subject
|
||||
|
||||
```javascript
|
||||
cy.get("button").should("have.class", "active").and("not.be.disabled")
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Chainers with Value Usage
|
||||
|
||||
## Chain assertions on subject change
|
||||
|
||||
```html
|
||||
<!-- App Code -->
|
||||
<ul>
|
||||
<li>
|
||||
<a href="users/123/edit">Edit User</a>
|
||||
</li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
```javascript
|
||||
cy
|
||||
// subject is now <a>
|
||||
.get("a")
|
||||
|
||||
// assert <a> contains text: "Edit User"
|
||||
// subject is still the <a>
|
||||
.should("contain", "Edit User")
|
||||
|
||||
// assert subject has 'href' attribute
|
||||
// subject now changes to return value from the 'href' attribute
|
||||
.and("have.attr", "href")
|
||||
|
||||
// assert that the string returned from 'href'
|
||||
// matches the RegExp /users/
|
||||
// the subject is still the same string
|
||||
.and("match", /users/)
|
||||
|
||||
// assert that the string does not
|
||||
// have a '#' character within it
|
||||
.and("not.include", "#")
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Chainers with Method and Value Usage
|
||||
|
||||
## Assert the href is equal to '/users'
|
||||
|
||||
```javascript
|
||||
// have.attr comes from chai-jquery
|
||||
cy
|
||||
.get("#header a")
|
||||
.should("have.class", "active")
|
||||
.and("have.attr", "href", "/users")
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Function Usage
|
||||
|
||||
## Verify length, content, and classes from multiple `<p>`
|
||||
|
||||
Passing a function to `cy.and` enables you to assert on arbitrary subjects. This gives you the opportunity to *massage* what you'd like to assert on.
|
||||
|
||||
Just be sure *not* to include any code that has side effects in your callback function.
|
||||
|
||||
The callback function will be retried over and over again until no assertions within it throw.
|
||||
|
||||
```html
|
||||
<div>
|
||||
<p class="text-primary">Hello World</p>
|
||||
<p class="text-danger">You have an error</p>
|
||||
<p class="text-default">Try again later</p>
|
||||
</div>
|
||||
```
|
||||
|
||||
```javascript
|
||||
cy
|
||||
.get("p")
|
||||
.should("not.be.empty")
|
||||
.and(function($p){
|
||||
// should have found 3 elements
|
||||
expect($p).to.have.length(3)
|
||||
|
||||
// make sure the first contains some text content
|
||||
expect($p.first()).to.contain("Hello World")
|
||||
|
||||
// use jquery's map to grab all of their classes
|
||||
// jquery's map returns a new jquery object
|
||||
var classes = $p.map(function(i, el){
|
||||
return cy.$(el).attr("class")
|
||||
})
|
||||
|
||||
// call classes.get() to make this a plain array
|
||||
expect(classes.get()).to.deep.eq([
|
||||
"text-primary",
|
||||
"text-danger",
|
||||
"text-default"
|
||||
])
|
||||
})
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## Using a callback function will not change the subject
|
||||
|
||||
```javascript
|
||||
cy
|
||||
.get("button")
|
||||
.should("be.active")
|
||||
.and(function($button){
|
||||
// whatever we return here is ignored
|
||||
// as Cypress will always force the return
|
||||
// value for future commands to be the same
|
||||
// as the previous subject which is <button>
|
||||
|
||||
expect({foo: "bar"}).to.deep.eq({foo: "bar"})
|
||||
|
||||
// whatever the return value (if any) is ignored
|
||||
return {foo: "bar"}
|
||||
})
|
||||
|
||||
.then(function($button){
|
||||
// $button === <button>
|
||||
// the subject is unchanged no matter what was returned
|
||||
})
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## Assertions that change the subject
|
||||
|
||||
Sometimes using a specific chainer will automatically change the assertion subject.
|
||||
|
||||
For instance in `chai`, the method [`have.property("...")`](http://chaijs.com/api/bdd/) will automatically change the subject.
|
||||
|
||||
Additionally in [`Chai-jQuery`](https://github.com/chaijs/chai-jquery#attrname-value), the methods: `attr`, `prop`, `css`, and `data` also change the subject.
|
||||
|
||||
This allows you to utilize other `chainer` methods such as `match` when making assertions about values.
|
||||
|
||||
```javascript
|
||||
// in this example our subject changed to the string 'sans-serif' because
|
||||
// have.css("font-family") returned a string instead of the <body> element
|
||||
cy
|
||||
// subject is <body>
|
||||
.get("body")
|
||||
|
||||
// subject changes to the string return value of 'font-family'
|
||||
.should("have.css", "font-family")
|
||||
|
||||
// use match to assert the string matches a regular expression
|
||||
.and("match", /sans-serif/)
|
||||
```
|
||||
|
||||
```javascript
|
||||
// in this example our subject changed to the string '/users' because
|
||||
// have.attr, href, /users returned a string instead of the <a> element
|
||||
cy
|
||||
// subject is <a>
|
||||
.get("a")
|
||||
|
||||
// subject changes to the string 'users'
|
||||
.should("have.attr", "href", "/users")
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Notes
|
||||
|
||||
## Similarities to Chai
|
||||
|
||||
If you've worked in [Chai](http://chaijs.com/) before, you will recognize that `cy.and` matches the same fluent assertion syntax.
|
||||
|
||||
Take this *explicit* assertion for example:
|
||||
|
||||
```javascript
|
||||
expect({foo: "bar"}).to.have.property("foo").and.eq("bar")
|
||||
```
|
||||
|
||||
`cy.and` reproduces this same assertion behavior.
|
||||
|
||||
***
|
||||
|
||||
## Can I pass options to cy.and()?
|
||||
|
||||
Options passed to the preceding command will be passed through to `cy.and`.
|
||||
|
||||
The following example is an example of increasing the `timeout` of the `cy.and`:
|
||||
|
||||
```javascript
|
||||
cy
|
||||
.get("input", {timeout: 10000}) // <-- wait up to 10 seconds for this 'input' to be found
|
||||
.should("have.value", "foo") // <-- and to have the value 'foo'
|
||||
.and("have.class", "radio") // <-- and to have the class 'radio'
|
||||
```
|
||||
|
||||
```javascript
|
||||
cy.find("input", {timeout: 10000}).should("have.value", "foo").and("have.class", "radio")
|
||||
↲
|
||||
// adding the timeout here will automatically
|
||||
// flow down to the assertions, and they will
|
||||
// be retried for up to 10 seconds
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## How do I know which assertions change the subject and which keep it the same?
|
||||
|
||||
The chainers that come from [Chai](https://on.cypress.io/guides/bundled-tools#section-chai) or [Chai-jQuery](https://on.cypress.io/guides/bundled-tools#section-chai-jquery) will always document what they return.
|
||||
|
||||
Alternatively, it is very easy to use Cypress itself to figure this out.
|
||||
|
||||
You can [read more about debugging assertions](https://on.cypress.io/guides/making-assertions#debugging-assertions) here.
|
||||
|
||||
***
|
||||
|
||||
# Command Log
|
||||
|
||||
## Chain assertions on the same subject
|
||||
|
||||
```javascript
|
||||
.find("input[type='checkbox']")
|
||||
.should("be.checked")
|
||||
.and("not.be.disabled")
|
||||
```
|
||||
|
||||
The commands above will display in the command log as:
|
||||
|
||||
<img width="530" alt="screen shot 2015-11-29 at 12 16 46 pm" src="https://cloud.githubusercontent.com/assets/1271364/11458700/36d1e646-9693-11e5-8771-158230530fdc.png">
|
||||
|
||||
When clicking on `assert` within the command log, the console outputs the following:
|
||||
|
||||
<img width="636" alt="screen shot 2015-11-29 at 12 17 03 pm" src="https://cloud.githubusercontent.com/assets/1271364/11458702/3b6873be-9693-11e5-88f7-a928ebdac80c.png">
|
||||
|
||||
***
|
||||
|
||||
# Related
|
||||
|
||||
- [should](https://on.cypress.io/api/should)
|
||||
- [Making Assertions](https://on.cypress.io/guides/making-assertions)
|
||||
@@ -1,73 +0,0 @@
|
||||
slug: as
|
||||
excerpt: Alias a route or DOM element for use later.
|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "info",
|
||||
"body": "[Read about Using Aliases first.](https://on.cypress.io/guides/using-aliases)",
|
||||
"title": "New to Cypress?"
|
||||
}
|
||||
[/block]
|
||||
|
||||
Assign an alias to a route or DOM element for use later. Reference the alias later within the [`cy.get`](https://on.cypress.io/api/get) or [`cy.wait`](https://on.cypress.io/api/wait) command with the prefix `@`.
|
||||
|
||||
| | |
|
||||
|--- | --- |
|
||||
| **Returns** | the DOM element or route being aliased |
|
||||
| **Timeout** | the alias will retry the chain of commands before the alias assignment for the duration of the [`defaultCommandTimeout`](https://on.cypress.io/guides/configuration#section-timeouts) |
|
||||
|
||||
***
|
||||
|
||||
# [cy.as( *text* )](#section-usage)
|
||||
|
||||
Create an alias to be used later, passing the name of the alias as a parameter.
|
||||
|
||||
***
|
||||
|
||||
# Usage
|
||||
|
||||
## Alias a route, then wait for that route using `@alias`
|
||||
|
||||
```javascript
|
||||
cy
|
||||
.route("PUT", /^\/users\/\d+/, "fixture:user").as("userPut")
|
||||
.get("form").submit()
|
||||
.wait("@userPut")
|
||||
.its("url").should("contain", "users")
|
||||
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Command Log
|
||||
|
||||
## Alias several routes
|
||||
|
||||
```javascript
|
||||
cy
|
||||
.route(/company/, "fixture:company").as("companyGet")
|
||||
.route(/roles/, "fixture:roles").as("rolesGet")
|
||||
.route(/teams/, "fixture:teams").as("teamsGet")
|
||||
.route(/users\/\d+/, "fixture:user").as("userGet")
|
||||
.route("PUT", /^\/users\/\d+/, "fixture:user").as("userPut")
|
||||
```
|
||||
|
||||
Aliases of routes display in the routes instrument panel:
|
||||
|
||||
<img width="567" alt="screen shot 2015-11-29 at 2 25 47 pm" src="https://cloud.githubusercontent.com/assets/1271364/11459470/22e31e54-96a5-11e5-8895-a6ff5f8bb973.png">
|
||||
|
||||
***
|
||||
|
||||
# Errors
|
||||
|
||||
## cy.as() cannot be aliased as: 'str'. This word is reserved.
|
||||
|
||||
Some strings are not allowed as aliases since they are reserved words in Cypress. These words include: test, runnable, timeout, slow, skip, and inspect.
|
||||
|
||||
***
|
||||
|
||||
# Related
|
||||
|
||||
- [get](https://on.cypress.io/api/get)
|
||||
- [wait](https://on.cypress.io/api/wait)
|
||||
- [Using Aliases](https://on.cypress.io/guides/using-aliases)
|
||||
@@ -1,224 +0,0 @@
|
||||
slug: clock
|
||||
excerpt: Control time in the browser
|
||||
|
||||
`cy.clock` overrides native global functions related to time, so you can test code using those functions in an easier, synchronous way. This includes the `setTimeout`, `clearTimeout`, `setInterval`, and `clearInterval` functions as well as controlling `Date` objects. Note that this only applies to the `top` window on a web page. It will not override the time functions on any iframes embedded on the web page.
|
||||
|
||||
`cy.clock` automatically restores the native functions in between tests without you having to explicitly restore them. You can still manually restore the functions within a test by calling `.restore()` on the `clock` object that `cy.clock` yields.
|
||||
|
||||
`cy.clock` pairs with [`cy.tick`](https://on.cypress.io/api/tick), which moves the clock along a certain number of milliseconds.
|
||||
|
||||
Subsequent calls to `cy.clock` will yield the `clock` object without re-overriding the native time functions.
|
||||
|
||||
If you call `cy.clock` before visiting a page with [`cy.visit`](https://on.cypress.io/api/visit), the page's native global functions will be overridden on window load, before any of your app code runs, so even if `setTimeout`, for example, is called on page load, it can still be controlled via [`cy.tick`](https://on.cypress.io/api/tick). This also applies if, during the course of a test, the page under test is reloaded or changed.
|
||||
|
||||
| | |
|
||||
|--- | --- |
|
||||
| **Returns** | a `clock` object. See [clock API](#section-clock-api) |
|
||||
|
||||
***
|
||||
|
||||
# [cy.clock()](#section-usage)
|
||||
|
||||
Replaces `setTimeout`, `clearTimeout`, `setInterval`, `clearInterval` and `Date` and allows them to be controlled synchronously via [`cy.tick`](https://on.cypress.io/api/tick) or the yielded `clock` object (see [clock API](#section-clock-api)).
|
||||
|
||||
The clock starts at the unix epoch (timestamp of 0). This means that when you instantiate `new Date` in your application, it will have a time of `January 1st, 1970`.
|
||||
|
||||
***
|
||||
|
||||
# [cy.clock( *now* )](#section-specify-the-now-timestamp)
|
||||
|
||||
Same as above, but starts the clock at the specified timestamp.
|
||||
|
||||
***
|
||||
|
||||
# [cy.clock( *now*, *functionNames* )](#section-specify-which-functions-to-override)
|
||||
|
||||
Same as above, but only overrides the functions in the array `functionNames`.
|
||||
|
||||
***
|
||||
|
||||
# clock API
|
||||
|
||||
`cy.clock` yields a `clock` object with the following methods. You can also access the `clock` object via `this.clock` in a [`cy.then`](https://on.cypress.io/api/then) callback.
|
||||
|
||||
## clock.tick(*milliseconds*)
|
||||
|
||||
Move the clock the specified number of `milliseconds`. Any timers within the affected range of time will be called.
|
||||
|
||||
## clock.restore()
|
||||
|
||||
Restore all overridden native functions. This is automatically called between tests, so should not generally be needed.
|
||||
|
||||
***
|
||||
|
||||
# Options
|
||||
|
||||
Pass in an options object to change the default behavior of `cy.clock`.
|
||||
|
||||
**cy.clock( *options* )**
|
||||
|
||||
**cy.clock( *now*, *options* )**
|
||||
|
||||
**cy.clock( *now*, *functionNames*, *options* )**
|
||||
|
||||
Option | Default | Notes
|
||||
--- | --- | ---
|
||||
`log` | `true` | whether to display command in command log
|
||||
|
||||
***
|
||||
|
||||
# Usage
|
||||
|
||||
## Create a clock and use it to trigger a setInterval
|
||||
|
||||
```javascript
|
||||
// your app code
|
||||
var seconds = 0
|
||||
|
||||
setInterval(function(){
|
||||
$('#seconds-elapsed').text(++seconds + ' seconds')
|
||||
}, 1000)
|
||||
```
|
||||
|
||||
```javascript
|
||||
// test code
|
||||
cy
|
||||
.clock()
|
||||
.visit("/index.html")
|
||||
.tick(1000)
|
||||
.get("#seconds-elapsed")
|
||||
.should("have.text", "1 seconds")
|
||||
.tick(1000)
|
||||
.get("#seconds-elapsed")
|
||||
.should("have.text", "2 seconds")
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## Specify the now timestamp
|
||||
|
||||
```javascript
|
||||
// your app code
|
||||
$('#date').text(new Date().toJSON())
|
||||
```
|
||||
|
||||
```javascript
|
||||
// test code
|
||||
const now = new Date(2017, 2, 14).getTime() // March 14, 2017 timestamp
|
||||
|
||||
cy
|
||||
.clock(now)
|
||||
.visit("/index.html")
|
||||
.get("#date")
|
||||
.contains("2017-03-14")
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## Specify which functions to override
|
||||
|
||||
This will only override `setTimeout` and `clearTimeout` and leave the other time-related functions as they are.
|
||||
|
||||
```javascript
|
||||
cy.clock(null, ["setTimeout", "clearTimeout"])
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## Access the clock object to synchronously move time
|
||||
|
||||
In most cases, it's easier to [`cy.tick`](https://on.cypress.io/api/tick) to move time, but you can also use `clock` object yielded by `cy.clock`.
|
||||
|
||||
```javascript
|
||||
cy.clock().then(function (clock) {
|
||||
clock.tick(1000)
|
||||
})
|
||||
```
|
||||
|
||||
You can call `cy.clock` again for this purpose later in a chain if necessary.
|
||||
|
||||
```javascript
|
||||
cy
|
||||
.clock()
|
||||
.get("#foo")
|
||||
.type("Foo")
|
||||
.clock().then(function (clock) {
|
||||
clock.tick(1000)
|
||||
})
|
||||
```
|
||||
|
||||
The clock object is also available via `this.clock` in any `.then` callback.
|
||||
|
||||
```javascript
|
||||
cy
|
||||
.clock()
|
||||
.get("#foo").then(function ($foo) {
|
||||
this.clock.tick(1000)
|
||||
// do something with $foo ...
|
||||
})
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## Access the clock object to restore native functions
|
||||
|
||||
In general, it should not be necessary to manually restore the native functions that `cy.clock` overrides, since this is done automatically between tests. But if you need to, the `clock` object yielded has `.restore` method.
|
||||
|
||||
```javascript
|
||||
cy.clock().then(function (clock) {
|
||||
clock.restore()
|
||||
})
|
||||
```
|
||||
|
||||
Or via `this.clock`:
|
||||
|
||||
```javascript
|
||||
cy
|
||||
.clock()
|
||||
.get("#foo").then(function ($foo) {
|
||||
this.clock.restore()
|
||||
// do something with $foo ...
|
||||
})
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## Example Recipe
|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "info",
|
||||
"body": "[Check out our example recipe testing spying, stubbing and time](https://github.com/cypress-io/cypress-example-recipes/blob/master/cypress/integration/spy_stub_clock_spec.js)",
|
||||
"title": "Using cy.clock and cy.tick"
|
||||
}
|
||||
[/block]
|
||||
|
||||
***
|
||||
|
||||
# Command Log
|
||||
|
||||
## Create a clock and tick it 1 second
|
||||
|
||||
```javascript
|
||||
cy
|
||||
.clock()
|
||||
.tick(1000)
|
||||
```
|
||||
|
||||
The command above will display in the command log as:
|
||||
|
||||
<img width="448" alt="screen shot of command log" src="https://cloud.githubusercontent.com/assets/1157043/22437918/059f60a6-e6f8-11e6-903d-d868e044615d.png">
|
||||
|
||||
When clicking on the `clock` command within the command log, the console outputs the following:
|
||||
|
||||
<img width="1059" alt="screen shot of console output" src="https://cloud.githubusercontent.com/assets/1157043/22437920/0786f9d8-e6f8-11e6-9e77-926b15aa8dae.png">
|
||||
|
||||
***
|
||||
|
||||
# Related
|
||||
|
||||
- [Guide: Stubs, Spies and Clocks ](https://on.cypress.io/guides/stubs-spies-clocks)
|
||||
- [Recipe: Controlling Behavior with Spies, Stubs, and Clocks](https://github.com/cypress-io/cypress-example-recipes#controlling-behavior-with-spies-stubs-and-clocks)
|
||||
- [tick](https://on.cypress.io/api/tick)
|
||||
- [spy](https://on.cypress.io/api/spy)
|
||||
- [stub](https://on.cypress.io/api/stub)
|
||||
@@ -1,64 +0,0 @@
|
||||
slug: closest
|
||||
excerpt: Get the closest ancestor DOM element
|
||||
|
||||
Get the first DOM element that matches the selector whether it be itself or one of it's ancestors.
|
||||
|
||||
| | |
|
||||
|--- | --- |
|
||||
| **Returns** | the new DOM element(s) found by the command. |
|
||||
| **Timeout** | `cy.filter` will retry for the duration of the [`defaultCommandTimeout`](https://on.cypress.io/guides/configuration#section-timeouts) |
|
||||
|
||||
***
|
||||
|
||||
# [cy.closest( *selector* )](#section-selector-usage)
|
||||
|
||||
For each DOM element in the set, get the first DOM element that matches the selector by testing the DOM element itself and traversing up through its ancestors in the DOM tree.
|
||||
|
||||
***
|
||||
|
||||
# Options
|
||||
|
||||
Pass in an options object to change the default behavior of `cy.closest`.
|
||||
|
||||
**cy.closest( *selector*, *options* )**
|
||||
|
||||
Option | Default | Notes
|
||||
--- | --- | ---
|
||||
`log` | `true` | whether to display command in command log
|
||||
`timeout` | [`defaultCommandTimeout`](https://on.cypress.io/guides/configuration#section-timeouts) | Total time to retry getting the element
|
||||
|
||||
***
|
||||
|
||||
# Selector Usage
|
||||
|
||||
## Find the closest element of the current subject with the class `nav`
|
||||
|
||||
```javascript
|
||||
cy.get("li.active").closest(".nav")
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Command Log
|
||||
|
||||
## Find the closest element of the current subject with the class `nav`
|
||||
|
||||
```javascript
|
||||
cy.get("li.active").closest(".nav")
|
||||
```
|
||||
|
||||
The commands above will display in the command log as:
|
||||
|
||||
<img width="530" alt="screen shot 2015-11-27 at 2 07 28 pm" src="https://cloud.githubusercontent.com/assets/1271364/11447200/500fe9ca-9510-11e5-8c77-8afb8325d937.png">
|
||||
|
||||
When clicking on the `closest` command within the command log, the console outputs the following:
|
||||
|
||||
<img width="478" alt="screen shot 2015-11-27 at 2 07 46 pm" src="https://cloud.githubusercontent.com/assets/1271364/11447201/535515c4-9510-11e5-9cf5-088bf21f34ac.png">
|
||||
|
||||
***
|
||||
|
||||
# Related
|
||||
|
||||
- [parents](https://on.cypress.io/api/parents)
|
||||
- [next](https://on.cypress.io/api/next)
|
||||
- [first](https://on.cypress.io/api/first)
|
||||
@@ -1,373 +0,0 @@
|
||||
slug: contains
|
||||
excerpt: Get a DOM element that contains specific text
|
||||
|
||||
Get the DOM element containing the text. DOM elements can contain *more* than the desired text and still match. Additionally, Cypress will prefer some DOM elements over the deepest element found.
|
||||
|
||||
**Preference order:**
|
||||
|
||||
- `input[type='submit']`
|
||||
- `button`
|
||||
- `a`
|
||||
- `label`
|
||||
|
||||
| | |
|
||||
|--- | --- |
|
||||
| **Returns** | the deepest DOM element containing the text |
|
||||
| **Timeout** | `cy.contains` will retry for the duration of the [`defaultCommandTimeout`](https://on.cypress.io/guides/configuration#section-timeouts) |
|
||||
|
||||
***
|
||||
|
||||
# [cy.contains( *text* )](#section-text-usage)
|
||||
|
||||
Get the deepest DOM element containing the text.
|
||||
|
||||
***
|
||||
|
||||
# [cy.contains( *number* )](#section-number-usage)
|
||||
|
||||
Get the deepest DOM element containing the number.
|
||||
|
||||
***
|
||||
|
||||
# [cy.contains( *regexp* )](#section-regular-expression-usage)
|
||||
|
||||
Get the deepest DOM element containing the text matching the regular expression.
|
||||
|
||||
***
|
||||
|
||||
# [cy.contains( *selector*, *text* )](#section-selector-and-text-usage)
|
||||
|
||||
Specify a selector to filter DOM elements containing the text. Cypress will **ignore** it's default preference for the specified selector. Using a selector allows you to return more *shallow* elements in the tree which contain the specific text.
|
||||
|
||||
***
|
||||
|
||||
# Options
|
||||
|
||||
Pass in an options object to change the default behavior of `cy.contains`.
|
||||
|
||||
**cy.contains( *text*, *options* )**
|
||||
**cy.contains( *selector*, *text*, *options* )**
|
||||
|
||||
Option | Default | Notes
|
||||
--- | --- | ---
|
||||
`log` | `true` | whether to display command in command log
|
||||
`timeout` | [`defaultCommandTimeout`](https://on.cypress.io/guides/configuration#section-timeouts) | Total time to retry finding an element
|
||||
|
||||
***
|
||||
|
||||
# Text Usage
|
||||
|
||||
## Find the first element containing some text
|
||||
|
||||
```html
|
||||
<ul>
|
||||
<li>apples</li>
|
||||
<li>oranges</li>
|
||||
<li>bananas</li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
```javascript
|
||||
// returns <li>apples</li>
|
||||
cy.contains("apples")
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## Find the input[type='submit'] by value
|
||||
|
||||
```html
|
||||
<div id="main">
|
||||
<form>
|
||||
<div>
|
||||
<label>name</label>
|
||||
<input name="name" />
|
||||
</div>
|
||||
<div>
|
||||
<label>age</label>
|
||||
<input name="age" />
|
||||
</div>
|
||||
<input type="submit" value="submit the form!" />
|
||||
</form>
|
||||
</div>
|
||||
```
|
||||
|
||||
```javascript
|
||||
// get the form element
|
||||
// search inside its descendants for the content 'submit the form!'
|
||||
// find the input[type='submit'] element
|
||||
// click it
|
||||
cy.get("form").contains("submit the form!").click()
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## Favor of `button` over other deeper elements
|
||||
|
||||
```html
|
||||
<form>
|
||||
<button>
|
||||
<i class="fa fa-search"></i>
|
||||
<span>Search</span>
|
||||
</button>
|
||||
</form>
|
||||
```
|
||||
|
||||
```javascript
|
||||
// even though the <span> is the deepest element that contains: "Search"
|
||||
// Cypress will automatically favor button elements higher in the chain
|
||||
|
||||
// in this case the <button> is returned which is why we can now drill
|
||||
// into its children
|
||||
cy.contains("Search").children("i").should("have.class", "fa-search")
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## Favor of `a` over other deeper elements
|
||||
|
||||
```html
|
||||
<nav>
|
||||
<a href="/dashboard">
|
||||
<span>Dashboard</span>
|
||||
</a>
|
||||
<a href="/users">
|
||||
<span>Users</span>
|
||||
</a>
|
||||
<a href="/signout">
|
||||
<span>Sign Out</span>
|
||||
</a>
|
||||
</nav>
|
||||
```
|
||||
|
||||
```javascript
|
||||
// even though the <span> is the deepest element that contains: "Sign Out"
|
||||
// Cypress will automatically favor anchor elements higher in the chain
|
||||
|
||||
// in this case we can assert on the anchors properties
|
||||
cy.get("nav").contains("Sign Out").should("have.attr", "href", "/signout")
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## Favor of `label` over other deeper elements
|
||||
|
||||
```html
|
||||
<form>
|
||||
<label>
|
||||
<span>Name:</span>
|
||||
<input name="name" />
|
||||
</label>
|
||||
<label>
|
||||
<span>Age:</span>
|
||||
<input name="age" />
|
||||
</label>
|
||||
</form>
|
||||
```
|
||||
|
||||
```javascript
|
||||
// even though the <span> is the deepest element that contains: "Age"
|
||||
// Cypress will favor label elements higher in the chain
|
||||
|
||||
// additionally we can omit the colon as long as the element
|
||||
// at least contains the text 'Age'
|
||||
|
||||
cy.contains("Age").find("input").type("29")
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## Only the *first* matched element will be returned
|
||||
|
||||
```html
|
||||
<ul id="header">
|
||||
<li>Welcome, Jane Lane</li>
|
||||
</ul>
|
||||
<div id="main">
|
||||
<span>These users have 10 connections with Jane Lane</span>
|
||||
<ul>
|
||||
<li>User 1</li>
|
||||
<li>User 2</li>
|
||||
<li>User 3</li>
|
||||
</ul>
|
||||
</div>
|
||||
```
|
||||
|
||||
```javascript
|
||||
// this will return the <li> in the #header since that is the first
|
||||
// element that contains the text "Jane Lane"
|
||||
cy.contains("Jane Lane")
|
||||
|
||||
// if you want to select the <span> inside of #main instead
|
||||
// you need to scope the contains first
|
||||
|
||||
//now the <span> is returned
|
||||
cy.get("#main").contains("Jane Lane")
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Number Usage
|
||||
|
||||
## Find the first element containing some number
|
||||
|
||||
```html
|
||||
<button class="btn btn-primary" type="button">
|
||||
Messages <span class="badge">4</span>
|
||||
</button>
|
||||
```
|
||||
|
||||
```javascript
|
||||
// even though the <span> is the deepest element that contains: 4
|
||||
// Cypress will automatically favor button elements higher in the chain
|
||||
|
||||
// in this case the <button> is returned
|
||||
cy.contains(4)
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Regular Expression Usage
|
||||
|
||||
## Find the first element with text matching the regular expression
|
||||
|
||||
```html
|
||||
<ul>
|
||||
<li>apples</li>
|
||||
<li>oranges</li>
|
||||
<li>bananas</li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
```javascript
|
||||
// <li>bananas</li> is returned
|
||||
cy.contains(/^b\w+/)
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
|
||||
# Selector and Text Usage
|
||||
|
||||
## Specify a selector to return a specific element
|
||||
|
||||
```html
|
||||
<html>
|
||||
<body>
|
||||
<ul>
|
||||
<li>apples</li>
|
||||
<li>oranges</li>
|
||||
<li>bananas</li>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```javascript
|
||||
// technically the <html>, <body>, <ul>, and first <li> all contain "apples"
|
||||
|
||||
// normally Cypress would return the first <li> since that is the deepest
|
||||
// element that contains: "apples"
|
||||
|
||||
// to override this behavior, pass a 'ul' selector
|
||||
// this returns the ul element since it also contains the text
|
||||
|
||||
// returns <ul>...</ul>
|
||||
cy.contains("ul", "apples")
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Notes
|
||||
|
||||
## Dual command can be either parent or child
|
||||
|
||||
`cy.contains` is a dual command. This means it can act as both a `parent` and a `child` command. Read more about [issuing commands](https://on.cypress.io/guides/issuing-commands) if this is unfamiliar.
|
||||
|
||||
Because it is a dual command it can either *begin* a chain of commands or work off of an *existing* subject.
|
||||
|
||||
**Start a chain of commands**
|
||||
|
||||
```javascript
|
||||
// search from the root scope (default: document)
|
||||
cy.contains("some content")
|
||||
```
|
||||
|
||||
**Find content within an existing scope**
|
||||
|
||||
```javascript
|
||||
// search within an existing subject for the content
|
||||
// contains is now scoped to the <aside> element and will
|
||||
// only search its DOM descendants for the content
|
||||
cy.get("#main").find("aside").contains("Add a user")
|
||||
```
|
||||
|
||||
**Be wary of chaining multiple contains**
|
||||
|
||||
```javascript
|
||||
// let's imagine a scenario where you click a user's delete button
|
||||
// and a dialog appears asking you to confirm this deletion.
|
||||
|
||||
// the following will not work:
|
||||
cy
|
||||
.contains("Delete User").click()
|
||||
|
||||
// because this is chained off of the existing button subject
|
||||
// Cypress will look inside of the existing button subject
|
||||
// for the new content
|
||||
|
||||
// in other words Cypress will look inside of the element
|
||||
// containing "Delete User" for the content: "Yes I'm sure!"
|
||||
.contains("Yes, I'm sure!").click()
|
||||
|
||||
```
|
||||
|
||||
**End previous chains to get back to the root scope**
|
||||
|
||||
```javascript
|
||||
cy
|
||||
// explicitly .end() the previous chain
|
||||
.contains("Delete User").click().end()
|
||||
|
||||
// Cypress will now search the root scope
|
||||
// for this content (default: document)
|
||||
.contains("Yes, I'm sure!").click()
|
||||
```
|
||||
|
||||
```javascript
|
||||
// alternatively just call cy again which
|
||||
// automatically creates a new chain from the root scope
|
||||
cy.contains("Delete User").click()
|
||||
cy.contains("Yes I'm sure!").click()
|
||||
```
|
||||
|
||||
```javascript
|
||||
// you can also do this
|
||||
cy
|
||||
.contains("Delete User").click()
|
||||
|
||||
// by using the parent command .get() we automatically
|
||||
// abort previous chains and change the scope to #dialog
|
||||
// which contains the content we're looking for
|
||||
.get("#dialog").contains("Yes I'm sure!").click()
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Command Log
|
||||
|
||||
## Element contains text "New User"
|
||||
|
||||
<img width="536" alt="screen shot 2015-11-27 at 1 43 22 pm" src="https://cloud.githubusercontent.com/assets/1271364/11446973/009ac32c-950d-11e5-9eaa-09f8b8ddf086.png">
|
||||
|
||||
When clicking on the `contains` command within the command log, the console outputs the following:
|
||||
|
||||
<img width="477" alt="screen shot 2015-11-27 at 1 43 50 pm" src="https://cloud.githubusercontent.com/assets/1271364/11446977/04b31be4-950d-11e5-811e-4fd83d364d00.png">
|
||||
|
||||
***
|
||||
|
||||
# Related
|
||||
|
||||
- [get](https://on.cypress.io/api/get)
|
||||
- [within](https://on.cypress.io/api/within)
|
||||
- [root](https://on.cypress.io/api/root)
|
||||
@@ -1,68 +0,0 @@
|
||||
slug: dblclick
|
||||
excerpt: Double-click on a DOM element
|
||||
|
||||
Double-click on the DOM element in the previous command.
|
||||
|
||||
**The following events are fired during dblclick:** `dblclick`
|
||||
|
||||
| | |
|
||||
|--- | --- |
|
||||
| **Returns** | the new DOM element(s) found by the command. |
|
||||
| **Timeout** | `cy.dblclick` will retry for the duration of the [`defaultCommandTimeout`](https://on.cypress.io/guides/configuration#section-timeouts) |
|
||||
|
||||
***
|
||||
|
||||
# [cy.dblclick()](#section-usage)
|
||||
|
||||
Double-click the current subject.
|
||||
|
||||
***
|
||||
|
||||
# Options
|
||||
|
||||
Pass in an options object to change the default behavior of `cy.dblclick`.
|
||||
|
||||
**cy.dblclick(*options* )**
|
||||
|
||||
Option | Default | Notes
|
||||
--- | --- | ---
|
||||
`log` | `true` | whether to display command in command log
|
||||
|
||||
***
|
||||
|
||||
# Usage
|
||||
|
||||
## Double click an anchor link
|
||||
|
||||
```html
|
||||
<a href='#nav1'>Menu</a>
|
||||
```
|
||||
|
||||
```javascript
|
||||
// returns the <a> for further chaining
|
||||
cy.get("#nav1").dblclick()
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Command Log
|
||||
|
||||
## Double click on a calendar schedule
|
||||
|
||||
```javascript
|
||||
cy.get("[data-schedule-id='4529114']:first").dblclick()
|
||||
```
|
||||
|
||||
The commands above will display in the command log as:
|
||||
|
||||
<img width="585" alt="screen shot 2015-11-29 at 1 12 02 pm" src="https://cloud.githubusercontent.com/assets/1271364/11459013/035a6c5e-969b-11e5-935f-dce5c8efbdd6.png">
|
||||
|
||||
When clicking on `dblclick` within the command log, the console outputs the following:
|
||||
|
||||
<img width="836" alt="screen shot 2015-11-29 at 1 12 26 pm" src="https://cloud.githubusercontent.com/assets/1271364/11459015/0755e216-969b-11e5-9f7e-ed04245d75ef.png">
|
||||
|
||||
***
|
||||
|
||||
# Related
|
||||
|
||||
- [click](https://on.cypress.io/api/click)
|
||||
@@ -1,47 +0,0 @@
|
||||
slug: debug
|
||||
excerpt: Set a `debugger`
|
||||
|
||||
`cy.debug` sets a `debugger` and logs the subject from the previous command.
|
||||
|
||||
Make sure you have your Developer Tools open for `cy.debug` to hit the breakpoint.
|
||||
|
||||
| | |
|
||||
|--- | --- |
|
||||
| **Returns** | the subject from the previous command for further chaining. |
|
||||
| **Timeout** | *cannot timeout* |
|
||||
|
||||
***
|
||||
|
||||
# [cy.debug()](#section-usage)
|
||||
|
||||
Debug the previous command.
|
||||
|
||||
***
|
||||
|
||||
# Options
|
||||
|
||||
Pass in an options object to change the default behavior of `cy.debug`.
|
||||
|
||||
**cy.debug(*options* )**
|
||||
|
||||
Option | Default | Notes
|
||||
--- | --- | ---
|
||||
`log` | `true` | whether to display command in command log
|
||||
|
||||
***
|
||||
|
||||
# Usage
|
||||
|
||||
## Log out the current subject for debugging
|
||||
|
||||
```javascript
|
||||
// Cypress will log out the current subject and other
|
||||
// useful debugging information to your console
|
||||
cy.get("a").debug().should("have.attr", "href")
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Related
|
||||
|
||||
- [pause](https://on.cypress.io/api/pause)
|
||||
@@ -1,69 +0,0 @@
|
||||
slug: document
|
||||
excerpt: Get the document
|
||||
|
||||
Get the document and work with its properties or methods.
|
||||
|
||||
| | |
|
||||
|--- | --- |
|
||||
| **Returns** | the `window.document` object |
|
||||
| **Timeout** | `cy.document` will retry for the duration of the [`defaultCommandTimeout`](https://on.cypress.io/guides/configuration#section-timeouts) |
|
||||
|
||||
***
|
||||
|
||||
# [cy.document()](#section-usage)
|
||||
|
||||
Get the document.
|
||||
|
||||
***
|
||||
|
||||
# Options
|
||||
|
||||
Pass in an options object to change the default behavior of `cy.document`.
|
||||
|
||||
**cy.document(*options* )**
|
||||
|
||||
Option | Default | Notes
|
||||
--- | --- | ---
|
||||
`log` | `true` | whether to display command in command log
|
||||
|
||||
***
|
||||
|
||||
# Usage
|
||||
|
||||
## Get document and do some work
|
||||
|
||||
```javascript
|
||||
cy.document().then(function(document) {
|
||||
// work with document element
|
||||
});
|
||||
```
|
||||
|
||||
## Make an assertion about the document
|
||||
|
||||
```javascript
|
||||
cy.document().its("contentType").should("eq", "text/html")
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Command Log
|
||||
|
||||
## Get the document
|
||||
|
||||
```javascript
|
||||
cy.document()
|
||||
```
|
||||
|
||||
The commands above will display in the command log as:
|
||||
|
||||
<img width="588" alt="screen shot 2015-11-29 at 2 00 09 pm" src="https://cloud.githubusercontent.com/assets/1271364/11459311/aab8fe88-96a1-11e5-9b72-b0501204030d.png">
|
||||
|
||||
When clicking on `document` within the command log, the console outputs the following:
|
||||
|
||||
<img width="491" alt="screen shot 2015-11-29 at 2 00 22 pm" src="https://cloud.githubusercontent.com/assets/1271364/11459314/ad27d7e8-96a1-11e5-8d1c-9c4ede6c54aa.png">
|
||||
|
||||
***
|
||||
|
||||
# Related
|
||||
|
||||
- [window](https://on.cypress.io/api/window)
|
||||
@@ -1,89 +0,0 @@
|
||||
slug: each
|
||||
excerpt: Interate over an array-like structure
|
||||
|
||||
The `cy.each()` will iterate through an array like structure (arrays and objects with a `length` property).
|
||||
|
||||
Each time the callback function runs, it is invoked with three arguments: `value`, `index`, and `collection`.
|
||||
|
||||
If your callback function returns a `Promise` it will be awaited before iterating over the next element in the collection.
|
||||
|
||||
You can stop the loop early by returning `false` in the callback function.
|
||||
|
||||
| | |
|
||||
|--- | --- |
|
||||
| **Returns** | the original array subject given to `cy.each()` |
|
||||
| **Timeout** | `cy.each` will retry for the duration of the [`defaultCommandTimeout`](https://on.cypress.io/guides/configuration#section-timeouts) |
|
||||
|
||||
***
|
||||
|
||||
# [cy.each( *function* )](#section-usage)
|
||||
|
||||
Iterate over an array-like structure.
|
||||
|
||||
***
|
||||
|
||||
# Usage
|
||||
|
||||
## Iterate over an array of DOM elements
|
||||
|
||||
```javascript
|
||||
cy
|
||||
.get("ul>li")
|
||||
.each(function($el, index, $list){
|
||||
// $el is wrapped jquery element
|
||||
if ($el.someMethod() === "something") {
|
||||
// wrap this element so we can
|
||||
// use cypress commands on it
|
||||
cy.wrap($el).click()
|
||||
} else {
|
||||
// do something else
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## Promises are awaited
|
||||
|
||||
```javascript
|
||||
cy
|
||||
.wrap([1,2,3])
|
||||
.each(function(num, index, array){
|
||||
// promises returned are automatically
|
||||
// awaited on before calling the next item
|
||||
return new Cypress.Promise(function(resolve){
|
||||
setTimeout(function(){
|
||||
resolve()
|
||||
}, num * 100)
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
## The original subject is always returned
|
||||
|
||||
```javascript
|
||||
cy
|
||||
.get("li").should("have.length", 3)
|
||||
.each(function($li, index, $lis){
|
||||
// no matter what we return here
|
||||
// the next subject will still
|
||||
// be the collection of <li>
|
||||
return "something else"
|
||||
})
|
||||
.then(function($lis){
|
||||
expect($lis).to.have.length(3) // true
|
||||
})
|
||||
```
|
||||
|
||||
# Errors
|
||||
|
||||
## cy.each() can only operate on an array like subject.
|
||||
|
||||
This error occurs when the subject passed to `cy.each()` does not have a `length` property. Ensure the subject passed to `cy.each()` is an array-like structure.
|
||||
|
||||
***
|
||||
|
||||
# Related
|
||||
|
||||
- [spread](https://on.cypress.io/api/spread)
|
||||
- [then](https://on.cypress.io/api/then)
|
||||
@@ -1,52 +0,0 @@
|
||||
slug: end
|
||||
excerpt: End the command chain
|
||||
|
||||
Ends the Cypress command chain and returns `null`. This is equivalent to the jQuery `end()` method.
|
||||
|
||||
| | |
|
||||
|--- | --- |
|
||||
| **Returns** | `null` |
|
||||
| **Timeout** | cannot timeout |
|
||||
|
||||
***
|
||||
|
||||
# [cy.end()](#section-usage)
|
||||
|
||||
End the command chain.
|
||||
|
||||
***
|
||||
|
||||
# Usage
|
||||
|
||||
```javascript
|
||||
// cy.end is useful when you want to end a chain of commands
|
||||
// and force Cypress to re-query from the root element
|
||||
cy
|
||||
.contains("User: Cheryl").click().end() // ends the current chain and returns null
|
||||
|
||||
// queries the entire document again
|
||||
.contains("User: Charles").click()
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Command Log
|
||||
|
||||
## `end` does *not* log in the command log
|
||||
|
||||
```javascript
|
||||
cy
|
||||
.contains(".modal-title", "Select Folder Type").end()
|
||||
.contains("li", "Maintenance").should("have.class", "active")
|
||||
```
|
||||
|
||||
The commands above will display in the command log as:
|
||||
|
||||

|
||||
|
||||
***
|
||||
|
||||
# Related
|
||||
|
||||
- [root](https://on.cypress.io/api/root)
|
||||
- [within](https://on.cypress.io/api/within)
|
||||
@@ -1,100 +0,0 @@
|
||||
slug: eq
|
||||
excerpt: Get a DOM element at a specific index
|
||||
|
||||
Get a DOM element in an array of elements at the specific index.
|
||||
|
||||
| | |
|
||||
|--- | --- |
|
||||
| **Returns** | the new DOM element(s) found by the command. |
|
||||
| **Timeout** | `cy.eq` will retry for the duration of the [`defaultCommandTimeout`](https://on.cypress.io/guides/configuration#section-timeouts) |
|
||||
|
||||
***
|
||||
|
||||
# [cy.eq( *index* )](#section-index-usage)
|
||||
|
||||
Reduce the set of matched DOM elements to the one at the specified index.
|
||||
|
||||
***
|
||||
|
||||
# [cy.eq( *indexFromEnd* )](#section-index-from-end-usage)
|
||||
|
||||
Providing a negative number indicates a position starting from the end of the set, rather than the beginning.
|
||||
|
||||
***
|
||||
|
||||
# Options
|
||||
|
||||
Pass in an options object to change the default behavior of `cy.eq`.
|
||||
|
||||
**cy.eq( *index*, *options* )**
|
||||
**cy.eq( *indexFromEnd*, *options* )**
|
||||
|
||||
Option | Default | Notes
|
||||
--- | --- | ---
|
||||
`log` | `true` | whether to display command in command log
|
||||
`timeout` | [`defaultCommandTimeout`](https://on.cypress.io/guides/configuration#section-timeouts) | Total time to retry getting the element
|
||||
|
||||
***
|
||||
|
||||
# Index Usage
|
||||
|
||||
## Find the 2nd element within the elements
|
||||
|
||||
```html
|
||||
<ul>
|
||||
<li>tabby</li>
|
||||
<li>siamese</li>
|
||||
<li>persian</li>
|
||||
<li>sphynx</li>
|
||||
<li>burmese</li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
```javascript
|
||||
cy.get("li").eq(1).should("contain", "siamese") // true
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Index Form End Usage
|
||||
|
||||
## Find the 2nd from the last element within the elements
|
||||
|
||||
```html
|
||||
<ul>
|
||||
<li>tabby</li>
|
||||
<li>siamese</li>
|
||||
<li>persian</li>
|
||||
<li>sphynx</li>
|
||||
<li>burmese</li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
```javascript
|
||||
cy.get("li").eq(-2).should("contain", "sphynx") // true
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Command Log
|
||||
|
||||
## Find the 4th `li` in the navigation
|
||||
|
||||
```javascript
|
||||
cy.get(".left-nav.nav").find(">li").eq(3)
|
||||
```
|
||||
|
||||
The commands above will display in the command log as:
|
||||
|
||||
<img width="532" alt="screen shot 2015-11-27 at 2 11 47 pm" src="https://cloud.githubusercontent.com/assets/1271364/11447231/e225e1f2-9510-11e5-8615-4a5b42ef71c1.png">
|
||||
|
||||
When clicking on the `eq` command within the command log, the console outputs the following:
|
||||
|
||||
<img width="569" alt="screen shot 2015-11-27 at 2 12 03 pm" src="https://cloud.githubusercontent.com/assets/1271364/11447234/e594ce52-9510-11e5-8794-712a7dbeae55.png">
|
||||
|
||||
***
|
||||
|
||||
# Related
|
||||
|
||||
- [first](https://on.cypress.io/api/first)
|
||||
- [last](https://on.cypress.io/api/last)
|
||||
@@ -1,124 +0,0 @@
|
||||
slug: exec
|
||||
excerpt: Execute a system command
|
||||
|
||||
Allows you to execute a system command. The system command can be anything you would normally run on the command line, such as `npm run build`, `rake db:seed`, etc.
|
||||
|
||||
`cy.exec` provides an escape hatch for running arbitrary system commands, so you can take actions necessary for your test, but outside the scope of Cypress. This is great for running build scripts, seeding your test database, starting or killing processes, etc.
|
||||
|
||||
`cy.exec` does not support commands that don't exit, such as `rails server`, a task that runs a watch, or any process that needs to be manually interrupted to stop. A command must exit within the timeout or Cypress will kill the command's process and fail the current test.
|
||||
|
||||
We don't recommend executing commands that take a long time to exit. Cypress will not continue running any other commands until `cy.exec` has finished, so a long-running command will drastically slow down your test cycle.
|
||||
|
||||
The current working directory is set to the project root (the directory that contains cypress.json).
|
||||
|
||||
| | |
|
||||
|--- | --- |
|
||||
| **Returns** | an object with the exit `code`, the `stdout`, and the `stderr` |
|
||||
| **Timeout** | `cy.exec` will allow the command to execute for the duration of the [`execTimeout`](https://on.cypress.io/guides/configuration#section-timeouts) |
|
||||
|
||||
***
|
||||
|
||||
# [cy.exec( *command* )](#section-command-usage)
|
||||
|
||||
Execute a system command.
|
||||
|
||||
***
|
||||
|
||||
# Options
|
||||
|
||||
Pass in an options object to change the default behavior of `cy.exec`.
|
||||
|
||||
**cy.exec( *command*, *options* )**
|
||||
|
||||
Option | Default | Notes
|
||||
--- | --- | ---
|
||||
`log` | `true` | whether to display command in command log
|
||||
`timeout` | [`execTimeout`](https://on.cypress.io/guides/configuration#section-timeouts) | Total time to allow the command to execute
|
||||
`failOnNonZeroExit` | `true` | Fail if the command exits with a non-zero code
|
||||
`env` | `{}` | Object of environment variables to set before the command executes (e.g. { USERNAME: 'johndoe' }). Will be merged with existing system environment variables
|
||||
|
||||
***
|
||||
|
||||
# Usage
|
||||
|
||||
## Run a build command
|
||||
|
||||
```javascript
|
||||
cy
|
||||
.exec("npm run build")
|
||||
.then(function (result) {
|
||||
// subject is now the result object
|
||||
// {
|
||||
// code: 0,
|
||||
// stdout: "Files successfully built",
|
||||
// stderr: ""
|
||||
// }
|
||||
})
|
||||
```
|
||||
|
||||
## Seed the database and assert it was successful
|
||||
|
||||
```javascript
|
||||
cy.exec("rake db:seed").its("code").should("eq", 0)
|
||||
```
|
||||
|
||||
## Run an arbitrary script and assert its output
|
||||
|
||||
```javascript
|
||||
cy.exec("npm run my-script").its("stdout").should("contain", "Done running the script")
|
||||
```
|
||||
|
||||
## Change the timeout
|
||||
|
||||
```javascript
|
||||
// will fail if script takes longer than 20 seconds to finish
|
||||
cy.exec("npm run build", { timeout: 20000 });
|
||||
```
|
||||
|
||||
## Choose not to fail on non-zero exit and assert on code and stderr
|
||||
|
||||
```javascript
|
||||
cy
|
||||
.exec("man bear pig", { failOnNonZeroExit: false })
|
||||
.its("code").should("eq", 1)
|
||||
.its("stderr").should("contain", "No manual entry for bear")
|
||||
```
|
||||
|
||||
## Specify environment variables
|
||||
|
||||
```javascript
|
||||
cy
|
||||
.exec("echo $USERNAME", { env: { USERNAME: "johndoe" } })
|
||||
.its("stdout").should("contain", "johndoe")
|
||||
```
|
||||
|
||||
## Write to a file to create a fixture from response body
|
||||
```javascript
|
||||
cy
|
||||
.server()
|
||||
.route("POST", "/comments").as("postComment")
|
||||
.get(".add-comment").click()
|
||||
.wait("@postComment").then(function(xhr){
|
||||
cy
|
||||
.exec("echo '" + JSON.stringify(xhr.responseBody) + "'>cypress/fixtures/comment.json")
|
||||
.fixture("comment.json").should("deep.eq", xhr.responseBody)
|
||||
})
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Command Log
|
||||
|
||||
## List the contents of cypress.json
|
||||
|
||||
```javascript
|
||||
cy.exec("cat cypress.json")
|
||||
```
|
||||
|
||||
The command above will display in the command log as:
|
||||
|
||||
<img width="445" alt="screen shot of command log" src="https://cloud.githubusercontent.com/assets/1157043/15369507/e03a7eca-1d00-11e6-8558-396d8c9b6d98.png">
|
||||
|
||||
When clicking on the `exec` command within the command log, the console outputs the following:
|
||||
|
||||
<img width="758" alt="screen shot of console output" src="https://cloud.githubusercontent.com/assets/1157043/15969867/e3ab646e-2eff-11e6-9199-987ca2f74025.png">
|
||||
@@ -1,93 +0,0 @@
|
||||
slug: blur
|
||||
excerpt: Blur a DOM element
|
||||
|
||||
Make the DOM element found in the previous command lose focus.
|
||||
|
||||
**The following events are fired during blur:** `focusout`, `blur`
|
||||
|
||||
| | |
|
||||
|--- | --- |
|
||||
| **Returns** | the new DOM element(s) found by the command. |
|
||||
| **Timeout** | `cy.blur` will retry for the duration of the [`defaultCommandTimeout`](https://on.cypress.io/guides/configuration#section-timeouts) |
|
||||
|
||||
***
|
||||
|
||||
# [cy.blur()](#section-usage)
|
||||
|
||||
Blur the DOM element from the previous command.
|
||||
|
||||
***
|
||||
|
||||
# Options
|
||||
|
||||
Pass in an options object to change the default behavior of `cy.blur`.
|
||||
|
||||
**[cy.blur( *options* )](#options-usage)**
|
||||
|
||||
Option | Default | Notes
|
||||
--- | --- | ---
|
||||
`force` | `false` | Forces blur, disables error checking prior to blur
|
||||
`log` | `true` | whether to display command in command log
|
||||
|
||||
***
|
||||
|
||||
# Usage
|
||||
|
||||
## Blur the comment input.
|
||||
|
||||
```javascript
|
||||
// returns the same <textarea> for further chaining
|
||||
cy.get("[name='comment']").type("Nice Product!").blur()
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Options Usage
|
||||
|
||||
## Blur the first input, ignoring whether the input is currently focused.
|
||||
|
||||
```javascript
|
||||
// returns the same <input> for further chaining
|
||||
cy.get("input:first").blur({force: true})
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Command Log
|
||||
|
||||
## Blur a textarea after typing.
|
||||
|
||||
```javascript
|
||||
cy.get("[name='comment']").type("Nice Product!").blur()
|
||||
```
|
||||
|
||||
The commands above will display in the command log as:
|
||||
|
||||
<img width="524" alt="screen shot 2015-11-27 at 1 37 36 pm" src="https://cloud.githubusercontent.com/assets/1271364/11446921/58a14e34-950c-11e5-85ba-633b7ed5d7f1.png">
|
||||
|
||||
When clicking on the `blur` command within the command log, the console outputs the following:
|
||||
|
||||
<img width="525" alt="screen shot 2015-11-27 at 1 37 53 pm" src="https://cloud.githubusercontent.com/assets/1271364/11446923/5c44a2ca-950c-11e5-8080-0dc108bc4959.png">
|
||||
|
||||
***
|
||||
|
||||
# Errors
|
||||
|
||||
## cy.blur() can only be called when there is a currently focused element.
|
||||
|
||||
There is currently no specific element that has focus. If you want to ensure focus before blurring, try using `cy.focus()` on the element before `cy.blur()`
|
||||
|
||||
## cy.blur() timed out because your browser did not receive any blur events. This is a known bug in Chrome when it is not the currently focused window.
|
||||
|
||||
If you see this error, you may want to ensure that the main browser window is currently focused. This means not being focused in debugger or any other window when the command is executed.
|
||||
|
||||
## cy.blur() can only be called on the focused element.
|
||||
|
||||
If you want to ensure focus on a specific element before blurring, try using `cy.focus()` on the element before `cy.blur()`
|
||||
|
||||
***
|
||||
|
||||
# Related
|
||||
|
||||
- [focused](https://on.cypress.io/api/focused)
|
||||
- [focus](https://on.cypress.io/api/focus)
|
||||
@@ -1,71 +0,0 @@
|
||||
slug: filter
|
||||
excerpt: Filter DOM elements by a selector
|
||||
|
||||
Get DOM elements that match a specific selector. Opposite of [`cy.not()`](https://on.cypress.io/api/not)
|
||||
|
||||
| | |
|
||||
|--- | --- |
|
||||
| **Returns** | the new DOM element(s) found by the command. |
|
||||
| **Timeout** | `cy.filter` will retry for the duration of the [`defaultCommandTimeout`](https://on.cypress.io/guides/configuration#section-timeouts) |
|
||||
|
||||
***
|
||||
|
||||
# [cy.filter( *selector* )](#section-selector-usage)
|
||||
|
||||
Reduce the set of matched DOM elements to those that match the selector.
|
||||
|
||||
***
|
||||
|
||||
# Options
|
||||
|
||||
Pass in an options object to change the default behavior of `cy.filter`.
|
||||
|
||||
**cy.filter( *selector*, *options* )**
|
||||
|
||||
Option | Default | Notes
|
||||
--- | --- | ---
|
||||
`log` | `true` | whether to display command in command log
|
||||
`timeout` | [`defaultCommandTimeout`](https://on.cypress.io/guides/configuration#section-timeouts) | Total time to retry getting the element
|
||||
|
||||
***
|
||||
|
||||
# Selector Usage
|
||||
|
||||
## Filter the current subject to the element with the class `active`.
|
||||
|
||||
```html
|
||||
<ul>
|
||||
<li>Home</li>
|
||||
<li class="active">About</li>
|
||||
<li>Services</li>
|
||||
<li>Pricing</li>
|
||||
<li>Contact</li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
```javascript
|
||||
// returns <li>About</li>
|
||||
cy.get("ul").find(">li").filter(".active")
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Command Log
|
||||
|
||||
## Filter the `li`'s to the `li` with the class `active`.
|
||||
|
||||
```javascript
|
||||
cy.get(".left-nav>.nav").find(">li").filter(".active")
|
||||
```
|
||||
|
||||
The commands above will display in the command log as:
|
||||
|
||||
<img width="522" alt="screen shot 2015-11-27 at 2 15 53 pm" src="https://cloud.githubusercontent.com/assets/1271364/11447263/7176e824-9511-11e5-93cc-fa10b3b94482.png">
|
||||
|
||||
When clicking on the `filter` command within the command log, the console outputs the following:
|
||||
|
||||
<img width="503" alt="screen shot 2015-11-27 at 2 16 09 pm" src="https://cloud.githubusercontent.com/assets/1271364/11447266/74b643a4-9511-11e5-8b42-6f6dfbdfb2a8.png">
|
||||
|
||||
# Related
|
||||
|
||||
- [not](https://on.cypress.io/api/not)
|
||||
@@ -1,70 +0,0 @@
|
||||
slug: find
|
||||
excerpt: Get descendants of DOM elements
|
||||
|
||||
Get the descendents DOM elements of a specific selector.
|
||||
|
||||
| | |
|
||||
|--- | --- |
|
||||
| **Returns** | the new DOM element(s) found by the command. |
|
||||
| **Timeout** | `cy.find` will retry for the duration of the [`defaultCommandTimeout`](https://on.cypress.io/guides/configuration#section-timeouts) |
|
||||
|
||||
***
|
||||
|
||||
# [cy.find( *selector* )](#section-selector-usage)
|
||||
|
||||
Get the descendants of each DOM element in the current set of matched DOM elements within the selector.
|
||||
|
||||
***
|
||||
|
||||
# Options
|
||||
|
||||
Pass in an options object to change the default behavior of `cy.find`.
|
||||
|
||||
**cy.find( *selector*, *options* )**
|
||||
|
||||
Option | Default | Notes
|
||||
--- | --- | ---
|
||||
`log` | `true` | whether to display command in command log
|
||||
`timeout` | [`defaultCommandTimeout`](https://on.cypress.io/guides/configuration#section-timeouts) | Total time to retry getting the element
|
||||
|
||||
***
|
||||
|
||||
# Selector Usage
|
||||
|
||||
## Get li's within parent
|
||||
|
||||
```html
|
||||
<ul id="parent">
|
||||
<li class="first"></li>
|
||||
<li class="second"></li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
```javascript
|
||||
// returns [<li class="first"></li>, <li class="second"></li>]
|
||||
cy.get("#parent").find("li")
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Command Log
|
||||
|
||||
## Find the `li`'s within the nav
|
||||
|
||||
```javascript
|
||||
cy.get(".left-nav>.nav").find(">li")
|
||||
```
|
||||
|
||||
The commands above will display in the command log as:
|
||||
|
||||
<img width="522" alt="screen shot 2015-11-27 at 2 19 38 pm" src="https://cloud.githubusercontent.com/assets/1271364/11447309/f6a9be4a-9511-11e5-84a5-a111215bf1e6.png">
|
||||
|
||||
When clicking on the `find` command within the command log, the console outputs the following:
|
||||
|
||||
<img width="516" alt="screen shot 2015-11-27 at 2 19 54 pm" src="https://cloud.githubusercontent.com/assets/1271364/11447312/fa3679cc-9511-11e5-9bea-904f8c70063d.png">
|
||||
|
||||
***
|
||||
|
||||
# Related
|
||||
|
||||
- [get](https://on.cypress.io/api/get)
|
||||
@@ -1,72 +0,0 @@
|
||||
slug: first
|
||||
excerpt: Get the first DOM element within a set of DOM elements
|
||||
|
||||
Get the first DOM element within a set of DOM elements.
|
||||
|
||||
| | |
|
||||
|--- | --- |
|
||||
| **Returns** | the new DOM element(s) found by the command. |
|
||||
| **Timeout** | `cy.first` will retry for the duration of the [`defaultCommandTimeout`](https://on.cypress.io/guides/configuration#section-timeouts) |
|
||||
|
||||
***
|
||||
|
||||
# [cy.first()](#section-usage)
|
||||
|
||||
Reduce the set of matched DOM elements to the first in the set.
|
||||
|
||||
***
|
||||
|
||||
# Options
|
||||
|
||||
Pass in an options object to change the default behavior of `cy.first`.
|
||||
|
||||
**cy.first(*options* )**
|
||||
|
||||
Option | Default | Notes
|
||||
--- | --- | ---
|
||||
`log` | `true` | whether to display command in command log
|
||||
`timeout` | [`defaultCommandTimeout`](https://on.cypress.io/guides/configuration#section-timeouts) | Total time to retry getting the element
|
||||
|
||||
***
|
||||
|
||||
# Usage
|
||||
|
||||
## Get the first list item in a list.
|
||||
|
||||
```html
|
||||
<ul>
|
||||
<li class="one">Knick knack on my thumb</li>
|
||||
<li class="two">Knick knack on my shoe</li>
|
||||
<li class="three">Knick knack on my knee</li>
|
||||
<li class="four">Knick knack on my door</li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
```javascript
|
||||
// returns <li class="one">Knick knack on my thumb</li>
|
||||
cy.get("ul").first()
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Command Log
|
||||
|
||||
## Find the first `input` in the `form`
|
||||
|
||||
```javascript
|
||||
cy.get("form").find("input").first()
|
||||
```
|
||||
|
||||
The commands above will display in the command log as:
|
||||
|
||||
<img width="527" alt="screen shot 2015-11-29 at 12 28 08 pm" src="https://cloud.githubusercontent.com/assets/1271364/11458770/d9439ee6-9694-11e5-8754-b2641ba44883.png">
|
||||
|
||||
When clicking on `first` within the command log, the console outputs the following:
|
||||
|
||||
<img width="616" alt="screen shot 2015-11-29 at 12 28 23 pm" src="https://cloud.githubusercontent.com/assets/1271364/11458771/db8cb516-9694-11e5-86c2-cf3bbb9a666d.png">
|
||||
|
||||
***
|
||||
|
||||
# Related
|
||||
|
||||
- [last](https://on.cypress.io/api/last)
|
||||
@@ -1,267 +0,0 @@
|
||||
slug: fixture
|
||||
excerpt: Load a fixture to represent data
|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "info",
|
||||
"body": "[Read about Creating Fixtures first.](https://on.cypress.io/guides/creating-fixtures)",
|
||||
"title": "New to Cypress?"
|
||||
}
|
||||
[/block]
|
||||
|
||||
Loads a single fixture file. Image fixtures will be sent as `base64`.
|
||||
|
||||
If an extension is omitted, Cypress will attempt to resolve the fixture by order of these extensions:
|
||||
|
||||
* `.json`
|
||||
* `.js`
|
||||
* `.coffee`
|
||||
* `.html`
|
||||
* `.txt`
|
||||
* `.csv`
|
||||
* `.png`
|
||||
* `.jpg`
|
||||
* `.jpeg`
|
||||
* `.gif`
|
||||
* `.tif`
|
||||
* `.tiff`
|
||||
* `.zip`
|
||||
|
||||
| | |
|
||||
|--- | --- |
|
||||
| **Returns** | the contents of the file, formatted by file extension |
|
||||
| **Timeout** | `cy.fixture` will wait up for the duration of [`responseTimeout`](https://on.cypress.io/guides/configuration#section-timeouts) for the server to process this command. |
|
||||
|
||||
***
|
||||
|
||||
# [cy.fixture( *fixture* )](#section-single-fixture-usage)
|
||||
|
||||
Loads the fixture at the specified filepath within the [`fixturesFolder`](https://on.cypress.io/guides/configuration#section-folders), which defaults to `cypress/fixtures`.
|
||||
|
||||
***
|
||||
|
||||
# [cy.fixture( *fixture*, *encoding* )](#section-encoding)
|
||||
|
||||
Loads the fixture at the specified filepath within the [`fixturesFolder`](https://on.cypress.io/guides/configuration#section-folders), which defaults to `cypress/fixtures`, using the encoding specified when reading the file.
|
||||
|
||||
***
|
||||
|
||||
# Options
|
||||
|
||||
Pass in an options object to change the default behavior of `cy.fixture`.
|
||||
|
||||
**[cy.fixture( *fixture*, *options* )](#options-usage)**
|
||||
|
||||
**[cy.fixture( *fixture*, *encoding*, *options* )](#options-usage)**
|
||||
|
||||
Option | Default | Notes
|
||||
--- | --- | ---
|
||||
`timeout` | [`responseTimeout`](https://on.cypress.io/guides/configuration#section-timeouts) | Total time to wait for the `cy.fixture` command to be processed
|
||||
|
||||
***
|
||||
|
||||
# Single Fixture Usage
|
||||
|
||||
## Load the `users.json` fixture
|
||||
|
||||
```javascript
|
||||
cy.fixture("users.json")
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## Omit the fixture file's extension
|
||||
|
||||
```javascript
|
||||
cy.fixture("admin")
|
||||
```
|
||||
|
||||
When no extension is passed to `cy.fixture`, Cypress will search for files with the specified name within the [`fixturesFolder`](https://on.cypress.io/guides/configuration#section-folders), which defaults to `cypress/fixtures`, and resolve the first one. The above example would resolve in the following order:
|
||||
|
||||
1. `{fixturesFolder}/admin.json`
|
||||
2. `{fixturesFolder}/admin.js`
|
||||
3. `{fixturesFolder}/admin.coffee`
|
||||
4. `{fixturesFolder}/admin.html`
|
||||
5. `{fixturesFolder}/admin.txt`
|
||||
6. `{fixturesFolder}/admin.csv`
|
||||
7. `{fixturesFolder}/admin.png`
|
||||
8. `{fixturesFolder}/admin.jpg`
|
||||
9. `{fixturesFolder}/admin.jpeg`
|
||||
10. `{fixturesFolder}/admin.gif`
|
||||
11. `{fixturesFolder}/admin.tif`
|
||||
12. `{fixturesFolder}/admin.tiff`
|
||||
13. `{fixturesFolder}/admin.zip`
|
||||
|
||||
***
|
||||
|
||||
## Image fixtures will be sent by default as `base64`
|
||||
|
||||
```javascript
|
||||
cy.fixture("images/logo.png").then(function(logo){
|
||||
// logo will be encoded as base64
|
||||
// and should look something like this:
|
||||
// aIJKnwxydrB10NVWqhlmmC+ZiWs7otHotSAAAOw==...
|
||||
})
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## Change encoding of Image fixture
|
||||
|
||||
```javascript
|
||||
cy.fixture("images/logo.png", "binary").then(function(logo){
|
||||
// logo will be encoded as binary
|
||||
// and should look something like this:
|
||||
// 000000000000000000000000000000000000000000...
|
||||
})
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Notes
|
||||
|
||||
## Nesting
|
||||
|
||||
You can nest fixtures within folders and reference them by defining the path to the file:
|
||||
|
||||
`{fixturesFolder}/users/admin.json`
|
||||
|
||||
```javascript
|
||||
cy.fixture("users/admin.json")
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## Validation
|
||||
|
||||
Cypress will automatically validate your fixtures. If your `.json`, `.js`, or `.coffee` files contain syntax errors, they will automatically be shown in the Command Log.
|
||||
|
||||
***
|
||||
|
||||
## Formatting
|
||||
|
||||
Cypress automatically formats your fixture files. That means you can paste in a single line of `json` and the next time Cypress serves this fixture, it will format / indent the `json`, which makes it easier to read and debug.
|
||||
|
||||
***
|
||||
|
||||
## Encoding
|
||||
|
||||
Cypress automatically determines the encoding for the following file types:
|
||||
|
||||
* `.json`
|
||||
* `.js`
|
||||
* `.coffee`
|
||||
* `.html`
|
||||
* `.txt`
|
||||
* `.csv`
|
||||
* `.png`
|
||||
* `.jpg`
|
||||
* `.jpeg`
|
||||
* `.gif`
|
||||
* `.tif`
|
||||
* `.tiff`
|
||||
* `.zip`
|
||||
|
||||
For other types of files, they will be read as `utf8` by default. You can specify a different encoding by passing it as the [second argument](https://on.cypress.io/api/fixture#section--cy-fixture-fixture-encoding-section-encoding-).
|
||||
|
||||
```javascript
|
||||
cy.fixture("foo.bmp", "base64")
|
||||
```
|
||||
|
||||
The following encodings are supported:
|
||||
|
||||
* `ascii`
|
||||
* `base64`
|
||||
* `binary`
|
||||
* `hex`
|
||||
* `latin1`
|
||||
* `utf8`
|
||||
* `utf-8`
|
||||
* `ucs2`
|
||||
* `ucs-2`
|
||||
* `utf16le`
|
||||
* `utf-16le`
|
||||
|
||||
***
|
||||
|
||||
# Usage with `cy.route()`
|
||||
|
||||
## Using fixture or fx shortcuts
|
||||
|
||||
Fixtures can be referenced directly by the special keywords: `fixture:` or `fx:`.
|
||||
|
||||
This enables you to set a fixture as the response for a route without having to first use the `cy.fixture` command.
|
||||
|
||||
```javascript
|
||||
cy.route("GET", /users/, "fixture:users") // this works
|
||||
cy.route("GET", /users/, "fx:users") // this also works
|
||||
```
|
||||
|
||||
This saves you from having to explicitly load the fixture first (like [below](https://on.cypress.io/api/fixture#section-using-cy-then-to-access-fixture-data)).
|
||||
|
||||
## Using cy.then to access fixture data
|
||||
|
||||
```javascript
|
||||
cy
|
||||
.fixture("users").then(function(json){
|
||||
cy.route("GET", /users/, json)
|
||||
})
|
||||
```
|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "info",
|
||||
"body": "[Check out our example recipe using cy.fixture to bootstrap data for our application.](https://github.com/cypress-io/cypress-example-recipes/blob/master/cypress/integration/bootstrapping_app_test_data_spec.js)",
|
||||
"title": "Using cy.fixture for response data"
|
||||
}
|
||||
[/block]
|
||||
|
||||
## Using an alias to access a fixture
|
||||
|
||||
However if you still need access to the fixture data, instead of [yielding the fixture's data](https://on.cypress.io/api/fixture#section-using-cy-then-to-access-fixture-data), we can make use of [aliasing](https://on.cypress.io/guides/using-aliases).
|
||||
|
||||
Using an alias provides the benefit of terseness and readability.
|
||||
|
||||
```javascript
|
||||
cy
|
||||
.fixture("users").as("usersJSON")
|
||||
.route("GET", /users/, "@usersJSON")
|
||||
|
||||
// ...later on...
|
||||
|
||||
.then(function(){
|
||||
// we have access to this.usersJSON since it was aliased
|
||||
this.usersJSON
|
||||
})
|
||||
```
|
||||
|
||||
## Modifying fixture data before using it
|
||||
|
||||
You can also modify fixture data directly before passing it along to the route.
|
||||
|
||||
```javascript
|
||||
cy
|
||||
.fixture("user").then(function(user){
|
||||
user.firstName = "Jane"
|
||||
|
||||
cy.route("GET", "/users/1", user)
|
||||
})
|
||||
.visit("/users")
|
||||
.get(".user").should("include", "Jane")
|
||||
})
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Command Log
|
||||
|
||||
## `fixture` does *not* log in the command log
|
||||
|
||||
***
|
||||
|
||||
# Related
|
||||
|
||||
- [Guide: Creating Fixtures](https://on.cypress.io/guides/creating-fixtures)
|
||||
- [Recipe: Bootstrapping App Test Data](https://github.com/cypress-io/cypress-example-recipes/blob/master/cypress/integration/bootstrapping_app_test_data_spec.js)
|
||||
- [route](https://on.cypress.io/api/route)
|
||||
- [then](https://on.cypress.io/api/then)
|
||||
@@ -1,84 +0,0 @@
|
||||
slug: focus
|
||||
excerpt: Focus on a DOM element
|
||||
|
||||
Focus on a DOM element. If there is currently a different DOM element currently with focus, Cypress will issue a `blur` event to that element first.
|
||||
|
||||
**The following events are fired during focus:** `focusin`, `focus`
|
||||
|
||||
| | |
|
||||
|--- | --- |
|
||||
| **Returns** | the new DOM element(s) found by the command. |
|
||||
| **Timeout** | `cy.focus` will retry for the duration of the [`defaultCommandTimeout`](https://on.cypress.io/guides/configuration#section-timeouts) |
|
||||
|
||||
***
|
||||
|
||||
# [cy.focus()](#section-usage)
|
||||
|
||||
Focus on the DOM element found in the previous command.
|
||||
|
||||
***
|
||||
|
||||
# Options
|
||||
|
||||
Pass in an options object to change the default behavior of `cy.focus`.
|
||||
|
||||
**cy.focus( *options* )**
|
||||
|
||||
Option | Default | Notes
|
||||
--- | --- | ---
|
||||
`log` | `true` | whether to display command in command log
|
||||
|
||||
***
|
||||
|
||||
# Usage
|
||||
|
||||
## Focus on the current subject.
|
||||
|
||||
```javascript
|
||||
cy.get("[name='comment']").focus()
|
||||
```
|
||||
|
||||
Focus, type, and blur the current subject.
|
||||
|
||||
```javascript
|
||||
// returns the <textarea> for further chaining
|
||||
cy.get("[name='comment']").focus().type("Nice Product!").blur()
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Command Log
|
||||
|
||||
## Focus the textarea.
|
||||
|
||||
```javascript
|
||||
cy.get("[name='comment']").focus()
|
||||
```
|
||||
|
||||
The commands above will display in the command log as:
|
||||
|
||||
<img width="524" alt="screen shot 2015-11-27 at 1 32 37 pm" src="https://cloud.githubusercontent.com/assets/1271364/11446856/6c2c36f4-950b-11e5-89c6-9bf14a448b23.png">
|
||||
|
||||
When clicking on the `focus` command within the command log, the console outputs the following:
|
||||
|
||||
<img width="526" alt="screen shot 2015-11-27 at 1 33 00 pm" src="https://cloud.githubusercontent.com/assets/1271364/11446857/703fa6c2-950b-11e5-9686-ce6b558cfd92.png">
|
||||
|
||||
***
|
||||
|
||||
# Errors
|
||||
|
||||
## cy.focus() can only be called on a valid focusable element.
|
||||
|
||||
Ensure the element you are trying to call `cy.focus()` on is a [focusable element](https://www.w3.org/TR/html5/editing.html#focusable). Most commonly, you'll want to ensure that the element is not disabled although there are [other factors](https://www.w3.org/TR/html5/editing.html#focusable).
|
||||
|
||||
|
||||
## cy.focus() timed out because your browser did not receive any focus events. This is a known bug in Chrome when it is not the currently focused window.
|
||||
|
||||
If you see this error, you may want to ensure that the main browser window is currently focused. This means not being focused in debugger or any other window when the command is executed.
|
||||
|
||||
|
||||
# Related
|
||||
|
||||
- [focused](https://on.cypress.io/api/focused)
|
||||
- [blur](https://on.cypress.io/api/blur)
|
||||
- [click](https://on.cypress.io/api/click)
|
||||
@@ -1,71 +0,0 @@
|
||||
slug: focused
|
||||
excerpt: Get the DOM element that is focused
|
||||
|
||||
Get the DOM element that is currently focused.
|
||||
|
||||
| | |
|
||||
|--- | --- |
|
||||
| **Returns** | the current DOM element that is focused or `null` |
|
||||
| **Timeout** | `cy.focused` will retry for the duration of the [`defaultCommandTimeout`](https://on.cypress.io/guides/configuration#section-timeouts) |
|
||||
|
||||
***
|
||||
|
||||
# [cy.focused()](#section-usage)
|
||||
|
||||
Get the focused DOM element.
|
||||
|
||||
***
|
||||
|
||||
# Options
|
||||
|
||||
Pass in an options object to change the default behavior of `cy.focused`.
|
||||
|
||||
**cy.focused( *options* )**
|
||||
|
||||
Option | Default | Notes
|
||||
--- | --- | ---
|
||||
`log` | `true` | whether to display command in command log
|
||||
|
||||
***
|
||||
|
||||
# Usage
|
||||
|
||||
## Get the element that is focused.
|
||||
|
||||
```javascript
|
||||
cy.focused()
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## Make an assertion on the focused element.
|
||||
|
||||
```javascript
|
||||
cy.focused().should("have.attr", "name", "username")
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Command Log
|
||||
|
||||
## Make an assertion on the focused element.
|
||||
|
||||
|
||||
```javascript
|
||||
cy.focused().should("have.attr", "name").and("eq", "num")
|
||||
```
|
||||
|
||||
The commands above will display in the command log as:
|
||||
|
||||
<img width="523" alt="screen shot 2015-11-27 at 1 01 51 pm" src="https://cloud.githubusercontent.com/assets/1271364/11446780/f71fb350-9509-11e5-963a-a6940fbc63b6.png">
|
||||
|
||||
When clicking on the `focused` command within the command log, the console outputs the following:
|
||||
|
||||
<img width="407" alt="screen shot 2015-11-27 at 1 02 02 pm" src="https://cloud.githubusercontent.com/assets/1271364/11446771/d104a6d0-9509-11e5-9464-2e397cb1eb24.png">
|
||||
|
||||
***
|
||||
|
||||
# Related
|
||||
|
||||
- [focus](https://on.cypress.io/api/focus)
|
||||
- [blur](https://on.cypress.io/api/blur)
|
||||
@@ -1,152 +0,0 @@
|
||||
slug: get
|
||||
excerpt: Get DOM element(s) by selector or alias
|
||||
|
||||
Get one or more DOM elements by selector or [alias](https://on.cypress.io/guides/using-aliases).
|
||||
|
||||
`cy.get` supports all CSS based selectors. It is analogous to jQuery's `$(...)` in that any selector you pass to jQuery you can also pass to `cy.get`.
|
||||
|
||||
| | |
|
||||
|--- | --- |
|
||||
| **Returns** | the new DOM element(s) found by the command. |
|
||||
| **Timeout** | `cy.get` will retry for the duration of the [`defaultCommandTimeout`](https://on.cypress.io/guides/configuration#section-timeouts) |
|
||||
|
||||
***
|
||||
|
||||
# [cy.get( *selector* )](#section-selector-usage)
|
||||
|
||||
Finds one or more DOM elements based on the selector.
|
||||
|
||||
***
|
||||
|
||||
# [cy.get( *alias* )](#section-alias-usage)
|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "info",
|
||||
"body": "[Read about using aliases first.](https://on.cypress.io/guides/using-aliases)",
|
||||
"title": "New to Cypress?"
|
||||
}
|
||||
[/block]
|
||||
|
||||
You can pass in the `@` character and the name of an alias as a parameter to find an [aliased](https://on.cypress.io/guides/using-aliases) element.
|
||||
|
||||
Internally Cypress keeps a cache of all aliased elements. If the element currently exists in the DOM, it is immediately returned. If the element no longer exists, Cypress will re-query the element based on the previous selector path to find it again.
|
||||
|
||||
***
|
||||
|
||||
# Options
|
||||
|
||||
Pass in an options object to change the default behavior of `cy.get`.
|
||||
|
||||
**cy.get( *selector*, *options* )**
|
||||
**cy.get( *alias*, *options* )**
|
||||
|
||||
Option | Default | Notes
|
||||
--- | --- | ---
|
||||
`log` | `true` | whether to display command in command log
|
||||
`timeout` | [`defaultCommandTimeout`](https://on.cypress.io/guides/configuration#section-timeouts) | Total time to retry getting the element
|
||||
|
||||
***
|
||||
|
||||
# Selector Usage
|
||||
|
||||
## Find the element with an id of main
|
||||
|
||||
```javascript
|
||||
cy.get("#main")
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## Find the first `li` descendent within a `ul`
|
||||
|
||||
```javascript
|
||||
cy.get("ul li:first")
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## Find the element with class dropdown-menu and click it.
|
||||
|
||||
```javascript
|
||||
cy
|
||||
.get(".dropdown-menu").click()
|
||||
|
||||
// Break out of the previous command chain and
|
||||
// query for #search from the root document.
|
||||
.get("#search").type("mogwai")
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## Reset the current scope in a [`cy.within`](https://on.cypress.io/api/within)
|
||||
|
||||
```javascript
|
||||
// Find form and scope all new queries to within form.
|
||||
cy.get("form").within(function(){
|
||||
cy
|
||||
// Find the input within form and type Pamela
|
||||
.get("input").type("Pamela")
|
||||
// Find the element textarea within form and type in it
|
||||
.get("textarea").type("is a developer")
|
||||
})
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Alias Usage
|
||||
|
||||
For a detailed explanation of aliasing, [read more about aliasing here](https://on.cypress.io/guides/using-aliases).
|
||||
|
||||
## Retrieve aliased `todos` elements
|
||||
|
||||
```javascript
|
||||
cy.get("ul#todos").as("todos")
|
||||
|
||||
//...hack hack hack...
|
||||
|
||||
//later retrieve the todos
|
||||
cy.get("@todos")
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## Alias the `submitBtn` in a `beforeEach`
|
||||
|
||||
```javascript
|
||||
beforeEach(function(){
|
||||
cy.get("button[type=submit]").as("submitBtn")
|
||||
})
|
||||
|
||||
it("disables on click", function(){
|
||||
cy.get("@submitBtn").should("be.disabled")
|
||||
})
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
|
||||
# Command Log
|
||||
|
||||
## Get an input and assert on the value
|
||||
|
||||
```javascript
|
||||
cy
|
||||
.get("input[name='firstName']")
|
||||
.should("have.value", "Homer")
|
||||
```
|
||||
|
||||
The commands above will display in the command log as:
|
||||
|
||||
<img width="524" alt="screen shot 2015-11-27 at 1 24 20 pm" src="https://cloud.githubusercontent.com/assets/1271364/11446808/5d2f2180-950a-11e5-8645-4f0f14321f86.png">
|
||||
|
||||
When clicking on the `get` command within the command log, the console outputs the following:
|
||||
|
||||
<img width="543" alt="screen shot 2015-11-27 at 1 24 45 pm" src="https://cloud.githubusercontent.com/assets/1271364/11446809/61a6f4f4-950a-11e5-9b23-a9efa1fbccfc.png">
|
||||
|
||||
# Related
|
||||
|
||||
- [contains](https://on.cypress.io/api/contains)
|
||||
- [within](https://on.cypress.io/api/within)
|
||||
- [find](https://on.cypress.io/api/find)
|
||||
- [root](https://on.cypress.io/api/root)
|
||||
@@ -1,93 +0,0 @@
|
||||
slug: getcookie
|
||||
excerpt: Get a browser cookie
|
||||
|
||||
Get a browser cookie.
|
||||
|
||||
| | |
|
||||
|--- | --- |
|
||||
| **Returns** | a cookie object literal |
|
||||
| **Timeout** | `cy.getCookie` will wait up for the duration of [`responseTimeout`](https://on.cypress.io/guides/configuration#section-timeouts) for the automation server to process this command. |
|
||||
|
||||
***
|
||||
|
||||
# [cy.getCookie( *name* )](#section-usage)
|
||||
|
||||
Gets a browser cookie by its name.
|
||||
|
||||
This object will have the following properties:
|
||||
|
||||
| Properties |
|
||||
| --- |
|
||||
| `name` |
|
||||
| `value` |
|
||||
| `path` |
|
||||
| `domain` |
|
||||
| `httpOnly` |
|
||||
| `secure` |
|
||||
| `expiry` |
|
||||
|
||||
***
|
||||
|
||||
# Options
|
||||
|
||||
Pass in an options object to change the default behavior of `cy.getCookie`.
|
||||
|
||||
**cy.getCookie( *name*, *options* )**
|
||||
|
||||
Option | Default | Notes
|
||||
--- | --- | ---
|
||||
`timeout` | [`responseTimeout`](https://on.cypress.io/guides/configuration#section-timeouts) | Total time to wait for the `cy.getCookie` command to be processed
|
||||
`log` | `true` | whether to display command in command log
|
||||
|
||||
***
|
||||
|
||||
# Usage
|
||||
|
||||
## Get `session_id` cookie after logging in
|
||||
|
||||
In this example, on first login our server sends us back a session cookie.
|
||||
|
||||
```javascript
|
||||
cy
|
||||
.login('bob@example.com', 'p@ssw0rd') // example of a custom command
|
||||
.getCookie('session_id')
|
||||
.should('have.property', 'value', '189jd09sufh33aaiidhf99d09')
|
||||
```
|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "info",
|
||||
"body": "Check out our example recipes using cy.getCookie to test [logging in using HTML web forms](https://github.com/cypress-io/cypress-example-recipes/blob/master/cypress/integration/logging_in_html_web_form_spec.js), [logging in using XHR web forms](https://github.com/cypress-io/cypress-example-recipes/blob/master/cypress/integration/logging_in_xhr_web_form_spec.js) and [logging in with single sign on](https://github.com/cypress-io/cypress-example-recipes/blob/master/cypress/integration/logging_in_single_sign_on_spec.js)",
|
||||
"title": "Using cy.getCookie to test login"
|
||||
}
|
||||
[/block]
|
||||
|
||||
***
|
||||
|
||||
# Command Log
|
||||
|
||||
## Get cookie
|
||||
|
||||
```javascript
|
||||
cy
|
||||
.getCookie('fakeCookie1')
|
||||
.should('have.property', 'value', '123ABC')
|
||||
```
|
||||
|
||||
The commands above will display in the command log as:
|
||||
|
||||

|
||||
|
||||
When clicking on `getCookie` within the command log, the console outputs the following:
|
||||
|
||||

|
||||
|
||||
***
|
||||
|
||||
# Related
|
||||
|
||||
- [clearCookie](https://on.cypress.io/api/clearcookie)
|
||||
- [clearCookies](https://on.cypress.io/api/clearcookies)
|
||||
- [getCookies](https://on.cypress.io/api/getcookies)
|
||||
- [setCookie](https://on.cypress.io/api/setcookie)
|
||||
- [Cypress Cookies API](https://on.cypress.io/api/cookies)
|
||||
@@ -1,98 +0,0 @@
|
||||
slug: getcookies
|
||||
excerpt: Get all browser cookies
|
||||
|
||||
Gets all of the browser cookies.
|
||||
|
||||
| | |
|
||||
|--- | --- |
|
||||
| **Returns** | an array of cookie objects |
|
||||
| **Timeout** | `cy.getCookies` will wait up for the duration of [`responseTimeout`](https://on.cypress.io/guides/configuration#section-timeouts) for the automation server to process this command. |
|
||||
|
||||
***
|
||||
|
||||
# [cy.getCookies()](#section-usage)
|
||||
|
||||
Gets the browser cookies.
|
||||
|
||||
Each cookie object will have the following properties:
|
||||
|
||||
| Properties |
|
||||
| --- |
|
||||
| `name` |
|
||||
| `value` |
|
||||
| `path` |
|
||||
| `domain` |
|
||||
| `httpOnly` |
|
||||
| `secure` |
|
||||
| `expiry` |
|
||||
|
||||
***
|
||||
|
||||
|
||||
# Options
|
||||
|
||||
Pass in an options object to change the default behavior of `cy.getCookies`.
|
||||
|
||||
**[cy.getCookies(*options* )](#options-usage)**
|
||||
|
||||
Option | Default | Notes
|
||||
--- | --- | ---
|
||||
`timeout` | [`responseTimeout`](https://on.cypress.io/guides/configuration#section-timeouts) | Total time to wait for the `cy.getCookies` command to be processed
|
||||
`log` | `true` | whether to display command in command log
|
||||
|
||||
***
|
||||
|
||||
# Usage
|
||||
|
||||
## Get cookies after logging in
|
||||
|
||||
In this example, on first login our server sends us back a session cookie.
|
||||
|
||||
```javascript
|
||||
cy
|
||||
.login("bob@example.com", "p@ssw0rd") // example of a custom command
|
||||
.getCookies()
|
||||
.should('have.length', 1)
|
||||
.then(function(cookies) {
|
||||
expect(cookies[0]).to.have.property('name', 'session_id')
|
||||
})
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Command Log
|
||||
|
||||
## Get cookies
|
||||
|
||||
```javascript
|
||||
cy
|
||||
.getCookies()
|
||||
.should('have.length', 1)
|
||||
.then( function(cookies) {
|
||||
// each cookie has these properties
|
||||
expect(cookies[0]).to.have.property('name', 'fakeCookie1')
|
||||
expect(cookies[0]).to.have.property('value', '123ABC')
|
||||
expect(cookies[0]).to.have.property('domain')
|
||||
expect(cookies[0]).to.have.property('httpOnly')
|
||||
expect(cookies[0]).to.have.property('path')
|
||||
expect(cookies[0]).to.have.property('secure')
|
||||
})
|
||||
```
|
||||
|
||||
The commands above will display in the command log as:
|
||||
|
||||

|
||||
|
||||
When clicking on `getCookies` within the command log, the console outputs the following:
|
||||
|
||||

|
||||
|
||||
***
|
||||
|
||||
# Related
|
||||
|
||||
- [clearCookie](https://on.cypress.io/api/clearcookie)
|
||||
- [clearCookies](https://on.cypress.io/api/clearcookies)
|
||||
- [getCookie](https://on.cypress.io/api/getcookie)
|
||||
- [setCookie](https://on.cypress.io/api/setcookie)
|
||||
- [Cypress Cookies API](https://on.cypress.io/api/cookies)
|
||||
@@ -1,110 +0,0 @@
|
||||
slug: go
|
||||
excerpt: Navigate back or forward to previous or next URL in the browser's history
|
||||
|
||||
Navigate back or forward to the previous or next URL in the browser's history. If going forward or back causes a full page refresh, Cypress will wait for the new page to load before moving on to new commands. Cypress additionally handles situations where a page load was not caused (such as hash routing) and will resolve immediately.
|
||||
|
||||
| | |
|
||||
|--- | --- |
|
||||
| **Returns** | the `window` object |
|
||||
| **Timeout** | `cy.go` will retry for the duration of the [pageLoadTimeout](https://on.cypress.io/guides/configuration#section-timeouts) or the duration of the `timeout` specified in the command's [options](#section-options). |
|
||||
|
||||
***
|
||||
|
||||
# [cy.go( *direction* )](#section-direction-usage)
|
||||
|
||||
Navigate back or forward to the URL in a specific direction (`back` or `forward`).
|
||||
|
||||
***
|
||||
|
||||
# [cy.go( *number* )](#section-number-usage)
|
||||
|
||||
Navigate back or forward going to the URL within a specific history position (-1 goes back one page, 1 goes forward one page).
|
||||
|
||||
***
|
||||
|
||||
# Options
|
||||
|
||||
Pass in an options object to change the default behavior of `cy.go`.
|
||||
|
||||
**cy.go( *direction*, *options* )**
|
||||
**cy.go( *number*, *options* )**
|
||||
|
||||
Option | Default | Notes
|
||||
--- | --- | ---
|
||||
`timeout` | [pageLoadTimeout](https://on.cypress.io/guides/configuration#section-timeouts) | Total time to retry the visit
|
||||
`log` | `true` | whether to display command in command log
|
||||
|
||||
***
|
||||
|
||||
# Direction Usage
|
||||
|
||||
## Go back in browser's history
|
||||
|
||||
```javascript
|
||||
cy.go("back") // equivalent to clicking back button
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
|
||||
## Go forward in browser's history
|
||||
|
||||
```javascript
|
||||
cy.go("forward") // equivalent to clicking forward button
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Number Usage
|
||||
|
||||
## Go back in browser's history
|
||||
|
||||
```javascript
|
||||
cy.go(-1) // equivalent to clicking back button
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## Go forward in browser's history
|
||||
|
||||
```javascript
|
||||
cy.go(1) // equivalent to clicking forward button
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Command Log
|
||||
|
||||
## Go back in browser's history
|
||||
|
||||
```javascript
|
||||
cy
|
||||
.visit("http://localhost:8000/folders")
|
||||
.go("back")
|
||||
```
|
||||
|
||||
The commands above will display in the command log as:
|
||||
|
||||

|
||||
|
||||
When clicking on the `get` command within the command log, the console outputs the following:
|
||||
|
||||

|
||||
|
||||
***
|
||||
|
||||
# Errors
|
||||
|
||||
## cy.go() accepts only a string or number argument
|
||||
|
||||
`cy.go()` specifically accepts the string arguments `back` or `forward` or a number argument to navigate to a specific position in the history.
|
||||
|
||||
## cy.go() cannot accept '0'. The number must be greater or less than '0'.
|
||||
|
||||
Ensure the number passed to `cy.go()` navigates forward or backward in history. For example, -1 goes back one page, 1 goes forward one page.
|
||||
|
||||
***
|
||||
|
||||
# Related
|
||||
|
||||
- [visit](https://on.cypress.io/api/visit)
|
||||
@@ -1,116 +0,0 @@
|
||||
slug: check
|
||||
excerpt: Select a checkbox or radio
|
||||
|
||||
Check the checkboxes or radios within the current subject.
|
||||
|
||||
**The following events are fired during check:** `mousedown`, `focus`, `mouseup`, `click`
|
||||
|
||||
| | |
|
||||
|--- | --- |
|
||||
| **Returns** | the new DOM element(s) found by the command. |
|
||||
| **Timeout** | `cy.check` will retry for the duration of the [`defaultCommandTimeout`](https://on.cypress.io/guides/configuration#section-timeouts) or the duration of the `timeout` specified in the command's [options](#section-options). |
|
||||
|
||||
***
|
||||
|
||||
# [cy.check()](#section-usage)
|
||||
|
||||
Checks checkboxes or radios.
|
||||
|
||||
***
|
||||
|
||||
# [cy.check( *value* )](#section-value-usage)
|
||||
|
||||
Checks the checkbox or radio with the matching value.
|
||||
|
||||
***
|
||||
|
||||
# [cy.check( *values* )](#section-values-usage)
|
||||
|
||||
Checks the checkboxes or radios with the matching values.
|
||||
|
||||
***
|
||||
|
||||
# Options
|
||||
|
||||
Pass in an options object to change the default behavior of `cy.check`.
|
||||
|
||||
**cy.check( *options* )**
|
||||
|
||||
Option | Default | Notes
|
||||
--- | --- | ---
|
||||
`interval` | `16` | Interval which to retry a check
|
||||
`timeout` | [`defaultCommandTimeout`](https://on.cypress.io/guides/configuration#section-timeouts) | Total time to retry the check
|
||||
`force` | `false` | Forces check, disables error checking prior to check
|
||||
`log` | `true` | whether to display command in command log
|
||||
`multiple` | `false` | Enables serially checking multiple elements
|
||||
|
||||
***
|
||||
|
||||
# Usage
|
||||
|
||||
## Check all checkboxes
|
||||
|
||||
```javascript
|
||||
cy.get("[type='checkbox']").check()
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## Check all radios
|
||||
|
||||
```javascript
|
||||
cy.get("[type='radio']").check()
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## Check the element with id of `saveUserName` and check it
|
||||
|
||||
```javascript
|
||||
cy.get("#saveUserName").check()
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Value Usage
|
||||
|
||||
## Check the checkbox with the value of "US"
|
||||
|
||||
```javascript
|
||||
cy.get("input[type='checkbox']").check("US")
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Values Usage
|
||||
|
||||
## Check the checkbox with the value of "ga" and "ca"
|
||||
|
||||
```javascript
|
||||
cy.get("input[type='checkbox']").check(["ga", "ca"])
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Command Log
|
||||
|
||||
## check the element with name of emailUser
|
||||
|
||||
```javascript
|
||||
cy.get("form").find("[name='emailUser']").check()
|
||||
```
|
||||
|
||||
The commands above will display in the command log as:
|
||||
|
||||
<img width="582" alt="screen shot 2015-11-29 at 12 53 25 pm" src="https://cloud.githubusercontent.com/assets/1271364/11458925/6226b39e-9698-11e5-9a2a-debf91f5989a.png">
|
||||
|
||||
When clicking on `check` within the command log, the console outputs the following:
|
||||
|
||||
<img width="547" alt="screen shot 2015-11-29 at 12 53 48 pm" src="https://cloud.githubusercontent.com/assets/1271364/11458927/65a2526c-9698-11e5-8b33-f59e666170e2.png">
|
||||
|
||||
***
|
||||
|
||||
# Related
|
||||
|
||||
- [uncheck](https://on.cypress.io/api/uncheck)
|
||||
- [click](https://on.cypress.io/api/click)
|
||||
@@ -1,104 +0,0 @@
|
||||
slug: hash
|
||||
excerpt: Get the current URL hash
|
||||
|
||||
Get the current URL hash. This is the same as [`cy.location().hash`](https://on.cypress.io/api/location)
|
||||
|
||||
| | |
|
||||
|--- | --- |
|
||||
| **Returns** | the current URL hash as a string, including the `#` character. If no `#` character is present, an empty string will be returned. |
|
||||
| **Timeout** | *cannot timeout* |
|
||||
|
||||
***
|
||||
|
||||
# [cy.hash()](#section-usage)
|
||||
|
||||
Get the current URL hash.
|
||||
|
||||
***
|
||||
|
||||
# Options
|
||||
|
||||
Pass in an options object to change the default behavior of `cy.hash`.
|
||||
|
||||
**cy.hash( *options* )**
|
||||
**cy.hash( *options* )**
|
||||
|
||||
Option | Default | Notes
|
||||
--- | --- | ---
|
||||
`log` | `true` | whether to display command in command log
|
||||
|
||||
***
|
||||
|
||||
# Usage
|
||||
|
||||
## Assert that the hash is `#/users/1` given the remote URL: `http://localhost:8000/app/#/users/1`
|
||||
|
||||
```javascript
|
||||
// Hash returns #/users/1
|
||||
cy.hash().should("eq", "#/users/1") // => true
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## Assert that the hash matches via RegExp
|
||||
|
||||
```html
|
||||
<ul id="users">
|
||||
<li>
|
||||
<a href="#/users/8fc45b67-d2e5-465a-b822-b281d9c8e4d1">Fred</a>
|
||||
</li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
```javascript
|
||||
cy
|
||||
.get("#users li").find("a")
|
||||
.hash().should("match", /users\/.+$/) // => true
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Notes
|
||||
|
||||
## Hash is a shortcut for `cy.location().hash`
|
||||
|
||||
These 3 assertions are all the same.
|
||||
|
||||
```javascript
|
||||
// 1. verbose
|
||||
cy.location().then(function(location){
|
||||
expect(location.hash).to.eq("#/users/1")
|
||||
})
|
||||
|
||||
// 2. better
|
||||
cy.location().its("hash").should("eq", "#/users/1")
|
||||
|
||||
// 3. best
|
||||
cy.hash().should("eq", "#/users/1")
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Command Log
|
||||
|
||||
## Assert that the hash matches `#users/new`
|
||||
|
||||
```javascript
|
||||
cy.hash().should("eq", "#users/new")
|
||||
```
|
||||
|
||||
The commands above will display in the command log as:
|
||||
|
||||
<img width="581" alt="screen shot 2015-11-29 at 1 34 12 pm" src="https://cloud.githubusercontent.com/assets/1271364/11459152/ed737be4-969d-11e5-823e-1d12cd7d03b1.png">
|
||||
|
||||
When clicking on `hash` within the command log, the console outputs the following:
|
||||
|
||||
<img width="472" alt="screen shot 2015-11-29 at 1 34 17 pm" src="https://cloud.githubusercontent.com/assets/1271364/11459153/f0aa6476-969d-11e5-9851-302957f9eb0f.png">
|
||||
|
||||
|
||||
***
|
||||
|
||||
# Related
|
||||
|
||||
- [location](https://on.cypress.io/api/location)
|
||||
- [url](https://on.cypress.io/api/url)
|
||||
@@ -1,53 +0,0 @@
|
||||
slug: hover
|
||||
excerpt: Hover over a DOM element
|
||||
|
||||
**Cypress does not have a `cy.hover()` command.** See [Issue #10](https://github.com/cypress-io/cypress/issues/10).
|
||||
|
||||
If `cy.hover()` is used, an error will display and redirect you to this page.
|
||||
|
||||
## Workaround
|
||||
|
||||
Sometimes an element has specific logic on hover and you *do* need to "hover" in Cypress. Maybe the element doesn't even display to be clickable until you hover over another element.
|
||||
|
||||
Oftentimes you can use [`cy.invoke`](https://on.cypress.io/api/invoke) or [`cy.wrap`](https://on.cypress.io/api/wrap) to show the element before you perform the action.
|
||||
|
||||
**Example of showing an element in order to perform action**
|
||||
```javascript
|
||||
cy.get(".content").invoke("show").click()
|
||||
```
|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "info",
|
||||
"body": "[Check out our example recipe on testing hover and working with hidden elements](https://github.com/cypress-io/cypress-example-recipes/blob/master/cypress/integration/hover_hidden_elements.js)",
|
||||
"title": "Dealing with hover and hidden elements"
|
||||
}
|
||||
[/block]
|
||||
|
||||
You can also force the action to be performed on the element regardless of whether the element is visible or not.
|
||||
|
||||
**Example of clicking on a hidden element**
|
||||
```javascript
|
||||
cy.get(".content").click({force: true})
|
||||
```
|
||||
|
||||
**Example of checking a hidden element**
|
||||
```javascript
|
||||
cy.get(".checkbox").check({force: true})
|
||||
```
|
||||
|
||||
If the hover behavior depends on a JavaScript event like `mouseover`, you can trigger the event to achieve that behavior.
|
||||
|
||||
**Example of triggering a mouseover event**
|
||||
```javascript
|
||||
cy.get(".content").trigger("mouseover")
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Related
|
||||
|
||||
- [Recipe: Dealing with Hover and Hidden Elements](https://github.com/cypress-io/cypress-example-recipes/blob/master/cypress/integration/hover_hidden_elements.js)
|
||||
- [invoke](https://on.cypress.io/api/invoke)
|
||||
- [trigger](https://on.cypress.io/api/trigger)
|
||||
- [wrap](https://on.cypress.io/api/wrap)
|
||||
@@ -1,136 +0,0 @@
|
||||
slug: invoke
|
||||
excerpt: Call properties on the current subject
|
||||
|
||||
`cy.invoke` invokes functions on the current subject.
|
||||
|
||||
If you want to call a regular property that is not a function on the current subject, use [`cy.its`](https://on.cypress.io/api/its).
|
||||
|
||||
| | |
|
||||
|--- | --- |
|
||||
| **Returns** | the return value of the invoked property |
|
||||
| **Timeout** | `cy.invoke` cannot timeout unless you've added assertions. The assertions will retry for the duration of [`defaultCommandTimeout`](https://on.cypress.io/guides/configuration#section-timeouts) |
|
||||
|
||||
***
|
||||
|
||||
# [cy.invoke( *functionName* )](#section-function-usage)
|
||||
|
||||
Invokes the function with the specified name
|
||||
|
||||
***
|
||||
|
||||
# [cy.invoke( *functionName*, **arguments* )](#section-function-with-arguments-usage)
|
||||
|
||||
Invokes the function with the specified name and forwards any additional arguments to the function call. There are no limits to the number of arguments.
|
||||
|
||||
***
|
||||
|
||||
# Function Usage
|
||||
|
||||
## Assert on a function after invoke
|
||||
|
||||
```javascript
|
||||
var fn = function(){
|
||||
return "bar"
|
||||
}
|
||||
|
||||
cy.wrap({foo: fn}).invoke("foo").should("eq", "bar") // true
|
||||
```
|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "info",
|
||||
"body": "[Check out our example recipe where we use cy.invoke('text') to test against HTML content](https://github.com/cypress-io/cypress-example-recipes/blob/master/cypress/integration/bootstrapping_app_test_data_spec.js)",
|
||||
"title": "Using cy.invoke('text')"
|
||||
}
|
||||
[/block]
|
||||
|
||||
## Properties that are functions are invoked
|
||||
|
||||
```javascript
|
||||
// force a hidden div to be 'display: block'
|
||||
// so we can interact with its children elements
|
||||
cy
|
||||
.get("div.container").should("be.hidden") // true
|
||||
|
||||
.invoke("show") // call the jquery method 'show' on the 'div.container'
|
||||
.should("be.visible") // true
|
||||
.find("input").type("Cypress is great")
|
||||
```
|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "info",
|
||||
"body": "[Check out our example recipe where we use cy.invoke('show') and cy.invoke('trigger') to click an element that is only visible on hover](https://github.com/cypress-io/cypress-example-recipes/blob/master/cypress/integration/hover_hidden_elements.js)",
|
||||
"title": "Using cy.invoke('show') and cy.invoke('trigger')"
|
||||
}
|
||||
[/block]
|
||||
|
||||
***
|
||||
|
||||
## Useful for 3rd party plugins
|
||||
|
||||
```javascript
|
||||
// as a slightly verbose approach
|
||||
cy.get("input").invoke("getKendoDropDownList").then(function(dropDownList){
|
||||
// the return of $input.getKendoDropDownList() has now become the new subject
|
||||
|
||||
// whatever the select method returns becomes the next subject after this
|
||||
return dropDownList.select("apples")
|
||||
})
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## We can rewrite the previous example in a more terse way and add an assertion.
|
||||
|
||||
```javascript
|
||||
cy
|
||||
.get("input")
|
||||
.invoke("getKendoDropDownList")
|
||||
.invoke("select", "apples")
|
||||
.its("val").should("match", /apples/)
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Function with Arguments Usage
|
||||
|
||||
## Send specific arguments to the function
|
||||
|
||||
```javascript
|
||||
var fn = function(a, b, c){
|
||||
return a + b + c
|
||||
}
|
||||
|
||||
cy
|
||||
.wrap({sum: fn})
|
||||
.invoke("sum", 2, 4, 6)
|
||||
.should("be.gt", 10) // true
|
||||
.and("be.lt", 20) // true
|
||||
```
|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "info",
|
||||
"body": "[Check out our example recipe where we use cy.invoke('removeAttr', 'target') to test clicking on a link without opening in a new tab](https://github.com/cypress-io/cypress-example-recipes/blob/master/cypress/integration/tab_handling_anchor_links_spec.js)",
|
||||
"title": "Using cy.invoke('removeAttr', 'target')"
|
||||
}
|
||||
[/block]
|
||||
|
||||
## Arguments are automatically forwarded to the function
|
||||
|
||||
```javascript
|
||||
cy
|
||||
.get("img").invoke("attr", "src")
|
||||
.should("include", "myLogo")
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Related
|
||||
|
||||
- [its](https://on.cypress.io/api/its)
|
||||
- [wrap](https://on.cypress.io/api/wrap)
|
||||
- [then](https://on.cypress.io/api/then)
|
||||
- [stub](https://on.cypress.io/api/stub)
|
||||
- [spy](https://on.cypress.io/api/spy)
|
||||
@@ -1,130 +0,0 @@
|
||||
slug: its
|
||||
excerpt: Get properties on the current subject
|
||||
|
||||
`cy.its` gets regular properties on the current subject.
|
||||
|
||||
If you want to call a function on the current subject, use [`cy.invoke`](https://on.cypress.io/api/invoke).
|
||||
|
||||
| | |
|
||||
|--- | --- |
|
||||
| **Returns** | the value of the property |
|
||||
| **Timeout** | `cy.its` cannot timeout unless you've added assertions. The assertions will retry for the duration of [`defaultCommandTimeout`](https://on.cypress.io/guides/configuration#section-timeouts) |
|
||||
|
||||
***
|
||||
|
||||
# [cy.its( *propertyName* )](#section-usage)
|
||||
|
||||
Gets the property with the specified name.
|
||||
|
||||
You can also access multiple nested properties with **dot notation**.
|
||||
|
||||
***
|
||||
|
||||
# Usage
|
||||
|
||||
## Access properties
|
||||
|
||||
```javascript
|
||||
cy.wrap({foo: "bar"}).its("foo").should("eq", "bar") // true
|
||||
```
|
||||
|
||||
Call the `length` property on the current subject
|
||||
|
||||
```javascript
|
||||
cy
|
||||
.get("ul li") // this returns us the jquery object
|
||||
.its("length") // calls the 'length' property returning that value
|
||||
.should("be.gt", 2) // ensure this length is greater than 2
|
||||
})
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## Access functions
|
||||
|
||||
You can access functions to then drill into their own properties instead of invoking them.
|
||||
|
||||
```javascript
|
||||
// Your app code
|
||||
|
||||
// a basic Factory constructor
|
||||
var Factory = function(arg){
|
||||
...
|
||||
}
|
||||
|
||||
Factory.create = function(arg){
|
||||
return new Factory(arg)
|
||||
}
|
||||
|
||||
// assign it to the window
|
||||
window.Factory = Factory
|
||||
```
|
||||
|
||||
```javascript
|
||||
cy
|
||||
.window() // get the window object
|
||||
.its("Factory") // now we are on the Factory function
|
||||
.invoke("create", "arg") // and now we can invoke properties on it
|
||||
|
||||
```
|
||||
|
||||
[block:callout]
|
||||
{
|
||||
"type": "info",
|
||||
"body": "[Check out our example recipe on testing window.fetch using cy.its()](https://github.com/cypress-io/cypress-example-recipes/blob/master/cypress/integration/spy_stub_clock_spec.js)",
|
||||
"title": "Testing cy.window().its('fetch')"
|
||||
}
|
||||
[/block]
|
||||
|
||||
***
|
||||
|
||||
## Drill into nested properties
|
||||
|
||||
You can additionally automatically drill into nested properties by using **dot notation**.
|
||||
|
||||
```javascript
|
||||
var obj = {
|
||||
foo: {
|
||||
bar: {
|
||||
baz: "quux"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cy.wrap(obj).its("foo.bar.baz").should("eq", "quux") // true
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Command Log
|
||||
|
||||
## Fetch 'comments' fixture
|
||||
|
||||
```javascript
|
||||
cy
|
||||
.server()
|
||||
.route(/comments/, 'fixture:comments.json').as('getComments')
|
||||
.get('#fetch-comments').click()
|
||||
.wait('@getComments')
|
||||
.its('responseBody')
|
||||
.should('deep.eq', [
|
||||
{id: 1, comment: 'hi'},
|
||||
{id: 2, comment: 'there'}
|
||||
])
|
||||
```
|
||||
|
||||
The commands above will display in the command log as:
|
||||
|
||||

|
||||
|
||||
When clicking on `its` within the command log, the console outputs the following:
|
||||
|
||||

|
||||
|
||||
***
|
||||
|
||||
# Related
|
||||
|
||||
- [invoke](https://on.cypress.io/api/invoke)
|
||||
- [wrap](https://on.cypress.io/api/wrap)
|
||||
- [then](https://on.cypress.io/api/then)
|
||||
@@ -1,72 +0,0 @@
|
||||
slug: last
|
||||
excerpt: Get the last DOM element
|
||||
|
||||
Get the last DOM element within a set of DOM elements.
|
||||
|
||||
| | |
|
||||
|--- | --- |
|
||||
| **Returns** | the new DOM element(s) found by the command. |
|
||||
| **Timeout** | `cy.last` will retry for the duration of the [`defaultCommandTimeout`](https://on.cypress.io/guides/configuration#section-timeouts) |
|
||||
|
||||
***
|
||||
|
||||
# [cy.last()](#section-usage)
|
||||
|
||||
Reduce the set of matched DOM elements to the final one in the set.
|
||||
|
||||
***
|
||||
|
||||
# Options
|
||||
|
||||
Pass in an options object to change the default behavior of `cy.last`.
|
||||
|
||||
**cy.last( *options* )**
|
||||
|
||||
Option | Default | Notes
|
||||
--- | --- | ---
|
||||
`log` | `true` | whether to display command in command log
|
||||
`timeout` | [`defaultCommandTimeout`](https://on.cypress.io/guides/configuration#section-timeouts) | Total time to retry getting the element
|
||||
|
||||
***
|
||||
|
||||
# Usage
|
||||
|
||||
## Get the last list item in a list.
|
||||
|
||||
```html
|
||||
<ul>
|
||||
<li class="one">Knick knack on my thumb</li>
|
||||
<li class="two">Knick knack on my shoe</li>
|
||||
<li class="three">Knick knack on my knee</li>
|
||||
<li class="four">Knick knack on my door</li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
```javascript
|
||||
// returns <li class="four">Knick knack on my door</li>
|
||||
cy.get("ul").last()
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Command Log
|
||||
|
||||
## Find the last button in the form
|
||||
|
||||
```javascript
|
||||
cy.get("form").find("button").last()
|
||||
```
|
||||
|
||||
The commands above will display in the command log as:
|
||||
|
||||
<img width="560" alt="screen shot 2015-11-29 at 12 33 52 pm" src="https://cloud.githubusercontent.com/assets/1271364/11458797/8e9abdf6-9695-11e5-8594-7044751d5199.png">
|
||||
|
||||
When clicking on `last` within the command log, the console outputs the following:
|
||||
|
||||
<img width="746" alt="screen shot 2015-11-29 at 12 34 07 pm" src="https://cloud.githubusercontent.com/assets/1271364/11458799/91a115cc-9695-11e5-8569-93fbaa2704d4.png">
|
||||
|
||||
***
|
||||
|
||||
# Related
|
||||
|
||||
- [first](https://on.cypress.io/api/first)
|
||||
@@ -1,131 +0,0 @@
|
||||
slug: location
|
||||
excerpt: Get the `window.location`
|
||||
|
||||
Get the remote `window.location` as a normalized object.
|
||||
|
||||
| | |
|
||||
|--- | --- |
|
||||
| **Returns** | location object detailed below |
|
||||
| **Timeout** | *cannot timeout* |
|
||||
|
||||
# [cy.location()](#section-usage)
|
||||
|
||||
Given a remote URL of `http://localhost:8000/app/index.html?q=dan#/users/123/edit`, an object would be returned with the following properties:
|
||||
|
||||
Key | Type | Returns
|
||||
--- | --- | ----
|
||||
`hash` | string | #/users/123/edit
|
||||
`host` | string | localhost:8000
|
||||
`hostname` | string | localhost
|
||||
`href` | string | http://localhost:8000/app/index.html?q=brian#/users/123/edit
|
||||
`origin` | string | http://localhost:8000
|
||||
`pathname` | string | /app.index.html
|
||||
`port` | string | 8000
|
||||
`protocol` | string | http:
|
||||
`search` | string | ?q=brian
|
||||
`toString` | function | http://localhost:8000/app/index.html?q=brian#/users/123/edit
|
||||
|
||||
***
|
||||
|
||||
# [cy.location( *key* )](#section-key-usage)
|
||||
|
||||
Get the specific value by key of the location object above.
|
||||
|
||||
***
|
||||
|
||||
# Options
|
||||
|
||||
Pass in an options object to change the default behavior of `cy.location`.
|
||||
|
||||
**cy.location( *options* )**
|
||||
**cy.location( *key*, *options* )**
|
||||
|
||||
Option | Default | Notes
|
||||
--- | --- | ---
|
||||
`log` | `true` | whether to display command in command log
|
||||
|
||||
***
|
||||
|
||||
# Usage
|
||||
|
||||
## Check location for query params and pathname
|
||||
|
||||
```javascript
|
||||
// we can yield the location subject and work with
|
||||
// it directly as an object
|
||||
cy
|
||||
.get("#search").type("brian{enter}")
|
||||
.location().should(function(location){
|
||||
expect(location.search).to.eq("?search=brian")
|
||||
expect(location.pathname).to.eq("/users")
|
||||
expect(location.toString()).to.eq("http://localhost:8000/users?search=brian")
|
||||
})
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Key Usage
|
||||
|
||||
## Assert that a redirect works
|
||||
|
||||
```javascript
|
||||
// we should be redirected to the login page
|
||||
cy
|
||||
.visit("http://localhost:3000/admin")
|
||||
.location("pathname").should("eq", "/login")
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Notes
|
||||
|
||||
## Do not use `window.location`
|
||||
|
||||
Let's examine the following scenario:
|
||||
|
||||
```javascript
|
||||
// get our remote window and log out
|
||||
// the window.location object
|
||||
cy.window().then(function(window){
|
||||
console.log(window.location)
|
||||
})
|
||||
```
|
||||
|
||||
```javascript
|
||||
// go through the location command
|
||||
// and log out this object
|
||||
cy.location().then(function(location){
|
||||
console.log(location)
|
||||
})
|
||||
```
|
||||
|
||||
Cypress automatically normalizes the `cy.location()` command and strips out extrenuous values and properties found in `window.location`. Also the object literal returned by `cy.location()` is just a basic object literal, not the special `window.location` object.
|
||||
|
||||
When changing properties on the real `window.location` object, it will force the browser to navigate away. In Cypress, the object we returned is a plain object, and changing or affecting its properties will have no effect on navigation.
|
||||
|
||||
***
|
||||
|
||||
# Command Log
|
||||
|
||||
## Assert on the location's href
|
||||
|
||||
```javascript
|
||||
cy.location().should(function(location){
|
||||
expect(location.href).to.include("commands/querying")
|
||||
})
|
||||
```
|
||||
|
||||
The commands above will display in the command log as:
|
||||
|
||||

|
||||
|
||||
When clicking on `location` within the command log, the console outputs the following:
|
||||
|
||||

|
||||
|
||||
***
|
||||
|
||||
# Related
|
||||
|
||||
- [hash](https://on.cypress.io/api/hash)
|
||||
- [url](https://on.cypress.io/api/url)
|
||||
@@ -1,62 +0,0 @@
|
||||
slug: log
|
||||
excerpt: Print a message to the command log
|
||||
|
||||
Print a message to the Command Log within Cypress.
|
||||
|
||||
| | |
|
||||
|--- | --- |
|
||||
| **Returns** | null |
|
||||
| **Timeout** | *cannot timeout* |
|
||||
|
||||
***
|
||||
|
||||
# [cy.log( *message* )](#section-usage)
|
||||
|
||||
Print the message to the Command Log.
|
||||
|
||||
***
|
||||
|
||||
# [cy.log( *message*, *arguments* )](#section-arguments-usage)
|
||||
|
||||
Print the message to the Command Log, along with any arguments.
|
||||
|
||||
***
|
||||
|
||||
# Usage
|
||||
|
||||
## Log a message to the Command Log.
|
||||
|
||||
```javascript
|
||||
cy.log("Login successful")
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Arguments Usage
|
||||
|
||||
## Log a message with arguments to the Command Log.
|
||||
|
||||
```javascript
|
||||
// print previously saved variable 'events' to the Command Log.
|
||||
cy.log("events triggered", events)
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Command Log
|
||||
|
||||
## Print messages with arguments to the Command Log.
|
||||
|
||||
```javascript
|
||||
cy
|
||||
.log("log out any message we want here")
|
||||
.log("another message", ["one", "two", "three"])
|
||||
```
|
||||
|
||||
The commands above will display in the command log as:
|
||||
|
||||
<img width="560" alt="command log with cy.log" src="https://cloud.githubusercontent.com/assets/1271364/21321329/55389b3c-c5e2-11e6-8607-592683d520da.png">
|
||||
|
||||
When clicking on `log` within the command log, the console outputs the following:
|
||||
|
||||
<img width="746" alt="console display of cy.log" src="https://cloud.githubusercontent.com/assets/1271364/21321324/4f616dec-c5e2-11e6-8c2f-924e7bfd6f87.png">
|
||||
@@ -1,98 +0,0 @@
|
||||
slug: next
|
||||
excerpt: Get the next sibling of the DOM elements
|
||||
|
||||
Get the immediately following sibling of each DOM element in the set of matched DOM elements.
|
||||
|
||||
| | |
|
||||
|--- | --- |
|
||||
| **Returns** | the new DOM element(s) found by the command. |
|
||||
| **Timeout** | `cy.next` will retry for the duration of the [`defaultCommandTimeout`](https://on.cypress.io/guides/configuration#section-timeouts) |
|
||||
|
||||
***
|
||||
|
||||
# [cy.next()](#section-usage)
|
||||
|
||||
Get the next sibling of the elements.
|
||||
|
||||
***
|
||||
|
||||
# [cy.next( *selector* )](#section-selector-usage)
|
||||
|
||||
When a selector is provided, it retrieves the next sibling only if it matches that selector.
|
||||
|
||||
***
|
||||
|
||||
# Options
|
||||
|
||||
Pass in an options object to change the default behavior of `cy.next`.
|
||||
|
||||
**cy.next( *options* )**
|
||||
**cy.next( *selector*, *options* )**
|
||||
|
||||
Option | Default | Notes
|
||||
--- | --- | ---
|
||||
`log` | `true` | whether to display command in command log
|
||||
`timeout` | [`defaultCommandTimeout`](https://on.cypress.io/guides/configuration#section-timeouts) | Total time to retry getting the element
|
||||
|
||||
***
|
||||
|
||||
# Usage
|
||||
|
||||
## Find the element next to `.second`
|
||||
|
||||
```html
|
||||
<ul>
|
||||
<li>apples</li>
|
||||
<li class="second">oranges</li>
|
||||
<li>bananas</li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
```javascript
|
||||
//returns <li>bananas</li>
|
||||
cy.get(".second").next()
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Selector Usage
|
||||
|
||||
## Find the very next sibling of each li. Keep only the ones with a class `selected`.
|
||||
|
||||
```html
|
||||
<ul>
|
||||
<li>apples</li>
|
||||
<li>oranges</li>
|
||||
<li>bananas</li>
|
||||
<li class="selected">pineapples</li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
```javascript
|
||||
//returns <li>pineapples</li>
|
||||
cy.get("li").next(".selected")
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Command Log
|
||||
|
||||
## Find the element next to the active li
|
||||
|
||||
```javascript
|
||||
cy.get(".left-nav").find("li.active").next()
|
||||
```
|
||||
|
||||
The commands above will display in the command log as:
|
||||
|
||||
<img width="563" alt="screen shot 2015-11-29 at 12 42 07 pm" src="https://cloud.githubusercontent.com/assets/1271364/11458857/afcfddf2-9696-11e5-9405-0cd994f70d45.png">
|
||||
|
||||
When clicking on `next` within the command log, the console outputs the following:
|
||||
|
||||
<img width="547" alt="screen shot 2015-11-29 at 12 42 22 pm" src="https://cloud.githubusercontent.com/assets/1271364/11458858/b30b0a0a-9696-11e5-99b9-d785b597287c.png">
|
||||
|
||||
***
|
||||
|
||||
# Related
|
||||
|
||||
- [prev](https://on.cypress.io/api/prev)
|
||||
@@ -1,103 +0,0 @@
|
||||
slug: nextall
|
||||
excerpt: Get all following siblings of the DOM elements
|
||||
|
||||
Get all following siblings of each DOM element in the set of matched DOM elements.
|
||||
|
||||
| | |
|
||||
|--- | --- |
|
||||
| **Returns** | the new DOM element(s) found by the command. |
|
||||
| **Timeout** | `cy.nextAll` will retry for the duration of the [`defaultCommandTimeout`](https://on.cypress.io/guides/configuration#timeouts) |
|
||||
|
||||
***
|
||||
|
||||
# [cy.nextAll()](#usage)
|
||||
|
||||
Get all of the next siblings of the elements.
|
||||
|
||||
***
|
||||
|
||||
# [cy.nextAll( *selector* )](#selector-usage)
|
||||
|
||||
When a selector is provided, it retrieves all of the following siblings only if it matches that selector.
|
||||
|
||||
***
|
||||
|
||||
# Options
|
||||
|
||||
Pass in an options object to change the default behavior of `cy.nextAll`.
|
||||
|
||||
**cy.nextAll( *options* )**
|
||||
**cy.nextAll( *selector*, *options* )**
|
||||
|
||||
Option | Default | Notes
|
||||
--- | --- | ---
|
||||
`log` | `true` | whether to display command in command log
|
||||
`timeout` | [`defaultCommandTimeout`](https://on.cypress.io/guides/configuration#timeouts) | Total time to retry getting the element
|
||||
|
||||
***
|
||||
|
||||
# Usage
|
||||
|
||||
## Find all of the element's siblings following `.second`
|
||||
|
||||
```html
|
||||
<ul>
|
||||
<li>apples</li>
|
||||
<li class="second">oranges</li>
|
||||
<li>bananas</li>
|
||||
<li>pineapples</li>
|
||||
<li>grapes</li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
```javascript
|
||||
//returns [<li>bananas</li>, <li>pineapples</li>, <li>grapes</li>]
|
||||
cy.get(".second").nextAll()
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Selector Usage
|
||||
|
||||
## Find all of the following siblings of each li. Keep only the ones with a class `selected`.
|
||||
|
||||
```html
|
||||
<ul>
|
||||
<li>apples</li>
|
||||
<li>oranges</li>
|
||||
<li>bananas</li>
|
||||
<li class="selected">pineapples</li>
|
||||
<li>grapes</li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
```javascript
|
||||
//returns <li>pineapples</li>
|
||||
cy.get("li").nextAll(".selected")
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Command Log
|
||||
|
||||
## Find all elements following the active li
|
||||
|
||||
```javascript
|
||||
cy.get(".left-nav").find("li.active").nextAll()
|
||||
```
|
||||
|
||||
The commands above will display in the command log as:
|
||||
|
||||
<img width="560" alt="screen shot 2017-03-23 at 2 05 32 pm" src="https://cloud.githubusercontent.com/assets/1271364/24262886/e1513334-0fd1-11e7-93b1-b413a9390828.png">
|
||||
|
||||
When clicking on `nextAll` within the command log, the console outputs the following:
|
||||
|
||||
<img width="567" alt="screen shot 2017-03-23 at 2 05 52 pm" src="https://cloud.githubusercontent.com/assets/1271364/24262907/f2b7fe78-0fd1-11e7-921c-6eabf6e32abb.png">
|
||||
|
||||
***
|
||||
|
||||
# Related
|
||||
|
||||
- [next](https://on.cypress.io/api/next)
|
||||
- [nextUntil](https://on.cypress.io/api/nextuntil)
|
||||
- [prevAll](https://on.cypress.io/api/prevall)
|
||||
@@ -1,103 +0,0 @@
|
||||
slug: nextuntil
|
||||
excerpt: Get all following siblings of the DOM elements until another element
|
||||
|
||||
Get all following siblings of each DOM element in the set of matched DOM elements up to, but not including, the element matched by the selector
|
||||
|
||||
| | |
|
||||
|--- | --- |
|
||||
| **Returns** | the new DOM element(s) found by the command. |
|
||||
| **Timeout** | `cy.nextUntil` will retry for the duration of the [`defaultCommandTimeout`](https://on.cypress.io/guides/configuration#timeouts) |
|
||||
|
||||
***
|
||||
|
||||
# [cy.nextUntil( *selector* )](#usage)
|
||||
|
||||
Get all of the next siblings of the elements until the selector.
|
||||
|
||||
***
|
||||
|
||||
# [cy.nextUntil( *selector*, *filter )](#filter-usage)
|
||||
|
||||
When a filter is provided, it retrieves all of the following siblings up until the selector only if it matches that filter.
|
||||
|
||||
***
|
||||
|
||||
# [cy.nextUntil( *element* )](#element-usage)
|
||||
|
||||
Get all of the next siblings of the elements until the DOM node or jQuery object.
|
||||
|
||||
***
|
||||
|
||||
# [cy.nextUntil( *element*, *filter )](#element-filter-usage)
|
||||
|
||||
When a filter is provided, it retrieves all of the following siblings up until the DOM node or jQuery object only if it matches that filter.
|
||||
|
||||
***
|
||||
|
||||
# Options
|
||||
|
||||
Pass in an options object to change the default behavior of `cy.nextUntil`.
|
||||
|
||||
**cy.nextUntil( *selector*, *options* )**
|
||||
**cy.nextUntil( *selector*, *filter*, *options* )**
|
||||
**cy.nextUntil( *element*, *options* )**
|
||||
**cy.nextUntil( *element*, *filter*, *options* )**
|
||||
|
||||
Option | Default | Notes
|
||||
--- | --- | ---
|
||||
`log` | `true` | whether to display command in command log
|
||||
`timeout` | [`defaultCommandTimeout`](https://on.cypress.io/guides/configuration#timeouts) | Total time to retry getting the element
|
||||
|
||||
***
|
||||
|
||||
# Usage
|
||||
|
||||
## Find all of the element's siblings following `#veggies` until `#nuts`
|
||||
|
||||
```html
|
||||
<ul>
|
||||
<li id="fruits" class="header">Fruits</li>
|
||||
<li>apples</li>
|
||||
<li>oranges</li>
|
||||
<li>bananas</li>
|
||||
<li id="veggies" class="header">Vegetables</li>
|
||||
<li>cucumbers</li>
|
||||
<li>carrots</li>
|
||||
<li>corn</li>
|
||||
<li id="nuts" class="header">Nuts</li>
|
||||
<li>walnuts</li>
|
||||
<li>cashews</li>
|
||||
<li>almonds</li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
```javascript
|
||||
//returns [<li>cucumbers</li>, <li>carrots</li>, <li>corn</li>]
|
||||
cy.get("#veggies").nextUntil("#nuts")
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Command Log
|
||||
|
||||
## Find all of the element's siblings following `#veggies` until `#nuts`
|
||||
|
||||
```javascript
|
||||
cy.get("#veggies").nextUntil("#nuts")
|
||||
```
|
||||
|
||||
The commands above will display in the command log as:
|
||||
|
||||
<img width="563" alt="screen shot 2017-03-23 at 2 17 52 pm" src="https://cloud.githubusercontent.com/assets/1271364/24263453/96a8c0b6-0fd3-11e7-8a66-da9177ca94a7.png">
|
||||
|
||||
When clicking on `nextUntil` within the command log, the console outputs the following:
|
||||
|
||||
<img width="514" alt="screen shot 2017-03-23 at 2 18 01 pm" src="https://cloud.githubusercontent.com/assets/1271364/24263481/a20ce2f2-0fd3-11e7-881c-f6bf8d652263.png">
|
||||
|
||||
***
|
||||
|
||||
# Related
|
||||
|
||||
- [next](https://on.cypress.io/api/next)
|
||||
- [nextAll](https://on.cypress.io/api/nextall)
|
||||
- [prevUntil](https://on.cypress.io/api/prevuntil)
|
||||
@@ -1,119 +0,0 @@
|
||||
slug: children
|
||||
excerpt: Get the children DOM elements of the DOM elements
|
||||
|
||||
Get the children of each DOM element in the set of matched DOM elements.
|
||||
|
||||
| | |
|
||||
|--- | --- |
|
||||
| **Returns** | the new DOM element(s) found by the command. |
|
||||
| **Timeout** | `cy.children` will retry for the duration of the [`defaultCommandTimeout`](https://on.cypress.io/guides/configuration#section-timeouts) |
|
||||
|
||||
***
|
||||
|
||||
# [cy.children()](#section-usage)
|
||||
|
||||
Get the children of each DOM element in the set of matched DOM elements.
|
||||
|
||||
***
|
||||
|
||||
# [cy.children( *selector* )](#section-selector-usage)
|
||||
|
||||
The `.children()` method optionally accepts a selector expression. If the selector is supplied, the DOM elements will be filtered by testing whether they match it.
|
||||
|
||||
***
|
||||
|
||||
# Options
|
||||
|
||||
Pass in an options object to change the default behavior of `cy.children`.
|
||||
|
||||
**cy.children( *options* )**
|
||||
**cy.children( *selector*, *options* )**
|
||||
|
||||
Option | Default | Notes
|
||||
--- | --- | ---
|
||||
`log` | `true` | whether to display command in command log
|
||||
`timeout` | [`defaultCommandTimeout`](https://on.cypress.io/guides/configuration#section-timeouts) | Total time to retry getting the element
|
||||
|
||||
|
||||
***
|
||||
|
||||
# Usage
|
||||
|
||||
## Get the children of the "secondary-nav"
|
||||
|
||||
```html
|
||||
<ul class="primary-nav">
|
||||
<li class="about">About</li>
|
||||
<li class="services">Services
|
||||
<ul class="secondary-nav">
|
||||
<li class="services-1">Web Design</li>
|
||||
<li class="services-2">Print Design
|
||||
<ul class="tertiary-nav">
|
||||
<li class="item-1">Signage</li>
|
||||
<li class="item-2">T-Shirt</li>
|
||||
<li class="item-3">Business Cards</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="services-3">Logo Design</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="Contact">Contact</li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
```javascript
|
||||
// returns [
|
||||
// <li class="services-1"></li>,
|
||||
// <li class="services-2"></li>,
|
||||
// <li class="services-3"></li>
|
||||
// ]
|
||||
cy.get("ul.secondary-nav").children()
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
|
||||
# Selector Usage
|
||||
|
||||
## Get the children with class "active"
|
||||
|
||||
```html
|
||||
<div>
|
||||
<ul>
|
||||
<li class="active">Unit Testing</li>
|
||||
<li>Integration Testing</li>
|
||||
</ul>
|
||||
</div>
|
||||
```
|
||||
|
||||
```javascript
|
||||
// returns [<li class="active">Unit Testing</li>]
|
||||
cy.get("ul").children(".active")
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Command Log
|
||||
|
||||
## Assert that there should be 8 children elements in a nav
|
||||
|
||||
```javascript
|
||||
cy.get(".left-nav>.nav").children().should("have.length", 8)
|
||||
```
|
||||
|
||||
The commands above will display in the command log as:
|
||||
|
||||
<img width="521" alt="screen shot 2015-11-27 at 1 52 26 pm" src="https://cloud.githubusercontent.com/assets/1271364/11447069/2b0f8a7e-950e-11e5-96b5-9d82d9fdddec.png">
|
||||
|
||||
When clicking on the `children` command within the command log, the console outputs the following:
|
||||
|
||||
<img width="542" alt="screen shot 2015-11-27 at 1 52 41 pm" src="https://cloud.githubusercontent.com/assets/1271364/11447071/2e9252bc-950e-11e5-9a32-e5860da89160.png">
|
||||
|
||||
***
|
||||
|
||||
# Related
|
||||
|
||||
- [parent](https://on.cypress.io/api/parent)
|
||||
- [parents](https://on.cypress.io/api/parents)
|
||||
- [next](https://on.cypress.io/api/next)
|
||||
- [siblings](https://on.cypress.io/api/siblings)
|
||||
@@ -1,61 +0,0 @@
|
||||
slug: not
|
||||
excerpt: Remove DOM elements from the set of DOM elements
|
||||
|
||||
Remove DOM elements from the set of DOM elements. Opposite of [`cy.filter()`](https://on.cypress.io/api/filter)
|
||||
|
||||
| | |
|
||||
|--- | --- |
|
||||
| **Returns** | the new DOM element(s) found by the command. |
|
||||
| **Timeout** | `cy.not` will retry for the duration of the [`defaultCommandTimeout`](https://on.cypress.io/guides/configuration#section-timeouts) |
|
||||
|
||||
***
|
||||
|
||||
# [cy.not( *selector* )](#section-selector-usage)
|
||||
|
||||
Remove the element(s) by it's selector from the elements
|
||||
|
||||
***
|
||||
|
||||
# Options
|
||||
|
||||
Pass in an options object to change the default behavior of `cy.not`.
|
||||
|
||||
**cy.not( *selector*, *options* )**
|
||||
|
||||
Option | Default | Notes
|
||||
--- | --- | ---
|
||||
`log` | `true` | whether to display command in command log
|
||||
`timeout` | [`defaultCommandTimeout`](https://on.cypress.io/guides/configuration#section-timeouts) | Total time to retry getting the element
|
||||
|
||||
***
|
||||
|
||||
# Selector Usage
|
||||
|
||||
## Filter the current subject to the elements that do not have class `active`.
|
||||
|
||||
```javascript
|
||||
cy.get(".left-nav>.nav").find(">li").not(".active")
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Command Log
|
||||
|
||||
## Find all buttons that are not of type submit
|
||||
|
||||
```javascript
|
||||
cy.get("form").find("button").not("[type='submit']")
|
||||
```
|
||||
|
||||
The commands above will display in the command log as:
|
||||
|
||||
<img width="572" alt="screen shot 2015-11-29 at 12 36 49 pm" src="https://cloud.githubusercontent.com/assets/1271364/11458817/0a846c3c-9696-11e5-9901-5f4376629e75.png">
|
||||
|
||||
When clicking on `not` within the command log, the console outputs the following:
|
||||
|
||||
<img width="689" alt="screen shot 2015-11-29 at 12 37 39 pm" src="https://cloud.githubusercontent.com/assets/1271364/11458819/0d6870f6-9696-11e5-9364-2685b8ffc71b.png">
|
||||
|
||||
***
|
||||
# Related
|
||||
|
||||
- [filter](https://on.cypress.io/api/filter)
|
||||
@@ -1,80 +0,0 @@
|
||||
slug: parent
|
||||
excerpt: Get the parent DOM element of the DOM elements
|
||||
|
||||
Get the parent DOM element of the DOM elements.
|
||||
|
||||
| | |
|
||||
|--- | --- |
|
||||
| **Returns** | the new DOM element(s) found by the command. |
|
||||
| **Timeout** | `cy.parent` will retry for the duration of the [`defaultCommandTimeout`](https://on.cypress.io/guides/configuration#section-timeouts) |
|
||||
|
||||
***
|
||||
|
||||
# [cy.parent()](#section-usage)
|
||||
|
||||
Get the parent of each element in the current set of matched elements.
|
||||
|
||||
***
|
||||
|
||||
# [cy.parent( *selector* )](#section-selector-usage)
|
||||
|
||||
Get the parent of each element in the current set of matched elements filtered by selector.
|
||||
|
||||
***
|
||||
|
||||
# Options
|
||||
|
||||
Pass in an options object to change the default behavior of `cy.parent`.
|
||||
|
||||
**cy.parent( *options* )**
|
||||
**cy.parent( *selector*, *options* )**
|
||||
|
||||
Option | Default | Notes
|
||||
--- | --- | ---
|
||||
`log` | `true` | whether to display command in command log
|
||||
`timeout` | [`defaultCommandTimeout`](https://on.cypress.io/guides/configuration#section-timeouts) | Total time to retry getting the element
|
||||
|
||||
***
|
||||
|
||||
# Usage
|
||||
|
||||
## Get the parent of the active `li`
|
||||
|
||||
```javascript
|
||||
cy.get("li.active").parent()
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Selector Usage
|
||||
|
||||
## Get the parent with class `nav` of the active `li`
|
||||
|
||||
```javascript
|
||||
cy.get("li.active").parent(".nav")
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Command Log
|
||||
|
||||
## Assert on the parent of the active li
|
||||
|
||||
```javascript
|
||||
cy.get("li.active").parent().should("have.class", "nav")
|
||||
```
|
||||
|
||||
The commands above will display in the command log as:
|
||||
|
||||
<img width="531" alt="screen shot 2015-11-27 at 1 58 32 pm" src="https://cloud.githubusercontent.com/assets/1271364/11447127/0d9ab5a8-950f-11e5-90ae-c317dd83aa65.png">
|
||||
|
||||
When clicking on the `parent` command within the command log, the console outputs the following:
|
||||
|
||||
<img width="440" alt="screen shot 2015-11-27 at 1 58 44 pm" src="https://cloud.githubusercontent.com/assets/1271364/11447130/11b22c02-950f-11e5-9b82-cc3b2ff8548e.png">
|
||||
|
||||
***
|
||||
|
||||
# Related
|
||||
|
||||
- [parents](https://on.cypress.io/api/parents)
|
||||
- [children](https://on.cypress.io/api/children)
|
||||
@@ -1,78 +0,0 @@
|
||||
slug: parents
|
||||
excerpt: Get the parents DOM elements of the DOM elements
|
||||
|
||||
Get the parents DOM elements of the DOM elements.
|
||||
|
||||
| | |
|
||||
|--- | --- |
|
||||
| **Returns** | the new DOM element(s) found by the command. |
|
||||
| **Timeout** | `cy.parents` will retry for the duration of the [`defaultCommandTimeout`](https://on.cypress.io/guides/configuration#section-timeouts) |
|
||||
|
||||
***
|
||||
|
||||
# [cy.parents()](#section-usage)
|
||||
|
||||
Get the ancestors of each element in the current set of matched elements.
|
||||
|
||||
***
|
||||
|
||||
# [cy.parents( *selector* )](#section-selector-usage)
|
||||
|
||||
Get the ancestors of each element in the current set of matched elements filtered by selector
|
||||
|
||||
***
|
||||
|
||||
# Options
|
||||
|
||||
Pass in an options object to change the default behavior of `cy.parents`.
|
||||
|
||||
**cy.parents( *options* )**
|
||||
**cy.parents( *selector*, *options* )**
|
||||
|
||||
Option | Default | Notes
|
||||
--- | --- | ---
|
||||
`log` | `true` | whether to display command in command log
|
||||
`timeout` | [`defaultCommandTimeout`](https://on.cypress.io/guides/configuration#section-timeouts) | Total time to retry getting the element
|
||||
|
||||
***
|
||||
|
||||
# Usage
|
||||
|
||||
## Get the parents of the active `li`
|
||||
|
||||
```javascript
|
||||
cy.get("li.active").parents()
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Selector Usage
|
||||
|
||||
## Get the parents with class `nav` of the active `li`
|
||||
|
||||
```javascript
|
||||
cy.get("li.active").parents(".nav")
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Command Log
|
||||
|
||||
## Get the parents of the active `li`
|
||||
|
||||
```javascript
|
||||
cy.get("li.active").parents()
|
||||
```
|
||||
|
||||
<img width="531" alt="screen shot 2015-11-27 at 2 02 59 pm" src="https://cloud.githubusercontent.com/assets/1271364/11447168/be286244-950f-11e5-82e8-9a2a6d1d08e8.png">
|
||||
|
||||
When clicking on the `parents` command within the command log, the console outputs the following:
|
||||
|
||||
<img width="537" alt="screen shot 2015-11-27 at 2 03 32 pm" src="https://cloud.githubusercontent.com/assets/1271364/11447171/c1ba5ef8-950f-11e5-9f2d-7fbd0b142649.png">
|
||||
|
||||
***
|
||||
|
||||
# Related
|
||||
|
||||
- [parent](https://on.cypress.io/api/parent)
|
||||
- [children](https://on.cypress.io/api/children)
|
||||
@@ -1,103 +0,0 @@
|
||||
slug: parentsuntil
|
||||
excerpt: Get all ancestors of the DOM elements until another element
|
||||
|
||||
Get all ancestors of each DOM element in the set of matched DOM elements up to, but not including, the element matched by the selector
|
||||
|
||||
| | |
|
||||
|--- | --- |
|
||||
| **Returns** | the new DOM element(s) found by the command. |
|
||||
| **Timeout** | `cy.parentsUntil` will retry for the duration of the [`defaultCommandTimeout`](https://on.cypress.io/guides/configuration#timeouts) |
|
||||
|
||||
***
|
||||
|
||||
# [cy.parentsUntil( *selector* )](#usage)
|
||||
|
||||
Get all of the ancestors of the elements until the selector.
|
||||
|
||||
***
|
||||
|
||||
# [cy.parentsUntil( *selector*, *filter )](#filter-usage)
|
||||
|
||||
When a filter is provided, it retrieves all of the ancestors up until the selector only if it matches that filter.
|
||||
|
||||
***
|
||||
|
||||
# [cy.parentsUntil( *element* )](#element-usage)
|
||||
|
||||
Get all of the ancestors of the elements until the DOM node or jQuery object.
|
||||
|
||||
***
|
||||
|
||||
# [cy.parentsUntil( *element*, *filter )](#element-filter-usage)
|
||||
|
||||
When a filter is provided, it retrieves all of the ancestors up until the DOM node or jQuery object only if it matches that filter.
|
||||
|
||||
***
|
||||
|
||||
# Options
|
||||
|
||||
Pass in an options object to change the default behavior of `cy.parentsUntil`.
|
||||
|
||||
**cy.parentsUntil( *selector*, *options* )**
|
||||
**cy.parentsUntil( *selector*, *filter*, *options* )**
|
||||
**cy.parentsUntil( *element*, *options* )**
|
||||
**cy.parentsUntil( *element*, *filter*, *options* )**
|
||||
|
||||
Option | Default | Notes
|
||||
--- | --- | ---
|
||||
`log` | `true` | whether to display command in command log
|
||||
`timeout` | [`defaultCommandTimeout`](https://on.cypress.io/guides/configuration#timeouts) | Total time to retry getting the element
|
||||
|
||||
***
|
||||
|
||||
# Usage
|
||||
|
||||
## Find all of the `.active` element's ancestors until `.nav`
|
||||
|
||||
```html
|
||||
<ul class="nav">
|
||||
<li>
|
||||
<a href="#">Clothes</a>
|
||||
<ul class="menu">
|
||||
<li>
|
||||
<a href="/shirts">Shirts</a>
|
||||
</li>
|
||||
<li class="active">
|
||||
<a href="/pants">Pants</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
```javascript
|
||||
//returns [ul.menu, li]
|
||||
cy.get(".active").parentsUntil(".nav")
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# Command Log
|
||||
|
||||
## Find all of the `.active` element's ancestors until `.nav`
|
||||
|
||||
```javascript
|
||||
cy.get(".active").parentsUntil(".nav")
|
||||
```
|
||||
|
||||
The commands above will display in the command log as:
|
||||
|
||||
<img width="561" alt="screen shot 2017-03-23 at 2 37 31 pm" src="https://cloud.githubusercontent.com/assets/1271364/24264301/516d5fd6-0fd6-11e7-9ab7-b55b211acde3.png">
|
||||
|
||||
When clicking on `parentsUntil` within the command log, the console outputs the following:
|
||||
|
||||
<img width="523" alt="screen shot 2017-03-23 at 2 37 39 pm" src="https://cloud.githubusercontent.com/assets/1271364/24264309/60cc75de-0fd6-11e7-97b4-d0aa184b0ba6.png">
|
||||
|
||||
***
|
||||
|
||||
# Related
|
||||
|
||||
- [parent](https://on.cypress.io/api/parent)
|
||||
- [parents](https://on.cypress.io/api/parents)
|
||||
- [prevUntil](https://on.cypress.io/api/prevuntil)
|
||||
- [nextUntil](https://on.cypress.io/api/nextuntil)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user