Skip to content

Commit e262e95

Browse files
authored
Add Cypress+Cucumber example (#765)
* Add example for Cypress + Cucumber
1 parent 1d9da9b commit e262e95

File tree

13 files changed

+8824
-2
lines changed

13 files changed

+8824
-2
lines changed

.github/workflows/cypress.yml

+32-2
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@ name: Cypress Testing
66
on:
77
push:
88
paths:
9-
- 'cypress/**'
9+
- 'cypress*/**'
1010
- '.github/workflows/cypress.yml'
1111
branches:
1212
- main
1313
pull_request:
1414
paths:
15-
- 'cypress/**'
15+
- 'cypress*/**'
1616
- '.github/workflows/cypress.yml'
1717
branches:
1818
- main
@@ -51,3 +51,33 @@ jobs:
5151
push: false
5252
secrets: |
5353
"dotenv=${{ secrets.DOTENV }}"
54+
55+
cypress-cucumber:
56+
runs-on: ubuntu-latest
57+
steps:
58+
- name: Checkout code
59+
uses: actions/checkout@v4
60+
61+
- name: Build Cypress example
62+
uses: docker/build-push-action@v5
63+
with:
64+
context: ./cypress-cucumber
65+
file: ./cypress/Dockerfile
66+
push: false
67+
secrets: |
68+
"dotenv=${{ secrets.DOTENV }}"
69+
70+
cypress-cucumber-saucectl:
71+
runs-on: ubuntu-latest
72+
steps:
73+
- name: Checkout code
74+
uses: actions/checkout@v4
75+
76+
- name: Build Cypress example
77+
uses: docker/build-push-action@v5
78+
with:
79+
context: ./cypress-cucumber
80+
file: ./cypress/Dockerfile.saucectl
81+
push: false
82+
secrets: |
83+
"dotenv=${{ secrets.DOTENV }}"

cypress-cucumber/.gitignore

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
node_modules/
2+
.work
3+
build/
4+
coverage/
5+
yarn.lock
6+
cypress/screenshots/
7+
cypress/videos/

cypress-cucumber/.npmrc

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
registry=https://registry.npmjs.org

cypress-cucumber/.sauce/config.yml

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
apiVersion: v1
2+
kind: cypress
3+
sauce:
4+
region: us-west-1
5+
concurrency: 1
6+
metadata:
7+
tags:
8+
- visual
9+
- demo
10+
build: Visual Demo
11+
cypress:
12+
version: 13.13.3 # See https://docs.saucelabs.com/web-apps/automated-testing/cypress/#supported-testing-platforms for a list of supported versions.
13+
configFile: "cypress.config.js"
14+
15+
npm:
16+
packages:
17+
"@saucelabs/cypress-visual-plugin": "^0.6.3"
18+
"@badeball/cypress-cucumber-preprocessor": "^20.1.2"
19+
"@bahmutov/cypress-esbuild-preprocessor": "^2.2.2"
20+
21+
rootDir: ./
22+
suites:
23+
- name: "Sauce Demo Cypress"
24+
browser: "chrome"
25+
platformName: "Windows 11"
26+
screenResolution: "1920x1080" # (optional)
27+
config:
28+
specPattern: [ "cypress/e2e/**/*.feature" ]
29+
env:
30+
SAUCE_USERNAME: $SAUCE_USERNAME
31+
SAUCE_ACCESS_KEY: $SAUCE_ACCESS_KEY
32+
CYPRESS_VISUAL_CHECK: $VISUAL_CHECK
33+
34+
artifacts:
35+
download:
36+
when: never
37+
match:
38+
- console.log
39+
directory: ./artifacts/

cypress-cucumber/.sauceignore

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# This file instructs saucectl to not package any files mentioned here.
2+
/artifacts/
3+
cypress/videos/
4+
cypress/results/
5+
cypress/screenshots/
6+
node_modules/
7+
.git/
8+
.github/
9+
.DS_Store
10+
.hg/
11+
.vscode/
12+
.idea/
13+
.gitignore
14+
.hgignore
15+
.gitlab-ci.yml
16+
.npmrc
17+
*.gif
18+
.env
19+
Dockerfile
20+
package-lock.json
21+
README.md

cypress-cucumber/README.md

+106
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
# Getting started with Cypress [![](https://badgen.net/badge/Run%20this%20/README/5B3ADF?icon=https://runme.dev/img/logo.svg)](https://runme.dev/api/runme?repository=git%40github.com%3Asaucelabs%2Fvisual-examples.git)
2+
3+
## Prerequisites
4+
5+
- OSX Ventura with Git and Brew
6+
- Linux with bash curl and git
7+
- Windows with NodeJS 18 (untested)
8+
- Sauce Labs Account
9+
10+
## Run the demo
11+
12+
- Install Node.js 18 on Mac:
13+
14+
```sh { name=nodejs-mac }
15+
brew install node@18
16+
```
17+
18+
- Install Node.js 18 + Dependencies on Linux:
19+
20+
```sh { name=nodejs-linux }
21+
curl -fsSLO https://deb.nodesource.com/nsolid_setup_deb.sh
22+
chmod +x nsolid_setup_deb.sh
23+
./nsolid_setup_deb.sh 18
24+
apt-get install nodejs -y
25+
apt-get install -y libgtk2.0-0 libgtk-3-0 libgbm-dev libnotify-dev libgconf-2-4 libnss3 libxss1 libasound2 libxtst6 xauth xvfb
26+
```
27+
28+
- Clone the repo:
29+
30+
```sh { name=clone }
31+
git clone https://github.com/saucelabs/visual-examples
32+
cd visual-examples/cypress-cucumber
33+
```
34+
35+
- install npm dependencies:
36+
37+
```sh { name=npm-install }
38+
npm install
39+
```
40+
41+
- Configure with your Sauce credentials from https://app.saucelabs.com/user-settings and run
42+
43+
```sh { name=set-credentials }
44+
export SAUCE_USERNAME=__YOUR_SAUCE_USER_NAME__
45+
export SAUCE_ACCESS_KEY=__YOUR_SAUCE_ACCESS_KEY__
46+
# to change the region you are testing in please change the `region` property in the cypress.config.ts file.
47+
```
48+
49+
- Run the test
50+
51+
```sh { name=npm-run }
52+
npm run sauce-visual
53+
```
54+
55+
- Review your screenshots by clicking on the url printed in the test or go to https://app.saucelabs.com/visual/builds.
56+
- Accept all diffs, so they become new baselines.
57+
- Re-run the tests
58+
59+
```sh { name=npm-run-visual-check }
60+
npm run sauce-visual-check
61+
```
62+
63+
- Open the test or go to https://app.saucelabs.com/visual/builds to review changes.
64+
65+
## Running with `saucectl`
66+
67+
Alternatively, you can run your tests on Sauce Labs.
68+
69+
- Install `saucectl`
70+
71+
```sh { name=npm-install-saucectl}
72+
npm install saucectl
73+
```
74+
75+
- Install the plugin in your `.sauce/config.yml`
76+
77+
```yml
78+
[...]
79+
80+
npm:
81+
packages:
82+
"@saucelabs/cypress-visual-plugin": "^0.3.33"
83+
84+
[...]
85+
```
86+
87+
- Run saucectl
88+
89+
```sh { name=saucectl-run }
90+
npx saucectl run
91+
```
92+
93+
- Review your screenshots by clicking on the url printed in the test or go to https://app.saucelabs.com/visual/builds.
94+
- Accept all diffs, so they become new baselines.
95+
96+
- Run saucectl (with a modified screen)
97+
98+
```sh { name=saucectl-run-visual-check }
99+
VISUAL_CHECK=true npx saucectl run
100+
```
101+
102+
- Go to https://app.saucelabs.com/visual/builds to review changes.
103+
104+
## Installation & Usage
105+
106+
View installation and usage instructions on the [Sauce Docs website](https://docs.saucelabs.com/visual-testing/integrations/cypress/).

cypress-cucumber/cypress.config.js

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
const { defineConfig } = require('cypress');
2+
const createBundler = require("@bahmutov/cypress-esbuild-preprocessor");
3+
const preprocessor = require("@badeball/cypress-cucumber-preprocessor");
4+
const createEsbuildPlugin = require("@badeball/cypress-cucumber-preprocessor/esbuild");
5+
const { CypressSauceVisual, DiffingMethod } = require('@saucelabs/cypress-visual-plugin');
6+
7+
// Cypress does not allow multiple plugin to listen to the same event.
8+
// Only the last one will receive the event.
9+
// This code is intended to forward it to all plugins to ensure plugins works properly.
10+
//
11+
// Limitation: Some unexpected behaviour may be noticed when using plugins with return values.
12+
// With the Cucumber + Sauce Visual pair, there is no issues.
13+
let tasks = {};
14+
let onActions = {};
15+
function on(action, arg) {
16+
if (action === 'task') {
17+
Object.assign(tasks, arg);
18+
} else {
19+
onActions[action] = [
20+
...onActions[action] ?? [],
21+
arg,
22+
];
23+
}
24+
};
25+
26+
function forwardOn(cypressOn) {
27+
for (const event of Object.keys(onActions)) {
28+
cypressOn(event, async function (...args) {
29+
let retValue = undefined;
30+
for (const fn of onActions[event]) {
31+
let ret = await fn(...args);
32+
retValue ??= ret;
33+
}
34+
return retValue;
35+
});
36+
}
37+
cypressOn('task', tasks);
38+
}
39+
// End of Helper.
40+
41+
module.exports = defineConfig({
42+
e2e: {
43+
saucelabs: {
44+
project: process.env.GITHUB_REPOSITORY || `Cypress Visual Testing Example for ${process.env.SAUCE_USERNAME}`,
45+
branch: process.env.GITHUB_REF_NAME,
46+
buildName: `Cypress - Sauce Demo Test`,
47+
region: 'us-west-1',
48+
diffingMethod: DiffingMethod.Balanced,
49+
},
50+
specPattern: "cypress/**/*.feature",
51+
supportFile: './cypress/support/e2e.ts',
52+
setupNodeEvents: async function(cypressOn, config) {
53+
await preprocessor.addCucumberPreprocessorPlugin(on, config);
54+
on(
55+
'file:preprocessor',
56+
createBundler({
57+
plugins: [createEsbuildPlugin.default(config)],
58+
})
59+
);
60+
61+
CypressSauceVisual.register(on, config);
62+
63+
forwardOn(cypressOn);
64+
return config;
65+
},
66+
},
67+
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
@smoke
2+
Feature: Sauce Visual Demo
3+
4+
I want to showcase Sauce Visual
5+
6+
Scenario: Open Login Page
7+
When on Sauce Demo Page
8+
Then ".login_container" should be visible
9+
Then I capture a screenshot named "login-page"
10+
11+
Scenario: Login as a User
12+
When on Sauce Demo Page
13+
Then I log in as "standard_user" with a screenshot
14+
Then ".inventory_list" should be visible
15+
Then I capture a screenshot named "inventory-list"
16+
17+
Scenario: Locked User
18+
When on Sauce Demo Page
19+
Then I log in as "locked_out_user"
20+
Then I capture a screenshot named "Locked-User-Error-Message"
+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { When, Then } from "@badeball/cypress-cucumber-preprocessor";
2+
3+
4+
When('on Sauce Demo Page', () => {
5+
cy.intercept('/service-worker.js', {body: undefined});
6+
cy.visit('https://www.saucedemo.com/');
7+
});
8+
9+
Then('{string} should be visible', (selector) => {
10+
cy.get(selector).should('be.visible');
11+
});
12+
13+
Then('I capture a screenshot named {string}', (screenshotName) => {
14+
cy.sauceVisualCheck(screenshotName, {
15+
captureDom: true
16+
});
17+
});
18+
19+
Then('I log in as {string}', (username) => {
20+
let user = Cypress.env('VISUAL_CHECK') ? 'visual_user' : 'standard_user';
21+
if (username != 'standard_user') {
22+
user = username;
23+
}
24+
cy.get('[data-test="username"]').type(user).should('have.value', user);
25+
cy.get('[data-test="password"]').type('secret_sauce').should('have.value', 'secret_sauce');
26+
});
27+
28+
Then('I log in as {string} with a screenshot', (username) => {
29+
let user = Cypress.env('VISUAL_CHECK') ? 'visual_user' : 'standard_user';
30+
if (username != 'standard_user') {
31+
user = username;
32+
}
33+
cy.get('[data-test="username"]').type(user).should('have.value', user);
34+
cy.get('[data-test="password"]').type('secret_sauce').should('have.value', 'secret_sauce');
35+
36+
cy.sauceVisualCheck('Before Login', {
37+
captureDom: true,
38+
regions: [
39+
{element: cy.get('[data-test="username"]'), enableOnly: []},
40+
{element: cy.get('[data-test="password"]'), enableOnly: ['style']},
41+
],
42+
});
43+
cy.get('input[data-test="login-button"]').click();
44+
});
45+
+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
require('@saucelabs/cypress-visual-plugin/commands');

0 commit comments

Comments
 (0)