Workshop Record: YouTube Video
We have a simple ToDo application deployed in different environments:
Dev Environment: http://devtodo.monfared.io/
Stag Environment: http://stagtodo.monfared.io/
Using Cypress, we will create a test automation framework with the following features:
- The Page Object Model is a pattern
- BDD (Cucumber) support
- Multi-environment and multi-browser testing support
- Create reports that include videos and screenshots
- Test results dashboard with options to compare, analyze history, and generate graphs.
- CI integration
As an example, the following are two requirements (Acceptance Criteria) that we will write tests for.
AC1
Scenario: Add todo
Given I open the Todo page app
When I add a todo with text "New Todo"
Then Verify last todo to match "New Todo"
And Verify remaining text to match "6 of 6 remaining"
AC2
Scenario: Check first todo
Given I open the Todo page app
When I check the todo checkbox with index 0
Then Verify checkbox with index 0 to be chechked
And Verify remaining text to match "4 of 5 remaining"
npm init -y
npm install cypress --save-dev
npx cypress open
npm install @bahmutov/cypress-esbuild-preprocessor --save-dev
npm install @badeball/cypress-cucumber-preprocessor --save-dev
cypress.config.js
const { defineConfig } = require("cypress");
const createBundler = require("@bahmutov/cypress-esbuild-preprocessor");
const preprocessor = require("@badeball/cypress-cucumber-preprocessor");
const createEsbuildPlugin = require("@badeball/cypress-cucumber-preprocessor/esbuild");
module.exports = defineConfig({
e2e: {
setupNodeEvents(on, config) {
on("file:preprocessor",
createBundler({
plugins: [createEsbuildPlugin.default(config)],
}));
preprocessor.addCucumberPreprocessorPlugin(on, config);
return config;
},
specPattern: "**/*.feature",
},
});
package.json
"cypress-cucumber-preprocessor": {
"step_definitions": "cypress/support/step_definitions/",
"nonGlobalStepDefinitions": false
}
e2e/pages/todoPage/elements.js
module.exports = {
TODOPAGE:{
REMAINING_TEXT: ".ng-binding",
CHECKBOX: "input[type=checkbox]",
CHECKBOX_TEXT: "li>span",
NEW_TODO_FIELD: "#sampletodotext",
ADD_BUTTON: "#addbutton"
}
}
e2e/pages/todoPage/todoPage.js
var elements = require('./elements')
class TodoPage {
typeInNewTodoField(value) {
return cy.get(elements.TODOPAGE.NEW_TODO_FIELD).type(value)
}
clickAddButton() {
return cy.get(elements.TODOPAGE.ADD_BUTTON).click()
}
getRemainingText() {
return cy.get(elements.TODOPAGE.REMAINING_TEXT).invoke('text')
}
clickOnCheckBox(index) {
return cy.get(elements.TODOPAGE.CHECKBOX).eq(index).check()
}
getCheckboxText(index) {
return cy.get(elements.TODOPAGE.CHECKBOX_TEXT).eq(index).invoke('text')
}
verifyCheckboxToBeChecked(index) {
return cy.get(elements.TODOPAGE.CHECKBOX).eq(index).should("be.checked")
}
getLastTodoText() {
return cy.get(elements.TODOPAGE.CHECKBOX_TEXT).last().invoke('text')
}
}
export default TodoPage
cypress/support/step_definitions/steps.js
import { Given, When, Then } from '@badeball/cypress-cucumber-preprocessor'
import TodoPage from '../../e2e/pages/todoPage/todoPage'
const todoPage = new TodoPage()
Given('I open the Todo page app', () => {
// cy.visit('/')
cy.visit('http://stagtodo.monfared.io/')
cy.contains('Environment Todo')
})
When(
'I add a todo with text {string}',
(query) => {
todoPage.typeInNewTodoField(query)
todoPage.clickAddButton(query)
}
)
When(
'I check the todo checkbox with index {int}',
(index) => {
todoPage.clickOnCheckBox(index)
}
)
Then(
'Verify last todo to match {string}', (value) => {
todoPage.getLastTodoText().then((text) => {
expect(text).to.include(value)
})
}
)
Then('Verify remaining text to match {string}', (value) => {
todoPage.getRemainingText().then((text) => {
expect(text).to.include(value)
})
})
Then('Verify checkbox with index {} to be chechked', (index) => {
todoPage.verifyCheckboxToBeChecked(index)
})
e2e/features/AddTodo.feature
Feature: Add todo and verifying it to be added
Scenario: Add todo
Given I open the Todo page app
When I add a todo with text "New Todo"
Then Verify last todo to match "New Todo"
And Verify remaining text to match "6 of 6 remaining"
e2e/features/CheckTodo.feature
Feature: Check todos and verifying them to be checked
Scenario: Check first todo
Given I open the Todo page app
When I check the todo checkbox with index 0
Then Verify checkbox with index 0 to be chechked
And Verify remaining text to match "4 of 5 remaining"
package.json
"scripts": {
"cy:dev:chrome": "cypress run --config baseUrl=http://devtodo.monfared.io/ --browser chrome --record",
"cy:stag:chrome": "cypress run --config baseUrl=http://stagtodo.monfared.io/ --browser chrome --record",
"cy:dev:firefox": "cypress run --config baseUrl=http://devtodo.monfared.io/ --browser firefox --record",
"cy:stag:firefox": "cypress run --config baseUrl=http://stagtodo.monfared.io/ --browser firefox --record",
"cy:headed": "cypress run --config baseUrl=http://devtodo.monfared.io/ --headed",
"cy:test": "cypress run --config baseUrl=http://devtodo.monfared.io/"
}
Given('I open the Todo page app', () => {
cy.visit('/')
// cy.visit('http://stagtodo.monfared.io/')
cy.contains('Environment Todo')
})
cypress.config.js
module.exports = defineConfig({
e2e: {
setupNodeEvents(on, config) {
...
},
...,
videosFolder: "cypress/reports/videos",
screenshotsFolder: "cypress/reports/screenshots",
},
})
npm install cypress-mochawesome-reporter --save-dev
cypress.config.js
module.exports = defineConfig({
e2e: {
setupNodeEvents(on, config) {
...,
require('cypress-mochawesome-reporter/plugin')(on);
},
...,
reporter: 'cypress-mochawesome-reporter',
reporterOptions: {
charts: true,
reportPageTitle: 'custom-title',
embeddedScreenshots: true,
inlineAssets: true,
saveAllAttempts: false,
},
},
})
e2e.js
import 'cypress-mochawesome-reporter/register';
https://cloud.cypress.io/
module.exports = defineConfig({
e2e: {
setupNodeEvents(on, config) {
...
},
...,
projectId: "xxxxxx",
},
})
"CYPRESS_RECORD_KEY" > From cypress dashboard
.github/workflows/main.yml
name: Run tests against STAG and PROD environment
on:
push:
branches: [stag, master]
jobs:
Test-on-Chrome:
runs-on: ubuntu-latest
steps:
- name: Checkout GitCode
uses: actions/[email protected]
- name: Install dependencies
uses: cypress-io/[email protected]
with:
runTests: false
- name: Run Cypress Tests
uses: cypress-io/[email protected]
with:
record: true
parallel: true
command: "npm run cy:stag:chrome"
env:
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
- name: Upload Videos to Build Artifacts
uses: actions/[email protected]
if: always()
with:
name: cypress-videos-chrome
path: "${{ github.workspace }}/cypress/reports/videos/"
- name: Upload Screenshots to Build Artifacts
uses: actions/[email protected]
if: failure()
with:
name: cypress-screenshots-chrome
path: "${{ github.workspace }}/cypress/reports/screenshots/"
- name: Upload Mocha report to Build Artifacts
uses: actions/[email protected]
if: always()
with:
name: cypress-mocha-chrome
path: "${{ github.workspace }}/cypress/reports/html/"
Test-on-Firefox:
runs-on: ubuntu-latest
steps:
- name: Checkout GitCode
uses: actions/[email protected]
- name: Install dependencies
uses: cypress-io/[email protected]
with:
runTests: false
- name: Run Cypress Tests
uses: cypress-io/[email protected]
with:
record: true
parallel: true
command: "npm run cy:stag:firefox"
env:
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
# Known issue: doesn't record video for Firefox (https://github.com/cypress-io/cypress/issues/18415)
- name: Upload Videos to Build Artifacts
uses: actions/[email protected]
if: always()
with:
name: cypress-videos-firefox
path: "${{ github.workspace }}/cypress/reports/videos/"
- name: Upload Screenshots to Build Artifacts
uses: actions/[email protected]
if: failure()
with:
name: cypress-screenshots-firefox
path: "${{ github.workspace }}/cypress/reports/screenshots/"
- name: Upload Mocha report to Build Artifacts
uses: actions/[email protected]
if: always()
with:
name: cypress-mocha-firefox
path: "${{ github.workspace }}/cypress/reports/html/"
.github/workflows/main.yml
`
name: Run tests against DEV environment
on:
push:
branches:
- dev
jobs:
Test-on-Chrome:
runs-on: ubuntu-latest
steps:
- name: Checkout GitCode
uses: actions/[email protected]
- name: Install dependencies
uses: cypress-io/[email protected]
with:
runTests: false
- name: Run Cypress Tests
uses: cypress-io/[email protected]
with:
record: true
parallel: true
command: "npm run cy:dev:chrome"
env:
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
- name: Upload Videos to Build Artifacts
uses: actions/[email protected]
if: always()
with:
name: cypress-videos-chrome
path: "${{ github.workspace }}/cypress/reports/videos/"
- name: Upload Screenshots to Build Artifacts
uses: actions/[email protected]
if: failure()
with:
name: cypress-screenshots-chrome
path: "${{ github.workspace }}/cypress/reports/screenshots/"
- name: Upload Mocha report to Build Artifacts
uses: actions/[email protected]
if: always()
with:
name: cypress-mocha-chrome
path: "${{ github.workspace }}/cypress/reports/html/"
Test-on-Firefox:
runs-on: ubuntu-latest
steps:
- name: Checkout GitCode
uses: actions/[email protected]
- name: Install dependencies
uses: cypress-io/[email protected]
with:
runTests: false
- name: Run Cypress Tests
uses: cypress-io/[email protected]
with:
record: true
parallel: true
command: "npm run cy:dev:firefox"
env:
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
# Known issue: doesn't record video for Firefox (https://github.com/cypress-io/cypress/issues/18415)
- name: Upload Videos to Build Artifacts
uses: actions/[email protected]
if: always()
with:
name: cypress-videos-firefox
path: "${{ github.workspace }}/cypress/reports/videos/"
- name: Upload Screenshots to Build Artifacts
uses: actions/[email protected]
if: failure()
with:
name: cypress-screenshots-firefox
path: "${{ github.workspace }}/cypress/reports/screenshots/"
- name: Upload Mocha report to Build Artifacts
uses: actions/[email protected]
if: always()
with:
name: cypress-mocha-firefox
path: "${{ github.workspace }}/cypress/reports/html/"
npx cypress run --record --key {PROJECT_ACCESS_KEY}