src/app/routes/onDemandAudio/getInitialData/index.test.ts

Summary

Maintainability
A
2 hrs
Test Coverage
import mergeDeepLeft from 'ramda/src/mergeDeepLeft';
import dissocPath from 'ramda/src/dissocPath';
import loggerMock from '#testHelpers/loggerMock';
import onDemandRadioJson from '#data/pashto/bbc_pashto_radio/w3ct0lz1.json';
import podcastJson from '#data/arabic/podcasts/p02pc9qc/p08wtg4d.json';
import { RADIO_MISSING_FIELD } from '#lib/logger.const';
import { MEDIA_PAGE } from '#app/routes/utils/pageTypes';
import { FetchMock } from 'jest-fetch-mock';
import arabicExternalLinks from '../tempData/podcastExternalLinks/arabic';
import * as fetchPageData from '../../utils/fetchPageData';
import getInitialData from '.';

const { env } = process;
const spy = jest.spyOn(fetchPageData, 'default');

const fetchMock = fetch as FetchMock;

describe('Get initial data for on demand radio', () => {
  afterEach(() => {
    process.env = { ...env };
    jest.clearAllMocks();
    fetchMock.resetMocks();
  });

  it('should return essential data for an on demand page to render', async () => {
    fetchMock.mockResponse(JSON.stringify(onDemandRadioJson));
    // @ts-expect-error partial data required for testing purposes
    const { pageData } = await getInitialData({
      path: 'mock-on-demand-radio-path',
      pageType: MEDIA_PAGE,
      toggles: {
        recentAudioEpisodes: { enabled: false, value: 4 },
      },
    });

    expect(pageData?.headline).toEqual('ماښامنۍ خپرونه');
    expect(pageData?.releaseDateTimeStamp).toEqual(1588291200000);
    expect(pageData?.summary).toEqual('د بي بي سي ورلډ سروس څخه پروګرام کول');
    expect(pageData?.language).toEqual('ps');
    expect(pageData?.metadata.type).toEqual('On Demand Radio');
    expect(pageData?.imageUrl).toEqual(
      'ichef.bbci.co.uk/images/ic/$recipe/p08b23c8.png',
    );
    expect(pageData?.promoBrandTitle).toEqual('ماښامنۍ خپرونه');
    expect(pageData?.durationISO8601).toEqual('PT29M30S');
    expect(pageData?.thumbnailImageUrl).toEqual(
      'https://ichef.bbci.co.uk/images/ic/1024x576/p08b23c8.png',
    );
    expect(pageData?.externalLinks).toEqual([]);
  });

  it('should return essential data for a podcast page to render', async () => {
    fetchMock.mockResponse(JSON.stringify(podcastJson));
    const { pageData } = await getInitialData({
      path: 'mock-podcast-path',
      pageType: MEDIA_PAGE,
      service: 'arabic',
      toggles: {
        recentPodcastEpisodes: { enabled: false, value: 8 },
      },
    });

    expect(pageData?.headline).toEqual('BBC Xtra');
    expect(pageData?.releaseDateTimeStamp).toEqual(1603929600000);
    expect(pageData?.summary).toEqual(
      'التصويت الانتخابي عبر البريد خيار يلجأ إليه البعض\nمتى وكيف بدأ توتر العلاقات بين الدولة العثمانية وفرنسا؟\nونتابع احتفالات المغاربة بالمولد النبوي\nبي بي سي إكسترا بصحبة محمد مطر',
    );
    expect(pageData?.language).toEqual('ar');
    expect(pageData?.metadata.type).toEqual('Podcast');
    expect(pageData?.imageUrl).toEqual(
      'ichef.bbci.co.uk/images/ic/$recipe/p02rt7vj.jpg',
    );
    expect(pageData?.promoBrandTitle).toEqual('BBC Xtra');
    expect(pageData?.durationISO8601).toEqual('PT6M50S');
    expect(pageData?.thumbnailImageUrl).toEqual(
      'https://ichef.bbci.co.uk/images/ic/1024x576/p02rt7vj.jpg',
    );
    expect(pageData?.externalLinks).toEqual([
      ...arabicExternalLinks.default.p02pc9qc,
      {
        linkText: 'RSS',
        linkUrl: 'https://podcasts.files.bbci.co.uk/p02pc9qc.rss',
        linkType: 'rss',
      },
      {
        linkText: 'Download',
        linkUrl:
          'https://open.live.bbc.co.uk/mediaselector/6/redir/version/2.0/mediaset/audio-nondrm-download-low/proto/https/vpid/p08wsxz2.mp3',
        linkType: 'download',
      },
    ]);
  });

  it('should use short synopsis as page summary for podcast pages when medium synopsis is absent', async () => {
    const podcastJsonNoMediumSynopsis = dissocPath(
      ['content', 'blocks', 0, 'synopses', 'medium'],
      podcastJson,
    );
    fetchMock.mockResponse(JSON.stringify(podcastJsonNoMediumSynopsis));
    // @ts-expect-error partial data required for testing purposes
    const { pageData } = await getInitialData({
      path: 'mock-podcast-path',
      pageType: MEDIA_PAGE,
      toggles: {
        recentPodcastEpisodes: { enabled: false, value: 8 },
      },
    });

    expect(pageData?.summary).toEqual(
      'التصويت عبر البريد في الانتخابات الرئاسية الأميركية',
    );
  });

  it('should return essential data for a page to render when the episode toggle is null', async () => {
    fetchMock.mockResponse(JSON.stringify(onDemandRadioJson));
    const { pageData } = await getInitialData({
      path: 'mock-on-demand-radio-path',
      pageType: MEDIA_PAGE,
      toggles: {
        // @ts-expect-error partial data required for testing purposes
        recentAudioEpisodes: null,
      },
    });

    expect(pageData?.headline).toEqual('ماښامنۍ خپرونه');
    expect(pageData?.releaseDateTimeStamp).toEqual(1588291200000);
    expect(pageData?.summary).toEqual('د بي بي سي ورلډ سروس څخه پروګرام کول');
    expect(pageData?.language).toEqual('ps');
    expect(pageData?.metadata.type).toEqual('On Demand Radio');
    expect(pageData?.imageUrl).toEqual(
      'ichef.bbci.co.uk/images/ic/$recipe/p08b23c8.png',
    );
    expect(pageData?.promoBrandTitle).toEqual('ماښامنۍ خپرونه');
    expect(pageData?.durationISO8601).toEqual('PT29M30S');
    expect(pageData?.thumbnailImageUrl).toEqual(
      'https://ichef.bbci.co.uk/images/ic/1024x576/p08b23c8.png',
    );
  });

  it('should return the correct page identifier used for on demand radio analytics', async () => {
    fetchMock.mockResponse(JSON.stringify(onDemandRadioJson));
    // @ts-expect-error partial data required for testing purposes
    const { pageData } = await getInitialData({
      path: 'mock-on-demand-radio-path',
      pageType: MEDIA_PAGE,
      toggles: {
        recentAudioEpisodes: { enabled: false, value: 4 },
      },
    });

    expect(pageData?.pageIdentifier).toEqual(
      'pashto.bbc_pashto_radio.w3ct0lz1.page',
    );
  });

  it('should return the correct page identifier used for podcast analytics', async () => {
    fetchMock.mockResponse(JSON.stringify(podcastJson));
    // @ts-expect-error partial data required for testing purposes
    const { pageData } = await getInitialData({
      path: 'mock-podcast-path',
      pageType: MEDIA_PAGE,
      toggles: {
        recentPodcastEpisodes: { enabled: false, value: 8 },
      },
    });

    expect(pageData?.pageIdentifier).toEqual(
      'arabic.bbc_arabic_radio.podcasts.p08wtg4d.page',
    );
  });

  it('should return on demand recent episode data when recentEpisode toggle is enabled', async () => {
    fetchMock.mockResponse(JSON.stringify(onDemandRadioJson));
    // @ts-expect-error partial data required for testing purposes
    const { pageData } = await getInitialData({
      path: 'mock-on-demand-radio-path',
      pageType: MEDIA_PAGE,
      toggles: {
        recentAudioEpisodes: { enabled: true, value: 4 },
      },
    });

    expect(pageData?.recentEpisodes.length).toEqual(4);
    expect(pageData?.recentEpisodes[0].id).toEqual('w3ct155x');
  });

  it('should return podcast recent episode data when recentEpisode toggle is enabled', async () => {
    fetchMock.mockResponse(JSON.stringify(podcastJson));
    // @ts-expect-error partial data required for testing purposes
    const { pageData: podcastPageData } = await getInitialData({
      path: 'mock-podcast-path',
      pageType: MEDIA_PAGE,
      toggles: {
        recentPodcastEpisodes: { enabled: true, value: 8 },
      },
    });

    expect(podcastPageData?.recentEpisodes.length).toEqual(8);
    expect(podcastPageData?.recentEpisodes[0].id).toEqual('p08wkzvd');
  });

  it('should override renderer on test', async () => {
    process.env.SIMORGH_APP_ENV = 'test';
    fetchMock.mockResponse(JSON.stringify(onDemandRadioJson));
    // @ts-expect-error partial data required for testing purposes
    await getInitialData({
      path: 'mock-live-radio-path',
      pageType: MEDIA_PAGE,
    });
    expect(spy).toHaveBeenCalledWith({
      path: 'mock-live-radio-path?renderer_env=live',
      pageType: MEDIA_PAGE,
    });
  });

  it('should not override renderer on live', async () => {
    process.env.SIMORGH_APP_ENV = 'live';
    fetchMock.mockResponse(JSON.stringify(onDemandRadioJson));
    // @ts-expect-error partial data required for testing purposes
    await getInitialData({
      path: 'mock-live-radio-path',
      pageType: MEDIA_PAGE,
    });
    expect(spy).toHaveBeenCalledWith({
      path: 'mock-live-radio-path',
      pageType: MEDIA_PAGE,
    });
  });

  it('invokes logging when expected data is missing in ARES response', async () => {
    const pageDataWithMissingFields = mergeDeepLeft(
      {
        metadata: {
          title: null, // info
          language: null, // info
          createdBy: null, // error
          releaseDateTimeStamp: null, // warn
          analyticsLabels: {
            contentType: null, // info
          },
        },
        promo: {
          headlines: {
            headline: null, // warn
          },
          media: {
            imageUrl: null, // info
            versions: [
              {
                durationISO8601: null, // info
              },
            ],
          },
        },
        content: {
          blocks: [
            {
              id: null, // error
              imageUrl: null, // info
              synopses: {
                short: null, // info
              },
            },
          ],
        },
      },
      onDemandRadioJson,
    );
    fetchMock.mockResponse(JSON.stringify(pageDataWithMissingFields));

    // @ts-expect-error partial data required for testing purposes
    await getInitialData({
      path: 'mock-on-demand-radio-path',
      pageType: MEDIA_PAGE,
      toggles: {
        recentAudioEpisodes: { enabled: false, value: 4 },
      },
    });

    const countMissingFieldCalls = (
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      mockedFunction: jest.Mock<any, any, any>,
    ) => {
      return mockedFunction.mock.calls.filter(([logCategory]) => {
        return logCategory === RADIO_MISSING_FIELD;
      }).length;
    };

    expect(countMissingFieldCalls(loggerMock.info)).toBe(7);
    expect(countMissingFieldCalls(loggerMock.warn)).toBe(2);
    expect(countMissingFieldCalls(loggerMock.error)).toBe(2);
  });

  it('should return media blocks in preparation for adding the media loader component', async () => {
    fetchMock.mockResponse(JSON.stringify(onDemandRadioJson));
    // @ts-expect-error partial data required for testing purposes
    const { pageData } = await getInitialData({
      path: 'mock-live-radio-path',
      pageType: MEDIA_PAGE,
    });

    expect(pageData).toHaveProperty('mediaBlocks');
    expect(pageData?.mediaBlocks).toStrictEqual(
      expect.arrayContaining(
        onDemandRadioJson.content.blocks.map(block => {
          return { type: 'audio', model: block };
        }),
      ),
    );
  });
});