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
Valid & invalid login
Beginnerdemo_user / Demo@123 succeeds; wrong password and empty fields show login-error.
- 2
Locked-out user
Beginnerlocked_user gets “Sorry, this user has been locked out.”
- 3
Slow login
Intermediateslow_user takes ~3s — wait for the URL or product grid, never sleep.
- 4
Route guard
BeginnerVisiting /shop/cart signed out redirects to /shop.
- 5
Search, filter & sort
IntermediateAssert product-count text, no-results state, and that product-price values are sorted.
- 6
Cart badge & quantities
IntermediateAdd/remove from the grid, change qty in the cart, check line-total and totals maths.
- 7
Coupons
IntermediateSAVE10 = 10% off, FLAT20 = $20 off above $100, anything else is rejected.
- 8
Checkout validation
IntermediateSubmit empty to collect error-* messages; fix them one by one.
- 9
Declined card
Advanced4000 0000 0000 0002 shows payment-error; switch to Cash on delivery and succeed.
- 10
Order history
AdvancedOrder id on the success page must appear in orders-table after navigation.
- 11
Cancel, return & buy again
IntermediateCancel a Processing order in cancel-modal; request a return on a Delivered one (a reason is required); reorder-* refills the cart.
- 12
Store manager role
Advancedadmin_user lands on /shop/admin, ships and delivers orders and decides returns; demo_user sees access-denied there.
- 13
Bug hunt
Advancedbuggy_user has 5 seeded defects (sort, search, badge, tax, order total) — write tests that fail for it and pass for demo_user.
- 14
UI vs API
AdvancedPOST /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.
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.
// 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));
});
});
});