Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions src/app/pages/ArticlePage/ArticlePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,15 @@ import {
OptimoBylineBlock,
OptimoBylineContributorBlock,
} from '#app/models/types/optimo';
import {
VISUAL_PROMINENCE,
VISUAL_STYLE,
} from '#app/models/types/curationData';
import { Translations } from '#app/models/types/translations';
import { Recommendation } from '#app/models/types/onwardJourney';

import ArticleLinksBlock from '#app/components/ArticleLinksBlock';
import Curation from '#app/components/Curation';
import Recommendations from '#app/components/Recommendations';
import ReadTimeArticle from '#app/components/ReadTime';
import PWAPromotionalBanner from '#app/components/PWAPromotionalBanner';
Expand Down Expand Up @@ -247,6 +252,18 @@ const ArticlePage = ({ pageData }: { pageData: Article }) => {
experimentName: referrerExperimentName,
experimentType: ExperimentType.CLIENT_SIDE,
});
// time of day 2 experiment for articles
const timeOfDayArticleExperimentName = 'newswb_ws_tod_article_2';
// local test override: force adaptive variation
const forcedTimeOfDayArticleVariant = 'adaptive_variation';
const optimizelyTimeOfDayArticleVariant = useOptimizelyVariation({
experimentName: timeOfDayArticleExperimentName,
experimentType: ExperimentType.CLIENT_SIDE,
});
const timeOfDayArticleVariant =
forcedTimeOfDayArticleVariant ?? optimizelyTimeOfDayArticleVariant;
const isAdaptiveTimeOfDayVariant =
timeOfDayArticleVariant === 'adaptive_variation';

const allowAdvertising = pageData?.metadata?.allowAdvertising ?? false;
const adcampaign = pageData?.metadata?.adCampaignKeyword;
Expand All @@ -267,6 +284,7 @@ const ArticlePage = ({ pageData }: { pageData: Article }) => {
const blocks = pageData?.content?.model?.blocks ?? [];
const topStoriesContent = pageData?.secondaryColumn?.topStories;
const featuresContent = pageData?.secondaryColumn?.features;
const mediaCurationContent = pageData?.secondaryColumn?.mediaCuration;
const startsWithHeading = blocks?.[0]?.type === 'headline' || false;

const bylineBlock = blocks.find(
Expand Down Expand Up @@ -408,6 +426,36 @@ const ArticlePage = ({ pageData }: { pageData: Article }) => {

const showTopics = Boolean(showRelatedTopics && topics.length > 0);
const authors = bylineLinkedData?.map(data => data?.authorName).join(',');
// show media curation only when the user is in adaptive variation
const showAdaptiveMediaCuration = Boolean(
!isAmp &&
!isLite &&
!isApp &&
!isPGL &&
isAdaptiveTimeOfDayVariant &&
mediaCurationContent?.summaries?.length,
);
const hasRelatedContent = Boolean(
blocks
.filter(block => block.type !== 'wsoj' && block.type !== 'mpu')
.slice(-1)[0]?.type === 'relatedContent',
);

const renderAdaptiveMediaCuration = () =>
showAdaptiveMediaCuration ? (
<section data-testid="adaptive-media-curation">
<Curation
visualStyle={VISUAL_STYLE.FEED}
visualProminence={VISUAL_PROMINENCE.NORMAL}
summaries={mediaCurationContent?.summaries}
title={mediaCurationContent?.title}
position={mediaCurationContent?.position || 0}
curationId={mediaCurationContent?.curationId}
curationLength={mediaCurationContent?.summaries?.length || 0}
link={mediaCurationContent?.link}
/>
</section>
) : null;

// EXPERIMENT: PWA Promotional Banner
const shouldRenderPWAPromotionalBanner =
Expand Down Expand Up @@ -483,6 +531,7 @@ const ArticlePage = ({ pageData }: { pageData: Article }) => {
mobileDivider={false}
/>
)}
{!hasRelatedContent && renderAdaptiveMediaCuration()}
{showPortraitVideoCarousel && (
<PortraitVideoCarousel
{...portraitVideoCarouselProps}
Expand All @@ -503,6 +552,7 @@ const ArticlePage = ({ pageData }: { pageData: Article }) => {
/>
</div>
</div>
{hasRelatedContent && renderAdaptiveMediaCuration()}
</div>
{!isApp && !isPGL && <SecondaryColumn pageData={pageData} />}
</div>
Expand Down
92 changes: 91 additions & 1 deletion src/app/pages/ArticlePage/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
articleDataRussianWithPVButNoWatchMomentsTranslation,
articleDataPortugueseWithPVNotUnderHeadline,
articleDataPortugueseWithPVUnderHeadline,
articleDataHindi,
promoSample,
articlePglDataPidgin,
articleStyDataPidgin,
Expand Down Expand Up @@ -57,7 +58,6 @@
atiAnalyticsSpy.mockImplementation(() => <div>ATI Analytics</div>);

jest.mock('#app/components/OptimizelyPageMetrics');

jest.mock('#app/hooks/useOptimizelyVariation', () => ({
__esModule: true,
...jest.requireActual('#app/hooks/useOptimizelyVariation'),
Expand Down Expand Up @@ -1018,6 +1018,96 @@
});
});

describe('Adaptive media curation', () => {
const mediaCurationFixture = {
title: 'वीडियो',
curationId: 'urn:bbc:vivo:curation:test-id',
link: 'https://www.bbc.com/hindi/topics/cw9kv0kpxydt',
summaries: [
{
type: 'video',
title: 'वीडियो 1',
link: 'https://www.bbc.com/hindi/articles/test-video-1',
imageUrl:
'https://ichef.bbci.co.uk/ace/ws/{width}/cpsprodpb/test.jpg.webp',
imageAlt: 'वीडियो 1',
},
],
};
const relatedContentBlock = {
id: 'related-content-test-id',
type: 'relatedContent',
model: {
blocks: [],
},
position: [99],
};

const pageDataWithMediaCuration = {
...articleDataHindi,
secondaryColumn: {
topStories: [],
features: [],
mediaCuration: mediaCurationFixture,
},
};
const pageDataWithMediaCurationAndRelatedContent = {

Check failure on line 1054 in src/app/pages/ArticlePage/index.test.tsx

View workflow job for this annotation

GitHub Actions / cypress-run (22.x)

Conversion of type '{ content: { model: { blocks: (OptimoBlock | { id: string; type: string; model: { blocks: never[]; }; position: number[]; })[]; }; }; secondaryColumn: { topStories: never[]; features: never[]; mediaCuration: { ...; }; }; ... 5 more ...; portraitVideoItems?: PortraitVideoItems; }' to type 'Article' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.

Check failure on line 1054 in src/app/pages/ArticlePage/index.test.tsx

View workflow job for this annotation

GitHub Actions / build (22.x)

Conversion of type '{ content: { model: { blocks: (OptimoBlock | { id: string; type: string; model: { blocks: never[]; }; position: number[]; })[]; }; }; secondaryColumn: { topStories: never[]; features: never[]; mediaCuration: { ...; }; }; ... 5 more ...; portraitVideoItems?: PortraitVideoItems; }' to type 'Article' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.

Check failure on line 1054 in src/app/pages/ArticlePage/index.test.tsx

View workflow job for this annotation

GitHub Actions / cypress-run (22.x)

Conversion of type '{ content: { model: { blocks: (OptimoBlock | { id: string; type: string; model: { blocks: never[]; }; position: number[]; })[]; }; }; secondaryColumn: { topStories: never[]; features: never[]; mediaCuration: { ...; }; }; ... 5 more ...; portraitVideoItems?: PortraitVideoItems; }' to type 'Article' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.

Check failure on line 1054 in src/app/pages/ArticlePage/index.test.tsx

View workflow job for this annotation

GitHub Actions / cypress-run (22.x)

Conversion of type '{ content: { model: { blocks: (OptimoBlock | { id: string; type: string; model: { blocks: never[]; }; position: number[]; })[]; }; }; secondaryColumn: { topStories: never[]; features: never[]; mediaCuration: { ...; }; }; ... 5 more ...; portraitVideoItems?: PortraitVideoItems; }' to type 'Article' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.
...pageDataWithMediaCuration,
content: {
...pageDataWithMediaCuration.content,
model: {
...pageDataWithMediaCuration.content.model,
blocks: [
...(pageDataWithMediaCuration.content?.model?.blocks || []),
relatedContentBlock,
],
},
},
} as Article;

it('renders media curation when adaptive variation is forced locally', () => {
const { queryByTestId } = render(
<Context service="hindi">
<ArticlePage pageData={pageDataWithMediaCuration} />

Check failure on line 1071 in src/app/pages/ArticlePage/index.test.tsx

View workflow job for this annotation

GitHub Actions / cypress-run (22.x)

Type '{ secondaryColumn: { topStories: never[]; features: never[]; mediaCuration: { title: string; curationId: string; link: string; summaries: { type: string; title: string; link: string; imageUrl: string; imageAlt: string; }[]; }; }; ... 6 more ...; portraitVideoItems?: PortraitVideoItems; }' is not assignable to type 'Article'.

Check failure on line 1071 in src/app/pages/ArticlePage/index.test.tsx

View workflow job for this annotation

GitHub Actions / build (22.x)

Type '{ secondaryColumn: { topStories: never[]; features: never[]; mediaCuration: { title: string; curationId: string; link: string; summaries: { type: string; title: string; link: string; imageUrl: string; imageAlt: string; }[]; }; }; ... 6 more ...; portraitVideoItems?: PortraitVideoItems; }' is not assignable to type 'Article'.

Check failure on line 1071 in src/app/pages/ArticlePage/index.test.tsx

View workflow job for this annotation

GitHub Actions / cypress-run (22.x)

Type '{ secondaryColumn: { topStories: never[]; features: never[]; mediaCuration: { title: string; curationId: string; link: string; summaries: { type: string; title: string; link: string; imageUrl: string; imageAlt: string; }[]; }; }; ... 6 more ...; portraitVideoItems?: PortraitVideoItems; }' is not assignable to type 'Article'.

Check failure on line 1071 in src/app/pages/ArticlePage/index.test.tsx

View workflow job for this annotation

GitHub Actions / cypress-run (22.x)

Type '{ secondaryColumn: { topStories: never[]; features: never[]; mediaCuration: { title: string; curationId: string; link: string; summaries: { type: string; title: string; link: string; imageUrl: string; imageAlt: string; }[]; }; }; ... 6 more ...; portraitVideoItems?: PortraitVideoItems; }' is not assignable to type 'Article'.
</Context>,
);

expect(queryByTestId('adaptive-media-curation')).toBeInTheDocument();
expect(queryByTestId('curation-grid-normal')).toBeInTheDocument();
});

it('renders media curation after related content when related content is present', () => {
const { queryByTestId, container } = render(
<Context service="hindi">
<ArticlePage pageData={pageDataWithMediaCurationAndRelatedContent} />
</Context>,
);

const relatedContentSection = container.querySelector(
'[data-e2e="related-content-heading"]',
);
const adaptiveMediaCuration = queryByTestId('adaptive-media-curation');

expect(relatedContentSection).toBeInTheDocument();
expect(adaptiveMediaCuration).toBeInTheDocument();
expect(
relatedContentSection?.compareDocumentPosition(

Check failure on line 1094 in src/app/pages/ArticlePage/index.test.tsx

View workflow job for this annotation

GitHub Actions / cypress-run (22.x)

Object is possibly 'undefined'.

Check failure on line 1094 in src/app/pages/ArticlePage/index.test.tsx

View workflow job for this annotation

GitHub Actions / build (22.x)

Object is possibly 'undefined'.

Check failure on line 1094 in src/app/pages/ArticlePage/index.test.tsx

View workflow job for this annotation

GitHub Actions / cypress-run (22.x)

Object is possibly 'undefined'.

Check failure on line 1094 in src/app/pages/ArticlePage/index.test.tsx

View workflow job for this annotation

GitHub Actions / cypress-run (22.x)

Object is possibly 'undefined'.
adaptiveMediaCuration as Node,
) & Node.DOCUMENT_POSITION_FOLLOWING,
).toBeTruthy();
});

it('does not render media curation when data is missing', () => {
const { queryByTestId } = render(
<Context service="hindi">
<ArticlePage pageData={articleDataHindi} />
</Context>,
);

expect(queryByTestId('adaptive-media-curation')).not.toBeInTheDocument();
});
});

describe('Continue Reading Toggle', () => {
it.each([
{
Expand Down
Loading