From 595948cf65d56e3723750e4507985d53f8bf8f13 Mon Sep 17 00:00:00 2001 From: Shayne Ahchoon Date: Tue, 16 Dec 2025 14:42:36 +0000 Subject: [PATCH 1/2] WS-MAINTENANCE: Refactors unused cypress suites. --- .../e2e/assertions/crossPlatformAssertion.ts | 30 +- .../cypress/e2e/testsForAllAMPPages.ts | 5 +- .../cypress/e2e/testsForAllCanonicalPages.ts | 3 +- .../cypress/support/config/services.ts | 15 - .../cypress/support/config/settings.ts | 379 ------------------ .../helpers/adsTests/testsForAMPOnly.ts | 9 +- .../helpers/adsTests/testsForCanonicalOnly.ts | 12 +- 7 files changed, 18 insertions(+), 435 deletions(-) delete mode 100644 ws-nextjs-app/cypress/support/config/services.ts delete mode 100644 ws-nextjs-app/cypress/support/config/settings.ts diff --git a/ws-nextjs-app/cypress/e2e/assertions/crossPlatformAssertion.ts b/ws-nextjs-app/cypress/e2e/assertions/crossPlatformAssertion.ts index 938d7b3f427..815ab9a68d3 100644 --- a/ws-nextjs-app/cypress/e2e/assertions/crossPlatformAssertion.ts +++ b/ws-nextjs-app/cypress/e2e/assertions/crossPlatformAssertion.ts @@ -1,11 +1,9 @@ import appConfig from '#src/server/utilities/serviceConfigs'; import { getMostReadEndpoint } from '#lib/utilities/getUrlHelpers/getMostReadUrls'; import { serviceNumerals } from '#app/components/MostRead/Canonical/Rank'; -import { Services } from '#app/models/types/global'; import { ServiceParametersType } from '../../types'; import ampOnlyServices from '../../support/helpers/ampOnlyServices'; import getAppEnv from '../../support/helpers/getAppEnv'; -import config from '../../support/config/services'; // news, newsround, and sport are services we serve on amp, but do not want to run most read tests on const MOST_READ_EXCLUDED_SERVICES = [...ampOnlyServices, 'ukchina']; @@ -14,20 +12,18 @@ export const crossPlatform = ({ service, variant = 'default', }: ServiceParametersType) => { - const serviceID = (config[service]?.name || service) as Services; - - if (!MOST_READ_EXCLUDED_SERVICES.includes(serviceID)) { + if (!MOST_READ_EXCLUDED_SERVICES.includes(service)) { const { mostRead: { hasMostRead, numberOfItems }, - } = appConfig[serviceID][variant]; + } = appConfig[service][variant]; if (hasMostRead) { describe('Most Read Component', () => { beforeEach(() => { - cy.getToggles(serviceID); + cy.getToggles(service); }); it(`should render ${numberOfItems} items`, () => { - cy.fixture(`toggles/${serviceID}.json`).then(toggles => { + cy.fixture(`toggles/${service}.json`).then(toggles => { if (toggles.mostRead?.enabled) { cy.get('[data-e2e="most-read"]').scrollIntoView(); cy.get('[data-e2e="most-read"] li').should( @@ -39,9 +35,9 @@ export const crossPlatform = ({ }); it(`should show correct numerals`, () => { - cy.fixture(`toggles/${serviceID}.json`).then(toggles => { + cy.fixture(`toggles/${service}.json`).then(toggles => { if (toggles.mostRead?.enabled) { - const expectedMostReadRank = serviceNumerals(serviceID); + const expectedMostReadRank = serviceNumerals(service); cy.get('[data-e2e="most-read"]').scrollIntoView(); cy.get('[data-e2e="most-read"]') .find('li span') @@ -53,7 +49,7 @@ export const crossPlatform = ({ }); it(`should have links with href and title`, () => { - cy.fixture(`toggles/${serviceID}.json`).then(toggles => { + cy.fixture(`toggles/${service}.json`).then(toggles => { if (toggles.mostRead?.enabled) { cy.get('[data-e2e="most-read"]').scrollIntoView(); cy.get('[data-e2e="most-read"]').within(() => { @@ -76,20 +72,18 @@ export const ampOnly = ({ service, variant = 'default', }: ServiceParametersType) => { - const serviceID = (config[service]?.name || service) as Services; - - if (!MOST_READ_EXCLUDED_SERVICES.includes(serviceID)) { + if (!MOST_READ_EXCLUDED_SERVICES.includes(service)) { const { mostRead: { hasMostRead }, - } = appConfig[serviceID][variant]; + } = appConfig[service][variant]; if (hasMostRead) { describe('Most Read Component', () => { beforeEach(() => { - cy.getToggles(serviceID); + cy.getToggles(service); }); it('should not render when data fetch fails', () => { const mostReadPath = getMostReadEndpoint({ - service: serviceID, + service, variant: variant !== 'default' && variant, isBff: getAppEnv() !== 'local', }); @@ -101,7 +95,7 @@ export const ampOnly = ({ { statusCode: 404 }, ); cy.reload(); - cy.fixture(`toggles/${serviceID}.json`).then(toggles => { + cy.fixture(`toggles/${service}.json`).then(toggles => { if (toggles.mostRead?.enabled) { cy.get('[data-e2e="most-read"]').scrollIntoView(); cy.get('[data-e2e="most-read"] li').should('not.exist'); diff --git a/ws-nextjs-app/cypress/e2e/testsForAllAMPPages.ts b/ws-nextjs-app/cypress/e2e/testsForAllAMPPages.ts index 20ec08b0236..861dddcc53e 100644 --- a/ws-nextjs-app/cypress/e2e/testsForAllAMPPages.ts +++ b/ws-nextjs-app/cypress/e2e/testsForAllAMPPages.ts @@ -1,15 +1,12 @@ /* eslint-disable import/prefer-default-export */ -import config from '../support/config/services'; import { ServiceParametersType } from '../types'; // For testing features that may differ across services but share a common logic e.g. translated strings. export default ({ service, pageType }: ServiceParametersType) => { describe(`testsThatFollowSmokeTestConfigForAllAMPPages to run for ${service} ${pageType}`, () => { describe('Header Tests', () => { - const serviceName = config[service]?.name || service; // limit number of tests to 2 services for navigation toggling - const testMobileNav = - serviceName === 'ukchina' || serviceName === 'persian'; + const testMobileNav = service === 'ukchina' || service === 'persian'; if (testMobileNav) { it('should show dropdown menu and hide scrollable menu when menu button is clicked', () => { cy.viewport(320, 480); diff --git a/ws-nextjs-app/cypress/e2e/testsForAllCanonicalPages.ts b/ws-nextjs-app/cypress/e2e/testsForAllCanonicalPages.ts index dec1cae3b37..17c356f13c7 100644 --- a/ws-nextjs-app/cypress/e2e/testsForAllCanonicalPages.ts +++ b/ws-nextjs-app/cypress/e2e/testsForAllCanonicalPages.ts @@ -1,6 +1,5 @@ /* eslint-disable import/prefer-default-export */ import envConfig, { EnvironmentConfigType } from '../support/config/envs'; -import config from '../support/config/services'; import { ServiceParametersType } from '../types'; // For testing features that may differ across services but share a common logic e.g. translated strings. @@ -26,7 +25,7 @@ export default ({ service, pageType }: ServiceParametersType) => { } describe('Header Tests', () => { - const serviceName = config[service]?.name || service; + const serviceName = service; // limit number of tests to 2 services for navigation toggling const testMobileNav = serviceName === 'ukchina' || serviceName === 'persian'; diff --git a/ws-nextjs-app/cypress/support/config/services.ts b/ws-nextjs-app/cypress/support/config/services.ts deleted file mode 100644 index 74581bc6de4..00000000000 --- a/ws-nextjs-app/cypress/support/config/services.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Services } from '#app/models/types/global'; -import allServices from './settings'; - -// Allow runs to be limited to a single service via the CYPRESS_ONLY_SERVICE env var -const runOnlyService = Cypress.env('ONLY_SERVICE'); - -export default (() => { - if (runOnlyService && Object.keys(allServices).includes(runOnlyService)) { - return { - [runOnlyService]: allServices[runOnlyService as Services], - }; - } - - return allServices; -})(); diff --git a/ws-nextjs-app/cypress/support/config/settings.ts b/ws-nextjs-app/cypress/support/config/settings.ts deleted file mode 100644 index 73039098bf0..00000000000 --- a/ws-nextjs-app/cypress/support/config/settings.ts +++ /dev/null @@ -1,379 +0,0 @@ -import { Services } from '#app/models/types/global'; - -export type ServiceConfigDataType = { - name: string; - font?: string; - isWorldService?: boolean; - variant: string; - pageTypes: Record; -}; - -export default { - afaanoromoo: { - name: 'afaanoromoo', - font: undefined, - isWorldService: true, - variant: 'default', - pageTypes: {}, - }, - afrique: { - name: 'afrique', - font: undefined, - isWorldService: true, - variant: 'default', - pageTypes: {}, - }, - amharic: { - name: 'amharic', - font: undefined, - isWorldService: true, - variant: 'default', - pageTypes: {}, - }, - arabic: { - name: 'arabic', - font: undefined, - isWorldService: true, - variant: 'default', - pageTypes: {}, - }, - azeri: { - name: 'azeri', - font: undefined, - isWorldService: true, - variant: 'default', - pageTypes: {}, - }, - bengali: { - name: 'bengali', - font: undefined, - isWorldService: true, - variant: 'default', - pageTypes: {}, - }, - burmese: { - name: 'burmese', - font: undefined, - isWorldService: true, - variant: 'default', - pageTypes: {}, - }, - cymrufyw: { - name: 'cymrufyw', - font: 'Reith', - isWorldService: false, - variant: 'default', - pageTypes: {}, - }, - gahuza: { - name: 'gahuza', - font: undefined, - isWorldService: true, - variant: 'default', - pageTypes: {}, - }, - gujarati: { - name: 'gujarati', - font: undefined, - isWorldService: true, - variant: 'default', - pageTypes: {}, - }, - hausa: { - name: 'hausa', - font: undefined, - isWorldService: true, - variant: 'default', - pageTypes: {}, - }, - hindi: { - name: 'hindi', - font: undefined, - isWorldService: true, - variant: 'default', - pageTypes: {}, - }, - igbo: { - name: 'igbo', - font: undefined, - isWorldService: true, - variant: 'default', - pageTypes: {}, - }, - indonesia: { - name: 'indonesia', - font: undefined, - isWorldService: true, - variant: 'default', - pageTypes: {}, - }, - japanese: { - name: 'japanese', - font: undefined, - isWorldService: false, - variant: 'default', - pageTypes: {}, - }, - korean: { - name: 'korean', - font: undefined, - isWorldService: true, - variant: 'default', - pageTypes: {}, - }, - kyrgyz: { - name: 'kyrgyz', - font: undefined, - isWorldService: true, - variant: 'default', - pageTypes: {}, - }, - marathi: { - name: 'marathi', - font: undefined, - isWorldService: true, - variant: 'default', - pageTypes: {}, - }, - mundo: { - name: 'mundo', - font: 'Reith', - isWorldService: true, - variant: 'default', - pageTypes: {}, - }, - naidheachdan: { - name: 'naidheachdan', - font: 'Reith', - isWorldService: false, - variant: 'default', - pageTypes: {}, - }, - nepali: { - name: 'nepali', - font: undefined, - isWorldService: true, - variant: 'default', - pageTypes: {}, - }, - news: { - name: 'news', - font: 'Reith', - isWorldService: false, - variant: 'default', - pageTypes: {}, - }, - newsround: { - name: 'newsround', - font: undefined, - variant: 'default', - pageTypes: {}, - }, - pashto: { - name: 'pashto', - font: undefined, - isWorldService: true, - variant: 'default', - pageTypes: {}, - }, - persian: { - name: 'persian', - font: 'Nassim', - isWorldService: true, - variant: 'default', - pageTypes: {}, - }, - pidgin: { - name: 'pidgin', - font: undefined, - isWorldService: true, - variant: 'default', - pageTypes: {}, - }, - portuguese: { - name: 'portuguese', - font: 'Reith', - isWorldService: true, - variant: 'default', - pageTypes: {}, - }, - punjabi: { - name: 'punjabi', - font: undefined, - variant: 'default', - pageTypes: {}, - }, - russian: { - name: 'russian', - font: undefined, - isWorldService: true, - variant: 'default', - pageTypes: {}, - }, - scotland: { - name: 'scotland', - font: undefined, - isWorldService: false, - variant: 'default', - pageTypes: {}, - }, - serbianCyr: { - name: 'serbian', - font: undefined, - isWorldService: true, - variant: 'cyr', - pageTypes: {}, - }, - serbianLat: { - name: 'serbian', - font: undefined, - isWorldService: true, - variant: 'lat', - pageTypes: {}, - }, - sinhala: { - name: 'sinhala', - font: undefined, - isWorldService: true, - variant: 'default', - pageTypes: {}, - }, - somali: { - name: 'somali', - font: undefined, - variant: 'default', - pageTypes: {}, - }, - sport: { - name: 'sport', - font: undefined, - variant: 'default', - pageTypes: {}, - }, - swahili: { - name: 'swahili', - font: undefined, - isWorldService: true, - variant: 'default', - pageTypes: {}, - }, - tamil: { - name: 'tamil', - font: undefined, - isWorldService: true, - variant: 'default', - pageTypes: {}, - }, - telugu: { - name: 'telugu', - font: undefined, - isWorldService: true, - variant: 'default', - pageTypes: {}, - }, - thai: { - name: 'thai', - font: undefined, - isWorldService: true, - variant: 'default', - pageTypes: {}, - }, - tigrinya: { - name: 'tigrinya', - font: undefined, - isWorldService: true, - variant: 'default', - pageTypes: {}, - }, - turkce: { - name: 'turkce', - font: 'Reith', - isWorldService: true, - variant: 'default', - pageTypes: {}, - }, - ukchinaSimp: { - name: 'ukchina', - font: undefined, - isWorldService: true, - variant: 'simp', - pageTypes: {}, - }, - ukchinaTrad: { - name: 'ukchina', - font: undefined, - isWorldService: true, - variant: 'trad', - pageTypes: {}, - }, - ukrainianRu: { - name: 'ukrainian', - font: undefined, - isWorldService: true, - variant: 'default', - pageTypes: {}, - }, - ukrainian: { - name: 'ukrainian', - font: undefined, - isWorldService: true, - variant: 'default', - pageTypes: {}, - }, - urdu: { - name: 'urdu', - font: undefined, - isWorldService: true, - variant: 'default', - pageTypes: {}, - }, - uzbek: { - name: 'uzbek', - font: undefined, - isWorldService: true, - variant: 'default', - pageTypes: {}, - }, - uzbekCyr: { - name: 'uzbek', - font: undefined, - isWorldService: true, - variant: 'cyr', - pageTypes: {}, - }, - uzbekLat: { - name: 'uzbek', - font: undefined, - isWorldService: true, - variant: 'lat', - pageTypes: {}, - }, - vietnamese: { - name: 'vietnamese', - font: undefined, - isWorldService: true, - variant: 'default', - pageTypes: {}, - }, - yoruba: { - name: 'yoruba', - font: undefined, - isWorldService: true, - variant: 'default', - pageTypes: {}, - }, - zhongwenSimp: { - name: 'zhongwen', - font: undefined, - isWorldService: true, - variant: 'simp', - pageTypes: {}, - }, - zhongwenTrad: { - name: 'zhongwen', - font: undefined, - isWorldService: true, - variant: 'trad', - pageTypes: {}, - }, -} as Partial>; diff --git a/ws-nextjs-app/cypress/support/helpers/adsTests/testsForAMPOnly.ts b/ws-nextjs-app/cypress/support/helpers/adsTests/testsForAMPOnly.ts index 248f98bc399..fd376a99550 100644 --- a/ws-nextjs-app/cypress/support/helpers/adsTests/testsForAMPOnly.ts +++ b/ws-nextjs-app/cypress/support/helpers/adsTests/testsForAMPOnly.ts @@ -1,16 +1,11 @@ import path from 'ramda/src/path'; -import serviceConfig from '../../config/services'; import { ServiceParametersType } from '../../../types'; -import { ServiceConfigDataType } from '../../config/settings'; export default ({ service }: ServiceParametersType) => { describe(`AMP Ads`, () => { it('should be displayed based on whether ads toggle is enabled/disabled', () => { - const config = serviceConfig[service] as ServiceConfigDataType; - const serviceName = config.name; - - cy.getToggles(serviceName).then(() => { - cy.fixture(`toggles/${serviceName}.json`).then(toggles => { + cy.getToggles(service).then(() => { + cy.fixture(`toggles/${service}.json`).then(toggles => { const adsEnabled = path(['ads', 'enabled'], toggles); if (adsEnabled) { diff --git a/ws-nextjs-app/cypress/support/helpers/adsTests/testsForCanonicalOnly.ts b/ws-nextjs-app/cypress/support/helpers/adsTests/testsForCanonicalOnly.ts index 8d6f1a53ae1..4077a237c49 100644 --- a/ws-nextjs-app/cypress/support/helpers/adsTests/testsForCanonicalOnly.ts +++ b/ws-nextjs-app/cypress/support/helpers/adsTests/testsForCanonicalOnly.ts @@ -1,18 +1,10 @@ -import serviceConfig from '../../config/services'; import { ServiceParametersType } from '../../../types'; -import { ServiceConfigDataType } from '../../config/settings'; export default ({ service }: ServiceParametersType) => { describe(`Canonical Ads`, () => { it('should be displayed based on whether ads toggle is enabled/disabled', () => { - let serviceName = service as string; - if (Object.keys(serviceConfig).includes(service)) { - const config = serviceConfig[service] as ServiceConfigDataType; - serviceName = config.name; - } - - cy.getToggles(serviceName).then(() => { - cy.fixture(`toggles/${serviceName}.json`).then(toggles => { + cy.getToggles(service).then(() => { + cy.fixture(`toggles/${service}.json`).then(toggles => { const adsEnabled = toggles.ads?.enabled; if (adsEnabled) { From c64401e258a9aa5aea242f4ebd56f03078e620a3 Mon Sep 17 00:00:00 2001 From: Shayne Ahchoon Date: Tue, 16 Dec 2025 14:54:01 +0000 Subject: [PATCH 2/2] WS-MAINTENANCE: Remove unused imports --- .../assertions/articleLiteSiteLink.ts | 5 +- .../assertions/continueReadingButton.ts | 5 +- .../assertions/featuresAnalysis.ts | 5 +- .../atiAnalytics/assertions/index.ts | 350 ++++++++++++++++++ .../atiAnalytics/assertions/latestMedia.ts | 5 +- .../atiAnalytics/assertions/mostRead.ts | 5 +- .../atiAnalytics/assertions/navigation.ts | 5 +- .../atiAnalytics/assertions/podcastPromo.ts | 5 +- .../assertions/recommendations.ts | 5 +- .../atiAnalytics/assertions/relatedContent.ts | 5 +- .../atiAnalytics/assertions/relatedTopics.ts | 5 +- .../assertions/scrollablePromo.ts | 5 +- .../atiAnalytics/assertions/socialEmbed.ts | 5 +- .../atiAnalytics/assertions/topBarOjs.ts | 5 +- .../atiAnalytics/assertions/topStories.ts | 5 +- 15 files changed, 364 insertions(+), 56 deletions(-) create mode 100644 ws-nextjs-app/cypress/e2e/specialFeatures/atiAnalytics/assertions/index.ts diff --git a/ws-nextjs-app/cypress/e2e/specialFeatures/atiAnalytics/assertions/articleLiteSiteLink.ts b/ws-nextjs-app/cypress/e2e/specialFeatures/atiAnalytics/assertions/articleLiteSiteLink.ts index 6d4694ed31c..c28cbcbd466 100644 --- a/ws-nextjs-app/cypress/e2e/specialFeatures/atiAnalytics/assertions/articleLiteSiteLink.ts +++ b/ws-nextjs-app/cypress/e2e/specialFeatures/atiAnalytics/assertions/articleLiteSiteLink.ts @@ -1,9 +1,6 @@ /* eslint-disable import/no-relative-packages */ import { interceptATIAnalyticsBeacons, COMPONENTS } from '../helpers'; -import { - assertATIComponentClickEvent, - assertATIComponentViewEvent, -} from '../../../../../../cypress/e2e/specialFeatures/atiAnalytics/assertions'; +import { assertATIComponentClickEvent, assertATIComponentViewEvent } from '.'; import { AtiAssertionFnProps } from './type'; const { ARTICLE_LITE_SITE_LINK } = COMPONENTS; diff --git a/ws-nextjs-app/cypress/e2e/specialFeatures/atiAnalytics/assertions/continueReadingButton.ts b/ws-nextjs-app/cypress/e2e/specialFeatures/atiAnalytics/assertions/continueReadingButton.ts index fcaa7d4576e..197f36c43cf 100644 --- a/ws-nextjs-app/cypress/e2e/specialFeatures/atiAnalytics/assertions/continueReadingButton.ts +++ b/ws-nextjs-app/cypress/e2e/specialFeatures/atiAnalytics/assertions/continueReadingButton.ts @@ -1,9 +1,6 @@ /* eslint-disable import/no-relative-packages */ import { interceptATIAnalyticsBeacons, COMPONENTS } from '../helpers'; -import { - assertATIComponentClickEvent, - assertATIComponentViewEvent, -} from '../../../../../../cypress/e2e/specialFeatures/atiAnalytics/assertions'; +import { assertATIComponentClickEvent, assertATIComponentViewEvent } from '.'; import { AtiAssertionFnProps } from './type'; const { CONTINUE_READING_BUTTON } = COMPONENTS; diff --git a/ws-nextjs-app/cypress/e2e/specialFeatures/atiAnalytics/assertions/featuresAnalysis.ts b/ws-nextjs-app/cypress/e2e/specialFeatures/atiAnalytics/assertions/featuresAnalysis.ts index 5661514ef68..c7c5212425e 100644 --- a/ws-nextjs-app/cypress/e2e/specialFeatures/atiAnalytics/assertions/featuresAnalysis.ts +++ b/ws-nextjs-app/cypress/e2e/specialFeatures/atiAnalytics/assertions/featuresAnalysis.ts @@ -1,9 +1,6 @@ /* eslint-disable import/no-relative-packages */ import { interceptATIAnalyticsBeacons, COMPONENTS } from '../helpers'; -import { - assertATIComponentClickEvent, - assertATIComponentViewEvent, -} from '../../../../../../cypress/e2e/specialFeatures/atiAnalytics/assertions'; +import { assertATIComponentClickEvent, assertATIComponentViewEvent } from '.'; import { AtiAssertionFnProps } from './type'; const { FEATURES } = COMPONENTS; diff --git a/ws-nextjs-app/cypress/e2e/specialFeatures/atiAnalytics/assertions/index.ts b/ws-nextjs-app/cypress/e2e/specialFeatures/atiAnalytics/assertions/index.ts new file mode 100644 index 00000000000..1f66d441597 --- /dev/null +++ b/ws-nextjs-app/cypress/e2e/specialFeatures/atiAnalytics/assertions/index.ts @@ -0,0 +1,350 @@ +import { eea, gbOrUnknown } from '#app/lib/utilities/cookieCountries'; +import { + VIEW_EVENT, + VIEWABILITY_CLICK_EVENT, +} from '#app/lib/analyticsUtils/analytics.const'; +import { + ATI_PAGE_VIEW, + ATI_PAGE_VIEW_REVERB, + // ATI_USER_ID_COOKIE, + getATIParamsFromURL, + interceptATIAnalyticsBeacons, + getExpectedAtiDestination, +} from '../helpers'; +import environment from '../../../../support/helpers/getAppEnv'; + +const usesReverbViewabilityModel = applicationType => + applicationType !== 'lite'; + +const getAppName = service => { + if (service === 'ws') { + return '[news]'; + } + + return ['archive', 'news', 'newsround', 'scotland', 'sport'].includes(service) + ? `[${service}]` + : `[news-${service}]`; +}; + +const assertATIPageViewEventParamsExist = ({ + params, + contentType, + applicationType, +}) => { + expect(params).to.have.property('s'); // destination + expect(params).to.have.property('s2'); // Level 2 Site / Producer ID + expect(params).to.have.property('p'); // page identifier + expect(params).to.have.property('x2'); // application type + expect(params).to.have.property('x3'); // application name + expect(params).to.have.property('x4'); // language + expect(params).to.have.property('x7'); // content type + expect(params).to.have.property('x8'); // library version + expect(params).to.have.property('x9'); // page title + + if (['responsive', 'amp'].includes(applicationType)) { + expect(params).to.have.property('r'); // screen resolution & colour depth + expect(params).to.have.property('re'); // browser/viewport resolution + expect(params).to.have.property('hl'); // timestamp + expect(params).to.have.property('lng'); // device language + expect(params).to.have.property('x5'); // url + } + + if (['responsive', 'lite'].includes(applicationType)) { + expect(params).to.have.property('idclient'); + } + + if (!['list-datadriven', 'static'].includes(contentType)) { + expect(params).to.have.property('x1'); // content ID + } + + if (contentType === 'article') { + expect(params).to.have.property('x11'); // first published + expect(params).to.have.property('x12'); // last published + expect(params).to.have.property('x13'); // ldp things + expect(params).to.have.property('x17'); // category + } +}; + +const assertLocationSpecificPianoDestinationExists = ({ service }) => { + cy.get( + 'head script[src*="https://cdn.ampproject.org/v0/amp-geo-0.1.js"]', + ).should('exist'); + + cy.get('amp-geo script[type="application/json"]').should(script => { + const ampGeoContent = JSON.parse(script.text()); + + expect(ampGeoContent).to.eql({ + AmpBind: true, + ISOCountryGroups: { + eea, + gbOrUnknown, + }, + }); + }); + + cy.get( + '[data-e2e="ati-amp-analytics"] script[type="application/json"]', + ).should(script => { + const ampAnalyticsContent = script.text(); + + expect(ampAnalyticsContent).to.contain( + `s=${getExpectedAtiDestination({ service, applicationEnv: environment() })}`, + ); + }); +}; + +const assertReverbViewabilityComponentEventParamsExist = ({ params }) => { + expect(params).to.have.property('s'); // destination + expect(params).to.have.property('events'); // event details + expect(params).to.have.property('context'); + + const eventContext = JSON.parse(params.context); + + expect(eventContext[0].data.page).to.have.property('$'); + expect(eventContext[0].data.site).to.have.property('level2_id'); +}; + +const fieldIsValidString = field => + typeof field === 'string' && field.trim().length > 0; + +const validateViewabilityEventDetails = ({ payload, actionType }) => { + const arr = JSON.parse(payload); + + return arr.some(event => { + if (event.name !== `viewability.${actionType}`) return false; + + const group = event.data?.group ?? {}; + const ev = event.data?.event ?? {}; + const item = event.data?.item ?? {}; + + // strict checks + if (ev.category !== 'viewability' || ev.action !== actionType) return false; + + // required fields in Group + const groupNameOk = fieldIsValidString(group.name); + + const groupTypeOk = fieldIsValidString(group.type); + + // optional fields in Group + const groupLinkOk = !group.link || fieldIsValidString(group.link); + + const groupItemCountOk = + !group.item_count || Number.isInteger(group.item_count); + + const groupResourceOk = + !group.resource_id || fieldIsValidString(group.resource_id); + + const groupPositionOk = !group.position || Number.isInteger(group.position); + + // required fields in Item + const itemNameOk = fieldIsValidString(item.name); + + // optional fields in Item + const itemLinkOk = !item.link || fieldIsValidString(item.link); + + const itemAdvertiserIdOk = + !item.advertiser_id || fieldIsValidString(item.advertiser_id); + + const itemTypeOk = !item.type || fieldIsValidString(item.type); + + const itemTextOk = !item.text || fieldIsValidString(item.text); + + const itemPositionOk = !item.position || Number.isInteger(item.position); + + const itemDurationOk = !item.duration || Number.isInteger(item.duration); + + const itemMediaTypeOk = + !item.media_type || fieldIsValidString(item.media_type); + + const itemLabelOk = !item.label || fieldIsValidString(item.label); + + const itemResourceIdOk = + !item.resource_id || fieldIsValidString(item.resource_id); + + return ( + groupNameOk && + groupTypeOk && + groupLinkOk && + groupItemCountOk && + groupResourceOk && + groupPositionOk && + itemNameOk && + itemLinkOk && + itemAdvertiserIdOk && + itemTypeOk && + itemTextOk && + itemPositionOk && + itemDurationOk && + itemMediaTypeOk && + itemLabelOk && + itemResourceIdOk + ); + }); +}; + +export const assertPageView = ({ + useReverb, + pageIdentifier, + applicationType, + contentType, + service, + path, + siteId, +}) => { + it(`should send a page view event with service = ${service}, page identifier = ${pageIdentifier}, site ID = ${siteId}, application type = ${applicationType} and content type = ${contentType}`, () => { + interceptATIAnalyticsBeacons(); + cy.visit(path, { retryOnStatusCodeFailure: true }); + + const useViewabilty = usesReverbViewabilityModel(applicationType); + const atiPageViewAlias = + useReverb && useViewabilty && applicationType !== 'amp' + ? ATI_PAGE_VIEW_REVERB + : ATI_PAGE_VIEW; + + cy.wait(`@${atiPageViewAlias}`).then(({ request }) => { + const params = getATIParamsFromURL(request.url); + + assertATIPageViewEventParamsExist({ + params, + contentType, + applicationType, + }); + + // TODO: Commenting out temporarily until old ATI code is removed - https://bbc.atlassian.net/browse/WS-222 + // if (['responsive', 'lite'].includes(applicationType)) { + // expect(params.idclient).to.equal( + // ATI_USER_ID_COOKIE, + // 'params.idclient (atuserid cookie value)', + // ); + // } + + expect(params.p).to.equal(pageIdentifier, 'params.p (page identifier)'); + expect(parseInt(params.s2, 10)).to.equal( + siteId, + 'params.s2 (Level 2 site / Producer ID)', + ); + expect(params.x2).to.equal( + `[${applicationType}]`, + 'params.x2 (application type)', + ); + expect(params.x3).to.equal( + getAppName(service), + 'params.x3 (application name)', + ); + expect(params.x7).to.equal( + `[${contentType}]`, + 'params.x7 (content type)', + ); + }); + + if (applicationType === 'amp') { + assertLocationSpecificPianoDestinationExists({ service }); + } + }); +}; + +const assertViewabilityModelViewEvent = ({ + pageIdentifier, + params, + // applicationType, + siteId, +}) => { + const eventContext = JSON.parse(params.context); + + assertReverbViewabilityComponentEventParamsExist({ params }); + + // TODO: Commenting out temporarily until old ATI code is removed - https://bbc.atlassian.net/browse/WS-222 + // if (['responsive', 'lite'].includes(applicationType)) { + // expect(params.idclient).to.equal( + // ATI_USER_ID_COOKIE, + // 'params.idclient (atuserid cookie value)', + // ); + // } + + expect(params.events).to.satisfy( + payload => + validateViewabilityEventDetails({ payload, actionType: VIEW_EVENT }), + 'params.events (publisher impression)', + ); + + expect(eventContext[0].data.page.$).to.equal(pageIdentifier); + expect(parseInt(eventContext[0].data.site.level2_id, 10)).to.equal(siteId); +}; + +export const assertATIComponentViewEvent = ({ + component, + pageIdentifier, + contentType, + siteId, +}) => { + const requestAlias = `@${component}-viewability-view`; + + cy.wait(requestAlias) + .its('request.url') + .then(url => { + const params = getATIParamsFromURL(url); + + assertViewabilityModelViewEvent({ + component, + pageIdentifier, + contentType, + params, + siteId, + }); + }); +}; + +const assertViewabilityModelClickEvent = ({ + pageIdentifier, + params, + // applicationType, + siteId, +}) => { + const eventContext = JSON.parse(params.context); + + assertReverbViewabilityComponentEventParamsExist({ + params, + }); + + // TODO: Commenting out temporarily until old ATI code is removed - https://bbc.atlassian.net/browse/WS-222 + // if (['responsive', 'lite'].includes(applicationType)) { + // expect(params.idclient).to.equal( + // ATI_USER_ID_COOKIE, + // 'params.idclient (atuserid cookie value)', + // ); + // } + + expect(params.events).to.satisfy( + payload => + validateViewabilityEventDetails({ + payload, + actionType: VIEWABILITY_CLICK_EVENT, + }), + 'params.events (publisher click)', + ); + + expect(eventContext[0].data.page.$).to.equal(pageIdentifier); + expect(parseInt(eventContext[0].data.site.level2_id, 10)).to.equal(siteId); +}; + +export const assertATIComponentClickEvent = ({ + component, + contentType, + pageIdentifier, + siteId, +}) => { + const requestAlias = `@${component}-viewability-click`; + + cy.wait(requestAlias) + .its('request.url') + .then(url => { + const params = getATIParamsFromURL(url); + assertViewabilityModelClickEvent({ + component, + contentType, + pageIdentifier, + params, + siteId, + }); + }); +}; diff --git a/ws-nextjs-app/cypress/e2e/specialFeatures/atiAnalytics/assertions/latestMedia.ts b/ws-nextjs-app/cypress/e2e/specialFeatures/atiAnalytics/assertions/latestMedia.ts index 1e146ae27ef..5294b3df78f 100644 --- a/ws-nextjs-app/cypress/e2e/specialFeatures/atiAnalytics/assertions/latestMedia.ts +++ b/ws-nextjs-app/cypress/e2e/specialFeatures/atiAnalytics/assertions/latestMedia.ts @@ -1,9 +1,6 @@ /* eslint-disable import/no-relative-packages */ import { interceptATIAnalyticsBeacons, COMPONENTS } from '../helpers'; -import { - assertATIComponentClickEvent, - assertATIComponentViewEvent, -} from '../../../../../../cypress/e2e/specialFeatures/atiAnalytics/assertions'; +import { assertATIComponentClickEvent, assertATIComponentViewEvent } from '.'; import { AtiAssertionFnProps } from './type'; const { LATEST_MEDIA } = COMPONENTS; diff --git a/ws-nextjs-app/cypress/e2e/specialFeatures/atiAnalytics/assertions/mostRead.ts b/ws-nextjs-app/cypress/e2e/specialFeatures/atiAnalytics/assertions/mostRead.ts index 7d41e4ea47e..d3e499daf0d 100644 --- a/ws-nextjs-app/cypress/e2e/specialFeatures/atiAnalytics/assertions/mostRead.ts +++ b/ws-nextjs-app/cypress/e2e/specialFeatures/atiAnalytics/assertions/mostRead.ts @@ -1,9 +1,6 @@ /* eslint-disable import/no-relative-packages */ import { interceptATIAnalyticsBeacons, COMPONENTS } from '../helpers'; -import { - assertATIComponentClickEvent, - assertATIComponentViewEvent, -} from '../../../../../../cypress/e2e/specialFeatures/atiAnalytics/assertions'; +import { assertATIComponentClickEvent, assertATIComponentViewEvent } from '.'; import { AtiAssertionFnProps } from './type'; import runIfToggleEnabled from '../../../../support/helpers/runIfToggleEnabled'; diff --git a/ws-nextjs-app/cypress/e2e/specialFeatures/atiAnalytics/assertions/navigation.ts b/ws-nextjs-app/cypress/e2e/specialFeatures/atiAnalytics/assertions/navigation.ts index d16f89d878c..5ba2820f656 100644 --- a/ws-nextjs-app/cypress/e2e/specialFeatures/atiAnalytics/assertions/navigation.ts +++ b/ws-nextjs-app/cypress/e2e/specialFeatures/atiAnalytics/assertions/navigation.ts @@ -1,9 +1,6 @@ /* eslint-disable import/no-relative-packages */ import { interceptATIAnalyticsBeacons, COMPONENTS } from '../helpers'; -import { - assertATIComponentClickEvent, - assertATIComponentViewEvent, -} from '../../../../../../cypress/e2e/specialFeatures/atiAnalytics/assertions'; +import { assertATIComponentClickEvent, assertATIComponentViewEvent } from '.'; import { AtiAssertionFnProps } from './type'; const { SCROLLABLE_NAVIGATION, DROPDOWN_NAVIGATION } = COMPONENTS; diff --git a/ws-nextjs-app/cypress/e2e/specialFeatures/atiAnalytics/assertions/podcastPromo.ts b/ws-nextjs-app/cypress/e2e/specialFeatures/atiAnalytics/assertions/podcastPromo.ts index 8b7c8b98141..e14962bfa33 100644 --- a/ws-nextjs-app/cypress/e2e/specialFeatures/atiAnalytics/assertions/podcastPromo.ts +++ b/ws-nextjs-app/cypress/e2e/specialFeatures/atiAnalytics/assertions/podcastPromo.ts @@ -1,9 +1,6 @@ /* eslint-disable import/no-relative-packages */ import { interceptATIAnalyticsBeacons, COMPONENTS } from '../helpers'; -import { - assertATIComponentClickEvent, - assertATIComponentViewEvent, -} from '../../../../../../cypress/e2e/specialFeatures/atiAnalytics/assertions'; +import { assertATIComponentClickEvent, assertATIComponentViewEvent } from '.'; import { AtiAssertionFnProps } from './type'; const { PODCAST_PROMO } = COMPONENTS; diff --git a/ws-nextjs-app/cypress/e2e/specialFeatures/atiAnalytics/assertions/recommendations.ts b/ws-nextjs-app/cypress/e2e/specialFeatures/atiAnalytics/assertions/recommendations.ts index bbd3cc9de52..cb6e2c9b7a4 100644 --- a/ws-nextjs-app/cypress/e2e/specialFeatures/atiAnalytics/assertions/recommendations.ts +++ b/ws-nextjs-app/cypress/e2e/specialFeatures/atiAnalytics/assertions/recommendations.ts @@ -1,9 +1,6 @@ /* eslint-disable import/no-relative-packages */ import { interceptATIAnalyticsBeacons, COMPONENTS } from '../helpers'; -import { - assertATIComponentClickEvent, - assertATIComponentViewEvent, -} from '../../../../../../cypress/e2e/specialFeatures/atiAnalytics/assertions'; +import { assertATIComponentClickEvent, assertATIComponentViewEvent } from '.'; import { AtiAssertionFnProps } from './type'; const { RECOMMENDATIONS } = COMPONENTS; diff --git a/ws-nextjs-app/cypress/e2e/specialFeatures/atiAnalytics/assertions/relatedContent.ts b/ws-nextjs-app/cypress/e2e/specialFeatures/atiAnalytics/assertions/relatedContent.ts index 10360b7d3e4..783248cd963 100644 --- a/ws-nextjs-app/cypress/e2e/specialFeatures/atiAnalytics/assertions/relatedContent.ts +++ b/ws-nextjs-app/cypress/e2e/specialFeatures/atiAnalytics/assertions/relatedContent.ts @@ -1,9 +1,6 @@ /* eslint-disable import/no-relative-packages */ import { interceptATIAnalyticsBeacons, COMPONENTS } from '../helpers'; -import { - assertATIComponentClickEvent, - assertATIComponentViewEvent, -} from '../../../../../../cypress/e2e/specialFeatures/atiAnalytics/assertions'; +import { assertATIComponentClickEvent, assertATIComponentViewEvent } from '.'; import { AtiAssertionFnProps } from './type'; const { RELATED_CONTENT } = COMPONENTS; diff --git a/ws-nextjs-app/cypress/e2e/specialFeatures/atiAnalytics/assertions/relatedTopics.ts b/ws-nextjs-app/cypress/e2e/specialFeatures/atiAnalytics/assertions/relatedTopics.ts index 30375524890..f1df5089c26 100644 --- a/ws-nextjs-app/cypress/e2e/specialFeatures/atiAnalytics/assertions/relatedTopics.ts +++ b/ws-nextjs-app/cypress/e2e/specialFeatures/atiAnalytics/assertions/relatedTopics.ts @@ -1,9 +1,6 @@ /* eslint-disable import/no-relative-packages */ import { interceptATIAnalyticsBeacons, COMPONENTS } from '../helpers'; -import { - assertATIComponentClickEvent, - assertATIComponentViewEvent, -} from '../../../../../../cypress/e2e/specialFeatures/atiAnalytics/assertions'; +import { assertATIComponentClickEvent, assertATIComponentViewEvent } from '.'; import { AtiAssertionFnProps } from './type'; const { RELATED_TOPICS } = COMPONENTS; diff --git a/ws-nextjs-app/cypress/e2e/specialFeatures/atiAnalytics/assertions/scrollablePromo.ts b/ws-nextjs-app/cypress/e2e/specialFeatures/atiAnalytics/assertions/scrollablePromo.ts index f8e6a8f259c..c59d73a847b 100644 --- a/ws-nextjs-app/cypress/e2e/specialFeatures/atiAnalytics/assertions/scrollablePromo.ts +++ b/ws-nextjs-app/cypress/e2e/specialFeatures/atiAnalytics/assertions/scrollablePromo.ts @@ -1,9 +1,6 @@ /* eslint-disable import/no-relative-packages */ import { interceptATIAnalyticsBeacons, COMPONENTS } from '../helpers'; -import { - assertATIComponentClickEvent, - assertATIComponentViewEvent, -} from '../../../../../../cypress/e2e/specialFeatures/atiAnalytics/assertions'; +import { assertATIComponentClickEvent, assertATIComponentViewEvent } from '.'; import { AtiAssertionFnProps } from './type'; const { SCROLLABLE_PROMO } = COMPONENTS; diff --git a/ws-nextjs-app/cypress/e2e/specialFeatures/atiAnalytics/assertions/socialEmbed.ts b/ws-nextjs-app/cypress/e2e/specialFeatures/atiAnalytics/assertions/socialEmbed.ts index 26a308e566e..f57eff74e6b 100644 --- a/ws-nextjs-app/cypress/e2e/specialFeatures/atiAnalytics/assertions/socialEmbed.ts +++ b/ws-nextjs-app/cypress/e2e/specialFeatures/atiAnalytics/assertions/socialEmbed.ts @@ -1,9 +1,6 @@ /* eslint-disable import/no-relative-packages */ import { interceptATIAnalyticsBeacons, COMPONENTS } from '../helpers'; -import { - assertATIComponentClickEvent, - assertATIComponentViewEvent, -} from '../../../../../../cypress/e2e/specialFeatures/atiAnalytics/assertions'; +import { assertATIComponentClickEvent, assertATIComponentViewEvent } from '.'; import { AtiAssertionFnProps } from './type'; const { SOCIAL_EMBED } = COMPONENTS; diff --git a/ws-nextjs-app/cypress/e2e/specialFeatures/atiAnalytics/assertions/topBarOjs.ts b/ws-nextjs-app/cypress/e2e/specialFeatures/atiAnalytics/assertions/topBarOjs.ts index 5ee6c57376a..fab0f32990a 100644 --- a/ws-nextjs-app/cypress/e2e/specialFeatures/atiAnalytics/assertions/topBarOjs.ts +++ b/ws-nextjs-app/cypress/e2e/specialFeatures/atiAnalytics/assertions/topBarOjs.ts @@ -1,9 +1,6 @@ /* eslint-disable import/no-relative-packages */ import { interceptATIAnalyticsBeacons, COMPONENTS } from '../helpers'; -import { - assertATIComponentClickEvent, - assertATIComponentViewEvent, -} from '../../../../../../cypress/e2e/specialFeatures/atiAnalytics/assertions'; +import { assertATIComponentClickEvent, assertATIComponentViewEvent } from '.'; import { AtiAssertionFnProps } from './type'; const { TOP_BAR_OJ } = COMPONENTS; diff --git a/ws-nextjs-app/cypress/e2e/specialFeatures/atiAnalytics/assertions/topStories.ts b/ws-nextjs-app/cypress/e2e/specialFeatures/atiAnalytics/assertions/topStories.ts index ebeac4ca5fb..d2cb306856d 100644 --- a/ws-nextjs-app/cypress/e2e/specialFeatures/atiAnalytics/assertions/topStories.ts +++ b/ws-nextjs-app/cypress/e2e/specialFeatures/atiAnalytics/assertions/topStories.ts @@ -1,9 +1,6 @@ /* eslint-disable import/no-relative-packages */ import { interceptATIAnalyticsBeacons, COMPONENTS } from '../helpers'; -import { - assertATIComponentClickEvent, - assertATIComponentViewEvent, -} from '../../../../../../cypress/e2e/specialFeatures/atiAnalytics/assertions'; +import { assertATIComponentClickEvent, assertATIComponentViewEvent } from '.'; import { AtiAssertionFnProps } from './type'; const { TOP_STORIES } = COMPONENTS;