From 1cb394946d7fe8d5ffe408c3776e74c6b8743348 Mon Sep 17 00:00:00 2001 From: Emma Imber Date: Thu, 10 Sep 2026 10:55:26 +0100 Subject: [PATCH 1/3] Support headline AB tests on card sublinks --- dotcom-rendering/src/frontend/feFront.ts | 1 + dotcom-rendering/src/model/enhanceCards.ts | 20 +++++++++++++++++--- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/dotcom-rendering/src/frontend/feFront.ts b/dotcom-rendering/src/frontend/feFront.ts index 1b3144b1704..8fcff1de1b7 100644 --- a/dotcom-rendering/src/frontend/feFront.ts +++ b/dotcom-rendering/src/frontend/feFront.ts @@ -434,6 +434,7 @@ export type FESupportingContent = { properties: { href?: string; webUrl?: string; + tests?: EditorialTest[]; }; header: { kicker?: { diff --git a/dotcom-rendering/src/model/enhanceCards.ts b/dotcom-rendering/src/model/enhanceCards.ts index f9a6eac8344..97b64535893 100644 --- a/dotcom-rendering/src/model/enhanceCards.ts +++ b/dotcom-rendering/src/model/enhanceCards.ts @@ -36,6 +36,9 @@ import { enhanceTags } from './enhanceTags'; const enhanceSupportingContent = ( supportingContent: FESupportingContent[], parentFormat: ArticleFormat, + serverSideABTests: Record, + isEditorialABTestingEnabled: boolean, + pageId?: string, ): DCRSupportingContent[] => { return supportingContent.map((subLink) => { /** Use link format where available and fallback to parent otherwise */ @@ -50,7 +53,12 @@ const enhanceSupportingContent = ( return { format: linkFormat, - headline: subLink.header.headline, + headline: decideHeadline( + subLink, + serverSideABTests, + isEditorialABTestingEnabled, + pageId, + ), url: decideUrl(subLink), kickerText: !kickerText && supportingContentIsLive ? 'Live' : kickerText, @@ -209,7 +217,7 @@ const findActiveEditorialTest = ( * return the variant headline matching the user test group. Otherwise, return the default headline */ export const decideHeadline = ( - faciaCard: FEFrontCard, + faciaCard: FEFrontCard | FESupportingContent, serverSideABTests: Record, isEditorialABTestingEnabled: boolean, pageId?: string, @@ -535,7 +543,13 @@ export const enhanceCards = ( : undefined, kickerText: decideKicker(faciaCard, cardInTagPage, pageId), supportingContent: faciaCard.supportingContent - ? enhanceSupportingContent(faciaCard.supportingContent, format) + ? enhanceSupportingContent( + faciaCard.supportingContent, + format, + serverSideABTests, + isEditorialABTestingEnabled, + pageId, + ) : undefined, discussionApiUrl, discussionId: faciaCard.discussion.isCommentable From afc1ea433f91d7ecf452992ce387146d60290b66 Mon Sep 17 00:00:00 2001 From: Emma Imber Date: Thu, 10 Sep 2026 11:04:55 +0100 Subject: [PATCH 2/3] Regenerate schemas --- .../src/frontend/schemas/feFront.json | 153 ++++++++++++++++++ .../src/frontend/schemas/feTagPage.json | 51 ++++++ 2 files changed, 204 insertions(+) diff --git a/dotcom-rendering/src/frontend/schemas/feFront.json b/dotcom-rendering/src/frontend/schemas/feFront.json index f73e02f5604..82eb44a640b 100644 --- a/dotcom-rendering/src/frontend/schemas/feFront.json +++ b/dotcom-rendering/src/frontend/schemas/feFront.json @@ -950,6 +950,57 @@ }, "webUrl": { "type": "string" + }, + "tests": { + "type": "array", + "items": { + "type": "object", + "properties": { + "testUuid": { + "type": "string" + }, + "variantMeta": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "$ref": "#/definitions/VariantId" + }, + "meta": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "id", + "meta" + ] + } + }, + "startDate": { + "type": "number" + }, + "expiryDate": { + "type": "number" + }, + "frontsThisTestCanRunOn": { + "type": "array", + "items": { + "type": "string" + } + }, + "hasManuallyEndedOnThisTrail": { + "type": "boolean" + } + }, + "required": [ + "frontsThisTestCanRunOn", + "hasManuallyEndedOnThisTrail", + "testUuid", + "variantMeta" + ] + } } } }, @@ -1836,6 +1887,57 @@ }, "webUrl": { "type": "string" + }, + "tests": { + "type": "array", + "items": { + "type": "object", + "properties": { + "testUuid": { + "type": "string" + }, + "variantMeta": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "$ref": "#/definitions/VariantId" + }, + "meta": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "id", + "meta" + ] + } + }, + "startDate": { + "type": "number" + }, + "expiryDate": { + "type": "number" + }, + "frontsThisTestCanRunOn": { + "type": "array", + "items": { + "type": "string" + } + }, + "hasManuallyEndedOnThisTrail": { + "type": "boolean" + } + }, + "required": [ + "frontsThisTestCanRunOn", + "hasManuallyEndedOnThisTrail", + "testUuid", + "variantMeta" + ] + } } } }, @@ -2722,6 +2824,57 @@ }, "webUrl": { "type": "string" + }, + "tests": { + "type": "array", + "items": { + "type": "object", + "properties": { + "testUuid": { + "type": "string" + }, + "variantMeta": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "$ref": "#/definitions/VariantId" + }, + "meta": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "id", + "meta" + ] + } + }, + "startDate": { + "type": "number" + }, + "expiryDate": { + "type": "number" + }, + "frontsThisTestCanRunOn": { + "type": "array", + "items": { + "type": "string" + } + }, + "hasManuallyEndedOnThisTrail": { + "type": "boolean" + } + }, + "required": [ + "frontsThisTestCanRunOn", + "hasManuallyEndedOnThisTrail", + "testUuid", + "variantMeta" + ] + } } } }, diff --git a/dotcom-rendering/src/frontend/schemas/feTagPage.json b/dotcom-rendering/src/frontend/schemas/feTagPage.json index 191d0b5be5a..5e1a5a833d0 100644 --- a/dotcom-rendering/src/frontend/schemas/feTagPage.json +++ b/dotcom-rendering/src/frontend/schemas/feTagPage.json @@ -793,6 +793,57 @@ }, "webUrl": { "type": "string" + }, + "tests": { + "type": "array", + "items": { + "type": "object", + "properties": { + "testUuid": { + "type": "string" + }, + "variantMeta": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "$ref": "#/definitions/VariantId" + }, + "meta": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "id", + "meta" + ] + } + }, + "startDate": { + "type": "number" + }, + "expiryDate": { + "type": "number" + }, + "frontsThisTestCanRunOn": { + "type": "array", + "items": { + "type": "string" + } + }, + "hasManuallyEndedOnThisTrail": { + "type": "boolean" + } + }, + "required": [ + "frontsThisTestCanRunOn", + "hasManuallyEndedOnThisTrail", + "testUuid", + "variantMeta" + ] + } } } }, From 95db2811724cad6b71e99d914e97b958179ef026 Mon Sep 17 00:00:00 2001 From: Emma Imber Date: Thu, 10 Sep 2026 17:12:37 +0100 Subject: [PATCH 3/3] Add a couple of test cases for the supporting content type --- .../src/model/enhanceCards.test.ts | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/dotcom-rendering/src/model/enhanceCards.test.ts b/dotcom-rendering/src/model/enhanceCards.test.ts index 65ead657afd..abc78edf251 100644 --- a/dotcom-rendering/src/model/enhanceCards.test.ts +++ b/dotcom-rendering/src/model/enhanceCards.test.ts @@ -2,6 +2,7 @@ import type { FEFrontCardStyle, FEMediaAsset, FEMediaAtom, + FESupportingContent, } from '../frontend/feFront'; import { ArticleDesign, ArticleDisplay, Pillar } from '../lib/articleFormat'; import type { EditorialTest, VariantMeta } from '../types/front'; @@ -656,6 +657,16 @@ describe('Enhance Cards', () => { }, }; + const cardWithSublinkWithEditorialTest = { + ...cardWithNoEditorialTest, + supportingContent: [cardWithEditorialTest], + }; + + const cardWithSublinkWithExpiredEditorialTest = { + ...cardWithNoEditorialTest, + supportingContent: [cardWithExpiredEditorialTest], + }; + it('returns the default headline if no editorial test exists on the card, page is not in allowed fronts list, and user is not in a test bucket', () => { expect( decideHeadline( @@ -789,5 +800,33 @@ describe('Enhance Cards', () => { ), ).toEqual('Headline'); }); + + it('returns the variant headline if an editorial test is present on a sublink', () => { + expect( + decideHeadline( + cardWithSublinkWithEditorialTest + .supportingContent[0] as FESupportingContent, + { + 'fronts-and-curation-editorial-test': 'a', + }, + true, + 'test-front', + ), + ).toEqual('Headline A'); + }); + + it('returns the default headline for a sublink if an editorial test is expired on a sublink', () => { + expect( + decideHeadline( + cardWithSublinkWithExpiredEditorialTest + .supportingContent[0] as FESupportingContent, + { + 'fronts-and-curation-editorial-test': 'a', + }, + true, + 'test-front', + ), + ).toEqual('Headline'); + }); }); });