src/app/pages/HomePage/index.test.tsx
import React from 'react';
import { BrowserRouter } from 'react-router-dom';
import { Helmet } from 'react-helmet';
import { data as kyrgyzHomePageData } from '#data/kyrgyz/homePage/index.json';
import { data as afriqueHomePageDataFixture } from '#data/afrique/homePage/index.json';
import { data as pidginHomePageDataFixture } from '#data/pidgin/homePage/index.json';
import { render } from '../../components/react-testing-library-with-providers';
import HomePage from './HomePage';
import { suppressPropWarnings } from '../../legacy/psammead/psammead-test-helpers/src';
jest.mock('../../components/ChartbeatAnalytics', () => {
const ChartbeatAnalytics = () => <div>Chartbeat Analytics</div>;
return ChartbeatAnalytics;
});
const homePageData = {
...kyrgyzHomePageData,
metadata: {
...kyrgyzHomePageData.metadata,
type: 'home',
},
};
const afriqueHomePageData = {
...afriqueHomePageDataFixture,
metadata: {
...afriqueHomePageDataFixture.metadata,
type: 'home',
},
};
describe('Home Page', () => {
suppressPropWarnings(['children', 'string', 'MediaIcon']);
it('should render a section for each curation with summaries', () => {
const { container } = render(<HomePage pageData={afriqueHomePageData} />, {
service: 'afrique',
toggles: {
mostRead: { enabled: true },
radioSchedule: { enabled: true },
},
});
const curationsWithSummaries = afriqueHomePageDataFixture.curations.filter(
({ summaries, mostRead, radioSchedule }) =>
(summaries && summaries?.length > 0) || mostRead || radioSchedule,
);
expect(container).not.toBeEmptyDOMElement();
expect(container.getElementsByTagName('section').length).toEqual(
curationsWithSummaries.length,
);
});
it('should have h2s for curation heading levels and h3 for summary heading levels', () => {
const { container } = render(
<HomePage pageData={pidginHomePageDataFixture} />,
{
service: 'pidgin',
},
);
expect(container.querySelectorAll('h2').length).toBe(5);
expect(container.querySelectorAll('h3').length).toBe(27);
});
it('should apply provided margin size to the main element', () => {
// @ts-expect-error suppress pageData prop type conflicts due to missing imageAlt on selected historical test data for curations
const { getByRole } = render(<HomePage pageData={homePageData} />, {
service: 'kyrgyz',
});
expect(getByRole('main')).toHaveStyle({
margin: '0px 0.5rem',
});
});
it('should have visually hidden text with the localised product, service - home as the H1', () => {
// @ts-expect-error suppress pageData prop type conflicts due to missing imageAlt on selected historical test data for curations
const { container } = render(<HomePage pageData={homePageData} />, {
service: 'kyrgyz',
});
const h1 = container.querySelector('h1');
expect(h1).toBeInTheDocument();
const content = h1?.getAttribute('id');
const tabIndex = h1?.getAttribute('tabIndex');
expect(content).toEqual('content');
expect(tabIndex).toBe('-1');
const span = h1?.querySelector('span');
expect(span?.getAttribute('role')).toEqual('text');
expect(span?.textContent).toEqual(
'BBC News, Кыргыз КызMATы - Башталгыч бет',
);
const langSpan = span?.querySelector('span');
expect(langSpan?.getAttribute('lang')).toEqual('en-GB');
expect(langSpan?.textContent).toEqual('BBC News');
});
it('should have a metadata title', () => {
// @ts-expect-error suppress pageData prop type conflicts due to missing imageAlt on selected historical test data for curations
render(<HomePage pageData={homePageData} />, {
service: 'kyrgyz',
});
expect(Helmet.peek().title).toEqual(
'Кабарлар, акыркы мүнөттөгү кабарлар, талдоо, видео - BBC News Кыргыз Кызматы',
);
});
it('should have a metadata description', () => {
// @ts-expect-error suppress pageData prop type conflicts due to missing imageAlt on selected historical test data for curations
render(<HomePage pageData={homePageData} />, {
service: 'kyrgyz',
});
const helmetContent = Helmet.peek();
const findDescription = helmetContent.metaTags.find(
({ name }) => name === 'description',
);
expect(findDescription?.content).toEqual(kyrgyzHomePageData.description);
});
it('should correctly render linked data for home pages', () => {
// @ts-expect-error suppress pageData prop type conflicts due to missing imageAlt on selected historical test data for curations
render(<HomePage pageData={homePageData} />, {
service: 'kyrgyz',
});
const getLinkedDataOutput = () => {
return JSON.parse(Helmet.peek().scriptTags[0].innerHTML);
};
expect(getLinkedDataOutput()).toMatchSnapshot();
});
it('should render images with the .webp image extension', () => {
const path =
homePageData.curations[1].summaries?.[0].imageUrl?.split('{width}')[1];
const imageURL = `https://ichef.test.bbci.co.uk/ace/standard/240${path}`;
const expectedWebpSrcSetURLs = [
`https://ichef.test.bbci.co.uk/ace/standard/85${path}.webp 85w`,
`https://ichef.test.bbci.co.uk/ace/standard/120${path}.webp 120w`,
`https://ichef.test.bbci.co.uk/ace/standard/170${path}.webp 170w`,
`https://ichef.test.bbci.co.uk/ace/standard/232${path}.webp 232w`,
`https://ichef.test.bbci.co.uk/ace/standard/325${path}.webp 325w`,
`https://ichef.test.bbci.co.uk/ace/standard/450${path}.webp 450w`,
`https://ichef.test.bbci.co.uk/ace/standard/660${path}.webp 660w`,
`https://ichef.test.bbci.co.uk/ace/standard/800${path}.webp 800w`,
].join(', ');
const expectedJPGSrcSetURLs = [
`https://ichef.test.bbci.co.uk/ace/standard/85${path} 85w`,
`https://ichef.test.bbci.co.uk/ace/standard/120${path} 120w`,
`https://ichef.test.bbci.co.uk/ace/standard/170${path} 170w`,
`https://ichef.test.bbci.co.uk/ace/standard/232${path} 232w`,
`https://ichef.test.bbci.co.uk/ace/standard/325${path} 325w`,
`https://ichef.test.bbci.co.uk/ace/standard/450${path} 450w`,
`https://ichef.test.bbci.co.uk/ace/standard/660${path} 660w`,
`https://ichef.test.bbci.co.uk/ace/standard/800${path} 800w`,
].join(', ');
// @ts-expect-error suppress pageData prop type conflicts due to missing imageAlt on selected historical test data for curations
const { container } = render(<HomePage pageData={homePageData} />, {
service: 'kyrgyz',
pageType: 'home',
});
const promoImage = container.querySelectorAll('div.promo-image picture')[0];
const [webpSource, jpgSource, img] = promoImage.childNodes as unknown as [
HTMLSourceElement,
HTMLSourceElement,
HTMLImageElement,
];
expect(webpSource.srcset).toEqual(expectedWebpSrcSetURLs);
expect(jpgSource.srcset).toEqual(expectedJPGSrcSetURLs);
expect(img.src).toEqual(imageURL);
});
describe('Analytics', () => {
it('should render a Chartbeat component', () => {
// @ts-expect-error suppress pageData prop type conflicts due to missing imageAlt on selected historical test data for curations
const { getByText } = render(<HomePage pageData={homePageData} />, {
service: 'kyrgyz',
});
expect(getByText('Chartbeat Analytics')).toBeInTheDocument();
});
});
describe('Lazy Loading', () => {
it('Only the first image, message banner, and billboard on the homepage are not lazy loaded, but all others are', () => {
// @ts-expect-error suppress pageData prop type conflicts due to missing imageAlt on selected historical test data for curations
render(<HomePage pageData={homePageData} />, {
service: 'kyrgyz',
});
const nonLazyLoadImages: string[] = [];
document
.querySelectorAll(
`[data-testid^="billboard"] img, [data-testid^="message-banner"] img`,
)
.forEach(image =>
nonLazyLoadImages.push(image.getAttribute(`src`) || ''),
);
const imageList = document.querySelectorAll('img');
imageList.forEach((image, index) => {
const src = image.getAttribute('src') || '';
if (index === 0 || nonLazyLoadImages.includes(src)) {
expect(image.getAttribute('loading')).toBeNull();
} else {
expect(image.getAttribute('loading')).toBe('lazy');
}
});
});
it('Only the first image on a homepage and all Billboard images have Fetch Priority set to high', () => {
// @ts-expect-error suppress pageData prop type conflicts due to missing imageAlt on selected historical test data for curations
render(<HomePage pageData={homePageData} />, {
service: 'kyrgyz',
});
const highFetchPriorityImages: string[] = [];
document
.querySelectorAll(`[data-testid^="billboard"] img`)
.forEach(image =>
highFetchPriorityImages.push(image.getAttribute(`src`) || ''),
);
const imageList = document.querySelectorAll('img');
imageList.forEach((image, index) => {
const src = image.getAttribute('src') || '';
if (index === 0 || highFetchPriorityImages.includes(src)) {
expect(image.getAttribute('fetchpriority')).toBe('high');
} else {
expect(image.getAttribute('fetchpriority')).toBeNull();
}
});
});
});
describe('Ads', () => {
const getBootstrapScript = () =>
Helmet.peek().scriptTags.find(({ innerHTML }) =>
innerHTML?.includes('window.dotcom'),
);
it('should display ads when ads toggle is enabled and showAdsBased on location is true', () => {
const { container } = render(
<BrowserRouter>
{/* @ts-expect-error suppress pageData prop type conflicts due to missing imageAlt on selected historical test data for curations */}
<HomePage pageData={homePageData} />
</BrowserRouter>,
{
service: 'kyrgyz',
toggles: {
ads: { enabled: true },
},
showAdsBasedOnLocation: true,
},
);
const homePageAds = container.querySelectorAll(`[id^="dotcom-"]`);
expect(homePageAds).toHaveLength(2);
expect(getBootstrapScript()).toBeTruthy();
});
it('should display the MPU ad in the correct location', () => {
const { container } = render(
<BrowserRouter>
{/* @ts-expect-error suppress pageData prop type conflicts due to missing imageAlt on selected historical test data for curations */}
<HomePage pageData={homePageData} />
</BrowserRouter>,
{
service: 'kyrgyz',
toggles: {
ads: { enabled: true },
},
showAdsBasedOnLocation: true,
},
);
const sections = container.querySelectorAll(`section`);
const sectionIds: (string | null)[] = Array.from(sections).map(
section =>
section.getAttribute('aria-labelledby') ||
section.getAttribute('data-e2e'),
);
const mpuIndex = sectionIds.lastIndexOf('advertisement');
const firstNonBannerIndex = sectionIds.findIndex(
sectionId =>
sectionId !== 'advertisement' &&
!sectionId?.startsWith('billboard') &&
!sectionId?.startsWith('message-banner'),
);
expect(mpuIndex).toBe(firstNonBannerIndex + 1);
});
it.each`
adsEnabled | showAdsBasedOnLocation | scenario
${true} | ${false} | ${'showAdsBasedOnLocation is false'}
${false} | ${true} | ${'adsEnabled is false'}
${false} | ${true} | ${'both adsEnabled and showAdsBasedOnLocation are false'}
`(
'should not display ads because $scenario',
({ adsEnabled, showAdsBasedOnLocation }) => {
const { container } = render(
<BrowserRouter>
{/* @ts-expect-error suppress pageData prop type conflicts due to missing imageAlt on selected historical test data for curations */}
<HomePage pageData={homePageData} />
</BrowserRouter>,
{
service: 'kyrgyz',
toggles: {
ads: { enabled: adsEnabled },
},
showAdsBasedOnLocation,
},
);
const homePageAds = container.querySelectorAll(`[id^="dotcom-"]`);
expect(homePageAds).toHaveLength(0);
expect(getBootstrapScript()).toBeUndefined();
},
);
});
it('should not have amphtml in the metadata', () => {
// @ts-expect-error suppress pageData prop type conflicts due to missing imageAlt on selected historical test data for curations
render(<HomePage pageData={homePageData} />, {
service: 'kyrgyz',
});
const amphtml = Helmet.peek().linkTags.find(
linkTag => linkTag.rel === 'amphtml',
);
expect(amphtml).toBeUndefined();
});
});