EPA Quote Forms — End-to-End Testing Approach

Overview of Automated End-to-End Testing for EPA Insurance Quote Forms

Purpose

This memo outlines the automated end-to-end (E2E) testing strategy in place for the EPA insurance quote platform. The test suite provides continuous quality assurance across all quote form journeys, ensuring that customers can complete quotes reliably and that business-critical outcomes are correctly handled.


Separation of Concerns — Unit Tests vs End-to-End Tests

The EPA platform has two distinct layers of automated testing, each with a different purpose and owned by a different part of the codebase.

Unit Tests — Inside the Product

Unit tests live within the flux-epa product repository itself. They test the application’s internal logic in isolation — individual functions, business rules, data transformations, and component rendering — without launching a browser or navigating a real form. Unit tests are:

  • Fast — They run in milliseconds because they don’t involve a browser or network
  • Narrow — Each test covers a single piece of logic (e.g. “does this function calculate the premium correctly?”)
  • Developer-facing — They catch regressions in code as it is written, typically running on every commit

Unit tests answer the question: does each piece of the application work correctly on its own?

End-to-End Tests — Outside the Product

This E2E test suite is a separate project, deliberately kept outside the product codebase. It tests the application as a deployed whole — launching a real browser, navigating to a real environment, and completing quote journeys exactly as a customer would. E2E tests are:

  • Slow — Each test takes seconds to minutes because it drives a real browser through multiple form pages
  • Wide — A single test exercises the full stack: frontend rendering, form validation, API calls, backend processing, and quote outcome routing
  • Customer-facing — They verify the experience from the customer’s perspective, not the developer’s

E2E tests answer the question: can a customer actually complete a quote and reach the right outcome?

Why Both Are Needed

Neither layer can replace the other. They catch fundamentally different types of problems:

Unit TestsE2E Tests
CatchesLogic errors in individual functionsBroken journeys, wrong destinations, missing content
MissesIntegration failures between componentsInternal implementation bugs
SpeedMillisecondsSeconds to minutes
Runs againstCode in isolationA deployed environment
Owned byProduct repository (flux-epa)Test repository (epa-tests-e2e)

A unit test might confirm that a premium calculation function returns the correct value, but it cannot tell you whether the customer actually sees that value on screen, or whether clicking “Get a Quote” sends them to the right page. Conversely, an E2E test can confirm the full journey works, but if it fails, it cannot pinpoint whether the issue is in the frontend, the API, or the business logic — that is where unit tests provide the detail.

Ownership Model

Each product has a product owner — a single developer who is responsible for that product’s E2E test suite. They maintain the component classes, step classes, test methods, and documentation, and they evolve the E2E coverage as the product grows. The product owner is the person who understands the customer journeys end to end and ensures the test suite reflects them accurately.

Other developers working on the product — building features, fixing bugs, refactoring code — are responsible for writing unit tests to cover their changes within the product repository. They are not expected to write or maintain E2E tests. However, the E2E suite is available to them as a tool:

  • Validation — After completing a feature or fix, a developer can run the E2E tests against their deployed changes to confirm that the customer-facing journeys still work correctly
  • Sanity checking — A quick smoke run provides confidence that a change has not broken anything obvious before handing work over for review
  • Regression testing — The full suite can be run to verify that a change has not introduced side effects in other parts of the form or in other brands

This creates a clear division of responsibility:

Product OwnerFeature/Bug Fix Developers
WritesE2E tests (components, steps, test methods)Unit tests (within the product repo)
MaintainsE2E test suite, documentation, parametrised rulesProduct code, unit test coverage
Uses E2E tests toValidate developer work, verify releases, expand coverageSanity check their own changes, run regression tests

The E2E tests become a quality gate that the product owner controls. When a developer submits work, the product owner can run the relevant E2E tests to verify that customer journeys are intact — without needing to manually click through forms. This frees developers to focus on building and unit-testing their code, while the product owner has an automated tool to confirm the end result works from the customer’s perspective.

The Separation Is Deliberate

Keeping the E2E tests in their own repository and distributing them as a package reinforces this ownership model:

  • Independent release cycles — The product owner can update the E2E suite without changing the product, and vice versa
  • Environment flexibility — E2E tests run against any deployed environment, not just a local development setup
  • No coupling — The E2E tests interact with the application purely through its UI, the same way a customer does. They have no knowledge of internal code, database schemas, or API contracts. If a developer refactors the product’s internals but the UI behaviour remains the same, the E2E tests continue to pass without changes
  • Low barrier for developers — Developers do not need to understand the E2E test codebase to benefit from it. They run the tests, review the report, and act on any failures

Scope of Testing

Products Covered

The test suite covers 6 insurance quote forms across 3 brands:

BrandProductTest Cases
Adrian FluxCar Insurance32
Adrian FluxLearner Driver Insurance21
Sterling InsuranceCar Insurance34
Sterling InsuranceLearner Driver Insurance16
BikesureMotorcycle Insurance34
BikesureShort-term Motorcycle Insurance32
Total169

Each form consists of up to 12 steps that a customer completes to receive a quote, including vehicle details, personal information, driving history, and policy preferences.

What the Tests Verify

Tests are organised into the following categories:

  • Smoke tests — Confirm that every page of the quote form loads correctly, displays the right content (titles, disclaimers, legal text), and that the core happy-path journey works end to end.
  • Validation tests — Verify that form fields reject invalid or missing input and display the correct error messages to the customer.
  • Quote outcome tests — Complete the full quote journey and confirm that customers are directed to the correct outcome page (buy online, request a callback, or call us) based on their details.
  • Source code tests — Verify that marketing source codes are correctly passed through the entire journey and appear in callback URLs and quote outcomes, ensuring accurate attribution.
  • Multi-party tests — Test the additional driver flows, including adding, editing, and removing named drivers from a policy.

Boundary: The Form, Not the Backend

These tests are scoped to the quote form itself — the customer-facing frontend application. They do not directly test connected backend services such as the quoting engine, pricing APIs, CRM systems, or payment gateways. The tests interact only with what the customer sees in the browser.

However, because the form depends on backend services to function, certain backend behaviours can be inferred from the test results. When a test completes a full quote journey and asserts the outcome, it is implicitly validating that the backend processed the submission correctly:

  • Destination page assertions — When a test submits a quote and verifies the customer is redirected to the “buy” page, this confirms that the backend quoting engine received the submission, processed it, returned a quotable result, and the form routed the customer to the correct destination. A failure here could indicate a backend issue (quoting engine down, pricing rules misconfigured) even though the test itself only checks the URL the customer lands on.
  • Quote reference assertions — When a test verifies that a quote reference number is displayed on the outcome page, this confirms that the backend successfully generated and returned a quote reference. The presence of this reference implies the quote was persisted in the backend system.
  • Content assertions on outcome pages — Tests verify specific content on outcome pages: premium amounts, policy benefits, insurer names, excess values, and callback phone numbers. This content is populated by backend responses. If the backend returns incorrect data, these assertions will fail — surfacing a backend problem through a frontend test.
  • Source code propagation — Tests verify that marketing source codes passed via URL parameters survive the full journey and appear in callback URLs and outcome pages. This validates that the form correctly passes source codes to the backend and that the backend includes them in its response data.

In this way, the E2E tests act as an early warning system for backend issues, even though they are not backend tests. A pattern of destination or content failures across multiple EPAs can indicate a shared backend service problem, while a failure isolated to a single EPA points to a product-specific configuration issue.

What these tests will not tell you is why a backend service failed — only that something in the chain produced an unexpected result from the customer’s perspective. Diagnosing the root cause requires backend logs, monitoring, and the product’s own unit and integration tests.


Architecture

The test suite is built on a three-layer architecture that separates concerns and promotes reuse. Each layer has a distinct responsibility:

 ┌─────────────────────────────────────────────────────┐
 │                      Tests                          │
 │  Orchestrate steps into full scenarios and assert   │
 │  expected outcomes.                                 │
 │  e.g. test_AFC004_simple_vehicle_lookup             │
 ├─────────────────────────────────────────────────────┤
 │                      Steps                          │
 │  Represent a single page/step of the form.          │
 │  Compose element actions into complete scenarios    │
 │  for that page.                                     │
 │  e.g. VehicleLookupStep.fill()                      │
 ├─────────────────────────────────────────────────────┤
 │                    Components                       │
 │  Represent a reusable form component. Expose        │
 │  individual user actions on that component.         │
 │  e.g. F058VehicleLookupCar.reg_lookup()             │
 └─────────────────────────────────────────────────────┘

Components — Individual Actions

At the lowest level, component classes represent a single reusable form element such as a vehicle registration lookup or a purchase details panel. Each method on a component maps to one discrete user action: entering a registration number, clicking “Find Car”, selecting a manufacturer from a dropdown, and so on.

Because components are self-contained, the same component can be shared across multiple brands and products. For example, the car vehicle lookup component is used by both Adrian Flux Car and Sterling Car forms without duplication.

Critically, the component numbering in the test suite mirrors the component numbering used in the flux-epa product itself and its documentation. The test class F058VehicleLookupCar corresponds directly to component F058 in the EPA platform; F066VehiclePurchaseDetails corresponds to component F066, and so on. This shared naming convention means:

  • Traceability — When a component is changed in the product, it is immediately clear which test component covers it
  • Common language — Developers, testers, and product documentation all refer to the same component by the same number
  • Incremental coverage — New actions and scenarios can be added to a component class over time without touching any existing tests. For example, F058VehicleLookupCar currently covers registration lookup, manual entry, and vehicle changes. As the product evolves, additional actions (e.g. a new vehicle data source, a different lookup flow) can be added to the same class and then consumed by new or existing step scenarios. Coverage grows gradually, component by component, without requiring large-scale rewrites

The current component library is small — F001 (motorcycle lookup), F058 (car lookup), and F066 (purchase details) — but it is designed to expand. As new components are built in the test suite, they follow the same F-number convention, keeping the test layer aligned with the product layer.

Steps — Page Scenarios

The middle layer contains step classes, one per page of the quote form. A step class composes the actions from one or more components into complete scenarios for that page. For example, the entry page step brings together the vehicle lookup component and the purchase details component, calling their actions in the right order and advancing to the next page.

Each step exposes different scenarios:

  • fill — the standard happy-path completion of that page
  • fill_complex — an advanced path that exercises more of the page’s functionality (manual entry, changing selections, toggling options)
  • error methods — deliberately trigger validation errors to test that the form rejects bad input correctly

This means the details of how a form page works are defined in one place. If the form changes, only the step class needs updating — not every test that uses it.

Tests — Orchestrated Journeys

At the top level, test methods orchestrate steps into meaningful scenarios and assert expected outcomes. A test might call a single step to verify one page works in isolation, or chain all steps together to complete a full end-to-end quote journey.

Tests are concerned with what should happen, not how to interact with the form. For example, a full quote journey test reads as a simple sequence — fill vehicle details, fill personal details, fill driving history, get a quote, verify the outcome — with each step handling its own interactions internally.

Why This Matters

This layered separation provides several practical benefits:

  • Reduced duplication — Common form components are written once and reused across brands. The vehicle lookup is defined in a single component class, not copied into every test.
  • Easier maintenance — When a form page changes, updates are made in one step class rather than across dozens of individual tests.
  • Readability — Tests read as business-level scenarios (“complete personal details, then get a quote”) rather than low-level browser interactions (“click this button, fill this field”).
  • Faster test development — New tests for existing forms can be composed from the library of steps and components that already exist.

Technology Stack

Playwright — The Engine

Playwright is the core of the test suite. It is a browser automation framework developed by Microsoft that launches and controls a real web browser (Chromium) programmatically. Playwright is responsible for everything the tests do:

  • Navigating to quote form URLs
  • Interacting with form elements — clicking buttons, filling text fields, selecting dropdowns, toggling radio buttons — exactly as a customer would
  • Waiting intelligently for pages to load, network requests to complete, and elements to become visible before proceeding
  • Asserting that the page is in the expected state — checking text content, element visibility, URL changes, and page metadata
  • Recording video of every test session and capturing screenshots on failure
  • Tracing detailed execution logs (DOM snapshots, network requests, console output) that can be replayed step-by-step when investigating failures

The EpaStep class wraps Playwright’s Page object with helpers specific to EPA quote forms — navigating between form steps, verifying step titles, checking field-level validation errors, and confirming quote outcome destinations. Every component and step class ultimately delegates to Playwright for all browser interactions.

pytest — The Runner

pytest serves as the test runner and orchestration layer. It does not interact with the browser directly — that is entirely Playwright’s domain. pytest’s responsibilities are:

  • Discovery — Automatically finding and collecting all test methods across the 6 product suites
  • Fixtures — Managing setup and teardown (browser contexts, cookies, environment configuration, form URLs) so that each test starts in a clean, correctly configured state
  • Markers — Providing the tagging system (@pytest.mark.smoke, @pytest.mark.adrianflux, etc.) that allows selective test execution
  • Parametrisation — Driving data-driven tests, such as running the same quote journey with 21 different source codes or multiple email/price combinations to verify different outcomes
  • Reporting — Generating the HTML test report with pass/fail results, embedded screenshots, video links, and links to test case documentation
  • Plugin system — The test suite is packaged as a pytest plugin, meaning consuming projects get all fixtures, markers, and configuration automatically just by installing the package

How They Work Together

pytest                              Playwright
─────                               ──────────
Discovers tests
Resolves fixtures (URLs, cookies)
                                    Launches browser
                                    Sets viewport, video recording
Calls test method
                                    Navigates to quote form
                                    Fills fields, clicks buttons
                                    Waits for pages to load
                                    Asserts page content
Collects pass/fail result
                                    Captures screenshot (on failure)
                                    Saves video recording
Generates HTML report
Attaches screenshots & video links

In short: Playwright does the work, pytest organises and reports on it.

Automatic Evidence Capture

Every test run automatically produces:

  • Video recordings of each test, viewable directly from the HTML report
  • Screenshots captured at the point of any failure
  • Playwright trace files for failed tests, providing a step-by-step replay of DOM state, network activity, and console output

These artifacts are compiled into a self-contained HTML report that can be opened in any browser and shared without special tooling. Each test result links directly to its video, failure screenshot, and corresponding test case documentation on GitHub.

Reports from CI runs are published to a CDN and accessible via a browser:

Test Environments

Tests can be targeted at any environment (development, UAT, staging, production) by changing a single configuration value. Each brand can also be pointed at a different environment independently, allowing testing to proceed in parallel across teams.


Test Execution

On-Demand via GitHub

Tests are triggered on demand through GitHub Actions. The person running the tests selects:

  1. The target environment (e.g. UAT, staging)
  2. Optionally, a subset of tests to run (e.g. only smoke tests, only a specific brand)

Results and artifacts are retained for 30 days.

Selective Execution

Tests are tagged with descriptive markers, making it straightforward to run targeted subsets:

MarkerWhat It Runs
smokeCore happy-path tests across all brands
validationInput validation and error handling
quotingFull end-to-end quote journeys
adrianfluxAll Adrian Flux tests only
sterlingAll Sterling tests only
bikesureAll Bikesure tests only
carCar insurance forms only
learnerLearner driver forms only
bikeMotorcycle forms only

Markers can be combined — for example, running only smoke tests for Bikesure motorcycle forms.


Parametrised Tests — Validating Destination Rules at Scale

One of the most powerful features of the test suite is its use of parametrisation to validate EPA destination rules comprehensively. Rather than writing a separate test for each combination of inputs and expected outcomes, a single test method is written once and then driven by a data table. pytest automatically generates and runs a distinct test for every row in that table.

The Problem: Destination Rules Vary by EPA

Each EPA has its own business rules that determine where a customer is directed after completing a quote. Depending on the combination of quote status, premium amount, and source code, the customer may be sent to:

  • Buy online — A quote was issued and the customer can purchase immediately
  • Callback — The quote needs further underwriting; the customer is offered a callback
  • Call us — The quote cannot be processed online; the customer is directed to call

These rules differ between EPAs. For example, a quoted customer with a premium of £1,000 might be directed to the buy page on Sterling Car, but to the callback page on Adrian Flux Car. Getting these rules wrong means customers end up on the wrong page — either unable to buy when they should be able to, or seeing incorrect messaging.

How Parametrisation Solves This

Each EPA’s destination rules are expressed as a simple data table directly in the test code:

Email PrefixPriceExpected Destination
quoted£0call-us
quoted£1callback
quoted£1,999callback
quoted£2,000callback
quoted£3,999callback
quoted£4,000call-us
rejected£0call-us
rejected£1call-us
rejected£1,999call-us

(Example: Adrian Flux Car — 12 combinations from a single test definition)

From this one table, pytest generates 12 independent tests — each running the full quote journey end to end with different inputs and verifying the customer lands on the correct outcome page. The report shows each combination as a separate pass or fail, making it immediately clear which specific rule has broken.

Scaling Across EPAs

The same pattern is applied across all 6 products, with each EPA’s table reflecting its own destination rules. This means:

  • Adding a new destination rule is as simple as adding a row to the table — no new test code to write
  • Changing a rule (e.g. moving the callback threshold from £2,000 to £3,000) means updating one value in the table
  • Each EPA’s rules are visible at a glance in the test file, serving as living documentation of the business logic

Beyond Destinations: Source Code Attribution

The same parametrisation approach is used to validate marketing source code handling across the quote journey. A second data table combines source codes, “where did you hear” selections, expected destinations, and expected callback URL parameters — generating up to 21 test runs from a single test method per EPA.

This covers scenarios such as:

  • A source code passed via URL appearing correctly in callback links
  • Short-form source codes being resolved to their canonical form
  • Missing or empty source codes falling back to the correct default
  • Source codes surviving the full journey through all form steps and appearing on the outcome page

The Net Effect

Across all 6 products, parametrisation generates a large number of test runs from a relatively small number of test definitions. One test method with a 12-row table and another with a 21-row table produces 33 full end-to-end journeys per EPA — nearly 200 destination and source code validations across the suite, each running independently and reporting individually. Adding a new EPA’s rules means defining its data table; the test logic is already written.


Reusability — Packaged as a Shared Library

The test suite is not a standalone script — it is built and distributed as a Python package (flux-epa-e2e-tests) that can be installed into any project. This means the tests, fixtures, step classes, and component classes are all reusable across multiple consuming applications.

How It Works

The package is registered as a pytest plugin via a standard entry point. When a consuming project installs the package, pytest automatically discovers and loads:

  • All test fixtures (browser configuration, cookie setup, environment resolution)
  • All test markers (smoke, quoting, adrianflux, etc.)
  • All default configuration (HTML reporting, video recording, tracing, output directories)
  • All test suites, step classes, and component classes

A consuming project needs only two things to run the full test suite:

  1. pip install flux-epa-e2e-tests (or add it to their dependencies)
  2. A .env file with the target environment URLs and QA bypass cookie

No additional pytest configuration, fixture definitions, or test imports are required — the plugin handles all of it. The consuming project can also override any default (report path, output directory, target URLs) through its own configuration or command line flags.

Versioned Releases

The package follows semantic versioning (currently v0.12) and is published to a private package registry (Gemfury). This ensures:

  • Consuming projects pin to a known version and upgrade deliberately
  • Test changes are tracked through a changelog
  • Rollback to a previous version is straightforward if needed

CI/CD Pipelines

The test suite has two GitHub Actions workflows that automate execution and distribution.

1. Test Execution Pipeline

A manually triggered workflow that runs the test suite against a configured environment:

  1. Provisions an Ubuntu runner with Python 3.12
  2. Installs the test package and Playwright’s Chromium browser
  3. Runs tests — optionally filtered by a marker expression (e.g. smoke, adrianflux and quoting)
  4. Captures the pytest summary line (passed/failed counts)
  5. Uploads the full results directory (HTML report, videos, screenshots, traces) as a GitHub artifact retained for 30 days
  6. Uploads the HTML report to Cloudflare R2 cloud storage for easy sharing via a public URL

Environment URLs and secrets (QA bypass cookie, R2 credentials) are managed through GitHub repository variables and secrets, keeping sensitive values out of the codebase.

2. Package Publishing Pipeline

Triggered automatically when a GitHub release is created:

  1. Builds the Python package (wheel)
  2. Uploads it to the Gemfury private package registry

This means the release process is: tag a version, create a GitHub release, and the package is available for consuming projects to install within minutes.

Pipeline Summary

                    ┌──────────────────────┐
  Manual trigger ──►│  Run E2E Tests       │
  (select markers)  │  - pytest on CI      │
                    │  - HTML report       │──► GitHub Artifacts (30 days)
                    │  - Videos & traces   │──► Cloudflare R2 (shareable URL)
                    └──────────────────────┘

                    ┌──────────────────────┐
  GitHub release ──►│  Publish Package     │
                    │  - Build wheel       │──► Gemfury Registry
                    │  - Upload to Gemfury │    (pip install in other projects)
                    └──────────────────────┘

Test Case Numbering, Documentation, and Traceability

The Numbering System

Every test method carries a unique, sequentially numbered identifier that ties together the test code, its documentation, and its result in the HTML report. The identifier is embedded directly in the method name and docstring:

  • Method name: test_AFC009_complex_vehicle_lookup
  • Docstring: AFC-009: Verify complex vehicle lookup with manual entry advances to car details.

Each product suite has its own prefix (AFC for Adrian Flux Car, BKSB for Bikesure Bike, SL for Sterling Learner, etc.) and numbers run sequentially from 001 upwards in the order the tests appear in the file. This means the case number reflects the test’s position in the suite — there are no gaps or out-of-order numbers.

Documentation with Claude Code

Test case documentation is generated and maintained using Claude Code (Anthropic’s AI coding assistant) through a set of custom slash commands built specifically for this project:

/new-test-case — When a new test is written, this command:

  1. Reads the test method, the step classes it calls, and the element classes those steps use
  2. Understands what the test does at the UI level by following the full call chain
  3. Assigns the next available case number and renames the test method accordingly
  4. Generates a plain-English documentation file describing every step the test performs
  5. Adds the new case to the suite’s index file

/check-test-case-order — Audits a test suite to verify that all case numbers are sequential, all documentation files exist, and all cross-references between documents are valid. If numbers have drifted (e.g. after tests were reordered), it re-indexes the entire suite — renaming test methods, updating doc files, and correcting cross-references in a single operation.

/remove-test-case — Removes a test from the suite, deletes its documentation, and re-indexes all subsequent cases so the numbering remains gapless.

This approach means documentation is never written from scratch by hand. Claude reads the actual test code — including the step and component layers — and produces accurate, up-to-date descriptions of what each test does. When tests change, the documentation can be regenerated from the code rather than manually updated.

What a Test Case Document Looks Like

Each test case has its own markdown file (e.g. docs/adrianflux_car/AFC-009.md) containing:

  • Category — Smoke, Validation, or Parametrised
  • Markers — The pytest markers applied to this test
  • Test method — The exact function name for traceability back to the code
  • Steps — A numbered list describing every action in plain English
  • Test data (for parametrised tests) — A table of input combinations and expected outcomes

There are currently 174 individual test case documents plus a summary index for each product that lists all cases in a single table.

Linked in the HTML Report

The test case documentation is not just a separate reference — it is linked directly from the HTML test report. A pytest hook inspects each test result, extracts the case ID from the test method name (e.g. AFC009 from test_AFC009_complex_vehicle_lookup), and appends a clickable link to the corresponding documentation file on GitHub.

This means that when reviewing test results, anyone can click through from a pass or failure directly to the full plain-English description of what that test was verifying — without needing to read the test code.

HTML Report Row
┌──────────────────────────────────────────────────────────────┐
│  test_AFC009_complex_vehicle_lookup     PASSED               │
│  [Video]  [Screenshot]  [Test Case ↗]                        │
│                              │                               │
│                              └─► docs/adrianflux_car/AFC-009.md
│                                  on GitHub                   │
└──────────────────────────────────────────────────────────────┘

This creates a full traceability chain: test result → test case documentation → test code → step classes → component classes, all connected by the case ID.


Quality Controls on the Test Suite Itself

The test codebase is held to the same engineering standards as production code:

  • 100% documentation coverage — Every module, class, and method has a descriptive docstring, enforced by automated checks.
  • Automated linting and formatting — Code style is enforced consistently across the entire suite.
  • Versioned releases — The test suite is versioned (currently v0.12) and distributed as a package, ensuring reproducible test runs.

Key Benefits

  1. Confidence in releases — Tests can be run against any environment before or after a deployment to verify that quote journeys work correctly.
  2. Rapid feedback — Issues are caught early with clear evidence (video, screenshots, traces) to support diagnosis.
  3. Cross-brand consistency — The same testing patterns are applied uniformly across all 3 brands and 6 products.
  4. Marketing attribution assurance — Source code tests verify that campaign tracking remains intact through the full customer journey.
  5. Scalability — Adding a new brand or product follows an established template, keeping the approach consistent as the platform grows.

Applying This Architecture to Other Products

The architecture behind this test suite is not specific to insurance quote forms. The three-layer pattern (components, steps, tests), the tooling (Playwright, pytest, Claude Code documentation), and the infrastructure (CI pipelines, CDN-hosted reports, package distribution) can be applied to any multi-step web application. Adopting it for a new product does not require starting from scratch — the patterns and infrastructure are already proven and can be replicated.

What Transfers Directly

The following elements are product-agnostic and can be reused as-is or with minimal adaptation:

  • The EpaStep wrapper — The helper class that wraps Playwright’s page with convenience methods (navigate, verify titles, check errors, wait for pages) is not EPA-specific. A similar wrapper could be created for any product, or the existing one extended.
  • The three-layer pattern — Components (individual UI actions), steps (page scenarios), and tests (orchestrated journeys) work for any multi-page application: onboarding flows, checkout processes, claims forms, account management, or any wizard-style UI.
  • Parametrised destination/outcome testing — Any product with branching outcomes based on user input (approval/rejection, pricing tiers, eligibility checks) can use the same data-table approach to validate all paths from a single test definition.
  • The CI pipeline — The GitHub Actions workflows for running tests on demand, uploading reports to cloud storage, and publishing the package require only configuration changes (environment URLs, secrets) to point at a different product.
  • The CDN-hosted report index — The R2 upload and index page pattern works for any test suite, giving stakeholders a single URL to find any historical test run.
  • Claude Code slash commands — The /new-test-case, /check-test-case-order, and /remove-test-case commands are driven by naming conventions (prefix + sequential number + markdown docs). Adapting them for a new product means defining a new prefix — the commands themselves handle discovery, numbering, documentation generation, and re-indexing.

What a New Product Needs

To bring a new product into the same testing architecture, the product-specific work is:

  1. Component classes — One class per reusable UI component in the new product, following the F-number convention if the product uses it, or a suitable naming scheme otherwise. Each class exposes the actions a user can perform on that component.
  2. Step classes — One class per page or screen, composing component actions into scenarios (happy path, complex path, error paths).
  3. Test methods — Orchestrating steps into the journeys that matter for that product, tagged with appropriate markers.
  4. Fixtures — Product-specific configuration: base URLs, environment overrides, expected content (titles, disclaimers, legal text).
  5. Parametrised data tables — The product’s business rules expressed as input/outcome tables for destination testing.

The infrastructure, reporting, documentation tooling, and package distribution are already in place. A new product plugs into the existing system rather than building its own.

A Practical Example

Consider a hypothetical home insurance quote form. Applying this architecture would look like:

LayerEPA (existing)Home Insurance (new)
ComponentsF058VehicleLookupCarF040PropertyLookup
F066VehiclePurchaseDetailsF041PropertyDetails
StepsS01_entry_page (vehicle reg)S01_entry_page (postcode lookup)
S04_policyholder_detailsS03_policyholder_details
Teststest_AFC024_quote_outcomestest_HI024_quote_outcomes
Parametrised rulesquoted + £1,000 → callbackstandard + £500 → buy
DocsAFC-024.mdHI-024.md

The component and step classes are new — they reflect the new product’s UI. But the test structure, documentation tooling, CI pipeline, and report infrastructure are identical. The investment made in the EPA suite pays forward into every subsequent product.


Summary

The EPA E2E test suite provides structured, automated coverage of all customer-facing quote journeys across Adrian Flux, Sterling Insurance, and Bikesure. With 169 documented test cases, automatic evidence capture, and flexible execution options, the suite gives stakeholders confidence that quote forms are functioning correctly and that customers are reaching the right outcomes.

Last modified March 10, 2026: Updates (4eda4cb)