PracticeQAAutomation logoPracticeQAAutomation
Cy

JavaScript · Cypress

E-commerce with Cypress

Login with test users, search/filter/sort products, cart & coupons, a validated checkout, a declined card, order history with cancel and return, a store-manager role and a buggy build.

App under test

🛍️ Nova Shop

URL
/shop
User
demo_user
Password
Demo@123
Manager
admin_user
Coupons
SAVE10 · FLAT20
Declined card
4000 0000 0000 0002

Scenarios to automate

Work top to bottom — each one adds a new technique.

  1. 1

    Valid & invalid login

    Beginner

    demo_user / Demo@123 succeeds; wrong password and empty fields show login-error.

  2. 2

    Locked-out user

    Beginner

    locked_user gets “Sorry, this user has been locked out.”

  3. 3

    Slow login

    Intermediate

    slow_user takes ~3s — wait for the URL or product grid, never sleep.

  4. 4

    Route guard

    Beginner

    Visiting /shop/cart signed out redirects to /shop.

  5. 5

    Search, filter & sort

    Intermediate

    Assert product-count text, no-results state, and that product-price values are sorted.

  6. 6

    Cart badge & quantities

    Intermediate

    Add/remove from the grid, change qty in the cart, check line-total and totals maths.

  7. 7

    Coupons

    Intermediate

    SAVE10 = 10% off, FLAT20 = $20 off above $100, anything else is rejected.

  8. 8

    Checkout validation

    Intermediate

    Submit empty to collect error-* messages; fix them one by one.

  9. 9

    Declined card

    Advanced

    4000 0000 0000 0002 shows payment-error; switch to Cash on delivery and succeed.

  10. 10

    Order history

    Advanced

    Order id on the success page must appear in orders-table after navigation.

  11. 11

    Cancel, return & buy again

    Intermediate

    Cancel a Processing order in cancel-modal; request a return on a Delivered one (a reason is required); reorder-* refills the cart.

  12. 12

    Store manager role

    Advanced

    admin_user lands on /shop/admin, ships and delivers orders and decides returns; demo_user sees access-denied there.

  13. 13

    Bug hunt

    Advanced

    buggy_user has 5 seeded defects (sort, search, badge, tax, order total) — write tests that fail for it and pass for demo_user.

  14. 14

    UI vs API

    Advanced

    POST /api/shop/quote prices the same cart; assert subtotal, discount, tax and total match the cart page.

Sample Cypress suite

A working starting point that finds elements every way Cypress can — by role, label, text, id, name, CSS, XPath and data-testid — so you practice them all. Pick your language, switch to data-testid only to compare, or try your own in the Locator Playground.

Language

Cypress runs inside the browser, so it only supports JavaScript and TypeScript — there is no Python, Java or C# version of Cypress. For those languages, see Selenium or Playwright.

nova-shop.cy.js
// Each line uses a different kind of selector on purpose — the comments name them.
describe('Nova Shop', () => {
  beforeEach(() => {
    cy.visit('/shop');
    cy.get('#username').type('demo_user');                      // id
    cy.get('input[name="password"]').type('Demo@123');          // tag + attribute
    cy.contains('button', 'Sign in').click();                   // tag + text
    cy.location('pathname').should('eq', '/shop/products');
  });

  it('buys headphones with a coupon', () => {
    cy.get('[placeholder="Search products…"]').type('headphones');   // placeholder
    cy.getByTestId('product-card').should('have.length', 1);         // data-testid (custom command)
    cy.contains('[data-testid="product-card"]', 'Aurora Wireless Headphones')
      .find('button').click();                                       // scoped: the button inside one card
    cy.getByTestId('cart-badge').should('have.text', '1');

    cy.get('a[aria-label^="Cart with"]').click();               // attribute starts-with
    cy.get('[aria-label="Coupon code"]').type('SAVE10');        // aria-label
    cy.contains('button', 'Apply').click();
    cy.getByTestId('discount').should('contain', '$13.00');

    cy.contains('a', 'Checkout').click();
    cy.get('#first-name').type('Ada');
    cy.get('[name="last-name"]').type('Lovelace');
    cy.get('input[type="email"]').type('ada@example.com');      // input type
    cy.get('#phone').type('5551234567');
    cy.contains('label', 'Street address').parent().find('input').type('1 Analytical Way');   // via its label
    cy.get('#city').type('Austin');
    cy.get('select#state').select('Texas');
    cy.get('[name="zip"]').type('73301');
    cy.get('#card-name').type('Ada Lovelace');
    cy.get('[placeholder="4242 4242 4242 4242"]').type('4242424242424242');
    cy.get('[placeholder="MM/YY"]').type('1230');
    cy.get('#card-cvv').type('123');
    cy.contains('label', 'I agree to the terms').find('input[type="checkbox"]').check();
    cy.contains('button', 'Place order').click();

    // Commands retry until the ~2s "processing" finishes.
    cy.contains('h1', 'Thank you for your order!').should('be.visible');
    cy.getByTestId('order-id').invoke('text').should('match', /^NS-\d+$/);
  });

  it('sorts by price, low to high', () => {
    cy.get('select[aria-label="Sort by"]').select('price-asc');
    cy.getByTestId('product-price').then(($prices) => {
      const prices = [...$prices].map((el) => Number(el.innerText.replace('$', '')));
      expect(prices).to.deep.equal([...prices].sort((a, b) => a - b));
    });
  });
});

Practicing another tool?