All work
Personal project

OrangeHRM Web Automation

Cypress UI + API end-to-end suite

CypressJavaScriptPage Object ModelGitHub ActionsMochawesomePlatzi Fake Store API
105
End-to-end tests
100%
Pass rate
4
Spec suites

Overview

An end-to-end automation suite for the OrangeHRM open-source demo, covering the authentication and directory flows a user hits first, plus a separate API layer exercised against the public Platzi Fake Store API.

The suite is built on the Page Object Model so selectors and actions live apart from the assertions, and it runs automatically on GitHub Actions with a publicly accessible Mochawesome report.

Objective

Cover the Login, Forgot Password, and Directory flows end to end - including UI, responsive, negative, and timing checks - and validate a public REST API for status codes, response structure, and data types.

Testing scope

  • Login (22 tests)

    • Redirect to login when reaching the dashboard unauthenticated
    • UI elements, placeholders, and password masking
    • Valid login via click and via Enter
    • Invalid username / password / both show an error
    • Empty-field and special-character validation
    • Responsive layout and login response time under threshold
    • Double-click submits a single request
  • Forgot Password (30 tests)

    • Navigate from login to the reset page and back via cancel
    • Form elements, placeholder, and copyright text
    • Submit with valid, unregistered, and special-character usernames
    • Confirmation page content after submit
    • Empty and whitespace-only usernames are blocked
    • Responsive layout and process timing
  • Directory (28 tests)

    • Auth redirect and filter-card labels
    • Search by valid and unregistered employee name
    • Clear and refill the name field
    • Reset restores data and dropdown defaults
    • Responsive layout with untruncated buttons on mobile
    • Page load and search results under a time budget
  • API - Platzi Fake Store (25 tests)

    • GET categories: array, non-empty, schema (id, name, image)
    • Content-Type and response time checks
    • GET category by ID: value and type assertions
    • PUT update: status 200, name changes, ID stays stable, image updates
    • DELETE category and GET products by category
    • Negative: invalid ID and POST without a name return errors

Test strategy

  • Page Object Model separates selectors and actions from assertions.
  • Data-driven fixtures hold the credential variants instead of hard-coding them.
  • cy.intercept asserts the HTTP status of navigation before checking the UI.
  • Negative and boundary cases: empty fields, whitespace, and special characters.
  • Responsive checks across viewports, plus response-time budgets.
  • GitHub Actions runs the suite and publishes the Mochawesome report.

Architecture

  1. Spec filedescribe / it, test case IDs
  2. Page Objectselectors + actions
  3. OrangeHRM demo / Platzi APIsystem under test
  4. GitHub ActionsCypress run on push
  5. Mochawesome → GitHub Pagespublished report

Implementation

The Page Object keeps selectors and actions in one class so specs stay readable.
class LoginPage {
  // Selectors
  get usernameInput() { return cy.get('input[name="username"]') }
  get passwordInput() { return cy.get('input[name="password"]') }
  get submitButton()  { return cy.get('button[type="submit"]') }
  get alertMessage()  { return cy.get('.oxd-alert-content-text') }

  // Actions
  login(username, password) {
    this.usernameInput.type(username)
    this.passwordInput.type(password)
    this.submitButton.click()
  }

  // Assertions
  assertInvalidCredentials() {
    this.alertMessage.should('be.visible').and('contain', 'Invalid credentials')
  }
}
export default LoginPage
cy.intercept confirms the page responds 200 before any UI assertion runs.
it('login page returns 200 before UI checks', () => {
  cy.intercept('GET', '**/auth/login').as('loginPage')
  loginPage.visitLogin()
  cy.wait('@loginPage').then((interception) => {
    expect(interception.response.statusCode).to.eq(200)
  })
  loginPage.assertBrandingVisible()
})
Credential variants live in a fixture, keeping the spec data-driven.
{
  "validUser":       { "username": "Admin",        "password": "admin123" },
  "invalidUsername": { "username": "admintesting", "password": "admin123" },
  "invalidPassword": { "username": "Admin",         "password": "123" },
  "specialChar":     { "username": "@@$$*&$",       "password": "@@$$*&$" }
}
API tests assert status, structure, and types against the Platzi Fake Store API.
it('GET categories returns a non-empty array', () => {
  cy.request('GET', `${BASE_URL}/categories`).then((response) => {
    expect(response.status).to.eq(200)
    expect(response.body).to.be.an('array')
    expect(response.body.length).to.be.greaterThan(0)
    expect(response.body[0]).to.have.all.keys('id', 'name', 'image')
  })
})

Results

105

Tests

across 4 spec suites

105

Passed

0

Failed

100%

Pass rate

Links