diff --git a/src/core/messenger/scroll.spec.ts b/src/core/messenger/scroll.spec.ts index d9c997dd1..731ca7c2f 100644 --- a/src/core/messenger/scroll.spec.ts +++ b/src/core/messenger/scroll.spec.ts @@ -1,4 +1,3 @@ -import { noop } from '../../utils/noop'; import { _ as testExports } from './scroll'; const addScrollListener = testExports.addScrollListener; @@ -91,8 +90,8 @@ describe.skip('Cross-frame messenger: scroll', () => { constructor(callback: ObsCallback) { onIntersect = callback; return Object.freeze({ - observe: noop, - unobserve: noop, + observe: () => {}, + unobserve: () => {}, disconnect: () => { onIntersect = null; }, diff --git a/src/define/define-slot.spec.ts b/src/define/define-slot.spec.ts index 6d199c2e8..095a2ab57 100644 --- a/src/define/define-slot.spec.ts +++ b/src/define/define-slot.spec.ts @@ -1,6 +1,5 @@ import type { SizeMapping } from '../core/ad-sizes'; import { adSizes, createAdSize } from '../core/ad-sizes'; -import { toGoogleTagSize } from '../utils/googletag-ad-size'; import { buildGoogletagSizeMapping, collectSizes, @@ -74,21 +73,11 @@ describe('buildGoogletagSizeMapping', () => { expect(result).toEqual([ [ [980, 0], - [ - toGoogleTagSize(adSizes.mpu), - 'fluid', - toGoogleTagSize(adSizes.googleCard), - toGoogleTagSize(adSizes.halfPage), - ], + [adSizes.mpu, 'fluid', adSizes.googleCard, adSizes.halfPage], ], [ [0, 0], - [ - toGoogleTagSize(adSizes.mpu), - 'fluid', - toGoogleTagSize(adSizes.googleCard), - toGoogleTagSize(adSizes.halfPage), - ], + [adSizes.mpu, 'fluid', adSizes.googleCard, adSizes.halfPage], ], ]); }); diff --git a/src/define/define-slot.ts b/src/define/define-slot.ts index 70fe6a683..db6f46661 100644 --- a/src/define/define-slot.ts +++ b/src/define/define-slot.ts @@ -1,9 +1,8 @@ import { breakpoints as sourceBreakpoints } from '@guardian/source/foundations'; import { once } from 'lodash-es'; -import type { SizeMapping, SlotName } from '../core/ad-sizes'; +import type { AdSize, SizeMapping, SlotName } from '../core/ad-sizes'; import { EventTimer } from '../core/index'; import { isEligibleForTeads } from '../core/targeting/teads-eligibility'; -import { toGoogleTagSize } from '../utils/googletag-ad-size'; import { getUrlVars } from '../utils/url'; import { initSlotIas } from './init-slot-ias'; @@ -29,6 +28,11 @@ const isBreakpoint = ( ): breakpoint is keyof typeof breakpointViewports => breakpoint in breakpointViewports; +const toGoogleTagSize = (size: AdSize): googletag.SingleSize => { + // not using width and height here as to maintain compatibility with plain arrays + return size[0] === 0 && size[1] === 0 ? 'fluid' : [size[0], size[1]]; +}; + /** * Builds a googletag size mapping based on the breakpoints and ad sizes from * the size mapping in commercial-core and the viewport sizes from source-foundations. diff --git a/src/init/consented/prepare-googletag.spec.ts b/src/init/consented/prepare-googletag.spec.ts index 4aa68b62e..a512e9779 100644 --- a/src/init/consented/prepare-googletag.spec.ts +++ b/src/init/consented/prepare-googletag.spec.ts @@ -3,39 +3,11 @@ import { getConsentFor, loadScript, onConsent } from '@guardian/libs'; import type * as AdSizesType from '../../core/ad-sizes'; import { loadAdvert } from '../../display/load-advert'; import { commercialFeatures } from '../../lib/commercial-features'; -import _config from '../../lib/config'; import { getCurrentBreakpoint as getCurrentBreakpoint_ } from '../../lib/detect/detect-breakpoint'; import { dfpEnv } from '../../lib/dfp/dfp-env'; import { init as prepareGoogletag } from './prepare-googletag'; import { fillStaticAdvertSlots } from './static-ad-slots'; -const config = _config as { - get: (k: string) => string; - set: ( - k: string, - v: - | boolean - | string - | Record - | { - adUnit: string; - contentType: string; - edition: string; - isFront: boolean; - keywordIds: string; - pageId: string; - section: string; - seriesId: string; - sharedAdTargeting: { - ct: string; - edition: string; - k: string[]; - se: string[]; - }; - }, - ) => void; -}; - const getAdverts = (withEmpty: boolean) => { return [...dfpEnv.adverts.values()].map((advert) => { // Do not return empty slots unless explicitly requested @@ -270,10 +242,11 @@ describe('DFP', () => { } = {} as const; beforeEach(() => { - config.set('page', { + // @ts-expect-error -- test + window.guardian.config.page = { adUnit: '/123456/theguardian.com/front', contentType: 'Article', - edition: 'us', + edition: 'US', isFront: true, keywordIds: 'world/korea,world/ukraine', pageId: 'world/uk', @@ -285,7 +258,7 @@ describe('DFP', () => { k: ['korea', 'ukraine'], se: ['happy-times'], }, - }); + }; document.body.innerHTML = domSnippet; diff --git a/src/insert/spacefinder/article.spec.ts b/src/insert/spacefinder/article.spec.ts index 0c764af47..4bf8dde4e 100644 --- a/src/insert/spacefinder/article.spec.ts +++ b/src/insert/spacefinder/article.spec.ts @@ -20,8 +20,6 @@ jest.mock('insert/spacefinder/space-filler', () => ({ }, })); -jest.mock('lib/config', () => ({ page: {}, get: () => false })); - jest.mock('experiments/ab', () => ({ isUserInVariant: () => false, })); diff --git a/src/lib/config.d.ts b/src/lib/config.d.ts deleted file mode 100644 index 74452c35e..000000000 --- a/src/lib/config.d.ts +++ /dev/null @@ -1,21 +0,0 @@ -declare function get(path: string): T | undefined; -declare function get(path: string, defaultValue: T): T; -declare const set: (path: string, defaultValue?: unknown) => void; -declare const hasTone: (s: string) => boolean; -declare const hasSeries: (s: string) => boolean; -declare const referenceOfType: (name: string) => string; -declare const referencesOfType: (name: string) => string[]; -declare const webPublicationDateAsUrlPart: () => string | null; -declare const dateFromSlug: () => string | null; - -// eslint-disable-next-line import/no-default-export -- it’s a declaration -export default { - get, - set, - hasTone, - hasSeries, - referenceOfType, - referencesOfType, - webPublicationDateAsUrlPart, - dateFromSlug, -}; diff --git a/src/lib/config.js b/src/lib/config.js deleted file mode 100644 index 36f80d0ce..000000000 --- a/src/lib/config.js +++ /dev/null @@ -1,86 +0,0 @@ -// This should be the only module accessing the window config object directly -// because this is the one that gets imported to all other modules -// eslint-disable-next-line guardian-frontend/global-config -const config = window.guardian.config; - -// allows you to safely get items from config using a query of -// dot or bracket notation, with optional default fallback -const get = (path = '', defaultValue) => { - const value = path - .replace(/\[(.+?)\]/g, '.$1') - .split('.') - .reduce((o, key) => o && o[key], config); - - if (typeof value !== 'undefined') { - return value; - } - - return defaultValue; -}; - -// let S = { l1, l2, ..., ln } be a non-empty ordered set of labels -// let s = l1.l2.....ln be the string representation of S -// set(s, x) is the function that takes any value x into the config -// object following the path described by S, making sure that path -// actually leads somewhere. -const set = (path, value) => { - const pathSegments = path.split('.'); - const last = pathSegments.pop(); - pathSegments.reduce((obj, subpath) => { - if (typeof obj[subpath] === 'object') { - return obj[subpath]; - } - obj[subpath] = {}; - return obj[subpath]; - }, config)[last] = value; -}; - -const hasTone = (name) => (config.page.tones || '').includes(name); - -const hasSeries = (name) => (config.page.series || '').includes(name); - -const referencesOfType = (name) => - (config.page.references || []) - .filter((reference) => typeof reference[name] !== 'undefined') - .map((reference) => reference[name]); - -const referenceOfType = (name) => referencesOfType(name)[0]; - -// the date nicely formatted and padded for use as part of a url -// looks like 2012/04/31 -const webPublicationDateAsUrlPart = () => { - const webPublicationDate = config.page.webPublicationDate; - - if (webPublicationDate) { - const pubDate = new Date(webPublicationDate); - return `${pubDate.getFullYear()}/${(pubDate.getMonth() + 1) - .toString() - .padStart(2, '0')}/${pubDate - .getDate() - .toString() - .padStart(2, '0')}`; - } - - return null; -}; - -// returns 2014/apr/22 -const dateFromSlug = () => { - const s = config.page.pageId.match(/\d{4}\/\w{3}\/\d{2}/); - return s ? s[0] : null; -}; - -export default Object.assign( - {}, - { - get, - set, - hasTone, - hasSeries, - referencesOfType, - referenceOfType, - webPublicationDateAsUrlPart, - dateFromSlug, - }, - config, -); diff --git a/src/lib/cookies.js b/src/lib/cookies.js deleted file mode 100644 index 0920ece33..000000000 --- a/src/lib/cookies.js +++ /dev/null @@ -1,65 +0,0 @@ -import { reportError } from '../utils/report-error'; -import config from './config'; - -const ERR_INVALID_COOKIE_NAME = `Cookie must not contain invalid characters (space, tab and the following characters: '()<>@,;"/[]?={}')`; - -// subset of https://github.com/guzzle/guzzle/pull/1131 -const isValidCookieValue = (name) => !/[()<>@,;"\\/[\]?={} \t]/g.test(name); - -const getShortDomain = ({ isCrossSubdomain = false } = {}) => { - const domain = document.domain || ''; - - if (domain === 'localhost' || config.get('page.isPreview')) { - return domain; - } - - // Trim any possible subdomain (will be shared with supporter, identity, etc) - if (isCrossSubdomain) { - return ['', ...domain.split('.').slice(-2)].join('.'); - } - // Trim subdomains for prod (www.theguardian), code (m.code.dev-theguardian) and dev (dev.theguardian, m.thegulocal) - return domain.replace(/^(www|m\.code|dev|m)\./, '.'); -}; - -const getDomainAttribute = ({ isCrossSubdomain = false } = {}) => { - const shortDomain = getShortDomain({ isCrossSubdomain }); - return shortDomain === 'localhost' ? '' : ` domain=${shortDomain};`; -}; - -const removeCookie = (name, currentDomainOnly = false) => { - const expires = 'expires=Thu, 01 Jan 1970 00:00:01 GMT;'; - const path = 'path=/;'; - - // Remove cookie, implicitly using the document's domain. - document.cookie = `${name}=;${path}${expires}`; - if (!currentDomainOnly) { - // also remove from the short domain - document.cookie = `${name}=;${path}${expires} domain=${getShortDomain()};`; - } -}; - -const addCookie = (name, value, daysToLive, isCrossSubdomain = false) => { - const expires = new Date(); - - if (!isValidCookieValue(name) || !isValidCookieValue(value)) { - reportError( - new Error(`${ERR_INVALID_COOKIE_NAME} .${name}=${value}`), - 'commercial', - ); - } - - if (daysToLive) { - expires.setDate(expires.getDate() + daysToLive); - } else { - expires.setMonth(expires.getMonth() + 5); - expires.setDate(1); - } - - document.cookie = `${name}=${value}; path=/; expires=${expires.toUTCString()};${getDomainAttribute( - { - isCrossSubdomain, - }, - )}`; -}; - -export { addCookie, removeCookie }; diff --git a/src/lib/header-bidding/a9/a9.ts b/src/lib/header-bidding/a9/a9.ts index 40b61fb29..f7678a37f 100644 --- a/src/lib/header-bidding/a9/a9.ts +++ b/src/lib/header-bidding/a9/a9.ts @@ -1,7 +1,7 @@ import { flatten } from 'lodash-es'; import type { Advert } from '../../../define/Advert'; import type { A9AdUnitInterface } from '../../../types/global'; -import { noop } from '../../../utils/noop'; +import { reportError } from '../../../utils/report-error'; import type { HeaderBiddingSlot, SlotFlatMap } from '../prebid-types'; import { getHeaderBiddingAdSlots } from '../slot-config'; @@ -82,7 +82,9 @@ const requestBids = async ( }); }), ) - .catch(noop); + .catch(() => { + reportError(new Error('a9 header bidding error'), 'commercial'); + }); return requestQueue; }; diff --git a/src/lib/header-bidding/prebid/appnexus.spec.ts b/src/lib/header-bidding/prebid/appnexus.spec.ts index a8170cfbc..77ab299bb 100644 --- a/src/lib/header-bidding/prebid/appnexus.spec.ts +++ b/src/lib/header-bidding/prebid/appnexus.spec.ts @@ -41,10 +41,6 @@ jest.mock('experiments/ab', () => ({ isUserInVariant: jest.fn(), })); -jest.mock('lib/cookies', () => ({ - getCookie: jest.fn(), -})); - const resetConfig = () => { window.guardian.ophan = { pageViewId: 'pvid', diff --git a/src/lib/header-bidding/prebid/bid-config.spec.ts b/src/lib/header-bidding/prebid/bid-config.spec.ts index 5e9794445..e51b4ee98 100644 --- a/src/lib/header-bidding/prebid/bid-config.spec.ts +++ b/src/lib/header-bidding/prebid/bid-config.spec.ts @@ -8,7 +8,6 @@ import { isInUsa as isInUsa_, isInUsOrCa as isInUsOrCa_, } from '../../../utils/geo-utils'; -import config from '../../config'; import type { HeaderBiddingSize, PrebidBidder } from '../prebid-types'; import { containsBillboard as containsBillboard_, @@ -84,10 +83,6 @@ jest.mock('experiments/ab', () => ({ isUserInVariant: jest.fn(), })); -jest.mock('lib/cookies', () => ({ - getCookie: jest.fn(), -})); - const resetConfig = () => { window.guardian.ophan = { pageViewId: 'pvid', @@ -144,11 +139,14 @@ describe('indexExchangeBidders', () => { beforeEach(() => { resetConfig(); getBreakpointKey.mockReturnValue('D'); - config.set('page.pbIndexSites', [ - { bp: 'D', id: 123456 }, - { bp: 'M', id: 234567 }, - { bp: 'T', id: 345678 }, - ]); + // @ts-expect-error -- test + window.guardian.config.page = { + pbIndexSites: [ + { bp: 'D', id: 123456 }, + { bp: 'M', id: 234567 }, + { bp: 'T', id: 345678 }, + ], + }; }); afterEach(() => { @@ -206,11 +204,14 @@ describe('getIndexSiteId', () => { }); test('should find the correct ID for the breakpoint', () => { - config.set('page.pbIndexSites', [ - { bp: 'D', id: 123456 }, - { bp: 'M', id: 234567 }, - { bp: 'T', id: 345678 }, - ]); + // @ts-expect-error -- test + window.guardian.config.page = { + pbIndexSites: [ + { bp: 'D', id: 123456 }, + { bp: 'M', id: 234567 }, + { bp: 'T', id: 345678 }, + ], + }; const breakpoints = ['M', 'D', 'M', 'T', 'D']; const results = []; for (let i = 0; i < breakpoints.length; i += 1) { diff --git a/src/lib/header-bidding/slot-config.spec.ts b/src/lib/header-bidding/slot-config.spec.ts index cc06c8600..8135c2155 100644 --- a/src/lib/header-bidding/slot-config.spec.ts +++ b/src/lib/header-bidding/slot-config.spec.ts @@ -20,10 +20,6 @@ jest.mock('experiments/ab', () => ({ ), })); -jest.mock('lib/cookies', () => ({ - getCookie: jest.fn(), -})); - jest.mock('define/init-slot-ias', () => ({ initSlotIas: jest.fn(() => Promise.resolve()), })); diff --git a/src/utils/googletag-ad-size.ts b/src/utils/googletag-ad-size.ts deleted file mode 100644 index 9d782a4f0..000000000 --- a/src/utils/googletag-ad-size.ts +++ /dev/null @@ -1,6 +0,0 @@ -import type { AdSize } from '../core/ad-sizes'; - -export const toGoogleTagSize = (size: AdSize): googletag.SingleSize => { - // not using width and height here as to maintain compatibility with plain arrays - return size[0] === 0 && size[1] === 0 ? 'fluid' : [size[0], size[1]]; -}; diff --git a/src/utils/noop.ts b/src/utils/noop.ts deleted file mode 100644 index cc6b342f9..000000000 --- a/src/utils/noop.ts +++ /dev/null @@ -1,6 +0,0 @@ -// eslint-disable-next-line @typescript-eslint/no-unused-vars -- they’re discarded -const noop = (..._args: unknown[]): void => { - // do nothing -}; - -export { noop }; diff --git a/src/utils/support-utilities.spec.ts b/src/utils/support-utilities.spec.ts deleted file mode 100644 index bd9fade67..000000000 --- a/src/utils/support-utilities.spec.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { removeCookie, setCookie } from '@guardian/libs'; -import { _ } from './geolocation'; -import { addCountryGroupToSupportLink } from './support-utilities'; - -const { countryCookieName } = _; - -describe('addCountryGroupToSupportLink', () => { - beforeEach(() => { - removeCookie({ name: countryCookieName }); - }); - - test('adds country group to subscribe link', () => { - setCookie({ name: countryCookieName, value: 'GB' }); - expect( - addCountryGroupToSupportLink( - 'https://support.theguardian.com/subscribe', - ), - ).toEqual('https://support.theguardian.com/uk/subscribe'); - }); - - test('adds country group to contribute link', () => { - setCookie({ name: countryCookieName, value: 'FR' }); - expect( - addCountryGroupToSupportLink( - 'https://support.theguardian.com/contribute', - ), - ).toEqual('https://support.theguardian.com/eu/contribute'); - }); - - test('does not add country group to contribute link with country group already in it', () => { - setCookie({ name: countryCookieName, value: 'GB' }); - expect( - addCountryGroupToSupportLink( - 'https://support.theguardian.com/int/contribute', - ), - ).toEqual('https://support.theguardian.com/int/contribute'); - }); -}); diff --git a/src/utils/support-utilities.ts b/src/utils/support-utilities.ts deleted file mode 100644 index 6d40dede3..000000000 --- a/src/utils/support-utilities.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { - countryCodeToSupportInternationalisationId, - getCountryCode, -} from './geolocation'; - -const supportUrlRegex = /(support.theguardian.com)\/(contribute|subscribe)/; - -/** - * @param {string} rawUrl - * return the support url with country group inserted at the beginning of the path - */ -const addCountryGroupToSupportLink = (rawUrl: string): string => { - const countryCode = getCountryCode(); - const countryGroup = - countryCodeToSupportInternationalisationId(countryCode); - return rawUrl.replace( - supportUrlRegex, - (_, domain: string, path: string) => - `${domain}/${countryGroup.toLowerCase()}/${path}`, - ); -}; - -const supportSubscribeURL = (): string => - addCountryGroupToSupportLink('https://support.theguardian.com/subscribe'); -const supportSubscribeDigitalURL = (): string => - `${supportSubscribeURL()}/digital`; - -export { supportSubscribeDigitalURL, addCountryGroupToSupportLink }; diff --git a/src/utils/time-utils.spec.ts b/src/utils/time-utils.spec.ts deleted file mode 100644 index 9cf5d4b62..000000000 --- a/src/utils/time-utils.spec.ts +++ /dev/null @@ -1,40 +0,0 @@ -import MockDate from 'mockdate'; -import { dateDiffDays, isExpired } from './time-utils'; - -describe('calculating the difference between 2 dates', () => { - it('should return the correct duration', () => { - const oneDayMs = 1000 * 60 * 60 * 24; - - const now = Date.now(); - - expect(dateDiffDays(now, now + oneDayMs)).toBe(1); - - expect(dateDiffDays(now, now + oneDayMs - 1)).toBe(0); - - expect(dateDiffDays(now, now + 4 * oneDayMs - 1)).toBe(3); - }); -}); - -describe('Determining whether or not an AB test is expired', () => { - beforeAll(() => { - MockDate.set('Thu Jan 01 2021 12:00:00 GMT+0000 (Greenwich Mean Time)'); - }); - - afterAll(() => { - MockDate.reset(); - }); - - it('should return the correct result', () => { - const expiredYesterdayJustBeforeMidnight = - 'Wed Dec 31 2020 23:59:59 GMT+0000 (Greenwich Mean Time)'; - expect(isExpired(expiredYesterdayJustBeforeMidnight)).toBe(true); - - const expiringTodayJustAfterMidnight = - 'Thu Jan 01 2021 00:00:01 GMT+0000 (Greenwich Mean Time)'; - expect(isExpired(expiringTodayJustAfterMidnight)).toBe(false); - - const expiringTomorrow = - 'Fri Jan 02 2021 12:00:00 GMT+0000 (Greenwich Mean Time)'; - expect(isExpired(expiringTomorrow)).toBe(false); - }); -}); diff --git a/src/utils/time-utils.ts b/src/utils/time-utils.ts deleted file mode 100644 index c15740f92..000000000 --- a/src/utils/time-utils.ts +++ /dev/null @@ -1,16 +0,0 @@ -// from and to should be Epoch time in milliseconds -const dateDiffDays = (from: number, to: number): number => { - const oneDayMs = 1000 * 60 * 60 * 24; - const diffMs = to - from; - return Math.floor(diffMs / oneDayMs); -}; - -const isExpired = (testExpiry: string): boolean => { - // new Date(test.expiry) sets the expiry time to 00:00:00 - // Using SetHours allows a test to run until the END of the expiry day - const startOfToday = new Date().setHours(0, 0, 0, 0); - const expiryDate = new Date(testExpiry).getTime(); - return startOfToday > expiryDate; -}; - -export { dateDiffDays, isExpired };