From 90c1f73795aebc501569069f026c4f753aa8e23b Mon Sep 17 00:00:00 2001 From: Emma Imber Date: Thu, 13 Nov 2025 15:07:39 +0000 Subject: [PATCH 1/7] Make docs clearer about use of kebab case for test name --- .../docs/development/ab-testing-in-dcr.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/dotcom-rendering/docs/development/ab-testing-in-dcr.md b/dotcom-rendering/docs/development/ab-testing-in-dcr.md index 69262a3ba3e..aff45f032dd 100644 --- a/dotcom-rendering/docs/development/ab-testing-in-dcr.md +++ b/dotcom-rendering/docs/development/ab-testing-in-dcr.md @@ -119,7 +119,7 @@ When your PR is merged, the A/B test will be automatically deployed to Fastly an #### Naming Conventions -A/B tests should be prefixed with the team associated with the test, for example `webex-example-test`. This helps to identify the team responsible for the test and is enforce by typescript validation, you can inspect & edit the allowed team name definitions [here](https://github.com/guardian/dotcom-rendering/blob/main/ab-testing/types.ts#L9). +A/B tests should be prefixed with the team associated with the test, for example `webex-example-test`. This helps to identify the team responsible for the test and is enforced by typescript validation, you can inspect & edit the allowed team name definitions [here](https://github.com/guardian/dotcom-rendering/blob/main/ab-testing/types.ts#L9). #### Test Size and Groups @@ -135,7 +135,7 @@ All requests are processed by Fastly at the edge, however, A/B testing of server Ensure that the `type` field is set to either `client` or `server` to indicate the type of test so that server side tests can be cached correctly, and client side tests are not splitting the cache unnecessarily. -There's a limit of the number of concurrent server-side tests that can be run, enforce by the validation script, so it's important to use client-side tests where possible. +There's a limit of the number of concurrent server-side tests that can be run, enforced by the validation script, so it's important to use client-side tests where possible. #### Test Expiration @@ -178,13 +178,13 @@ const someComponent = () => { const abTests = useBetaAB(); // Am I in the test at all? - const isInTest = abTests?.isUserInTest('AbTestTest') ?? false; + const isInTest = abTests?.isUserInTest('webex-example-test') ?? false; const isInControlGroup = - (abTests?.isUserInTestGroup('AbTestTest', 'control') ?? false); + (abTests?.isUserInTestGroup('webex-example-test', 'control') ?? false); const isInVariantGroup = - abTests?.isUserInTestGroup('AbTestTest', 'variant') ?? false; + abTests?.isUserInTestGroup('webex-example-test', 'variant') ?? false; if (isInControlGroup) { return ( @@ -224,15 +224,15 @@ const abTests = useBetaAB(); const abTestParticipations = abTests?.getParticipations(); // EG. { commercial-dev-client-side-test: 'variant', commercial-dev-server-side-test: 'variant' } // Is user in the AbTestTest test (any cohort) -const isInTest = abTests?.isUserInTest('AbTestTest') ?? false; +const isInTest = abTests?.isUserInTest('webex-example-test') ?? false; // Is user in the AbTestTest test (control cohort) const isInControlGroup = - abTests?.isUserInTestGroup('AbTestTest', 'control') ?? false; + abTests?.isUserInTestGroup('webex-example-test', 'control') ?? false; // Is user in the AbTestTest test (variant cohort) const isInVariantGroup = - abTests?.isUserInTestGroup('AbTestTest', 'variant') ?? false; + abTests?.isUserInTestGroup('webex-example-test', 'variant') ?? false; ``` #### On the Client From cf647991578960c91334f4e231182bc52e6ea14f Mon Sep 17 00:00:00 2001 From: Emma Imber Date: Thu, 13 Nov 2025 15:08:03 +0000 Subject: [PATCH 2/7] Add thefilter to ab test team type --- ab-testing/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ab-testing/types.ts b/ab-testing/types.ts index dfbfab8dc2e..8502128c9a5 100644 --- a/ab-testing/types.ts +++ b/ab-testing/types.ts @@ -4,7 +4,7 @@ type AudienceSpace = Map; type AllSpace = Map; -type Team = "commercial" | "webex"; +type Team = "commercial" | "webex" | "thefilter"; type TestName = `${Team}-${string}`; From 0cea1c4eb575924dc7106a343f6dcadff8bb7723 Mon Sep 17 00:00:00 2001 From: Emma Imber Date: Thu, 13 Nov 2025 15:08:27 +0000 Subject: [PATCH 3/7] Add product element hold back test defintion --- ab-testing/abTests.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/ab-testing/abTests.ts b/ab-testing/abTests.ts index 913dbd4a226..6e3c1e76378 100644 --- a/ab-testing/abTests.ts +++ b/ab-testing/abTests.ts @@ -44,6 +44,18 @@ const ABTests: ABTest[] = [ groups: ["control", "variant"], shouldForceMetricsCollection: true, }, + { + name: 'thefilter-product-element', + description: + 'A hold back test to measure uplift of the product element', + owners: ['thefilter.dev@guardian.co.uk'], + status: 'ON', + expirationDate: '2025-12-30', + type: 'server', + audienceSize: 10 / 100, + groups: ['control', 'variant'], + shouldForceMetricsCollection: false, + }, ]; const activeABtests = ABTests.filter((test) => test.status === "ON"); From 40e6ed288645e7ead7ad7bd88fae772f34355943 Mon Sep 17 00:00:00 2001 From: Emma Imber Date: Thu, 13 Nov 2025 15:26:26 +0000 Subject: [PATCH 4/7] Don't render left col cards or inline product cards in the hold back test variant --- .../src/components/ProductElement.tsx | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/dotcom-rendering/src/components/ProductElement.tsx b/dotcom-rendering/src/components/ProductElement.tsx index 7a465580254..c15397e68e5 100644 --- a/dotcom-rendering/src/components/ProductElement.tsx +++ b/dotcom-rendering/src/components/ProductElement.tsx @@ -4,6 +4,7 @@ import type { ReactNode } from 'react'; import type { ArticleFormat } from '../lib/articleFormat'; import { parseHtml } from '../lib/domUtils'; import type { NestedArticleElement } from '../lib/renderElement'; +import { useBetaAB } from '../lib/useAB'; import type { ProductBlockElement } from '../types/content'; import { ProductCardInline } from './ProductCardInline'; import { ProductCardLeftCol } from './ProductCardLeftCol'; @@ -39,12 +40,22 @@ export const ProductElement = ({ format: ArticleFormat; shouldShowLeftColCard: boolean; }) => { + const abTests = useBetaAB(); + const isInHoldBackTestVariant = + abTests?.isUserInTestGroup('thefilter-product-element', 'variant') ?? + false; + const showContent = product.displayType === 'InlineOnly' || product.displayType === 'InlineWithProductCard'; + // In the hold back test variant, if the product element has a display type of ProductCardOnly, + // we should still render the product card. This is because there may not be any suitable + // nested content to render, which could result in some unintended display issues. For the + // InlineWithProductCard display type, we won't render the cards in the hold back test. const showProductCard = product.displayType === 'ProductCardOnly' || - product.displayType === 'InlineWithProductCard'; + (!isInHoldBackTestVariant && + product.displayType === 'InlineWithProductCard'); return ( <> {showContent && ( @@ -52,7 +63,9 @@ export const ProductElement = ({ product={product} format={format} ArticleElementComponent={ArticleElementComponent} - shouldShowLeftColCard={shouldShowLeftColCard} + shouldShowLeftColCard={ + shouldShowLeftColCard && !isInHoldBackTestVariant + } /> )} {showProductCard && ( From 996291199321fec2215dc5e2ebc8ea51f1cc52b2 Mon Sep 17 00:00:00 2001 From: Emma Imber Date: Thu, 13 Nov 2025 15:32:03 +0000 Subject: [PATCH 5/7] Amend test id in comments in AB testing doc --- dotcom-rendering/docs/development/ab-testing-in-dcr.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dotcom-rendering/docs/development/ab-testing-in-dcr.md b/dotcom-rendering/docs/development/ab-testing-in-dcr.md index aff45f032dd..d2772e3df03 100644 --- a/dotcom-rendering/docs/development/ab-testing-in-dcr.md +++ b/dotcom-rendering/docs/development/ab-testing-in-dcr.md @@ -223,14 +223,14 @@ const abTests = useBetaAB(); // Get all of the user's server/client-side A/B test participations const abTestParticipations = abTests?.getParticipations(); // EG. { commercial-dev-client-side-test: 'variant', commercial-dev-server-side-test: 'variant' } -// Is user in the AbTestTest test (any cohort) +// Is user in the webex-example-test test (any cohort) const isInTest = abTests?.isUserInTest('webex-example-test') ?? false; -// Is user in the AbTestTest test (control cohort) +// Is user in the webex-example-test test (control cohort) const isInControlGroup = abTests?.isUserInTestGroup('webex-example-test', 'control') ?? false; -// Is user in the AbTestTest test (variant cohort) +// Is user in the webex-example-test test (variant cohort) const isInVariantGroup = abTests?.isUserInTestGroup('webex-example-test', 'variant') ?? false; ``` From 603e8788e1be6133c9e0cc118701d53109d09602 Mon Sep 17 00:00:00 2001 From: Emma Imber Date: Wed, 19 Nov 2025 10:49:18 +0000 Subject: [PATCH 6/7] Prettier --- ab-testing/abTests.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/ab-testing/abTests.ts b/ab-testing/abTests.ts index 6e3c1e76378..376d9ea7b82 100644 --- a/ab-testing/abTests.ts +++ b/ab-testing/abTests.ts @@ -45,15 +45,15 @@ const ABTests: ABTest[] = [ shouldForceMetricsCollection: true, }, { - name: 'thefilter-product-element', + name: "thefilter-product-element", description: - 'A hold back test to measure uplift of the product element', - owners: ['thefilter.dev@guardian.co.uk'], - status: 'ON', - expirationDate: '2025-12-30', - type: 'server', + "A hold back test to measure uplift of the product element", + owners: ["thefilter.dev@guardian.co.uk"], + status: "ON", + expirationDate: "2025-12-30", + type: "server", audienceSize: 10 / 100, - groups: ['control', 'variant'], + groups: ["control", "variant"], shouldForceMetricsCollection: false, }, ]; From b88136baf25bb9f1ae71eb70aeb89e14f9ebce82 Mon Sep 17 00:00:00 2001 From: Emma Imber Date: Wed, 19 Nov 2025 15:33:22 +0000 Subject: [PATCH 7/7] Set to 0% test --- ab-testing/abTests.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ab-testing/abTests.ts b/ab-testing/abTests.ts index 376d9ea7b82..1f336f8fb9f 100644 --- a/ab-testing/abTests.ts +++ b/ab-testing/abTests.ts @@ -52,7 +52,7 @@ const ABTests: ABTest[] = [ status: "ON", expirationDate: "2025-12-30", type: "server", - audienceSize: 10 / 100, + audienceSize: 0 / 100, groups: ["control", "variant"], shouldForceMetricsCollection: false, },