Skip to content
Viewing: all (26)
  1. Roger Whittlesea What does collaboration look like at the proposal stage? Digital strategyTechnical validationBrand automation
  2. Matthew Froggatt Internal work has no client Digital strategyMarketingContent strategy
  3. Barry Cumberlidge The gift of time Digital strategyBrand automationMarketing
  4. David Lowbridge From fixed values to behaviours Brand automationUX & UIDigital strategy
  5. Mike Leonard The web is fun Motion designUX & UITechnical validation
  6. Jara Santamaria Martinez Every year is a new brief PlatformsContent strategyTechnical validation
  7. Kristina Radonjic The work between the work Digital strategyMarketing
  8. Guanglun Wu Choosing the right tech stack Technical validationPlatformsDigital strategy
  9. Barry Cumberlidge Security deep dive Technical validationContent strategyDigital strategy
  10. David Lowbridge Startups are prototypes Digital strategyBrand automation
  11. Tara Heal Shapeshifting the Future UX & UIMarketing
  12. Tom Small The theme is the guardrail Brand automationPlatforms
  13. Barry Cumberlidge Your biggest asset in an RFP, your website SEOContent strategyDigital strategyMarketing
  14. Michael Gunner Cache Components: the right content at the right time PlatformsTechnical validationContent strategy
  15. David Lowbridge From static frames to a creative operating system Brand automationUX & UI
  16. Tom Small We build by leaving things out PlatformsDigital strategyUX & UI
  17. Michael Gunner Standardise the foundations, not the brand experience PlatformsBrand automation
  18. Charlie Clarke Letting type move Motion designUX & UI
  19. Andy Purbrick Putting video back where it belongs PlatformsTechnical validation
  20. Mike Leonard The cache that doubled as a coordinator PlatformsTechnical validation
  21. Andy Purbrick We test the revenue path Technical validationDigital strategy
  22. Mike Leonard Two months in Three.js, we started over Motion designTechnical validation
  23. Rhona Mackay The form we actually trust PlatformsTechnical validation
  24. Andy Purbrick The testbed nobody will see PlatformsTechnical validation
  25. Andy Purbrick Twenty-four modules, one wrapper PlatformsContent strategy
  26. Andy Purbrick We haven’t missed a Thursday in 33 weeks Content strategyMarketing

We test the revenue path

Andy Purbrick 2 min read Technical validation Digital strategy

The contact form is the only page on this site that generates leads. So that’s where the tests live.

We test the revenue path

We don’t chase coverage numbers. What we have is a targeted set of tests aimed at the things that would hurt if they broke: the forms, the pipeline that processes submissions and the CI that catches problems before production.

Dependency injection as a testing technique

Our submission pipeline accepts a dependencies object containing every external service: CRM client, file uploader, error notifier, spam verifier. Both form handlers use it.

That turns the pipeline into a pure function. In production, real services get passed in. In tests, we inject fakes directly — no mocking library, no patching globals. The function doesn’t know whether its dependencies are real.

What the unit tests cover

Five scenarios, each chosen because we saw it go wrong or imagined it going wrong in a way that costs money:

  1. Date formatting for availability labels. Garbled dates look unprofessional in client-facing notifications.
  2. File upload fails after lead creation. Losing a PDF is recoverable, losing the lead is not.
  3. Derived fields reach the CRM correctly. Silent data mismatches are the worst kind of bug.
  4. Fatal failures alert the team and rethrow. A swallowed error means a lost inquiry.
  5. Newsletter signup blocks low-scoring spam while still capturing legitimate subscribers.

What the e2e tests cover

The e2e suite tests what the user actually sees: inline validation messages appearing as they type, the success screen after a valid submission, the error state when something goes wrong. Three form variants (start-a-project, minimal and newsletter) are each tested through the real UI in a headless browser. Playwright intercepts server calls at the network layer, so the tests run without a backend but the user-facing behavior is identical to production.

Why Node’s built-in runner

We use Node’s built-in test runner — not Jest, not Vitest. The DI pattern means tests are just function calls with fake inputs, so there’s nothing to mock and no lifecycle hooks to manage. A test framework would add a dependency and a configuration layer for problems we don’t have. The built-in runner handles assertions and test grouping, which is the whole requirement here.

The same philosophy extends to e2e. Playwright intercepts network calls so the tests run against the real UI without touching real services — the suite runs anywhere node does, with nothing to spin up first.

What it caught

Since this setup went live, no form submission has been lost without the team knowing about it within minutes. Each test is still just a function call with fake inputs and a check on the output.