Skip to content

Commit a564980

Browse files
committed
feat: Add Cypress tests with Page Object Model
1 parent 8503f47 commit a564980

File tree

17 files changed

+1708
-275
lines changed

17 files changed

+1708
-275
lines changed

.github/workflows/cypress.yml

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,33 +11,40 @@ jobs:
1111
runs-on: ubuntu-latest
1212
steps:
1313
- name: Checkout
14-
uses: actions/checkout@v3
14+
uses: actions/checkout@v4
1515

1616
- name: Setup Node.js
17-
uses: actions/setup-node@v3
17+
uses: actions/setup-node@v4
1818
with:
19-
node-version: 18
19+
node-version: '20.x'
2020

2121
- name: Install dependencies
22-
run: npm install
22+
run: npm ci
2323

2424
- name: Create env file
2525
run: |
2626
echo '{
27-
"users": {
28-
"standard": {
29-
"username": "standard_user",
30-
"password": "secret_sauce"
31-
},
32-
"locked": {
33-
"username": "locked_out_user",
34-
"password": "secret_sauce"
35-
}
36-
}
27+
"standardUser": "standard_user",
28+
"lockedOutUser": "locked_out_user",
29+
"password": "secret_sauce"
3730
}' > cypress.env.json
3831
39-
- name: Run Cypress tests
40-
uses: cypress-io/github-action@v5
32+
- name: Cypress run
33+
uses: cypress-io/github-action@v6
4134
with:
4235
browser: chrome
43-
headless: true
36+
headless: true
37+
38+
- name: Upload screenshots
39+
uses: actions/upload-artifact@v4
40+
if: failure()
41+
with:
42+
name: cypress-screenshots
43+
path: cypress/screenshots
44+
45+
- name: Upload reports
46+
uses: actions/upload-artifact@v4
47+
if: always()
48+
with:
49+
name: cypress-reports
50+
path: cypress/reports

.gitignore

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,5 @@
1-
# Dependency directories
21
node_modules/
3-
4-
# TypeScript compiled files
5-
*.js
6-
*.map
7-
8-
# Cypress
2+
cypress.env.json
93
cypress/videos/
104
cypress/screenshots/
11-
cypress/downloads/
12-
cypress.env.json
13-
14-
# OS specific files
15-
.DS_Store
16-
Thumbs.db
17-
18-
# Editor directories and files
19-
.idea/
20-
.vscode/
21-
*.swp
22-
*.swo
5+
cypress/reports/

README.md

Lines changed: 24 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,54 @@
1-
# SauceDemo Cypress Tests
1+
# Sauce Demo Cypress Tests
22

3-
This project contains Cypress end-to-end tests for the SauceDemo website using TypeScript and the Page Object Model pattern.
3+
This project contains automated tests for the Sauce Demo website using Cypress with TypeScript.
44

55
## Project Structure
66

77
```
88
├── cypress/
9-
│ ├── e2e/ # Test files
10-
│ ├── fixtures/ # Test data
11-
│ ├── pages/ # Page Object Model files
12-
│ ├── support/ # Custom commands and utilities
13-
│ └── tsconfig.json # TypeScript configuration
14-
├── cypress.config.ts # Cypress configuration
15-
├── cypress.env.json # Environment variables (not committed to git)
16-
├── package.json # Project dependencies and scripts
17-
└── README.md # Project documentation
9+
│ ├── e2e/ # Test files
10+
│ ├── fixtures/ # Test data
11+
│ ├── pages/ # Page Objects
12+
│ └── support/ # Support files
13+
├── cypress.config.ts # Cypress configuration
14+
├── cypress.env.json # Environment variables (gitignored)
15+
├── tsconfig.json # TypeScript configuration
16+
└── package.json # Project dependencies
1817
```
1918

2019
## Prerequisites
2120

22-
- Node.js (v14 or later)
23-
- npm (v6 or later)
21+
- Node.js (v14 or higher)
22+
- npm (v6 or higher)
2423

25-
## Setup
24+
## Installation
2625

2726
1. Clone the repository
2827
2. Install dependencies:
29-
```
28+
```bash
3029
npm install
3130
```
3231
3. Create a `cypress.env.json` file with the following content:
3332
```json
3433
{
35-
"users": {
36-
"standard": {
37-
"username": "standard_user",
38-
"password": "secret_sauce"
39-
},
40-
"locked": {
41-
"username": "locked_out_user",
42-
"password": "secret_sauce"
43-
}
44-
}
34+
"standardUser": "standard_user",
35+
"lockedOutUser": "locked_out_user",
36+
"password": "secret_sauce"
4537
}
4638
```
4739

4840
## Running Tests
4941

50-
### Headless Mode
51-
5242
To run tests in headless mode:
53-
43+
```bash
44+
npx cypress run
5445
```
55-
npm run cy:run
56-
```
57-
58-
### Interactive Mode
5946

60-
To open Cypress in interactive mode:
61-
62-
```
63-
npm run cy:open
47+
To open Cypress Test Runner:
48+
```bash
49+
npx cypress open
6450
```
6551

66-
## Test Cases
67-
68-
1. **Valid Login**: Tests successful login using standard_user
69-
2. **Locked Out User**: Tests error handling with locked_out_user
70-
71-
## Logging
52+
## Test Reports
7253

73-
The tests include a custom task for logging which displays detailed information in the terminal during test runs.
54+
Test reports are generated using cypress-mochawesome-reporter and can be found in the `cypress/reports` directory after test execution.

cypress.config.ts

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
1-
import { defineConfig } from 'cypress';
1+
import { defineConfig } from 'cypress'
22

33
export default defineConfig({
4+
reporter: 'cypress-mochawesome-reporter',
45
e2e: {
56
setupNodeEvents(on, config) {
7+
require('cypress-mochawesome-reporter/plugin')(on);
8+
9+
// Task for console logging
610
on('task', {
711
log(message) {
8-
console.log(message);
9-
return null;
10-
}
11-
});
12+
console.log(message)
13+
return null
14+
},
15+
})
1216
},
1317
baseUrl: 'https://www.saucedemo.com',
14-
chromeWebSecurity: false,
15-
video: false
18+
supportFile: 'cypress/support/e2e.ts',
1619
},
17-
});
20+
})

cypress/e2e/login.cy.ts

Lines changed: 14 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,19 @@
1-
/// <reference types="cypress" />
1+
import LoginPage from '../pages/LoginPage'
2+
import InventoryPage from '../pages/InventoryPage'
23

3-
import LoginPage from '../pages/LoginPage';
4-
import ProductsPage from '../pages/ProductsPage';
5-
6-
describe('SauceDemo Login Tests', () => {
4+
describe('Sauce Demo Login Tests', () => {
75
beforeEach(() => {
8-
cy.task('log', 'Starting a new test');
9-
LoginPage.visit();
10-
});
11-
12-
it('should login successfully with valid credentials', () => {
13-
cy.task('log', 'Running test: should login successfully with valid credentials');
14-
15-
// Get test data from environment
16-
cy.fixture('testData.json').then((testData) => {
17-
// Login with valid credentials
18-
LoginPage.login(
19-
Cypress.env('users').standard.username,
20-
Cypress.env('users').standard.password
21-
);
6+
LoginPage.visit()
7+
})
228

23-
// Verify successful login
24-
ProductsPage.getProductsHeader()
25-
.should('be.visible')
26-
.and('have.text', testData.pageTexts.productsHeader);
27-
});
28-
});
9+
it('should login successfully with standard user', () => {
10+
LoginPage.login(Cypress.env('standardUser'), Cypress.env('password'))
11+
InventoryPage.isVisible()
12+
InventoryPage.logout()
13+
})
2914

3015
it('should show error message for locked out user', () => {
31-
cy.task('log', 'Running test: should show error message for locked out user');
32-
33-
cy.fixture('testData.json').then((testData) => {
34-
// Login with locked out user
35-
LoginPage.login(
36-
Cypress.env('users').locked.username,
37-
Cypress.env('users').locked.password
38-
);
39-
40-
// Verify error message
41-
LoginPage.getErrorMessage()
42-
.should('be.visible')
43-
.and('have.text', testData.errorMessages.lockedOutUser);
44-
});
45-
});
46-
});
16+
LoginPage.login(Cypress.env('lockedOutUser'), Cypress.env('password'))
17+
LoginPage.getErrorMessage().should('contain.text', 'Epic sadface: Sorry, this user has been locked out')
18+
})
19+
})

cypress/fixtures/testData.json

Lines changed: 0 additions & 11 deletions
This file was deleted.

cypress/pages/InventoryPage.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
class InventoryPage {
2+
// Selectors
3+
private burgerMenu = '#react-burger-menu-btn'
4+
private logoutLink = '#logout_sidebar_link'
5+
private inventoryContainer = '#inventory_container'
6+
7+
// Methods
8+
isVisible() {
9+
cy.task('log', 'Verifying inventory page is visible')
10+
return cy.get(this.inventoryContainer).should('be.visible')
11+
}
12+
13+
logout() {
14+
cy.task('log', 'Logging out')
15+
cy.get(this.burgerMenu).click()
16+
cy.get(this.logoutLink).click()
17+
}
18+
}
19+
20+
export default new InventoryPage()

cypress/pages/LoginPage.ts

Lines changed: 15 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,27 @@
1-
/// <reference types="cypress" />
2-
31
class LoginPage {
4-
// Elements
5-
private usernameInput = '#user-name';
6-
private passwordInput = '#password';
7-
private loginButton = '#login-button';
8-
private errorMessage = '[data-test="error"]';
2+
// Selectors
3+
private usernameInput = '[data-test="username"]'
4+
private passwordInput = '[data-test="password"]'
5+
private loginButton = '[data-test="login-button"]'
6+
private errorMessage = '[data-test="error"]'
97

10-
// Actions
8+
// Methods
119
visit() {
12-
cy.task('log', 'Visiting login page');
13-
cy.visit('/');
14-
return this;
10+
cy.visit('/')
11+
return this
1512
}
1613

1714
login(username: string, password: string) {
18-
cy.task('log', `Attempting to login with username: ${username}`);
19-
cy.get(this.usernameInput).clear().type(username);
20-
cy.get(this.passwordInput).clear().type(password);
21-
cy.get(this.loginButton).click();
22-
return this;
15+
cy.task('log', `Logging in with username: ${username}`)
16+
cy.get(this.usernameInput).type(username)
17+
cy.get(this.passwordInput).type(password)
18+
cy.get(this.loginButton).click()
19+
return this
2320
}
2421

25-
// Assertions
2622
getErrorMessage() {
27-
cy.task('log', 'Getting error message');
28-
return cy.get(this.errorMessage);
23+
return cy.get(this.errorMessage)
2924
}
3025
}
3126

32-
export default new LoginPage();
27+
export default new LoginPage()

cypress/pages/ProductsPage.ts

Lines changed: 0 additions & 29 deletions
This file was deleted.

0 commit comments

Comments
 (0)