Cypress Beyond Testing: Executable Demos for Your Pipeline

Turn Your Product Demos Into Quality Gates

13.12.2025, By Stephan Schwab

What if every product demo you gave also served as a quality gate in your CI/CD pipeline? Cypress, traditionally positioned as an end-to-end testing tool, can be repurposed to create executable demonstrations that both showcase application features to stakeholders and validate critical user journeys. This approach delivers double value from a single investment while keeping your testing pyramid properly balanced.

Most teams encounter Cypress as “yet another UI testing framework.” Install it, write some selectors, click some buttons, assert some outcomes. The documentation emphasizes testing, the tutorials focus on testing, and so teams dutifully add Cypress tests to their suites — often duplicating coverage they already have at lower levels.

There is a more powerful way to think about Cypress: as a tool for creating executable demonstrations that happen to also function as tests.

The Testing Pyramid and Where E2E Fits

Before diving into the approach, let’s revisit the testing pyramid — a concept that remains as relevant today as when Mike Cohn introduced it. The pyramid suggests that the bulk of your automated tests should be fast, isolated unit tests at the base. Above that, a smaller layer of integration tests validates that components work together. At the very top, a thin layer of end-to-end (E2E) tests confirms that critical user journeys work through the complete system.

        /\
       /  \       E2E Tests (few, slow, expensive)
      /----\
     /      \     Integration Tests (moderate)
    /--------\
   /          \   Unit Tests (many, fast, cheap)
  --------------

The pyramid shape is intentional. E2E tests are slow, flaky, and expensive to maintain. They require the full application stack to be running. A single selector change can break dozens of tests. Teams that invert this pyramid — writing more E2E tests than unit tests — often find themselves drowning in test maintenance while still shipping bugs.

So why use Cypress at all? Because those few E2E tests at the top of the pyramid serve a purpose that unit and integration tests cannot: they validate that the application works as a user would experience it, through a real browser, with all the JavaScript, CSS, and network interactions intact.

The key is to be strategic about what you test at this level.

Demos as Executable Specifications

Consider the typical rhythm of a development team. Features get built, and then someone needs to demo them — to product owners, stakeholders, or during sprint reviews. These demos follow a script: “First, I’ll log in as an admin user. Then I’ll navigate to the settings page. Watch as I update the notification preferences. See how the confirmation message appears?”

What if that demo script were executable code?

describe('Notification Preferences Feature Demo', () => {
  it('allows an admin to update notification settings', () => {
    // Authenticate as an admin user
    cy.login('admin@example.com', 'securepassword')
    
    // Navigate to the settings area
    cy.get('[data-cy="settings-menu"]').click()
    cy.get('[data-cy="notifications-tab"]').click()
    
    // Update email notification preferences
    cy.get('[data-cy="email-notifications-toggle"]')
      .should('be.visible')
      .click()
    
    // Adjust notification frequency
    cy.get('[data-cy="frequency-dropdown"]').select('Weekly')
    
    // Save changes
    cy.get('[data-cy="save-preferences"]').click()
    
    // Verify the confirmation appears
    cy.get('[data-cy="success-message"]')
      .should('be.visible')
      .and('contain', 'Preferences saved successfully')
  })
})

This code does three things simultaneously:

  1. Documents the feature in a way that any developer can read and understand
  2. Provides a demo script that can be run visually for stakeholders
  3. Acts as a quality gate in your CI/CD pipeline

For stakeholder presentations, run the tests in headless mode with cypress run — Cypress automatically records videos that you can pause and narrate at your own pace. Alternatively, add cy.pause() commands at key moments in your code; when running in interactive mode with cypress open, execution stops at each pause point, letting you explain what’s happening before clicking “Resume” to continue. Either way, the same code that serves your demo also runs in your pipeline to ensure this critical user journey hasn’t regressed.

Writing Demos, Not Tests

The mental shift is subtle but important. When you set out to “write a test,” you focus on coverage, edge cases, and assertions. When you set out to “write a demo,” you focus on the user journey, the narrative, and the visible outcomes.

This changes how you structure the code:

describe('Order Fulfillment Workflow', () => {
  beforeEach(() => {
    // Set up the scenario: a customer has placed an order
    cy.task('seedOrder', { status: 'pending', items: 3 })
    cy.login('warehouse@example.com')
  })

  it('demonstrates the complete fulfillment process', () => {
    // Warehouse staff sees pending orders on their dashboard
    cy.visit('/fulfillment/dashboard')
    cy.get('[data-cy="pending-orders"]')
      .should('contain', '1 order awaiting fulfillment')

    // They open the order details
    cy.get('[data-cy="order-row"]').first().click()
    cy.get('[data-cy="order-items"]')
      .find('li')
      .should('have.length', 3)

    // Each item gets scanned and marked as picked
    cy.get('[data-cy="scan-item-input"]').type('SKU-001{enter}')
    cy.get('[data-cy="picked-count"]').should('contain', '1 of 3')
    
    cy.get('[data-cy="scan-item-input"]').type('SKU-002{enter}')
    cy.get('[data-cy="scan-item-input"]').type('SKU-003{enter}')
    cy.get('[data-cy="picked-count"]').should('contain', '3 of 3')

    // Order is marked ready for shipping
    cy.get('[data-cy="complete-fulfillment"]').click()
    cy.get('[data-cy="order-status"]')
      .should('contain', 'Ready for Shipping')
  })
})

Notice the comments. They read like a demo script, not test documentation. When you present this to stakeholders, you can literally read the comments aloud while Cypress executes the steps.

Practical Considerations

Use Data Attributes for Selectors

Resist the temptation to select elements by CSS classes or complex DOM paths. Data attributes like data-cy are explicit, stable, and communicate intent:

// Fragile - breaks when styling changes
cy.get('.btn.btn-primary.submit-form')

// Robust - survives refactoring
cy.get('[data-cy="submit-order"]')

Create Custom Commands for Common Actions

Your demos will share common patterns. Encapsulate them:

// cypress/support/commands.js
Cypress.Commands.add('login', (email, password) => {
  cy.visit('/login')
  cy.get('[data-cy="email-input"]').type(email)
  cy.get('[data-cy="password-input"]').type(password)
  cy.get('[data-cy="login-button"]').click()
  cy.url().should('not.include', '/login')
})

Cypress.Commands.add('addToCart', (productId) => {
  cy.visit(`/products/${productId}`)
  cy.get('[data-cy="add-to-cart"]').click()
  cy.get('[data-cy="cart-notification"]').should('be.visible')
})

Now your demos read even more cleanly:

it('demonstrates a complete purchase flow', () => {
  cy.login('customer@example.com', 'password')
  cy.addToCart('product-123')
  cy.addToCart('product-456')
  cy.visit('/checkout')
  // ...continue the journey
})

Keep the Count Low

Remember the pyramid. You’re not trying to test every permutation at the E2E level. Aim for a handful of demos that cover the critical user journeys — the paths that, if broken, would make the application unusable or cost the business significant money.

A typical application might have:

  • 2-3 demos covering the core purchase or conversion flow
  • 1-2 demos for administrative workflows
  • 1 demo for the user onboarding experience

That’s perhaps 5-10 Cypress demos total, not 500 E2E tests.

Running Demos in Your Pipeline

In your CI/CD configuration, these demos become a quality gate:

# Example GitHub Actions workflow
cypress-demos:
  runs-on: ubuntu-latest
  steps:
    - uses: actions/checkout@v4
    - uses: cypress-io/github-action@v6
      with:
        start: npm run start:ci
        wait-on: 'http://localhost:3000'
        spec: cypress/e2e/demos/**/*.cy.js

When a demo fails in the pipeline, it means a critical user journey is broken. This is a much stronger signal than “a test failed.” It forces a conversation: “The order fulfillment demo is failing — we cannot ship this release until warehouse staff can complete orders.”

The Double Return on Investment

The traditional approach treats demos and tests as separate activities. Someone writes Cypress tests for the pipeline. Someone else prepares PowerPoint slides and manually clicks through the application for stakeholders. These activities happen independently, consuming time and often falling out of sync.

The executable demo approach collapses these into one artifact. The demo you show stakeholders is the same code that guards your production releases. When the application changes, updating the demo updates the test. When stakeholders ask “can you show me that feature again?”, you run the demo — and you’ve just verified it still works.

This isn’t about abandoning proper testing. Your unit tests still cover the edge cases. Your integration tests still verify component interactions. The Cypress demos sit at the apex of your pyramid, ensuring the critical paths work end-to-end while serving double duty as stakeholder communication tools.

The next time you reach for Cypress, ask yourself: am I writing a test, or am I scripting a demo? The answer might change how you approach the entire exercise.

Contact

Let's talk about your real situation. Want to accelerate delivery, remove technical blockers, or validate whether an idea deserves more investment? Book a short conversation (20 min): I listen to your context and give 1–2 practical recommendations—no pitch, no obligation. If it fits, we continue; if not, you leave with clarity. Confidential and direct.

Prefer email? Write me: sns@caimito.net