@bahmutov
@digitaldrummerj
"Interactive Mode"
"Headless Mode"
# work locally
git add .
git commit -m "feature A + tests"
git push
# CI runs all tests on each commit
cypress run
CI without tests is like never changing the oil in your car. It is just a matter of time before it blows up
said by me, just now
(Justin)
Nothing to install, Cypress should just work
apt-get install xvfb libgtk-3-dev \
libnotify-dev libgconf-2-4 \
libnss3 libxss1 libasound2
Nothing to install, Cypress should just work
Use one of our prepared images from
https://github.com/cypress-io/cypress-docker-images
npx cypress verify
# either exits fine
# or shows the OS / install error
"cypress run" calls "verify" when it runs for the very first time on the machine
npx cypress cache path
/root/.cache/Cypress
/root/.cache/Cypress/3.3.1/Cypress/Cypress --smoke-test --ping=101
101
ldd /home/person/.cache/Cypress/3.3.1/Cypress/Cypress
linux-vdso.so.1 (0x00007ffe9eda0000)
libnode.so => /home/person/.cache/Cypress/3.3.1/Cypress/libnode.so (0x00007fecb43c8000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fecb41ab000)
libgtk-3.so.0 => not found
libgdk-3.so.0 => not found
...
this is what "cypress verify" does
Performance
(on CI server)
Performance
(on CI server)
Performance
npx cypress cache path
(on CI server)
Performance
(on CI server)
Performance
- checkout
- restore_cache:
keys:
- cache-{{ checksum "package.json" }}
- run: npm ci
- run: npx cypress verify
- save_cache:
key: cache-{{ checksum "package.json" }}
paths:
- ~/.npm
- ~/.cache
typical CI config file
npm install
cypress run
npm install
cypress run
npm install
cypress run
npm install
cypress run
npm install
cypress run
--record --parallel
cypress run
--record --parallel
cypress run
--record --parallel
Cypress Dashboard
Load balancing spec files
npm install -D npm-run-all
# run in serial lint:ci and build:prod
npx run-s lint:ci build:prod
Cross Platform Run Tasks in Serial or Parallel
npm install -D start-server-and-test
# run start server, wait for localhost:4200, and run cy:run
# "ci:start-server": "angular-http-server --path ./dist/ngws -p 4200",
# "cy:run": "cypress run",
npx start-server-and-test ci:start-server 4200 cy:run
Start Server and Wait for Response
npm install -D angular-http-server
# start server in ./dist/ngws on port 4200
angular-http-server --path ./dist/ngws -p 4200
Angular Server with Deep Linking
"ci": "run-s lint:ci build:prod ci:cy-run",
"lint:ci": "ng lint",
"build:prod": "ng build --prod",
"ci:cy-run": "start-server-and-test ci:start-server 4200 cy:run",
"ci:start-server": "angular-http-server --path ./dist/ngws -p 4200",
"cy:run": "cypress run"
Lint, Unit Tests, Prod Build, Cypress Tests
package.json
service hook
language: node_js
node_js:
- "lts/*"
addons:
chrome: stable
apt:
packages:
- libgconf-2-4
.travis.yml
cache:
npm: true
directories:
- ~/.npm
- ./node_modules
- ~/.cache
override:
- npm ci
- npm run cy:verify
.travis.yml
install:
- npm ci
script:
- npm run ci
.travis.yml
git push
git add .travis.yml
git commit .travis.yml
service hook
run tests / builds
fresh environments
npm install mocha-teamcity-reporter mocha
"ci:teamcity-cy-run": "cypress run --reporter mocha-teamcity-reporter"
package.json
git push
service hook
run tests / builds
version: 2.1
orbs:
# our orb will take care of environment
# install, caching, build, etc
cypress: cypress-io/cypress@1
workflows:
build:
jobs:
# "cypress" is the name of the imported orb
# "run" is the name of the job defined in Cypress orb
- cypress/run
version: 2.1
orbs:
cypress: cypress-io/cypress@1
workflows:
build:
jobs:
- cypress/install:
build: 'npm run build'
- cypress/run:
requires:
- cypress/install
record: true # record results on Cypress Dashboard
parallel: true # split all specs across machines
parallelism: 4 # use 4 CircleCI machines to finish quickly
start: 'npm start' # start server before running tests
{
"baseUrl": "http://localhost:8080",
"video": false,
"env": {
"username": "Joe Tester",
"testAccount": {
"id": "1123",
"admin": false
}
}
}
{
"baseUrl": "https://staging.server.com",
"video": true,
"env": {
"username": "Joe Tester",
"testAccount": {
"id": "1123",
"admin": false
}
}
}
local
staging
cypress.json
{
"baseUrl": "http://localhost:8080",
"video": false,
"env": {
"username": "Joe Tester",
"testAccount": {
"id": "1123",
"admin": false
}
}
}
cypress.json
Locally run tests against "localhost:8000"
{
"baseUrl": "http://localhost:8080"
}
cypress run --config baseUrl=https://staging.server.com
Testing staging server
export CYPRESS_baseUrl=https://staging.server.com
cypress run
CLI
env
cy.visit('/')
cypress.json
Command timeout
{
"baseUrl": "http://localhost:8080",
"defaultCommandTimeout": 1000
}
cypress run --config \
baseUrl=https://staging.server.com,defaultCommandTimeout=5000
Testing staging server
export CYPRESS_baseUrl=https://staging.server.com
export CYPRESS_defaultCommandTimeout=5000
cypress run
CLI
env
cy.visit('/')
cypress.json
// cypress/plugins/index.js
module.exports = (on, config) => {
config.baseUrl = 'https://staging.server.com/app/'
// change more options ...
return config
}
Use plugins file
cypress.json
< environment variable
< CLI parameter
< plugin
< run-time Cypress.config(...)
Resolution preference
Wins
cypress run --config "/path/to/config.json"
Coming soon (issue #1369)
Need test user name and password
const name = Cypress.env('username')
const pass = Cypress.env('password')
cy.get('#name').type(name)
cy.get('#pass').type(pass)
// log in
How do store and use username and password environment variables?
{
"env": {
"username": "Joe Tester",
"password": ""
}
}
cypress.json
const name = Cypress.env('username')
expect(name, 'username').to.be.a('string')
.and.be.not.empty
const pass = Cypress.env('password')
// password will not appear in the UI
assert(pass, 'forgot password')
cy.get('#name').type(name)
cy.get('#pass').type(pass)
// log in
test
Password is sensitive, user name is not
{
"env": {
"username": "Joe Tester",
"password": ""
}
}
cypress.json
cypress run --env password=***
export CYPRESS_password=****
cypress run
CLI
env
⚠️ 🛑
Cypress automatically puts all unknown env variables that start with CYPRESS_ into env vars
✅
; create a section for each app settings
[my-app]
CYPRESS_password=test1234
~/.as-a/.as-a.ini
$ npm i -g as-a
$ as-a my-app cypress run
loads variables from section "my-app" and runs Cypress with those environment variables
cypress run --record --key=***
# use CI private variables
# CYPRESS_RECORD_KEY=****
cypress run --record
CLI
env
⚠️ 🛑
✅
How to pass your private record key
Security
Be careful with forked pull requests, a hacker might simply do "echo $CYPRESS_RECORD_KEY"
cypress/
integration/
main/
spec.js
spec2.js
admin/
auth-spec.js
prod/
read-only-spec.js
# run tests safe to run in production
$ npx cypress run --spec "cypress/integration/prod/*.js"
// cypress/support/index.js
Cypress.isDevelopment = () =>
// NODE_ENV is set in plugins file
Cypress.env('NODE_ENV') === 'development'
// cypress/integration/main_spec.js
if (!Cypress.isDevelopment()) {
it('has robots.txt', () => {
cy.request('/robots.txt').its('body')
.should('include', 'Disallow: /ja/')
.and('include', 'Disallow: /zh-cn/')
.and('include', 'Disallow: /pt-br/')
})
}
// cypress/support/index.js
Cypress.isDevelopment = () =>
// NODE_ENV is set in plugins file
Cypress.env('NODE_ENV') === 'development'
// cypress/integration/main_spec.js
it('shows initial users', () => {
if (!Cypress.isDevelopment()) {
cy.server()
.route('/users', 'fixture:ten-users')
}
cy.get('.list').should('have.length', 10)
})
closed issue #777
# set on CI CYPRESS_RECORD_KEY=...
cypress run --record
1. First, trying to use "git" commands
branch: 'git rev-parse --abbrev-ref HEAD'
author: 'git show -s --pretty=%an'
2. If Git information is unavailable, check env variables (CI-specific)
branch: BITBUCKET_BRANCH (BitBucket)
branch: CIRCLE_BRANCH (CircleCI)
ci_provider.js
3. If env variables are not found, use fallback env variables COMMIT_INFO_*
COMMIT_INFO_BRANCH=$MY_CI_BRANCH_NAME
COMMIT_INFO_SHA=$MY_CI_COMMIT_REF
...
cypress run --record
your CI file
branch: COMMIT_INFO_BRANCH
message: COMMIT_INFO_MESSAGE
email: COMMIT_INFO_EMAIL
author: COMMIT_INFO_AUTHOR
sha: COMMIT_INFO_SHA
timestamp: COMMIT_INFO_TIMESTAMP
remote: COMMIT_INFO_REMOTE
env vars
$ docker run \
-e COMMIT_INFO_BRANCH=develop \
-e COMMIT_INFO_SHA=e5d9eb66474bc0b681da9240aa5a457fe17bc8f3 \
<container name>
cypress run
uses headless Electron (old version)
cypress run --headed
uses headed Electron just like "cypress open"
📹
📹
🚫
cypress run --browser chrome
uses headed Chrome just like "cypress open"
📹
🚫
There might be race condition between the test and the application. CI with its speed difference catches the problem.
Control the tests more:
If you mocking XHR calls, make sure the API isn't running locally when running tests
Automatically saves JSON file in "cypress/logs"
{
"specName": "failing-spec.js",
"title": "loads the About tab",
"suiteName": "Website",
"testName": "Website loads the About tab",
"testError": "Timed out retrying: Expected to find content: 'Join Us' but never did.",
"testCommands": [
"visit",
"new url https://www.company.com/#/",
"contains a.nav-link, About",
"click",
"new url https://www.company.com/#/about",
"hash",
"assert expected **#/about** to equal **#/about**",
"contains Join Us",
"assert expected **body :not(script):contains(**'Join Us'**), [type='submit'][value~='Join Us']** to exist in the DOM"
],
"screenshot": "loads-the-about-tab.png"
}
cypress run --browser=chrome
Zach Bloomquist
Point Linux XVFB at your local X11 server
export DISPLAY=<your IP X11 display>
cypress run --headed
closed issue #4722
If CI container is low on CPU, then video starts dropping frames or stops completely. Use more powerful CI machine.