NDA note: Internal system names, specific product metrics, and Vodafone-proprietary tooling are not disclosed. The descriptions below cover engineering approach and outcomes at the level cleared for public discussion.
Context
_VOIS is a Vodafone subsidiary. The platform served millions of users across a UK telecoms marketing and product site — high-traffic, performance-sensitive, with a content team publishing changes via Contentful CMS.
Two distinct workstreams during my 18-month tenure:
- CRO feature delivery — building data-driven UI features against an internal A/B testing framework, shipping product comparison and decision-flow improvements
- Test infrastructure migration — leading the Enzyme → React Testing Library migration, raising coverage from 70% to 85%
Workstream 1 — Product comparison feature (CRO)
Problem
Purchase decision flows had identifiable drop-off in funnel analytics. The hypothesis, shared by product and analytics teams, was that users couldn't compare tariffs side-by-side without leaving the page. A new comparison component would reduce that friction.
Architecture
Implementation
The comparison component needed to:
- Handle an arbitrary number of tariff items (1–4) with graceful layout at each count
- Remain accessible — keyboard navigable, with correct
role="table"semantics for screen readers - Integrate with the CMS schema without requiring a structural change to existing Contentful content types
The Contentful integration used a reference field on the existing page type — editors could tag up to 4 tariff entries for comparison without any new content modelling. The component reads the reference list and renders accordingly.
Outcome
The feature shipped, was A/B tested against the existing decision flow, and contributed to CRO uplift for the purchase funnel. Specific conversion percentages are commercially sensitive and not disclosed here.
Workstream 2 — Enzyme → RTL migration
Problem
The test suite had ~70% coverage but was brittle. Tests were written against component internals — checking whether specific internal methods were called, asserting on implementation details like state variable names. When the team refactored a component's internals (without changing any observable behaviour), multiple tests broke, creating false alarm noise.
The result: engineers were spending time fixing tests that didn't actually catch regressions, and coverage was a vanity metric rather than a signal of confidence.
Architecture — migration strategy
Implementation
The core principle shift: RTL tests ask "does this component behave correctly from the user's perspective?" not "does this component do what I think it does internally?".
// Before (Enzyme) — tests implementation detail
it('should set loading state', () => {
const wrapper = shallow(<TariffCard id="t1" />)
expect(wrapper.state('isLoading')).toBe(true)
})
// After (RTL) — tests observable behaviour
it('shows a loading skeleton while fetching', async () => {
render(<TariffCard id="t1" />)
expect(screen.getByRole('status', { name: /loading/i })).toBeInTheDocument()
await waitForElementToBeRemoved(() => screen.queryByRole('status'))
expect(screen.getByText(/monthly/i)).toBeInTheDocument()
})Handling the connected components: the trickiest migrations were components deeply integrated with Redux or React Query. The pattern we settled on: test at the feature level with a rendered Provider tree, not by mocking the store internals. This made the tests slower but dramatically more reliable — a fake Redux state that mirrors the real API contract.
Mentor role: I led the migration rather than doing it solo. Pair programming sessions with junior engineers on the team were part of the approach — each engineer migrated one component category with guidance. This spread RTL fluency across the team rather than creating a single point of knowledge.
Outcome
- Coverage: 70% → 85% — 15pp increase
- Brittle implementation-detail tests removed; remaining tests are behaviour-focused
- PR turnaround improved — engineers stopped spending time fixing tests that didn't represent real regressions
- Team fluent in RTL by end of migration (not just one person)
This migration was also what I'd now recommend for any team still on Enzyme. The testing pyramid doesn't change, but the confidence per test improves significantly when tests describe observable behaviour.