
In this project I designed and built a complete end-to-end test automation framework from scratch for SauceDemo — a standard e-commerce demo application widely used in the QA industry. The framework covers UI testing, API testing, test reporting, video recording, and a fully automated CI/CD pipeline using GitHub Actions.
| Tool | Purpose |
|---|---|
| Cypress 13 | End-to-end test automation framework |
| Cucumber BDD | Behaviour Driven Development with Gherkin syntax |
| @badeball/cypress-cucumber-preprocessor | Cucumber integration for Cypress |
| Mochawesome | HTML test report generation |
| GitHub Actions | CI/CD pipeline for automated test execution |
| Node.js | JavaScript runtime environment |
| JavaScript (ES6+) | Programming language |
The framework follows industry best practices with a clear separation of concerns across three layers:
Feature Files (What to test — plain English) ↓ Step Definitions (How to test — JavaScript) ↓ Page Objects (Where to find elements — selectors and actions)
Tests are written in Gherkin — a plain English language that both technical and non-technical stakeholders can read and understand. This bridges the gap between business requirements and automated tests.
`Feature: Login functionality
Background: Given I am on the login page
Scenario: Successful login with valid credentials When I login with username "standard_user" and password "secret_sauce" Then I should be redirected to the inventory page
Scenario Outline: Login with different user types When I login with username "<username>" and password "secret_sauce" Then I should be redirected to the inventory page
Examples:
| username |
| standard_user |
| problem_user |
| performance_glitch_user |`
All element selectors and page actions are centralised in dedicated Page Object files. This means when the UI changes, only one file needs to be updated — not every test.
`class LoginPage { get usernameInput() { return cy.get('#user-name'); } get passwordInput() { return cy.get('#password'); } get loginButton() { return cy.get('#login-button'); } get errorMessage() { return cy.get('[data-test="error"]'); }
visit() { cy.visit('/'); }
login(username, password) { this.usernameInput.type(username); this.passwordInput.type(password); this.loginButton.click(); } }
export default new LoginPage();`
Steps shared across multiple feature files live in a dedicated common.steps.js file, eliminating duplication and keeping the codebase maintainable.