diff --git a/dotcom-rendering/eslint.config.mjs b/dotcom-rendering/eslint.config.mjs index a5085a9a3b7..ec9e3fd6fcb 100644 --- a/dotcom-rendering/eslint.config.mjs +++ b/dotcom-rendering/eslint.config.mjs @@ -65,7 +65,11 @@ const rulesToEnforce = { export default defineConfig([ ...guardian.configs.recommended, - ...guardian.configs.jest, + ...guardian.configs.jest.map((config) => ({ + ...config, + // Don't apply Jest globals to Node test files + ignores: [...(config.ignores ?? []), '**/*.node.test.ts'], + })), ...guardian.configs.react, ...guardian.configs.storybook, // eslint-plugin-prettier/recommended should be the last item in the configuration array so that eslint-config-prettier has the opportunity to override other configs diff --git a/dotcom-rendering/jest.config.js b/dotcom-rendering/jest.config.js index 5257a547f9c..02fd5aad4d6 100644 --- a/dotcom-rendering/jest.config.js +++ b/dotcom-rendering/jest.config.js @@ -23,6 +23,7 @@ module.exports = { '^.+\\.(mjs|js|ts|tsx)$': ['@swc/jest', swcConfig], }, testMatch: ['**/*.test.+(ts|tsx|js)'], + testPathIgnorePatterns: ['\\.node\\.test\\.'], setupFilesAfterEnv: ['/scripts/jest/setup.ts'], moduleNameMapper: { '^svgs/(.*)$': '/__mocks__/svgMock.tsx', diff --git a/dotcom-rendering/package.json b/dotcom-rendering/package.json index 2cfea960681..3fd983c6d01 100644 --- a/dotcom-rendering/package.json +++ b/dotcom-rendering/package.json @@ -10,6 +10,7 @@ "lint:stats": "pnpm lint --format node_modules/eslint-stats/byError.js", "tsc": "tsc", "test": "IMAGE_DIGEST=sha256:12345 jest --maxWorkers=50%", + "test:node": "IMAGE_DIGEST=sha256:12345 node --import=tsx --test \"src/**/*.node.test.ts\"", "test:watch": "IMAGE_DIGEST=sha256:12345 jest --watch --maxWorkers=25%", "test:ci": "IMAGE_DIGEST=sha256:12345 jest --runInBand", "playwright:open": "playwright test --ui", diff --git a/dotcom-rendering/src/components/ElectionTrackers/electionComponent.test.ts b/dotcom-rendering/src/components/ElectionTrackers/electionComponent.node.test.ts similarity index 74% rename from dotcom-rendering/src/components/ElectionTrackers/electionComponent.test.ts rename to dotcom-rendering/src/components/ElectionTrackers/electionComponent.node.test.ts index e076e41aa63..1cc4beec484 100644 --- a/dotcom-rendering/src/components/ElectionTrackers/electionComponent.test.ts +++ b/dotcom-rendering/src/components/ElectionTrackers/electionComponent.node.test.ts @@ -1,3 +1,4 @@ +import { it } from 'node:test'; import { parse } from 'valibot'; import { euParliament } from '../../../fixtures/manual/electionTrackers/euParliament'; import { ukGeneralExitPoll } from '../../../fixtures/manual/electionTrackers/ukGeneralExitPoll'; @@ -7,26 +8,26 @@ import { usCongressEmpty } from '../../../fixtures/manual/electionTrackers/usCon import { usPresidential } from '../../../fixtures/manual/electionTrackers/usPresidential'; import { ElectionComponents } from './electionComponent'; -it('validates US Congress data', () => { +void it('validates US Congress data', () => { parse(ElectionComponents, usCongressEmpty); }); -it('validates UK General data', () => { +void it('validates UK General data', () => { parse(ElectionComponents, ukGeneralFinal); }); -it('validates UK General Exit Poll data', () => { +void it('validates UK General Exit Poll data', () => { parse(ElectionComponents, ukGeneralExitPoll); }); -it('validates UK Local data', () => { +void it('validates UK Local data', () => { parse(ElectionComponents, ukLocal); }); -it('validates US Presidential data', () => { +void it('validates US Presidential data', () => { parse(ElectionComponents, usPresidential); }); -it('validates EU Parliament data', () => { +void it('validates EU Parliament data', () => { parse(ElectionComponents, euParliament); }); diff --git a/dotcom-rendering/src/cricketMatch.test.ts b/dotcom-rendering/src/cricketMatch.node.test.ts similarity index 61% rename from dotcom-rendering/src/cricketMatch.test.ts rename to dotcom-rendering/src/cricketMatch.node.test.ts index 3391ddd6ff8..2c4b45ad2e7 100644 --- a/dotcom-rendering/src/cricketMatch.test.ts +++ b/dotcom-rendering/src/cricketMatch.node.test.ts @@ -1,14 +1,16 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; import { liveMatch, resultMatch } from '../fixtures/manual/cricketMatch'; import { parseCricketMatch } from './cricketMatch'; -describe('parseCricketMatchV2', () => { - it('parses a winner result cricket match correctly', () => { +void describe('parseCricketMatchV2', () => { + void it('parses a winner result cricket match correctly', () => { const result = parseCricketMatch(resultMatch).getOrThrow( 'Expected parsing cricket match to succeed', ); - expect(result.kind).toEqual('Result'); - expect(result.result).toEqual({ + assert.equal(result.kind, 'Result'); + assert.deepEqual(result.result, { type: 'home-win', description: 'England win by 115 runs', winner: { @@ -17,32 +19,35 @@ describe('parseCricketMatchV2', () => { margin: 115, }, }); - expect(result.matchDate).toEqual(new Date('2026-06-17T10:00:00.000Z')); + assert.deepEqual( + result.matchDate, + new Date('2026-06-17T10:00:00.000Z'), + ); }); - it('parses a cricket match in pre-match status', () => { + void it('parses a cricket match in pre-match status', () => { const result = parseCricketMatch({ ...liveMatch, result: 'pre-match', fullResult: undefined, }).getOrThrow('Expected parsing cricket match to succeed'); - expect(result.kind).toEqual('Fixture'); - expect(result.result).toEqual(undefined); + assert.equal(result.kind, 'Fixture'); + assert.equal(result.result, undefined); }); - it('parses a cricket match in in-play status', () => { + void it('parses a cricket match in in-play status', () => { const result = parseCricketMatch({ ...liveMatch, result: 'in-play', fullResult: undefined, }).getOrThrow('Expected parsing cricket match to succeed'); - expect(result.kind).toEqual('Live'); - expect(result.result).toEqual(undefined); + assert.equal(result.kind, 'Live'); + assert.equal(result.result, undefined); }); - it('parses an abandoned cricket match correctly', () => { + void it('parses an abandoned cricket match correctly', () => { const result = parseCricketMatch({ ...liveMatch, fullResult: { @@ -52,14 +57,13 @@ describe('parseCricketMatchV2', () => { }, }).getOrThrow('Expected parsing cricket match to succeed'); - expect(result.result).toEqual({ + assert.deepEqual(result.result, { type: 'abandoned', description: 'Match abandoned due to rain', - winner: undefined, }); }); - it('parses a cricket match with no winner', () => { + void it('parses a cricket match with no winner', () => { const result = parseCricketMatch({ ...liveMatch, fullResult: { @@ -69,10 +73,9 @@ describe('parseCricketMatchV2', () => { }, }).getOrThrow('Expected parsing cricket match to succeed'); - expect(result.result).toEqual({ + assert.deepEqual(result.result, { type: 'no-result', description: 'No result', - winner: undefined, }); }); }); diff --git a/dotcom-rendering/src/footballMatches.test.ts b/dotcom-rendering/src/footballMatches.node.test.ts similarity index 72% rename from dotcom-rendering/src/footballMatches.test.ts rename to dotcom-rendering/src/footballMatches.node.test.ts index 35faf608661..cbdee653b5c 100644 --- a/dotcom-rendering/src/footballMatches.test.ts +++ b/dotcom-rendering/src/footballMatches.node.test.ts @@ -1,3 +1,5 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; import { footballData } from '../fixtures/generated/football-live'; import { emptyMatches, @@ -25,25 +27,25 @@ const withMatches = ( })), })); -describe('footballMatches', () => { - it('should parse match fixtures correctly', () => { +void describe('footballMatches', () => { + void it('should parse match fixtures correctly', () => { const result = parse(footballData.matchesList).getOrThrow( 'Expected football match parsing to succeed', ); - expect(result.length).toBe(1); + assert.equal(result.length, 1); const day = result[0]; - expect(day?.dateISOString).toBe('2025-04-28T00:00:00.000Z'); - expect(day?.competitions.length).toBe(2); + assert.equal(day?.dateISOString, '2025-04-28T00:00:00.000Z'); + assert.equal(day?.competitions.length, 2); const competition = day?.competitions[0]; - expect(competition?.name).toBe('Serie A'); - expect(competition?.matches[0]?.kind).toBe('Fixture'); - expect(competition?.tag).toBe('football/serieafootball'); + assert.equal(competition?.name, 'Serie A'); + assert.equal(competition?.matches[0]?.kind, 'Fixture'); + assert.equal(competition?.tag, 'football/serieafootball'); }); - it('should return an error when football days have invalid dates', () => { + void it('should return an error when football days have invalid dates', () => { const invalidDate: FEMatchByDateAndCompetition[] = emptyMatches.map( (day) => ({ ...day, @@ -55,10 +57,10 @@ describe('footballMatches', () => { 'Expected football match parsing to fail', ); - expect(result.kind).toBe('FootballDayInvalidDate'); + assert.equal(result.kind, 'FootballDayInvalidDate'); }); - it('should return an error when football matches have an invalid date', () => { + void it('should return an error when football matches have an invalid date', () => { const invalidMatchResult: FEMatchByDateAndCompetition[] = withMatches([ matchFixture, { ...matchResult, date: '' }, @@ -85,24 +87,24 @@ describe('footballMatches', () => { 'Expected football match parsing to fail', ); - expect(resultOne.kind).toBe('FootballMatchInvalidDate'); - expect(resultTwo.kind).toBe('FootballMatchInvalidDate'); + assert.equal(resultOne.kind, 'FootballMatchInvalidDate'); + assert.equal(resultTwo.kind, 'FootballMatchInvalidDate'); if (resultThree.kind !== 'InvalidMatchDay') { throw new Error('Expected an invalid match day error'); } - expect(resultThree.errors[0]!.kind).toBe('FootballMatchInvalidDate'); + assert.equal(resultThree.errors[0]!.kind, 'FootballMatchInvalidDate'); }); - it('should return an error when it receives a live match', () => { + void it('should return an error when it receives a live match', () => { const result = parse(withMatches([liveMatch])).getErrorOrThrow( 'Expected football match parsing to fail', ); - expect(result.kind).toBe('UnexpectedLiveMatch'); + assert.equal(result.kind, 'UnexpectedLiveMatch'); }); - it('should return a clean team name', () => { + void it('should return a clean team name', () => { const matchesListWithTeamName = (teamName: string): FEResult => { return { ...matchResult, @@ -136,10 +138,10 @@ describe('footballMatches', () => { throw new Error('Expected Result'); } - expect(match.homeTeam.name).toBe(cleanName); + assert.equal(match.homeTeam.name, cleanName); } }); - it('should replace known live match status with our status', () => { + void it('should replace known live match status with our status', () => { const matchDay = parse( withMatches([matchDayLiveSecondHalf]), ).getOrThrow('Expected football live match parsing to succeed'); @@ -149,9 +151,9 @@ describe('footballMatches', () => { throw new Error('Expected live match'); } - expect(match.status).toBe('2nd'); + assert.equal(match.status, '2nd'); }); - it('should replace unknown live match status with first two characters', () => { + void it('should replace unknown live match status with first two characters', () => { const matchDayLiveUnknownStatus = { ...matchDayLiveSecondHalf, matchStatus: 'Something odd', @@ -166,6 +168,6 @@ describe('footballMatches', () => { throw new Error('Expected live match'); } - expect(match.status).toBe('So'); + assert.equal(match.status, 'So'); }); }); diff --git a/dotcom-rendering/src/lib/acquisitions.test.ts b/dotcom-rendering/src/lib/acquisitions.node.test.ts similarity index 82% rename from dotcom-rendering/src/lib/acquisitions.test.ts rename to dotcom-rendering/src/lib/acquisitions.node.test.ts index c48945d3eda..f178aae651c 100644 --- a/dotcom-rendering/src/lib/acquisitions.test.ts +++ b/dotcom-rendering/src/lib/acquisitions.node.test.ts @@ -1,7 +1,9 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; import { addTrackingCodesToUrl } from './acquisitions'; -describe('acquisitions', () => { - it('should addTrackingCodesToUrl', () => { +void describe('acquisitions', () => { + void it('should addTrackingCodesToUrl', () => { const result = addTrackingCodesToUrl({ base: `https://support.theguardian.com/contribute`, componentType: 'ACQUISITIONS_HEADER', @@ -15,7 +17,8 @@ describe('acquisitions', () => { referrerUrl: 'https://theguardian.com/uk', }); - expect(result).toEqual( + assert.equal( + result, 'https://support.theguardian.com/contribute?REFPVID=abcdefg&INTCMP=header_support&acquisitionData=%7B%22source%22%3A%22GUARDIAN_WEB%22%2C%22componentId%22%3A%22header_support%22%2C%22componentType%22%3A%22ACQUISITIONS_HEADER%22%2C%22campaignCode%22%3A%22header_support%22%2C%22abTest%22%3A%7B%22name%22%3A%22testName%22%2C%22variant%22%3A%22variantName%22%7D%2C%22referrerPageviewId%22%3A%22abcdefg%22%2C%22referrerUrl%22%3A%22https%3A%2F%2Ftheguardian.com%2Fuk%22%7D', ); }); diff --git a/dotcom-rendering/src/lib/ad-targeting.test.ts b/dotcom-rendering/src/lib/ad-targeting.node.test.ts similarity index 81% rename from dotcom-rendering/src/lib/ad-targeting.test.ts rename to dotcom-rendering/src/lib/ad-targeting.node.test.ts index 88da2cf0099..d4f6204fe42 100644 --- a/dotcom-rendering/src/lib/ad-targeting.test.ts +++ b/dotcom-rendering/src/lib/ad-targeting.node.test.ts @@ -1,3 +1,5 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; import { buildAdTargeting } from './ad-targeting'; const sharedAdTargeting = { @@ -12,7 +14,7 @@ const sharedAdTargeting = { url: '/money/2017/mar/10/ministers-to-criminalise-use-of-ticket-tout-harvesting-software', }; -describe('buildAdTargeting', () => { +void describe('buildAdTargeting', () => { const expectedAdTargeting = { adUnit: '/59666047/theguardian.com/money/article/ng', customParams: { @@ -38,8 +40,8 @@ describe('buildAdTargeting', () => { }, }; - it('builds adTargeting correctly', () => { - expect( + void it('builds adTargeting correctly', () => { + assert.deepEqual( buildAdTargeting({ isAdFreeUser: false, isSensitive: false, @@ -48,6 +50,7 @@ describe('buildAdTargeting', () => { sharedAdTargeting, adUnit: '/59666047/theguardian.com/money/article/ng', }), - ).toEqual(expectedAdTargeting); + expectedAdTargeting, + ); }); }); diff --git a/dotcom-rendering/src/lib/affiliateLinksUtils.test.ts b/dotcom-rendering/src/lib/affiliateLinksUtils.node.test.ts similarity index 66% rename from dotcom-rendering/src/lib/affiliateLinksUtils.test.ts rename to dotcom-rendering/src/lib/affiliateLinksUtils.node.test.ts index afbfd29bb00..25a786c1fb7 100644 --- a/dotcom-rendering/src/lib/affiliateLinksUtils.test.ts +++ b/dotcom-rendering/src/lib/affiliateLinksUtils.node.test.ts @@ -1,29 +1,31 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; import { buildMergedAbTestString, buildXcustParamForAffiliateLink, extractAbTestParticipationFromUrl, } from './affiliateLinksUtils'; -describe('extractAbTestParticipationFromUrl', () => { - it('extracts AB test participations from xcust', () => { +void describe('extractAbTestParticipationFromUrl', () => { + void it('extracts AB test participations from xcust', () => { const url = 'https://go.skimresources.com/?id=114047X1572903&url=https%3A%2F%2Fwww.argos.co.uk%2Fproduct%2F8112969&sref=https://www.theguardian.com/thefilter/2024/nov/21/best-coffee-machines&xcust=referrer%7Cwww.theguardian.com%7CaccountId%7C114047X1572903%7CabTestParticipations%7Cthefilter-at-a-glance-redesign-v2%3Acarousel%7CcomponentId%7Ccarousel-card'; - expect(extractAbTestParticipationFromUrl(url)).toEqual({ + assert.deepEqual(extractAbTestParticipationFromUrl(url), { 'thefilter-at-a-glance-redesign-v2': 'carousel', }); }); - it('returns empty object when xcust has no AB test section', () => { + void it('returns empty object when xcust has no AB test section', () => { const url = 'https://go.skimresources.com/?id=114047X1572903&url=https%3A%2F%2Fwww.argos.co.uk%2Fproduct%2F8112969&xcust=referrer%7Cwww.theguardian.com%7CaccountId%7C114047X1572903%7CcomponentId%7Ccarousel-card'; - expect(extractAbTestParticipationFromUrl(url)).toEqual({}); + assert.deepEqual(extractAbTestParticipationFromUrl(url), {}); }); }); -describe('buildXcustValueForAffiliateLink', () => { - it('returns xcust value for skimlinks URLs', () => { +void describe('buildXcustValueForAffiliateLink', () => { + void it('returns xcust value for skimlinks URLs', () => { const xcustResult = buildXcustParamForAffiliateLink({ url: new URL( 'https://go.skimresources.com/?id=1234X9876&url=https%3A%2F%2Fwww.theguardian.com%2Fuk', @@ -34,12 +36,13 @@ describe('buildXcustValueForAffiliateLink', () => { xcustComponentId: null, }); - expect(xcustResult).toBe( + assert.equal( + xcustResult, 'referrer|www.theguardian.com|accountId|1234X9876', ); }); - it('includes optional xcust values when provided', () => { + void it('includes optional xcust values when provided', () => { const xcustResult = buildXcustParamForAffiliateLink({ url: new URL( 'https://go.skimresources.com/?id=1111&url=https%3A%2F%2Fwww.theguardian.com%2Fus-news', @@ -50,12 +53,13 @@ describe('buildXcustValueForAffiliateLink', () => { xcustComponentId: 'related-content', }); - expect(xcustResult).toBe( + assert.equal( + xcustResult, 'referrer|www.theguardian.com|accountId|1111|abTestParticipations|abTest1:variantA|utm_medium|cpc|utm_campaign|summer|componentId|related-content', ); }); - it('merges existing and incoming AB test participations', () => { + void it('merges existing and incoming AB test participations', () => { const xcustResult = buildXcustParamForAffiliateLink({ url: new URL( 'https://go.skimresources.com/?id=1111&url=https%3A%2F%2Fwww.theguardian.com%2Fus-news&xcust=referrer%7Cwww.theguardian.com%7CaccountId%7C1111%7CabTestParticipations%7CexistingTest%3Acontrol%2CabTest1%3AoldVariant', @@ -66,14 +70,14 @@ describe('buildXcustValueForAffiliateLink', () => { xcustComponentId: null, }); - expect(xcustResult).toContain('|abTestParticipations|'); - expect(xcustResult).toContain('existingTest:control'); - expect(xcustResult).toContain('newTest:variantB'); - expect(xcustResult).toContain('abTest1:oldVariant'); - expect(xcustResult).not.toContain('abTest1:variantA'); + assert(xcustResult.includes('|abTestParticipations|')); + assert(xcustResult.includes('existingTest:control')); + assert(xcustResult.includes('newTest:variantB')); + assert(xcustResult.includes('abTest1:oldVariant')); + assert(!xcustResult.includes('abTest1:variantA')); }); - it('preserves existing AB participations when url already has xcust', () => { + void it('preserves existing AB participations when url already has xcust', () => { const xcustResult = buildXcustParamForAffiliateLink({ url: new URL( 'https://go.skimresources.com/?id=1111&url=https%3A%2F%2Fwww.theguardian.com%2Fus-news&xcust=referrer%7Cold.example%7CaccountId%7C1111%7CabTestParticipations%7ColdTest%3AoldVariant', @@ -84,20 +88,20 @@ describe('buildXcustValueForAffiliateLink', () => { xcustComponentId: null, }); - expect(xcustResult).toContain( - 'referrer|www.theguardian.com|accountId|1111', + assert( + xcustResult.includes('referrer|www.theguardian.com|accountId|1111'), ); - expect(xcustResult).toContain('newTest:newVariant'); - expect(xcustResult).toContain('oldTest:oldVariant'); + assert(xcustResult.includes('newTest:newVariant')); + assert(xcustResult.includes('oldTest:oldVariant')); }); }); -describe('buildMergedAbTestString', () => { - it('returns incoming AB test string when URL has no existing participations', () => { +void describe('buildMergedAbTestString', () => { + void it('returns incoming AB test string when URL has no existing participations', () => { const url = 'https://go.skimresources.com/?id=1111&url=https%3A%2F%2Fwww.theguardian.com%2Fus-news'; - expect( + assert.equal( buildMergedAbTestString({ url, abTestParticipations: { @@ -105,20 +109,22 @@ describe('buildMergedAbTestString', () => { abTest2: 'variantB', }, }), - ).toBe('abTest1:variantA,abTest2:variantB'); + 'abTest1:variantA,abTest2:variantB', + ); }); - it('keeps existing URL values when keys collide', () => { + void it('keeps existing URL values when keys collide', () => { const url = 'https://go.skimresources.com/?id=1111&url=https%3A%2F%2Fwww.theguardian.com%2Fus-news&xcust=referrer%7Cwww.theguardian.com%7CaccountId%7C1111%7CabTestParticipations%7ColdTest%3AoldVariant'; - expect( + assert.equal( buildMergedAbTestString({ url, abTestParticipations: { newTest: 'newVariant', }, }), - ).toBe('newTest:newVariant,oldTest:oldVariant'); + 'newTest:newVariant,oldTest:oldVariant', + ); }); }); diff --git a/dotcom-rendering/src/lib/age-warning.node.test.ts b/dotcom-rendering/src/lib/age-warning.node.test.ts new file mode 100644 index 00000000000..e3d299e2bac --- /dev/null +++ b/dotcom-rendering/src/lib/age-warning.node.test.ts @@ -0,0 +1,57 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; +import type { TagType } from '../types/tag'; +import { getAgeWarning } from './age-warning'; + +void describe('getAgeWarning', () => { + const infoTag: TagType = { + id: 'info/info', + type: 'info', + title: 'info', + }; + const studentsTag: TagType = { + id: 'education/students', + type: 'topic', + title: 'info', + }; + + const today = new Date(); + const oneMonthOld = new Date( + new Date().setDate(today.getDate() - 31), + ).toDateString(); + const twoMonthsOld = new Date( + new Date().setDate(today.getDate() - 65), + ).toDateString(); + const oneYearOld = new Date( + new Date().setDate(today.getDate() - 370), + ).toDateString(); + const twoYearsOld = new Date( + new Date().setDate(today.getDate() - 750), + ).toDateString(); + + void it('shows age warning when publication date is more than 1 month ago', () => { + assert.equal(getAgeWarning([studentsTag], oneMonthOld), '1 month old'); + }); + + void it('shows age warning when publication date is more than 2 months ago', () => { + assert.equal( + getAgeWarning([studentsTag], twoMonthsOld), + '2 months old', + ); + }); + + void it('shows age warning when publication date is more than 1 year ago', () => { + assert.equal(getAgeWarning([studentsTag], oneYearOld), '1 year old'); + }); + + void it('shows age warning when publication date is more than 2 years ago', () => { + assert.equal(getAgeWarning([studentsTag], twoYearsOld), '2 years old'); + }); + + void it('is undefined if one of the tags is excluded from age warning', () => { + assert.equal( + getAgeWarning([studentsTag, infoTag], oneMonthOld), + undefined, + ); + }); +}); diff --git a/dotcom-rendering/src/lib/age-warning.test.ts b/dotcom-rendering/src/lib/age-warning.test.ts deleted file mode 100644 index 277532da78b..00000000000 --- a/dotcom-rendering/src/lib/age-warning.test.ts +++ /dev/null @@ -1,54 +0,0 @@ -import type { TagType } from '../types/tag'; -import { getAgeWarning } from './age-warning'; - -describe('getAgeWarning', () => { - const infoTag: TagType = { - id: 'info/info', - type: 'info', - title: 'info', - }; - const studentsTag: TagType = { - id: 'education/students', - type: 'topic', - title: 'info', - }; - - const today = new Date(); - const oneMonthOld = new Date( - new Date().setDate(today.getDate() - 31), - ).toDateString(); - const twoMonthsOld = new Date( - new Date().setDate(today.getDate() - 65), - ).toDateString(); - const oneYearOld = new Date( - new Date().setDate(today.getDate() - 370), - ).toDateString(); - const twoYearsOld = new Date( - new Date().setDate(today.getDate() - 750), - ).toDateString(); - - it('shows age warning when publication date is more than 1 month ago', () => { - const testTags = [studentsTag]; - expect(getAgeWarning(testTags, oneMonthOld)).toEqual('1 month old'); - }); - - it('shows age warning when publication date is more than 2 months ago', () => { - const testTags = [studentsTag]; - expect(getAgeWarning(testTags, twoMonthsOld)).toEqual('2 months old'); - }); - - it('shows age warning when publication date is more than 1 year ago', () => { - const testTags = [studentsTag]; - expect(getAgeWarning(testTags, oneYearOld)).toEqual('1 year old'); - }); - - it('shows age warning when publication date is more than 2 years ago', () => { - const testTags = [studentsTag]; - expect(getAgeWarning(testTags, twoYearsOld)).toEqual('2 years old'); - }); - - it('is undefined if one of the tags is excluded from age warning', () => { - const testTags = [studentsTag, infoTag]; - expect(getAgeWarning(testTags, oneMonthOld)).toEqual(undefined); - }); -}); diff --git a/dotcom-rendering/src/lib/alternate-lang-links.test.ts b/dotcom-rendering/src/lib/alternate-lang-links.node.test.ts similarity index 61% rename from dotcom-rendering/src/lib/alternate-lang-links.test.ts rename to dotcom-rendering/src/lib/alternate-lang-links.node.test.ts index e23db28bb89..69371d1311d 100644 --- a/dotcom-rendering/src/lib/alternate-lang-links.test.ts +++ b/dotcom-rendering/src/lib/alternate-lang-links.node.test.ts @@ -1,3 +1,5 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; import { generateAlternateLangLinks } from './alternate-lang-links'; import { editionalisedPages, editionList } from './edition'; @@ -11,31 +13,24 @@ const everyEditionWithNoLangLocale = editionList.filter( const everyEditionWithEditionalisedPages = editionList .filter((edition) => edition.hasEditionalisedPages) - .map((edition) => + .flatMap((edition) => editionalisedPages.map((page) => `${edition.pageId}/${page}`), - ) - .flat(); + ); -// This is an invalid combination e.g. europe/travel const everyEditionWithNoEditionalisedPages = editionList .filter((edition) => !edition.hasEditionalisedPages) - .map((edition) => + .flatMap((edition) => editionalisedPages.map((page) => `${edition.pageId}/${page}`), - ) - .flat(); + ); -describe('alternate lang links', () => { - it('generate hreflang links for network fronts with a lang locale', () => { - const langLinksForEditionsWithLangLocale = - everyEditionWithLangLocale.map((edition) => { - return generateAlternateLangLinks( - 'https://www.theguardian.com', - edition.pageId, - ); - }); - for (const langLinks of langLinksForEditionsWithLangLocale) { - expect(langLinks.length).toBe(5); - expect(langLinks).toEqual([ +void describe('alternate lang links', () => { + void it('generate hreflang links for network fronts with a lang locale', () => { + for (const edition of everyEditionWithLangLocale) { + const langLinks = generateAlternateLangLinks( + 'https://www.theguardian.com', + edition.pageId, + ); + assert.deepEqual(langLinks, [ '', '', '', @@ -44,43 +39,47 @@ describe('alternate lang links', () => { ]); } }); - it('do NOT generate hreflang links for network fronts with NO lang locale', () => { - const langLinksForEditionsWithNoLangLocale = - everyEditionWithNoLangLocale.map((edition) => { - return generateAlternateLangLinks( + + void it('do NOT generate hreflang links for network fronts with NO lang locale', () => { + for (const edition of everyEditionWithNoLangLocale) { + assert.deepEqual( + generateAlternateLangLinks( 'https://www.theguardian.com', edition.pageId, - ); - }); - for (const langLinks of langLinksForEditionsWithNoLangLocale) { - expect(langLinks.length).toBe(0); + ), + [], + ); } }); - it('generate hreflang links for editionalised pages', () => { + + void it('generate hreflang links for editionalised pages', () => { for (const pageId of everyEditionWithEditionalisedPages) { const langLinks = generateAlternateLangLinks( 'https://www.theguardian.com', pageId, ); const pageIdSuffix = pageId.split('/')[1] ?? ''; - expect(langLinks.length).toBe(3); - expect(langLinks).toEqual([ + assert.deepEqual(langLinks, [ ``, ``, ``, ]); } }); - it('do NOT generate hreflang links for editions with NO editionalised pages', () => { + + void it('do NOT generate hreflang links for editions with NO editionalised pages', () => { for (const pageId of everyEditionWithNoEditionalisedPages) { - const langLinks = generateAlternateLangLinks( - 'https://www.theguardian.com', - pageId, + assert.deepEqual( + generateAlternateLangLinks( + 'https://www.theguardian.com', + pageId, + ), + [], ); - expect(langLinks.length).toBe(0); } }); - it('do NOT generate hreflang links for NON editionalised pages', () => { + + void it('do NOT generate hreflang links for NON editionalised pages', () => { const pageIdsNotEditionalisedPages = [ 'uk/something', 'us/something', @@ -89,11 +88,13 @@ describe('alternate lang links', () => { 'uk/business/something', ]; for (const pageId of pageIdsNotEditionalisedPages) { - const langLinks = generateAlternateLangLinks( - 'https://www.theguardian.com', - pageId, + assert.deepEqual( + generateAlternateLangLinks( + 'https://www.theguardian.com', + pageId, + ), + [], ); - expect(langLinks.length).toBe(0); } }); }); diff --git a/dotcom-rendering/src/lib/articleMeta.node.test.ts b/dotcom-rendering/src/lib/articleMeta.node.test.ts new file mode 100644 index 00000000000..1d7a38dfbfe --- /dev/null +++ b/dotcom-rendering/src/lib/articleMeta.node.test.ts @@ -0,0 +1,61 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; +import { ArticleDesign, ArticleDisplay, Pillar } from './articleFormat'; +import { shouldShowContributor } from './articleMeta'; + +void describe('shouldShowContributor', () => { + const standardFormat = { + theme: Pillar.News, + design: ArticleDesign.Standard, + display: ArticleDisplay.Standard, + }; + const standardComment = { + ...standardFormat, + design: ArticleDesign.Comment, + }; + const showcaseStandard = { + ...standardFormat, + display: ArticleDisplay.Showcase, + }; + const showcaseComment = { + ...showcaseStandard, + design: ArticleDesign.Comment, + }; + const numberedList = { + ...standardFormat, + display: ArticleDisplay.NumberedList, + }; + + const immersive = { + ...standardFormat, + display: ArticleDisplay.Immersive, + }; + + void it('should return true if Standard display and Standard design', () => { + assert.equal(shouldShowContributor(standardFormat), true); + }); + + void it('should return false if Standard display and Comment design', () => { + assert.equal(shouldShowContributor(standardComment), false); + }); + + void it('should return true if Showcase display and Standard design', () => { + assert.equal(shouldShowContributor(showcaseStandard), true); + }); + + void it('should return false if Showcase display and Comment design', () => { + assert.equal(shouldShowContributor(showcaseComment), false); + }); + + void it('should return true if Numbered list display', () => { + assert.equal(shouldShowContributor(numberedList), true); + }); + + void it('should return false if Immersive display', () => { + assert.equal(shouldShowContributor(immersive), false); + }); + + void it('should return true if Immersive display uses the new grid', () => { + assert.equal(shouldShowContributor(immersive, true), true); + }); +}); diff --git a/dotcom-rendering/src/lib/articleMeta.test.ts b/dotcom-rendering/src/lib/articleMeta.test.ts deleted file mode 100644 index 2955e6918c7..00000000000 --- a/dotcom-rendering/src/lib/articleMeta.test.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { ArticleDesign, ArticleDisplay, Pillar } from './articleFormat'; -import { shouldShowContributor } from './articleMeta'; - -describe('shouldShowContributor', () => { - const standardFormat = { - theme: Pillar.News, - design: ArticleDesign.Standard, - display: ArticleDisplay.Standard, - }; - const standardComment = { - ...standardFormat, - design: ArticleDesign.Comment, - }; - const showcaseStandard = { - ...standardFormat, - display: ArticleDisplay.Showcase, - }; - const showcaseComment = { - ...showcaseStandard, - design: ArticleDesign.Comment, - }; - const numberedList = { - ...standardFormat, - display: ArticleDisplay.NumberedList, - }; - - const immersive = { - ...standardFormat, - display: ArticleDisplay.Immersive, - }; - - it('should return true if Standard display and Standard design', () => { - expect(shouldShowContributor(standardFormat)).toBe(true); - }); - - it('should return false if Standard display and Comment design', () => { - expect(shouldShowContributor(standardComment)).toBe(false); - }); - - it('should return true if Showcase display and Standard design', () => { - expect(shouldShowContributor(showcaseStandard)).toBe(true); - }); - - it('should return false if Showcase display and Comment design', () => { - expect(shouldShowContributor(showcaseComment)).toBe(false); - }); - - it('should return true if Numbered list display', () => { - expect(shouldShowContributor(numberedList)).toBe(true); - }); - - it('should return false if Immersive display', () => { - expect(shouldShowContributor(immersive)).toBe(false); - }); - - it('should return true if Immersive display uses the new grid', () => { - expect(shouldShowContributor(immersive, true)).toBe(true); - }); -}); diff --git a/dotcom-rendering/src/lib/branding.test.ts b/dotcom-rendering/src/lib/branding.node.test.ts similarity index 84% rename from dotcom-rendering/src/lib/branding.test.ts rename to dotcom-rendering/src/lib/branding.node.test.ts index 498c28b6f89..5b229e3c9d7 100644 --- a/dotcom-rendering/src/lib/branding.test.ts +++ b/dotcom-rendering/src/lib/branding.node.test.ts @@ -1,11 +1,13 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; import type { Branding } from '../types/branding'; import { decideCollectionBranding, decideTagPageBranding } from './branding'; // For the purpose of these tests we don't care about the contents of the logo objects const logo = {} as Branding['logo']; -describe('decideCollectionBranding', () => { - it('picks branding from a card by their edition', () => { +void describe('decideCollectionBranding', () => { + void it('picks branding from a card by their edition', () => { const cards = [ { properties: { @@ -39,7 +41,7 @@ describe('decideCollectionBranding', () => { editionId: 'UK', isContainerBranding: false, }); - expect(ukBranding).toMatchObject({ + assert.deepEqual(ukBranding, { kind: 'paid-content', isFrontBranding: false, branding: { @@ -58,7 +60,7 @@ describe('decideCollectionBranding', () => { editionId: 'US', isContainerBranding: false, }); - expect(usBranding).toMatchObject({ + assert.deepEqual(usBranding, { kind: 'sponsored', isFrontBranding: false, branding: { @@ -72,7 +74,7 @@ describe('decideCollectionBranding', () => { }); }); - it('is paid content derived from multiple cards', () => { + void it('is paid content derived from multiple cards', () => { const cardBranding = { brandingType: { name: 'paid-content' as const }, sponsorName: 'foo', @@ -117,14 +119,16 @@ describe('decideCollectionBranding', () => { editionId: 'UK', isContainerBranding: false, }); - expect(collectionBranding).toMatchObject({ + assert.deepEqual(collectionBranding, { kind: 'paid-content', isFrontBranding: false, branding: cardBranding, + isContainerBranding: false, + hasMultipleBranding: false, }); }); - it('undefined when not all cards have branding', () => { + void it('undefined when not all cards have branding', () => { // The branding we'll apply to each card in this test const collectionBranding = decideCollectionBranding({ frontBranding: undefined, @@ -169,10 +173,10 @@ describe('decideCollectionBranding', () => { editionId: 'UK', isContainerBranding: false, }); - expect(collectionBranding).toBeUndefined(); + assert.equal(collectionBranding, undefined); }); - it('is undefined when no cards have branding', () => { + void it('is undefined when no cards have branding', () => { const collectionBranding = decideCollectionBranding({ frontBranding: undefined, couldDisplayFrontBranding: false, @@ -196,10 +200,10 @@ describe('decideCollectionBranding', () => { editionId: 'UK', isContainerBranding: false, }); - expect(collectionBranding).toBeUndefined(); + assert.equal(collectionBranding, undefined); }); - it('is undefined when cards have different branding types', () => { + void it('is undefined when cards have different branding types', () => { const collectionBranding = decideCollectionBranding({ frontBranding: undefined, couldDisplayFrontBranding: false, @@ -253,10 +257,10 @@ describe('decideCollectionBranding', () => { editionId: 'UK', isContainerBranding: false, }); - expect(collectionBranding).toBeUndefined(); + assert.equal(collectionBranding, undefined); }); - it('is sponsored branding when all of the branding types are sponsored and the names match', () => { + void it('is sponsored branding when all of the branding types are sponsored and the names match', () => { const cardBranding = { brandingType: { name: 'sponsored' as const }, sponsorName: 'foo', @@ -301,7 +305,7 @@ describe('decideCollectionBranding', () => { editionId: 'UK', isContainerBranding: false, }); - expect(collectionBranding).toStrictEqual({ + assert.deepEqual(collectionBranding, { kind: 'sponsored', isFrontBranding: false, branding: cardBranding, @@ -310,7 +314,7 @@ describe('decideCollectionBranding', () => { }); }); - it('is undefined when branding cards are sponsored and have different sponsor names', () => { + void it('is undefined when branding cards are sponsored and have different sponsor names', () => { // The branding we'll apply to each card in this test const collectionBranding = decideCollectionBranding({ frontBranding: undefined, @@ -365,10 +369,10 @@ describe('decideCollectionBranding', () => { editionId: 'UK', isContainerBranding: false, }); - expect(collectionBranding).toBeUndefined(); + assert.equal(collectionBranding, undefined); }); - it('is paid content branding when all of the branding types are paid-content and the names match', () => { + void it('is paid content branding when all of the branding types are paid-content and the names match', () => { const collectionBranding = decideCollectionBranding({ frontBranding: undefined, couldDisplayFrontBranding: false, @@ -407,7 +411,7 @@ describe('decideCollectionBranding', () => { editionId: 'UK', isContainerBranding: false, }); - expect(collectionBranding).toStrictEqual({ + assert.deepEqual(collectionBranding, { kind: 'paid-content', isFrontBranding: false, branding: { @@ -421,7 +425,7 @@ describe('decideCollectionBranding', () => { }); }); - it('is paid content multiple branding when branding cards are paid-content and have different sponsor names', () => { + void it('is paid content multiple branding when branding cards are paid-content and have different sponsor names', () => { const collectionBranding = decideCollectionBranding({ frontBranding: undefined, couldDisplayFrontBranding: false, @@ -460,7 +464,7 @@ describe('decideCollectionBranding', () => { editionId: 'UK', isContainerBranding: false, }); - expect(collectionBranding).toStrictEqual({ + assert.deepEqual(collectionBranding, { kind: 'paid-content', isFrontBranding: false, branding: { @@ -474,7 +478,7 @@ describe('decideCollectionBranding', () => { }); }); - it('is front branding when present and possible to display', () => { + void it('is front branding when present and possible to display', () => { const collectionBranding = decideCollectionBranding({ frontBranding: { brandingType: { name: 'paid-content' }, @@ -487,7 +491,7 @@ describe('decideCollectionBranding', () => { editionId: 'UK', isContainerBranding: false, }); - expect(collectionBranding).toStrictEqual({ + assert.deepEqual(collectionBranding, { kind: 'paid-content', isFrontBranding: true, branding: { @@ -501,7 +505,7 @@ describe('decideCollectionBranding', () => { }); }); - it('is undefined when there is front branding (and no card branding) that is not eligible for display on this collection', () => { + void it('is undefined when there is front branding (and no card branding) that is not eligible for display on this collection', () => { const collectionBranding = decideCollectionBranding({ frontBranding: { brandingType: { name: 'paid-content' }, @@ -514,10 +518,10 @@ describe('decideCollectionBranding', () => { editionId: 'UK', isContainerBranding: false, }); - expect(collectionBranding).toBeUndefined(); + assert.equal(collectionBranding, undefined); }); - it('when cards are present', () => { + void it('when cards are present', () => { const cardBranding = { brandingType: { name: 'paid-content' as const }, sponsorName: 'foo', @@ -567,7 +571,7 @@ describe('decideCollectionBranding', () => { editionId: 'UK', isContainerBranding: false, }); - expect(collectionBranding).toStrictEqual({ + assert.deepEqual(collectionBranding, { kind: 'paid-content', isFrontBranding: false, branding: cardBranding, @@ -576,7 +580,7 @@ describe('decideCollectionBranding', () => { }); }); - it('is undefined when front branding matches card branding, but we are not displaying front branding', () => { + void it('is undefined when front branding matches card branding, but we are not displaying front branding', () => { const cardBranding = { brandingType: { name: 'paid-content' as const }, sponsorName: 'foo', @@ -626,12 +630,12 @@ describe('decideCollectionBranding', () => { editionId: 'UK', isContainerBranding: false, }); - expect(collectionBranding).toBeUndefined(); + assert.equal(collectionBranding, undefined); }); }); -describe('decideTagPageBranding', () => { - it('picks branding from a tag page by their edition', () => { +void describe('decideTagPageBranding', () => { + void it('picks branding from a tag page by their edition', () => { const branding = { brandingType: { name: 'sponsored' }, sponsorName: 'Guardian.org', @@ -643,19 +647,20 @@ describe('decideTagPageBranding', () => { branding, }); - expect(tagPageBranding).toMatchObject({ + assert.deepEqual(tagPageBranding, { kind: 'sponsored', isFrontBranding: true, branding: { brandingType: { name: 'sponsored' }, sponsorName: 'Guardian.org', aboutThisLink: '', + logo, }, isContainerBranding: false, hasMultipleBranding: false, }); }); - it('is undefined when branding does not have a brandingType name present', () => { + void it('is undefined when branding does not have a brandingType name present', () => { const branding = { sponsorName: 'Guardian.org', aboutThisLink: '', @@ -665,6 +670,6 @@ describe('decideTagPageBranding', () => { const tagPageBranding = decideTagPageBranding({ branding, }); - expect(tagPageBranding).toBeUndefined(); + assert.equal(tagPageBranding, undefined); }); }); diff --git a/dotcom-rendering/src/lib/byline.test.ts b/dotcom-rendering/src/lib/byline.node.test.ts similarity index 77% rename from dotcom-rendering/src/lib/byline.test.ts rename to dotcom-rendering/src/lib/byline.node.test.ts index 0ba313e554d..e6751504d2c 100644 --- a/dotcom-rendering/src/lib/byline.test.ts +++ b/dotcom-rendering/src/lib/byline.node.test.ts @@ -1,7 +1,9 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; import { getBylineComponentsFromTokens, getSoleContributor } from './byline'; -describe('Byline utilities', () => { - it('should link a single tag by linking name tokens with Contributor tag titles', () => { +void describe('Byline utilities', () => { + void it('should link a single tag by linking name tokens with Contributor tag titles', () => { const bylineTokens = ['Eva Smith', 'and friends']; const tags = [ { @@ -16,13 +18,13 @@ describe('Byline utilities', () => { tags, ); - expect(bylineComponents).toEqual([ + assert.deepEqual(bylineComponents, [ { tag: tags[0], token: 'Eva Smith' }, 'and friends', ]); }); - it('should link multiple tags by linking name tokens with Contributor tag titles', () => { + void it('should link multiple tags by linking name tokens with Contributor tag titles', () => { const bylineTokens = ['Eva Smith', ' and ', 'Duncan Campbell']; const tags = [ { @@ -41,14 +43,14 @@ describe('Byline utilities', () => { tags, ); - expect(bylineComponents).toEqual([ + assert.deepEqual(bylineComponents, [ { tag: tags[0], token: 'Eva Smith' }, ' and ', { tag: tags[1], token: 'Duncan Campbell' }, ]); }); - it('should not reuse a contributor tag, to successfully disambiguate identical names', () => { + void it('should not reuse a contributor tag, to successfully disambiguate identical names', () => { const bylineTokens = ['Duncan Campbell', ' and ', 'Duncan Campbell']; const tags = [ { @@ -68,16 +70,16 @@ describe('Byline utilities', () => { tags, ); - expect(bylineComponents).toEqual([ + assert.deepEqual(bylineComponents, [ { tag: tags[0], token: 'Duncan Campbell' }, ' and ', { tag: tags[1], token: 'Duncan Campbell' }, ]); }); - describe('getSoleContributor', () => { - describe('returns a contributor', () => { - it('Sebastian Köhn, as told to Wilfried Chan', () => { + void describe('getSoleContributor', () => { + void describe('returns a contributor', () => { + void it('Sebastian Köhn, as told to Wilfried Chan', () => { // https://www.theguardian.com/world/2022/jul/23/i-literally-screamed-out-loud-in-pain-my-two-weeks-of-monkeypox-hell const soleContributor = getSoleContributor( @@ -91,10 +93,10 @@ describe('Byline utilities', () => { 'Sebastian Köhn, as told to Wilfred Chan', ); - expect(soleContributor?.title).toBe('Wilfred Chan'); + assert.equal(soleContributor?.title, 'Wilfred Chan'); }); - it('Jim Waterson Media editor', () => { + void it('Jim Waterson Media editor', () => { // https://www.theguardian.com/media/2021/nov/17/geordie-greig-ousted-as-editor-of-the-daily-mail const soleContributor = getSoleContributor( @@ -116,10 +118,10 @@ describe('Byline utilities', () => { 'Jim Waterson Media editor', ); - expect(soleContributor?.title).toBe('Jim Waterson'); + assert.equal(soleContributor?.title, 'Jim Waterson'); }); - it('First Dog on the Moon', () => { + void it('First Dog on the Moon', () => { // https://www.theguardian.com/commentisfree/2022/jul/22/europe-is-ablaze-italian-glaciers-are-collapsing-the-climate-crisis-is-here const soleContributor = getSoleContributor( @@ -133,10 +135,10 @@ describe('Byline utilities', () => { 'First Dog on the Moon', ); - expect(soleContributor?.title).toBe('First Dog on the Moon'); + assert.equal(soleContributor?.title, 'First Dog on the Moon'); }); - it('Sam Levine in New York', () => { + void it('Sam Levine in New York', () => { // https://www.theguardian.com/us-news/2022/jul/22/january-6-panel-american-democracy-nose-dive const soleContributor = getSoleContributor( @@ -150,12 +152,12 @@ describe('Byline utilities', () => { 'Sam Levine in New York', ); - expect(soleContributor?.title).toBe('Sam Levine'); + assert.equal(soleContributor?.title, 'Sam Levine'); }); }); - describe('returns `undefined`', () => { - it('Sam Levin in Los Angeles and Sam Levine in New York', () => { + void describe('returns `undefined`', () => { + void it('Sam Levin in Los Angeles and Sam Levine in New York', () => { // https://www.theguardian.com/us-news/2020/oct/12/republicans-election-2020-unauthorized-ballot-boxes const soleContributor = getSoleContributor( @@ -174,10 +176,10 @@ describe('Byline utilities', () => { ], 'Sam Levin in Los Angeles and Sam Levine in New York', ); - expect(soleContributor).toBe(undefined); + assert.equal(soleContributor, undefined); }); - it('Gabriel Smith', () => { + void it('Gabriel Smith', () => { const soleContributor = getSoleContributor( [ { @@ -189,10 +191,10 @@ describe('Byline utilities', () => { ], 'Gabriel Smith', ); - expect(soleContributor).toBe(undefined); + assert.equal(soleContributor, undefined); }); - it('Zoe Williams and others', () => { + void it('Zoe Williams and others', () => { // https://www.theguardian.com/commentisfree/2022/jul/20/britain-next-prime-minister-rishi-sunak-liz-truss-conservative-leader const soleContributor = getSoleContributor( @@ -227,10 +229,10 @@ describe('Byline utilities', () => { 'Zoe Williams and others', ); - expect(soleContributor).toBe(undefined); + assert.equal(soleContributor, undefined); }); - it('Paul MacInnes, Nesrine Malik, Julie Bindel, Peter Preston', () => { + void it('Paul MacInnes, Nesrine Malik, Julie Bindel, Peter Preston', () => { // https://www.theguardian.com/commentisfree/2011/dec/30/person-of-2011-writers-verdict const soleContributor = getSoleContributor( @@ -261,7 +263,7 @@ describe('Byline utilities', () => { 'Paul MacInnes, Nesrine Malik, Julie Bindel, Peter Preston', ); - expect(soleContributor).toBe(undefined); + assert.equal(soleContributor, undefined); }); }); }); diff --git a/dotcom-rendering/src/lib/canRenderAds.test.ts b/dotcom-rendering/src/lib/canRenderAds.node.test.ts similarity index 50% rename from dotcom-rendering/src/lib/canRenderAds.test.ts rename to dotcom-rendering/src/lib/canRenderAds.node.test.ts index ddf11a270fa..72a0e61b5ea 100644 --- a/dotcom-rendering/src/lib/canRenderAds.test.ts +++ b/dotcom-rendering/src/lib/canRenderAds.node.test.ts @@ -1,25 +1,27 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; import { Standard } from '../../fixtures/generated/fe-articles/Standard'; import { enhanceArticleType } from '../types/article'; import { canRenderAds } from './canRenderAds'; const standardPage = enhanceArticleType(Standard, 'Web'); -describe('canRenderAds', () => { - it('shows ads by default', () => { - expect(canRenderAds(standardPage.frontendData)).toBe(true); +void describe('canRenderAds', () => { + void it('shows ads by default', () => { + assert.equal(canRenderAds(standardPage.frontendData), true); }); - it('does not show ads if user is ad-free', () => { + void it('does not show ads if user is ad-free', () => { const adFreePage = Object.assign({}, standardPage.frontendData); adFreePage.isAdFreeUser = true; - expect(canRenderAds(adFreePage)).toBe(false); + assert.equal(canRenderAds(adFreePage), false); }); - it('does not show ads if page should not display them', () => { + void it('does not show ads if page should not display them', () => { const adFreePage = Object.assign({}, standardPage.frontendData); adFreePage.shouldHideAds = true; - expect(canRenderAds(adFreePage)).toBe(false); + assert.equal(canRenderAds(adFreePage), false); }); }); diff --git a/dotcom-rendering/src/lib/cardHelpers.test.ts b/dotcom-rendering/src/lib/cardHelpers.node.test.ts similarity index 64% rename from dotcom-rendering/src/lib/cardHelpers.test.ts rename to dotcom-rendering/src/lib/cardHelpers.node.test.ts index a0376496b99..ec6dbef7e3f 100644 --- a/dotcom-rendering/src/lib/cardHelpers.test.ts +++ b/dotcom-rendering/src/lib/cardHelpers.node.test.ts @@ -1,7 +1,9 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; import type { DCRContainerPalette } from '../types/front'; import { cardHasDarkBackground } from './cardHelpers'; -describe('cardHasDarkBackground', () => { +void describe('cardHasDarkBackground', () => { const testCases = [ { containerPalette: undefined, @@ -32,12 +34,12 @@ describe('cardHasDarkBackground', () => { expectedResult: boolean; }[]; - it.each(testCases)( - 'returns $expectedResult for $format format, $containerPalette containerPalette', - ({ containerPalette, expectedResult }) => { - expect(cardHasDarkBackground(containerPalette)).toBe( + for (const { containerPalette, expectedResult } of testCases) { + void it(`returns ${expectedResult} for $format format, ${containerPalette} containerPalette`, () => { + assert.equal( + cardHasDarkBackground(containerPalette), expectedResult, ); - }, - ); + }); + } }); diff --git a/dotcom-rendering/src/lib/decide-cation.test.ts b/dotcom-rendering/src/lib/decide-cation.node.test.ts similarity index 55% rename from dotcom-rendering/src/lib/decide-cation.test.ts rename to dotcom-rendering/src/lib/decide-cation.node.test.ts index ef4fc81d1be..43da6caf249 100644 --- a/dotcom-rendering/src/lib/decide-cation.test.ts +++ b/dotcom-rendering/src/lib/decide-cation.node.test.ts @@ -1,3 +1,5 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; import type { EmbedBlockElement, ImageBlockElement, @@ -5,23 +7,24 @@ import type { } from '../types/content'; import { decideMainMediaCaption } from './decide-caption'; -describe('decideMainMediaCaption', () => { - describe('when mainMedia is not supported', () => { - it('undefined returns an empty string', () => { - expect(decideMainMediaCaption(undefined)).toEqual(''); +void describe('decideMainMediaCaption', () => { + void describe('when mainMedia is not supported', () => { + void it('undefined returns an empty string', () => { + assert.deepEqual(decideMainMediaCaption(undefined), ''); }); - it('a text block returns an empty string', () => { - expect( + void it('a text block returns an empty string', () => { + assert.deepEqual( decideMainMediaCaption({ elementId: 'test-id', html: '

test

', _type: 'model.dotcomrendering.pageElements.TextBlockElement', } as TextBlockElement), - ).toEqual(''); + '', + ); }); }); - describe('ImageBlockElement', () => { + void describe('ImageBlockElement', () => { const mockImageBlockElement = { elementId: 'mock-element-id', data: {}, @@ -29,23 +32,24 @@ describe('decideMainMediaCaption', () => { _type: 'model.dotcomrendering.pageElements.ImageBlockElement', } as ImageBlockElement; - it('returns an empty string if there is no caption, displayCredit, or credit', () => { - expect(decideMainMediaCaption(mockImageBlockElement)).toEqual(''); + void it('returns an empty string if there is no caption, displayCredit, or credit', () => { + assert.deepEqual(decideMainMediaCaption(mockImageBlockElement), ''); }); - it('includes the caption, if it exists', () => { - expect( + void it('includes the caption, if it exists', () => { + assert.deepEqual( decideMainMediaCaption({ ...mockImageBlockElement, data: { caption: 'image block caption', }, }), - ).toEqual('image block caption'); + 'image block caption', + ); }); - it('includes the credit, if it should be displayed', () => { - expect( + void it('includes the credit, if it should be displayed', () => { + assert.deepEqual( decideMainMediaCaption({ ...mockImageBlockElement, displayCredit: true, @@ -53,11 +57,12 @@ describe('decideMainMediaCaption', () => { credit: 'image block display credit', }, }), - ).toEqual('image block display credit'); + 'image block display credit', + ); }); - it('does not include the credit, if it should not be displayed', () => { - expect( + void it('does not include the credit, if it should not be displayed', () => { + assert.deepEqual( decideMainMediaCaption({ ...mockImageBlockElement, displayCredit: false, @@ -65,11 +70,12 @@ describe('decideMainMediaCaption', () => { credit: 'image block display credit', }, }), - ).toEqual(''); + '', + ); }); - it('includes both the credit and caption, if they exist', () => { - expect( + void it('includes both the credit and caption, if they exist', () => { + assert.deepEqual( decideMainMediaCaption({ ...mockImageBlockElement, displayCredit: true, @@ -78,24 +84,26 @@ describe('decideMainMediaCaption', () => { credit: 'mock display credit', }, }), - ).toEqual('mock caption mock display credit'); + 'mock caption mock display credit', + ); }); }); - describe('EmbedBlockElement', () => { - it('returns an empty string if there is no caption', () => { - expect( + void describe('EmbedBlockElement', () => { + void it('returns an empty string if there is no caption', () => { + assert.deepEqual( decideMainMediaCaption({ elementId: 'test-id', html: '

test

', isMandatory: false, _type: 'model.dotcomrendering.pageElements.EmbedBlockElement', } as EmbedBlockElement), - ).toEqual(''); + '', + ); }); - it('returns the correct caption, if exists', () => { - expect( + void it('returns the correct caption, if exists', () => { + assert.deepEqual( decideMainMediaCaption({ elementId: 'test-id', html: '

test

', @@ -103,7 +111,8 @@ describe('decideMainMediaCaption', () => { caption: 'test caption', _type: 'model.dotcomrendering.pageElements.EmbedBlockElement', } as EmbedBlockElement), - ).toEqual('test caption'); + 'test caption', + ); }); }); }); diff --git a/dotcom-rendering/src/lib/edition.node.test.ts b/dotcom-rendering/src/lib/edition.node.test.ts new file mode 100644 index 00000000000..56db4a2a398 --- /dev/null +++ b/dotcom-rendering/src/lib/edition.node.test.ts @@ -0,0 +1,56 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; +import { + editionalisedPages, + editionList, + isEditionalisedPage, + isNetworkFront, +} from './edition'; + +const everyNetworkFront = editionList.map((edition) => edition.pageId); + +const everyEditionalisedPage = editionList + .map((edition) => + editionalisedPages.map((page) => `${edition.pageId}/${page}`), + ) + .flat(); + +void describe('is network front', () => { + void it('returns true if pageId is a network front', () => { + assert.equal( + everyNetworkFront.every((page) => isNetworkFront(page)), + true, + ); + }); + void it('returns false if pageId is NOT a network front', () => { + assert.equal(everyEditionalisedPage.every(isNetworkFront), false); + assert.equal(isNetworkFront('eu'), false); + assert.equal(isNetworkFront('int'), false); + assert.equal(isNetworkFront('uk/'), false); + }); +}); + +void describe('is editionalised page', () => { + void it('returns true if pageId is editionalised', () => { + assert.equal( + everyEditionalisedPage.every((page) => isEditionalisedPage(page)), + true, + ); + }); + void it('returns false if pageId is NOT editionalised', () => { + assert.equal( + everyNetworkFront.every((page) => isEditionalisedPage(page)), + false, + ); + assert.equal(isEditionalisedPage('uk'), false); + assert.equal(isEditionalisedPage('au'), false); + assert.equal(isEditionalisedPage('international'), false); + assert.equal(isEditionalisedPage('travel'), false); + assert.equal(isEditionalisedPage('culture'), false); + assert.equal(isEditionalisedPage('lifeandstyle'), false); + assert.equal( + isEditionalisedPage('lifeandstyle/health-and-wellbeing'), + false, + ); + }); +}); diff --git a/dotcom-rendering/src/lib/edition.test.ts b/dotcom-rendering/src/lib/edition.test.ts deleted file mode 100644 index 406e2a311a9..00000000000 --- a/dotcom-rendering/src/lib/edition.test.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { - editionalisedPages, - editionList, - isEditionalisedPage, - isNetworkFront, -} from './edition'; - -const everyNetworkFront = editionList.map((edition) => edition.pageId); - -const everyEditionalisedPage = editionList - .map((edition) => - editionalisedPages.map((page) => `${edition.pageId}/${page}`), - ) - .flat(); - -describe('is network front', () => { - it('returns true if pageId is a network front', () => { - expect(everyNetworkFront.every((page) => isNetworkFront(page))).toBe( - true, - ); - }); - it('returns false if pageId is NOT a network front', () => { - expect(everyEditionalisedPage.every(isNetworkFront)).toBe(false); - expect(isNetworkFront('eu')).toBe(false); - expect(isNetworkFront('int')).toBe(false); - expect(isNetworkFront('uk/')).toBe(false); - }); -}); - -describe('is editionalised page', () => { - it('returns true if pageId is editionalised', () => { - expect( - everyEditionalisedPage.every((page) => isEditionalisedPage(page)), - ).toBe(true); - }); - it('returns false if pageId is NOT editionalised', () => { - expect( - everyNetworkFront.every((page) => isEditionalisedPage(page)), - ).toBe(false); - expect(isEditionalisedPage('uk')).toBe(false); - expect(isEditionalisedPage('au')).toBe(false); - expect(isEditionalisedPage('international')).toBe(false); - expect(isEditionalisedPage('travel')).toBe(false); - expect(isEditionalisedPage('culture')).toBe(false); - expect(isEditionalisedPage('lifeandstyle')).toBe(false); - expect(isEditionalisedPage('lifeandstyle/health-and-wellbeing')).toBe( - false, - ); - }); -}); diff --git a/dotcom-rendering/src/lib/formatAttrString.node.test.ts b/dotcom-rendering/src/lib/formatAttrString.node.test.ts new file mode 100644 index 00000000000..2ec3aba6e4b --- /dev/null +++ b/dotcom-rendering/src/lib/formatAttrString.node.test.ts @@ -0,0 +1,32 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; +import { formatAttrString } from './formatAttrString'; + +const expectedOutput = 'this-headline-should-be-converted'; + +void describe('formatAttrString', () => { + void it('Lowercases all', () => { + const input = 'This Headline Should Be Converted'; + assert.equal(formatAttrString(input), expectedOutput); + }); + + void it('Converts spaces to hyphens', () => { + const input = 'this headline should be converted'; + assert.equal(formatAttrString(input), expectedOutput); + }); + + void it('Removes anything but spaces and letters', () => { + const input = '/this headline should be converted.'; + assert.equal(formatAttrString(input), expectedOutput); + }); + + void it('Does not remove numbers', () => { + const input = 'this headline should be converted 12'; + assert.equal(formatAttrString(input), `${expectedOutput}-12`); + }); + + void it('Puts it all together', () => { + const input = '/this Headline should be converted. 12'; + assert.equal(formatAttrString(input), `${expectedOutput}-12`); + }); +}); diff --git a/dotcom-rendering/src/lib/formatAttrString.test.ts b/dotcom-rendering/src/lib/formatAttrString.test.ts deleted file mode 100644 index 726778855f4..00000000000 --- a/dotcom-rendering/src/lib/formatAttrString.test.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { formatAttrString } from './formatAttrString'; - -const expectedOutput = 'this-headline-should-be-converted'; - -describe('formatAttrString', () => { - it('Lowercases all', () => { - const input = 'This Headline Should Be Converted'; - expect(formatAttrString(input)).toBe(expectedOutput); - }); - - it('Converts spaces to hyphens', () => { - const input = 'this headline should be converted'; - expect(formatAttrString(input)).toBe(expectedOutput); - }); - - it('Removes anything but spaces and letters', () => { - const input = '/this headline should be converted.'; - expect(formatAttrString(input)).toBe(expectedOutput); - }); - - it('Does not remove numbers', () => { - const input = 'this headline should be converted 12'; - expect(formatAttrString(input)).toBe(`${expectedOutput}-12`); - }); - - it('Puts it all together', () => { - const input = '/this Headline should be converted. 12'; - expect(formatAttrString(input)).toBe(`${expectedOutput}-12`); - }); -}); diff --git a/dotcom-rendering/src/lib/formatCount.node.test.ts b/dotcom-rendering/src/lib/formatCount.node.test.ts new file mode 100644 index 00000000000..b13c5ea64bb --- /dev/null +++ b/dotcom-rendering/src/lib/formatCount.node.test.ts @@ -0,0 +1,21 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; +import { formatCount } from './formatCount'; + +void describe('formatCount', () => { + void it('formats simple numbers', () => { + assert.deepEqual(formatCount(123), { short: '123', long: '123' }); + }); + void it('formats medium numbers', () => { + assert.deepEqual(formatCount(9876), { short: '9876', long: '9,876' }); + }); + void it('formats very long numbers', () => { + assert.deepEqual(formatCount(92878), { short: '93k', long: '92,878' }); + }); + void it('returns zero for zero', () => { + assert.deepEqual(formatCount(0), { short: '0', long: '0' }); + }); + void it('returns an ellipsis for undefined', () => { + assert.deepEqual(formatCount(), { short: '…', long: '…' }); + }); +}); diff --git a/dotcom-rendering/src/lib/formatCount.test.ts b/dotcom-rendering/src/lib/formatCount.test.ts deleted file mode 100644 index f16401b2823..00000000000 --- a/dotcom-rendering/src/lib/formatCount.test.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { formatCount } from './formatCount'; - -describe('formatCount', () => { - it('formats simple numbers', () => { - expect(formatCount(123)).toEqual({ short: '123', long: '123' }); - }); - it('formats medium numbers', () => { - expect(formatCount(9876)).toEqual({ short: '9876', long: '9,876' }); - }); - it('formats very long numbers', () => { - expect(formatCount(92878)).toEqual({ short: '93k', long: '92,878' }); - }); - it('returns zero for zero', () => { - expect(formatCount(0)).toEqual({ short: '0', long: '0' }); - }); - it('returns an ellipsis for undefined', () => { - expect(formatCount()).toEqual({ short: '…', long: '…' }); - }); -}); diff --git a/dotcom-rendering/src/lib/getFrontsAdPositions.test.ts b/dotcom-rendering/src/lib/getFrontsAdPositions.node.test.ts similarity index 80% rename from dotcom-rendering/src/lib/getFrontsAdPositions.test.ts rename to dotcom-rendering/src/lib/getFrontsAdPositions.node.test.ts index bec47bd6ab9..efa9950309f 100644 --- a/dotcom-rendering/src/lib/getFrontsAdPositions.test.ts +++ b/dotcom-rendering/src/lib/getFrontsAdPositions.node.test.ts @@ -1,3 +1,5 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; import { brandedTestCollections, largeFlexibleGeneralCollection, @@ -32,8 +34,8 @@ const defaultTestCollections: AdCandidate[] = [...Array(12)].map( () => ({ ...testCollection }), ); -describe('Mobile Ads', () => { - it(`Should not insert ad after container if it's the first one and it's a thrasher`, () => { +void describe('Mobile Ads', () => { + void it(`Should not insert ad after container if it's the first one and it's a thrasher`, () => { const testCollections = [ { ...testCollection, collectionType: 'fixed/thrasher' }, ...defaultTestCollections, @@ -41,19 +43,19 @@ describe('Mobile Ads', () => { const mobileAdPositions = getMobileAdPositions(testCollections, 'uk'); - expect(mobileAdPositions).not.toContain(0); + assert(!mobileAdPositions.includes(0)); }); - it(`should not insert an ad in the merchandising-high position`, () => { + void it(`should not insert an ad in the merchandising-high position`, () => { const testCollections = [ ...defaultTestCollections.slice(0, 3), { ...testCollection, collectionType: 'news/most-popular' }, ] satisfies AdCandidate[]; const mobileAdPositions = getMobileAdPositions(testCollections, 'uk'); - expect(mobileAdPositions).not.toContain(3); + assert(!mobileAdPositions.includes(3)); }); - it('Should not insert ad before a thrasher container', () => { + void it('Should not insert ad before a thrasher container', () => { const testCollections = [...defaultTestCollections]; testCollections.splice(5, 0, { ...testCollection, @@ -66,11 +68,11 @@ describe('Mobile Ads', () => { const mobileAdPositions = getMobileAdPositions(testCollections, 'uk'); - expect(mobileAdPositions).not.toContain(6); - expect(mobileAdPositions).not.toContain(8); + assert(!mobileAdPositions.includes(6)); + assert(!mobileAdPositions.includes(8)); }); - it(`Should allow inserting an ad before a thrasher container if it's a filter page`, () => { + void it(`Should allow inserting an ad before a thrasher container if it's a filter page`, () => { const testCollections = [...defaultTestCollections]; testCollections.splice(5, 0, { ...testCollection, @@ -86,12 +88,12 @@ describe('Mobile Ads', () => { 'uk/thefilter', ); - expect(mobileAdPositions).toContain(6); - expect(mobileAdPositions).toContain(8); + assert(mobileAdPositions.includes(6)); + assert(mobileAdPositions.includes(8)); }); // We used https://www.theguardian.com/uk/commentisfree as a blueprint - it('Non-network front, with more than 4 collections, without thrashers', () => { + void it('Non-network front, with more than 4 collections, without thrashers', () => { const testCollections: AdCandidate[] = [ { ...testCollection, collectionType: 'flexible/general' }, // Ad position (0) { ...testCollection, collectionType: 'flexible/general' }, @@ -110,11 +112,11 @@ describe('Mobile Ads', () => { const mobileAdPositions = getMobileAdPositions(testCollections, 'uk'); - expect(mobileAdPositions).toEqual([0, 2, 4, 6, 8]); + assert.deepEqual(mobileAdPositions, [0, 2, 4, 6, 8]); }); // We used https://www.theguardian.com/uk as a blueprint - it('UK Network Front, with more than 4 collections, with thrashers at various places', () => { + void it('UK Network Front, with more than 4 collections, with thrashers at various places', () => { const testCollections: AdCandidate[] = [ { ...testCollection, collectionType: 'flexible/general' }, // Ad position (0) { ...testCollection, collectionType: 'static/medium/4' }, @@ -144,11 +146,11 @@ describe('Mobile Ads', () => { const mobileAdPositions = getMobileAdPositions(testCollections, 'uk'); - expect(mobileAdPositions).toEqual([0, 2, 4, 8, 11, 14, 17, 19]); + assert.deepEqual(mobileAdPositions, [0, 2, 4, 8, 11, 14, 17, 19]); }); // We used https://www.theguardian.com/international as a blueprint - it('International Network Front, with more than 4 collections, with thrashers at various places', () => { + void it('International Network Front, with more than 4 collections, with thrashers at various places', () => { const testCollections: AdCandidate[] = [ { ...testCollection, collectionType: 'flexible/general' }, // Ad position (0) { ...testCollection, collectionType: 'static/medium/4' }, @@ -174,11 +176,11 @@ describe('Mobile Ads', () => { const mobileAdPositions = getMobileAdPositions(testCollections, 'uk'); - expect(mobileAdPositions).toEqual([0, 2, 5, 7, 11, 14, 16]); + assert.deepEqual(mobileAdPositions, [0, 2, 5, 7, 11, 14, 16]); }); // We used https://www.theguardian.com/us as a blueprint - it('US Network Front, with more than 4 collections, with thrashers at various places', () => { + void it('US Network Front, with more than 4 collections, with thrashers at various places', () => { const testCollections: AdCandidate[] = [ { ...testCollection, collectionType: 'flexible/general' }, // Ad position (0) { ...testCollection, collectionType: 'static/medium/4' }, @@ -205,11 +207,11 @@ describe('Mobile Ads', () => { const mobileAdPositions = getMobileAdPositions(testCollections, 'uk'); - expect(mobileAdPositions).toEqual([0, 2, 5, 9, 12, 14, 16]); + assert.deepEqual(mobileAdPositions, [0, 2, 5, 9, 12, 14, 16]); }); // We used https://www.theguardian.com/uk/lifeandstyle as a blueprint - it('Lifeandstyle front, with more than 4 collections, with thrashers at various places', () => { + void it('Lifeandstyle front, with more than 4 collections, with thrashers at various places', () => { const testCollections: AdCandidate[] = [ { ...testCollection, collectionType: 'flexible/special' }, // Ad position (0) { ...testCollection, collectionType: 'flexible/general' }, // Ignored - before thrasher @@ -231,11 +233,11 @@ describe('Mobile Ads', () => { const mobileAdPositions = getMobileAdPositions(testCollections, 'uk'); - expect(mobileAdPositions).toEqual([0, 3, 6, 9, 12]); + assert.deepEqual(mobileAdPositions, [0, 3, 6, 9, 12]); }); // We used https://www.theguardian.com/tone/recipes as a blueprint - it('Recipes front, with more than 4 collections, with thrasher at the first position', () => { + void it('Recipes front, with more than 4 collections, with thrasher at the first position', () => { const testCollections: AdCandidate[] = [ { ...testCollection, collectionType: 'fixed/thrasher' }, // Ignored - is first container and thrasher { ...testCollection, collectionType: 'flexible/general' }, // Ad position (1) @@ -255,10 +257,10 @@ describe('Mobile Ads', () => { const mobileAdPositions = getMobileAdPositions(testCollections, 'uk'); - expect(mobileAdPositions).toEqual([1, 3, 5, 7, 9]); + assert.deepEqual(mobileAdPositions, [1, 3, 5, 7, 9]); }); - it('Europe Network Front, with more than 4 collections and thrashers in various places', () => { + void it('Europe Network Front, with more than 4 collections and thrashers in various places', () => { const testCollections: AdCandidate[] = [ { ...testCollection, @@ -362,39 +364,39 @@ describe('Mobile Ads', () => { const mobileAdPositions = getMobileAdPositions(testCollections, 'uk'); - expect(mobileAdPositions).toEqual([4, 6, 8, 13, 18]); + assert.deepEqual(mobileAdPositions, [4, 6, 8, 13, 18]); }); }); -describe('Desktop Ads', () => { - it('calculates ad positions correctly for an example of the UK network front', () => { +void describe('Desktop Ads', () => { + void it('calculates ad positions correctly for an example of the UK network front', () => { const adPositions = getDesktopAdPositions(testCollectionsUk, 'uk'); - expect(adPositions).toEqual([3, 6, 8, 14, 17]); + assert.deepEqual(adPositions, [3, 6, 8, 14, 17]); }); - it('calculates ad positions correctly for an example of the US network front', () => { + void it('calculates ad positions correctly for an example of the US network front', () => { const adPositions = getDesktopAdPositions(testCollectionsUs, 'us'); - expect(adPositions).toEqual([3, 6, 10, 12, 19]); + assert.deepEqual(adPositions, [3, 6, 10, 12, 19]); }); - it('does NOT insert ads above or below branded content', () => { + void it('does NOT insert ads above or below branded content', () => { const adPositions = getDesktopAdPositions(brandedTestCollections, 'uk'); - expect(adPositions).toEqual([]); + assert.deepEqual(adPositions, []); }); - it('does NOT insert ads above secondary level containers', () => { + void it('does NOT insert ads above secondary level containers', () => { const adPositions = getDesktopAdPositions( testCollectionsWithSecondaryLevel, 'europe', ); - expect(adPositions).toEqual([]); + assert.deepEqual(adPositions, []); }); - it('inserts a maximum of 8 ads for fronts', () => { + void it('inserts a maximum of 8 ads for fronts', () => { const adPositions = getDesktopAdPositions( // 10x number of test collections in fixture to reach maximum level new Array(10) @@ -403,13 +405,13 @@ describe('Desktop Ads', () => { 'europe', ); - expect(adPositions.length).toEqual(8); + assert.deepEqual(adPositions.length, 8); }); }); -describe('inserting an ad after the first collection', () => { - describe('on mobile', () => { - it('inserts an ad after the first collection if it is a LARGE flexible general container', () => { +void describe('inserting an ad after the first collection', () => { + void describe('on mobile', () => { + void it('inserts an ad after the first collection if it is a LARGE flexible general container', () => { const adPositions = getMobileAdPositions( [ ...largeFlexibleGeneralCollection, @@ -427,11 +429,11 @@ describe('inserting an ad after the first collection', () => { 'uk', ); - expect(adPositions).toContain(0); - expect(adPositions).not.toContain(1); + assert(adPositions.includes(0)); + assert(!adPositions.includes(1)); }); - it('inserts an ad after the first collection if it is a LARGE flexible special container', () => { + void it('inserts an ad after the first collection if it is a LARGE flexible special container', () => { const adPositions = getMobileAdPositions( [ ...largeFlexibleSpecialCollection, @@ -449,11 +451,11 @@ describe('inserting an ad after the first collection', () => { 'uk', ); - expect(adPositions).toContain(0); - expect(adPositions).not.toContain(1); + assert(adPositions.includes(0)); + assert(!adPositions.includes(1)); }); - it('does NOT insert an ad after the first collection if it is a SMALL flexible general container', () => { + void it('does NOT insert an ad after the first collection if it is a SMALL flexible general container', () => { const adPositions = getMobileAdPositions( [ ...smallFlexibleGeneralCollection, @@ -466,10 +468,10 @@ describe('inserting an ad after the first collection', () => { 'uk', ); - expect(adPositions).not.toContain(0); + assert(!adPositions.includes(0)); }); - it('does NOT insert an ad after the first collection if it is a SMALL flexible special container', () => { + void it('does NOT insert an ad after the first collection if it is a SMALL flexible special container', () => { const adPositions = getMobileAdPositions( [ ...smallFlexibleSpecialCollection, @@ -482,12 +484,12 @@ describe('inserting an ad after the first collection', () => { 'uk', ); - expect(adPositions).not.toContain(0); + assert(!adPositions.includes(0)); }); }); - describe('on desktop', () => { - it('inserts an ad before the second collection if it is preceded by a LARGE flexible general container', () => { + void describe('on desktop', () => { + void it('inserts an ad before the second collection if it is preceded by a LARGE flexible general container', () => { const adPositions = getDesktopAdPositions( [ ...largeFlexibleGeneralCollection, @@ -505,11 +507,11 @@ describe('inserting an ad after the first collection', () => { 'uk', ); - expect(adPositions).toContain(1); - expect(adPositions).not.toContain(2); + assert(adPositions.includes(1)); + assert(!adPositions.includes(2)); }); - it('inserts an ad before the second collection if it is preceded by a LARGE flexible special container', () => { + void it('inserts an ad before the second collection if it is preceded by a LARGE flexible special container', () => { const adPositions = getDesktopAdPositions( [ ...largeFlexibleSpecialCollection, @@ -527,11 +529,11 @@ describe('inserting an ad after the first collection', () => { 'uk', ); - expect(adPositions).toContain(1); - expect(adPositions).not.toContain(2); + assert(adPositions.includes(1)); + assert(!adPositions.includes(2)); }); - it('does NOT insert an ad before the second collection if it is preceded by a SMALL flexible general container', () => { + void it('does NOT insert an ad before the second collection if it is preceded by a SMALL flexible general container', () => { const adPositions = getDesktopAdPositions( [ ...smallFlexibleGeneralCollection, @@ -549,10 +551,10 @@ describe('inserting an ad after the first collection', () => { 'uk', ); - expect(adPositions).not.toContain(1); + assert(!adPositions.includes(1)); }); - it('does NOT insert an ad before the second collection if it is preceded by a SMALL flexible special container', () => { + void it('does NOT insert an ad before the second collection if it is preceded by a SMALL flexible special container', () => { const adPositions = getDesktopAdPositions( [ ...smallFlexibleSpecialCollection, @@ -570,27 +572,27 @@ describe('inserting an ad after the first collection', () => { 'uk', ); - expect(adPositions).not.toContain(1); + assert(!adPositions.includes(1)); }); }); }); -describe('removeConsecutiveAdSlotsReducer', () => { - it('removes consecutive slots from array of all consecutive numbers', () => { +void describe('removeConsecutiveAdSlotsReducer', () => { + void it('removes consecutive slots from array of all consecutive numbers', () => { const arr = [0, 1, 2, 3, 4, 5]; const result = arr.reduce(removeConsecutiveAdSlotsReducer, []); - expect(result).toEqual([0, 2, 4]); + assert.deepEqual(result, [0, 2, 4]); }); - it('removes consecutive slots from array of some consecutive numbers', () => { + void it('removes consecutive slots from array of some consecutive numbers', () => { const arr = [0, 3, 7, 11, 12, 13, 19, 20]; const result = arr.reduce(removeConsecutiveAdSlotsReducer, []); - expect(result).toEqual([0, 3, 7, 11, 13, 19]); + assert.deepEqual(result, [0, 3, 7, 11, 13, 19]); }); - it('handles empty array', () => { + void it('handles empty array', () => { const arr: number[] = []; const result = arr.reduce(removeConsecutiveAdSlotsReducer, []); - expect(result).toEqual([]); + assert.deepEqual(result, []); }); }); diff --git a/dotcom-rendering/src/lib/getLiveblogAdPositions.node.test.ts b/dotcom-rendering/src/lib/getLiveblogAdPositions.node.test.ts new file mode 100644 index 00000000000..9f4d0d69faa --- /dev/null +++ b/dotcom-rendering/src/lib/getLiveblogAdPositions.node.test.ts @@ -0,0 +1,80 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; +import { liveBlock as mockBlock } from '../../fixtures/manual/liveBlock'; +import type { Block } from '../types/blocks'; +import { getLiveblogAdPositions } from './getLiveblogAdPositions'; + +void describe('get liveblog ad positions', () => { + const twoBlocks = Array(2).fill(mockBlock); + + void it('should insert zero ads if zero blocks', () => { + assert.deepEqual(getLiveblogAdPositions([]).desktopAdPositions, []); + assert.deepEqual(getLiveblogAdPositions([]).mobileAdPositions, []); + }); + void it('should insert zero ads if one block', () => { + assert.deepEqual( + getLiveblogAdPositions([mockBlock]).desktopAdPositions, + [], + ); + assert.deepEqual( + getLiveblogAdPositions([mockBlock]).mobileAdPositions, + [], + ); + }); + void it('should insert an ad after the first block if two blocks', () => { + assert.deepEqual( + getLiveblogAdPositions(twoBlocks).desktopAdPositions, + [0], + ); + assert.deepEqual( + getLiveblogAdPositions(twoBlocks).mobileAdPositions, + [0], + ); + }); + + void describe('many blocks', () => { + const block: Block = { + ...mockBlock, + elements: [ + { + elementId: '4ac2fcd8-284c-4038-91a1-093811f389ba', + _type: 'model.dotcomrendering.pageElements.TextBlockElement', + html: `

${'a'.repeat(1000)}

`, + }, + ], + }; + + const tenBlocks = Array(10).fill(block); + + void it('On desktop, it should insert an ad after every fourth block given repeated text elements of 1,000 characters', () => { + assert.deepEqual( + getLiveblogAdPositions(tenBlocks).desktopAdPositions, + [0, 4, 8], + ); + }); + + void it('On mobile, it should insert an ad after every second block given repeated text elements of 1,000 characters', () => { + assert.deepEqual( + getLiveblogAdPositions(tenBlocks).mobileAdPositions, + [0, 2, 4, 6, 8], + ); + }); + + // 40 blocks is enough that without a limit, there would be more than 8 blocks inserted on both mobile and desktop. + const fortyBlocks = Array(40).fill(block); + + void it('On desktop, it should not insert more that 8 slots', () => { + assert.equal( + getLiveblogAdPositions(fortyBlocks).desktopAdPositions.length, + 8, + ); + }); + + void it('On mobile, it should not insert more that 8 slots', () => { + assert.equal( + getLiveblogAdPositions(fortyBlocks).mobileAdPositions.length, + 8, + ); + }); + }); +}); diff --git a/dotcom-rendering/src/lib/getLiveblogAdPositions.test.ts b/dotcom-rendering/src/lib/getLiveblogAdPositions.test.ts deleted file mode 100644 index 32d0f203d5d..00000000000 --- a/dotcom-rendering/src/lib/getLiveblogAdPositions.test.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { liveBlock as mockBlock } from '../../fixtures/manual/liveBlock'; -import type { Block } from '../types/blocks'; -import { getLiveblogAdPositions } from './getLiveblogAdPositions'; - -describe('get liveblog ad positions', () => { - const twoBlocks = Array(2).fill(mockBlock); - - it('should insert zero ads if zero blocks', () => { - expect(getLiveblogAdPositions([]).desktopAdPositions).toEqual([]); - expect(getLiveblogAdPositions([]).mobileAdPositions).toEqual([]); - }); - it('should insert zero ads if one block', () => { - expect(getLiveblogAdPositions([mockBlock]).desktopAdPositions).toEqual( - [], - ); - expect(getLiveblogAdPositions([mockBlock]).mobileAdPositions).toEqual( - [], - ); - }); - it('should insert an ad after the first block if two blocks', () => { - expect(getLiveblogAdPositions(twoBlocks).desktopAdPositions).toEqual([ - 0, - ]); - expect(getLiveblogAdPositions(twoBlocks).mobileAdPositions).toEqual([ - 0, - ]); - }); - - describe('many blocks', () => { - const block: Block = { - ...mockBlock, - elements: [ - { - elementId: '4ac2fcd8-284c-4038-91a1-093811f389ba', - _type: 'model.dotcomrendering.pageElements.TextBlockElement', - html: `

${'a'.repeat(1000)}

`, - }, - ], - }; - - const tenBlocks = Array(10).fill(block); - - it('On desktop, it should insert an ad after every fourth block given repeated text elements of 1,000 characters', () => { - expect( - getLiveblogAdPositions(tenBlocks).desktopAdPositions, - ).toEqual([0, 4, 8]); - }); - - it('On mobile, it should insert an ad after every second block given repeated text elements of 1,000 characters', () => { - expect(getLiveblogAdPositions(tenBlocks).mobileAdPositions).toEqual( - [0, 2, 4, 6, 8], - ); - }); - - // 40 blocks is enough that without a limit, there would be more than 8 blocks inserted on both mobile and desktop. - const fortyBlocks = Array(40).fill(block); - - it('On desktop, it should not insert more that 8 slots', () => { - expect( - getLiveblogAdPositions(fortyBlocks).desktopAdPositions, - ).toHaveLength(8); - }); - - it('On mobile, it should not insert more that 8 slots', () => { - expect( - getLiveblogAdPositions(fortyBlocks).mobileAdPositions, - ).toHaveLength(8); - }); - }); -}); diff --git a/dotcom-rendering/src/lib/getTagPageAdPositions.node.test.ts b/dotcom-rendering/src/lib/getTagPageAdPositions.node.test.ts new file mode 100644 index 00000000000..c810e9b2346 --- /dev/null +++ b/dotcom-rendering/src/lib/getTagPageAdPositions.node.test.ts @@ -0,0 +1,31 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; +import { getTagPageBannerAdPositions } from './getTagPageAdPositions'; + +void describe('Tag page fronts-banner ad slots', () => { + void it('should insert 0 ads if there are less than 5 containers', () => { + assert.deepEqual(getTagPageBannerAdPositions(1), []); + assert.deepEqual(getTagPageBannerAdPositions(3), []); + }); + + void it('should insert 1 ad if there are 5-7 containers', () => { + assert.deepEqual(getTagPageBannerAdPositions(4), [2]); + assert.deepEqual(getTagPageBannerAdPositions(6), [2]); + }); + + void it('should insert 2 ads if there are 8-10 containers', () => { + assert.deepEqual(getTagPageBannerAdPositions(7), [2, 5]); + assert.deepEqual(getTagPageBannerAdPositions(9), [2, 5]); + }); + + void it('should insert no more than 8 ads if there are more than 18 containers', () => { + assert.deepEqual( + getTagPageBannerAdPositions(19), + [2, 5, 8, 11, 14, 17], + ); + assert.deepEqual( + getTagPageBannerAdPositions(25), + [2, 5, 8, 11, 14, 17, 20, 23], + ); + }); +}); diff --git a/dotcom-rendering/src/lib/getTagPageAdPositions.test.ts b/dotcom-rendering/src/lib/getTagPageAdPositions.test.ts deleted file mode 100644 index 3b743740430..00000000000 --- a/dotcom-rendering/src/lib/getTagPageAdPositions.test.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { getTagPageBannerAdPositions } from './getTagPageAdPositions'; - -describe('Tag page fronts-banner ad slots', () => { - it('should insert 0 ads if there are less than 5 containers', () => { - expect(getTagPageBannerAdPositions(1)).toEqual([]); - expect(getTagPageBannerAdPositions(3)).toEqual([]); - }); - - it('should insert 1 ad if there are 5-7 containers', () => { - expect(getTagPageBannerAdPositions(4)).toEqual([2]); - expect(getTagPageBannerAdPositions(6)).toEqual([2]); - }); - - it('should insert 2 ads if there are 8-10 containers', () => { - expect(getTagPageBannerAdPositions(7)).toEqual([2, 5]); - expect(getTagPageBannerAdPositions(9)).toEqual([2, 5]); - }); - - it('should insert no more than 8 ads if there are more than 18 containers', () => { - expect(getTagPageBannerAdPositions(19)).toEqual([2, 5, 8, 11, 14, 17]); - expect(getTagPageBannerAdPositions(25)).toEqual([ - 2, 5, 8, 11, 14, 17, 20, 23, - ]); - }); -}); diff --git a/dotcom-rendering/src/lib/getZIndex.node.test.ts b/dotcom-rendering/src/lib/getZIndex.node.test.ts new file mode 100644 index 00000000000..8e30a07ebdd --- /dev/null +++ b/dotcom-rendering/src/lib/getZIndex.node.test.ts @@ -0,0 +1,21 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; +import { getZIndex } from './getZIndex'; + +void describe('getZIndex', () => { + void it('gets the correct zindex for group and sibling', () => { + assert(getZIndex('sticky-video-button') > getZIndex('sticky-video')); + assert( + getZIndex('expanded-veggie-menu-wrapper') > + getZIndex('expanded-veggie-menu'), + ); + assert( + getZIndex('stickyAdWrapperLabsHeader') > + getZIndex('stickyAdWrapper'), + ); + assert(getZIndex('tableOfContents') > getZIndex('articleHeadline')); + assert(getZIndex('subNavBanner') > getZIndex('articleHeadline')); + assert(getZIndex('subNavBanner') > getZIndex('bodyArea')); + assert(getZIndex('card-nested-link') > getZIndex('card-link')); + }); +}); diff --git a/dotcom-rendering/src/lib/getZIndex.test.ts b/dotcom-rendering/src/lib/getZIndex.test.ts deleted file mode 100644 index 78bb441772c..00000000000 --- a/dotcom-rendering/src/lib/getZIndex.test.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { getZIndex } from './getZIndex'; - -describe('getZIndex', () => { - it('gets the correct zindex for group and sibling', () => { - expect(getZIndex('sticky-video-button')).toBeGreaterThan( - getZIndex('sticky-video'), - ); - expect(getZIndex('expanded-veggie-menu-wrapper')).toBeGreaterThan( - getZIndex('expanded-veggie-menu'), - ); - expect(getZIndex('stickyAdWrapperLabsHeader')).toBeGreaterThan( - getZIndex('stickyAdWrapper'), - ); - expect(getZIndex('tableOfContents')).toBeGreaterThan( - getZIndex('articleHeadline'), - ); - expect(getZIndex('subNavBanner')).toBeGreaterThan( - getZIndex('articleHeadline'), - ); - expect(getZIndex('subNavBanner')).toBeGreaterThan( - getZIndex('bodyArea'), - ); - expect(getZIndex('card-nested-link')).toBeGreaterThan( - getZIndex('card-link'), - ); - }); -}); diff --git a/dotcom-rendering/src/lib/identity-component-event.node.test.ts b/dotcom-rendering/src/lib/identity-component-event.node.test.ts new file mode 100644 index 00000000000..6791d7dfcf0 --- /dev/null +++ b/dotcom-rendering/src/lib/identity-component-event.node.test.ts @@ -0,0 +1,19 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; +import { createAuthenticationEventParams } from './identity-component-event'; + +void describe('createAuthenticationEventParams', () => { + void it('creates authentication event params given a component Id', () => { + assert.equal( + createAuthenticationEventParams('amp_sidebar_signin'), + 'componentEventParams=componentType%3Didentityauthentication%26componentId%3Damp_sidebar_signin', + ); + }); + + void it('creates authentication event params given a component Id and a page view Id', () => { + assert.equal( + createAuthenticationEventParams('amp_sidebar_signin', 'pageViewId'), + 'componentEventParams=componentType%3Didentityauthentication%26componentId%3Damp_sidebar_signin%26viewId%3DpageViewId', + ); + }); +}); diff --git a/dotcom-rendering/src/lib/identity-component-event.test.ts b/dotcom-rendering/src/lib/identity-component-event.test.ts deleted file mode 100644 index ab697b80550..00000000000 --- a/dotcom-rendering/src/lib/identity-component-event.test.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { createAuthenticationEventParams } from './identity-component-event'; - -describe('createAuthenticationEventParams', () => { - it('creates authentication event params given a component Id', () => { - expect(createAuthenticationEventParams('amp_sidebar_signin')).toBe( - 'componentEventParams=componentType%3Didentityauthentication%26componentId%3Damp_sidebar_signin', - ); - }); - it('creates authentication event params given a component Id and a page view Id', () => { - expect( - createAuthenticationEventParams('amp_sidebar_signin', 'pageViewId'), - ).toBe( - 'componentEventParams=componentType%3Didentityauthentication%26componentId%3Damp_sidebar_signin%26viewId%3DpageViewId', - ); - }); -}); diff --git a/dotcom-rendering/src/lib/isLight.node.test.ts b/dotcom-rendering/src/lib/isLight.node.test.ts new file mode 100644 index 00000000000..53b7122e902 --- /dev/null +++ b/dotcom-rendering/src/lib/isLight.node.test.ts @@ -0,0 +1,45 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; +import { isLight } from './isLight'; + +void describe('isLight', () => { + void it('should return the correct response for dark hex colours', () => { + for (const colour of [ + '#791a4e', + '#644f4e', + '#7f4e2a', + '#aa365e', + '#5e387c', + '#87656e', + '#223cdd', + '#555eee', + '#334cde', + '#b54bbb', + ]) { + assert.equal(isLight(colour), false); + } + }); + + void it('should return the correct response for light hex colours', () => { + for (const colour of ['#ea3eee', '#97dc45', '#7ec621', '#54dbb6']) { + assert.equal(isLight(colour), true); + } + }); + + void it('should return the correct response for 3 digit hex colours', () => { + assert.equal(isLight('#f4e'), true); + assert.equal(isLight('#fff'), true); + assert.equal(isLight('#999'), true); + assert.equal(isLight('#64e'), false); + assert.equal(isLight('#000'), false); + }); + + void it('should handle if the # is missing', () => { + assert.equal(isLight('97dc45'), true); + assert.equal(isLight('000'), false); + }); + + void it('should handle if the colour string is invalid', () => { + assert.equal(isLight('wyx'), false); + }); +}); diff --git a/dotcom-rendering/src/lib/isLight.test.ts b/dotcom-rendering/src/lib/isLight.test.ts deleted file mode 100644 index 4e97490462c..00000000000 --- a/dotcom-rendering/src/lib/isLight.test.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { isLight } from './isLight'; - -describe('isLight', () => { - it('should return the correct response for dark hex colours', () => { - expect(isLight('#791a4e')).toBe(false); - expect(isLight('#644f4e')).toBe(false); - expect(isLight('#7f4e2a')).toBe(false); - expect(isLight('#aa365e')).toBe(false); - expect(isLight('#5e387c')).toBe(false); - expect(isLight('#87656e')).toBe(false); - expect(isLight('#223cdd')).toBe(false); - expect(isLight('#555eee')).toBe(false); - expect(isLight('#334cde')).toBe(false); - expect(isLight('#b54bbb')).toBe(false); - }); - - it('should return the correct response for light hex colours', () => { - expect(isLight('#ea3eee')).toBe(true); - expect(isLight('#97dc45')).toBe(true); - expect(isLight('#7ec621')).toBe(true); - expect(isLight('#54dbb6')).toBe(true); - }); - - it('should return the correct response for 3 digit hex colours', () => { - expect(isLight('#f4e')).toBe(true); - expect(isLight('#fff')).toBe(true); - expect(isLight('#999')).toBe(true); - expect(isLight('#64e')).toBe(false); - expect(isLight('#000')).toBe(false); - }); - - it('should handle if the # is missing', () => { - expect(isLight('97dc45')).toBe(true); - expect(isLight('000')).toBe(false); - }); - - it('should handle if the colour string is invalid', () => { - expect(isLight('wyx')).toBe(false); - }); -}); diff --git a/dotcom-rendering/src/lib/isValidUrl.node.test.ts b/dotcom-rendering/src/lib/isValidUrl.node.test.ts new file mode 100644 index 00000000000..b4144202a9f --- /dev/null +++ b/dotcom-rendering/src/lib/isValidUrl.node.test.ts @@ -0,0 +1,35 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; +import { isValidUrl } from './isValidUrl'; + +void describe('isValidUrl', () => { + void describe('invalidInputs', () => { + const invalidInputs = [ + '', + 'guardian.co', + 'anemailaddress@company.com', + 'com/hello?athing=1&anotherthing=%20', + 'https://guardian.co.uk withASpace', + ]; + + for (const input of invalidInputs) { + void it(`returns false for invalid input of \`${input}\``, () => { + assert.equal(isValidUrl(input), false); + }); + } + }); + + void describe('validInputs', () => { + const validInputs = [ + 'https://guardian.co.uk/australia-news/series/guardian-australia-s-morning-mail', + 'https://regexr.com/39nr7', + 'http://www.google.com/hello?athing=1&anotherthing=%20', + ]; + + for (const input of validInputs) { + void it(`returns true for valid input of \`${input}\``, () => { + assert.equal(isValidUrl(input), true); + }); + } + }); +}); diff --git a/dotcom-rendering/src/lib/isValidUrl.test.ts b/dotcom-rendering/src/lib/isValidUrl.test.ts deleted file mode 100644 index e67eac86dd4..00000000000 --- a/dotcom-rendering/src/lib/isValidUrl.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { isValidUrl } from './isValidUrl'; - -describe('isValidUrl', () => { - describe('invalidInputs', () => { - const invalidInputs = [ - '', - 'guardian.co', - 'anemailaddress@company.com', - 'com/hello?athing=1&anotherthing=%20', - 'https://guardian.co.uk withASpace', - ]; - - it.each(invalidInputs)( - 'returns false for invalid input of `%s`', - (input) => { - expect(isValidUrl(input)).toBeFalsy(); - }, - ); - }); - - describe('validInputs', () => { - const validInputs = [ - 'https://guardian.co.uk/australia-news/series/guardian-australia-s-morning-mail', - 'https://regexr.com/39nr7', - 'http://www.google.com/hello?athing=1&anotherthing=%20', - ]; - - it.each(validInputs)( - 'returns true for valid input of `%s`', - (input) => { - expect(isValidUrl(input)).toBeTruthy(); - }, - ); - }); -}); diff --git a/dotcom-rendering/src/lib/labs.node.test.ts b/dotcom-rendering/src/lib/labs.node.test.ts new file mode 100644 index 00000000000..e9ff20ca680 --- /dev/null +++ b/dotcom-rendering/src/lib/labs.node.test.ts @@ -0,0 +1,36 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; +import type { Branding } from '../types/branding'; +import { getOphanComponents } from './labs'; + +void describe('getOphanComponents', () => { + void it('constructs the correct data attributes for branding in article meta', () => { + const branding = { sponsorName: 'Some Sponsor' } as Branding; + assert.deepEqual( + getOphanComponents({ + branding, + locationPrefix: 'article-meta', + }), + { + ophanComponentName: 'labs-logo | article-meta-some-sponsor', + ophanComponentLink: 'labs-logo-article-meta-some-sponsor', + }, + ); + }); + + void it('constructs the correct data attributes for branding in related content', () => { + const branding = { sponsorName: 'Some Sponsor' } as Branding; + assert.deepEqual( + getOphanComponents({ + branding, + locationPrefix: 'article-related-content', + }), + { + ophanComponentName: + 'labs-logo | article-related-content-some-sponsor', + ophanComponentLink: + 'labs-logo-article-related-content-some-sponsor', + }, + ); + }); +}); diff --git a/dotcom-rendering/src/lib/labs.test.ts b/dotcom-rendering/src/lib/labs.test.ts deleted file mode 100644 index 178db5e7622..00000000000 --- a/dotcom-rendering/src/lib/labs.test.ts +++ /dev/null @@ -1,29 +0,0 @@ -import type { Branding } from '../types/branding'; -import { getOphanComponents } from './labs'; - -describe('getOphanComponents', () => { - it('constructs the correct data attributes for branding in article meta', () => { - const branding = { sponsorName: 'Some Sponsor' } as Branding; - expect( - getOphanComponents({ branding, locationPrefix: 'article-meta' }), - ).toStrictEqual({ - ophanComponentName: 'labs-logo | article-meta-some-sponsor', - ophanComponentLink: 'labs-logo-article-meta-some-sponsor', - }); - }); - - it('constructs the correct data attributes for branding in related content', () => { - const branding = { sponsorName: 'Some Sponsor' } as Branding; - expect( - getOphanComponents({ - branding, - locationPrefix: 'article-related-content', - }), - ).toStrictEqual({ - ophanComponentName: - 'labs-logo | article-related-content-some-sponsor', - ophanComponentLink: - 'labs-logo-article-related-content-some-sponsor', - }); - }); -}); diff --git a/dotcom-rendering/src/lib/lang.node.test.ts b/dotcom-rendering/src/lib/lang.node.test.ts new file mode 100644 index 00000000000..9528912b57f --- /dev/null +++ b/dotcom-rendering/src/lib/lang.node.test.ts @@ -0,0 +1,21 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; +import { decideLanguage, decideLanguageDirection } from './lang'; + +void describe('decideLanguage', () => { + void it('returns undefined if input is "en"', () => { + assert.equal(decideLanguage('en'), undefined); + }); + + void it('returns input if it is not "en"', () => { + assert.equal(decideLanguage('at'), 'at'); + assert.equal(decideLanguage('fr'), 'fr'); + }); +}); + +void describe('describeLanguageDirection', () => { + void it('returns rtl if input is true', () => { + assert.equal(decideLanguageDirection(true), 'rtl'); + assert.equal(decideLanguageDirection(false), undefined); + }); +}); diff --git a/dotcom-rendering/src/lib/lang.test.ts b/dotcom-rendering/src/lib/lang.test.ts deleted file mode 100644 index 42140d1e226..00000000000 --- a/dotcom-rendering/src/lib/lang.test.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { decideLanguage, decideLanguageDirection } from './lang'; - -describe('decideLanguage', () => { - test('returns undefined if input is "en"', () => { - expect(decideLanguage('en')).toBe(undefined); - }); - - test('returns input if it is not "en"', () => { - expect(decideLanguage('at')).toBe('at'); - expect(decideLanguage('fr')).toBe('fr'); - }); -}); - -describe('describeLanguageDirection', () => { - test('returns rtl if input is true', () => { - expect(decideLanguageDirection(true)).toBe('rtl'); - expect(decideLanguageDirection(false)).toBe(undefined); - }); -}); diff --git a/dotcom-rendering/src/lib/linkNotificationCount.test.ts b/dotcom-rendering/src/lib/linkNotificationCount.node.test.ts similarity index 75% rename from dotcom-rendering/src/lib/linkNotificationCount.test.ts rename to dotcom-rendering/src/lib/linkNotificationCount.node.test.ts index 73b30391896..14ef88dc439 100644 --- a/dotcom-rendering/src/lib/linkNotificationCount.test.ts +++ b/dotcom-rendering/src/lib/linkNotificationCount.node.test.ts @@ -1,8 +1,10 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; import type { DropdownLinkType } from '../components/Dropdown.island'; import { linkNotificationCount } from './linkNotificationCount'; -describe('linksNotificationCount', () => { - it('returns the sum of notifications across all links', () => { +void describe('linksNotificationCount', () => { + void it('returns the sum of notifications across all links', () => { const links: DropdownLinkType[] = [ { id: 'one', @@ -40,12 +42,10 @@ describe('linksNotificationCount', () => { }, ]; - const notificationCount = linkNotificationCount(links); - - expect(notificationCount).toEqual(3); + assert.equal(linkNotificationCount(links), 3); }); - it('returns 0 when there are no notifications', () => { + void it('returns 0 when there are no notifications', () => { const links = [ { id: 'one', @@ -61,8 +61,6 @@ describe('linksNotificationCount', () => { }, ]; - const notificationCount = linkNotificationCount(links); - - expect(notificationCount).toEqual(0); + assert.equal(linkNotificationCount(links), 0); }); }); diff --git a/dotcom-rendering/src/lib/liveblogAdSlots.test.ts b/dotcom-rendering/src/lib/liveblogAdSlots.node.test.ts similarity index 61% rename from dotcom-rendering/src/lib/liveblogAdSlots.test.ts rename to dotcom-rendering/src/lib/liveblogAdSlots.node.test.ts index 99cb32ef8f7..cbc34c58b52 100644 --- a/dotcom-rendering/src/lib/liveblogAdSlots.test.ts +++ b/dotcom-rendering/src/lib/liveblogAdSlots.node.test.ts @@ -1,10 +1,12 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; import type { FEElement } from '../types/content'; import { calculateApproximateBlockHeight, shouldDisplayAd, } from './liveblogAdSlots'; -describe('calculateApproximateBlockHeight', () => { +void describe('calculateApproximateBlockHeight', () => { const textElementOneLineDesktop: FEElement[] = [ { elementId: '1', @@ -63,85 +65,83 @@ describe('calculateApproximateBlockHeight', () => { const defaultBlockSpacing = 75; - describe('zero elements', () => { - it.each(['mobile', 'desktop'])( - 'should return zero when there are zero elements on %s', - (screenSize) => { + void describe('zero elements', () => { + for (const screenSize of ['mobile', 'desktop']) { + void it(`should return zero when there are zero elements on ${screenSize}`, () => { const isMobile = screenSize === 'mobile'; - expect(calculateApproximateBlockHeight([], isMobile)).toEqual( + assert.deepEqual( + calculateApproximateBlockHeight([], isMobile), 0, ); - }, - ); + }); + } }); - describe('text block elements', () => { + void describe('text block elements', () => { const textLineHeight = 23.8; const margin = 14; - it.each([ + for (const [screenSize, textElementOneLine, textElementTwoLines] of [ ['mobile', textElementOneLineMobile, textElementTwoLinesMobile], ['desktop', textElementOneLineDesktop, textElementTwoLinesDestkop], - ])( - 'should return the correct height for varying line length on %s', - (screenSize, textElementOneLine, textElementTwoLines) => { + ] as const) { + void it(`should return the correct height for varying line length on ${screenSize}`, () => { const isMobile = screenSize === 'mobile'; - expect( + assert.deepEqual( calculateApproximateBlockHeight( textElementOneLine, isMobile, ), - ).toEqual(textLineHeight + margin + defaultBlockSpacing); - expect( + textLineHeight + margin + defaultBlockSpacing, + ); + assert.deepEqual( calculateApproximateBlockHeight( textElementTwoLines, isMobile, ), - ).toEqual(2 * textLineHeight + margin + defaultBlockSpacing); - }, - ); + 2 * textLineHeight + margin + defaultBlockSpacing, + ); + }); + } - it.each(['mobile', 'desktop'])( - 'should return the correct height when there are multiple elements on %s', - (screenSize) => { + for (const screenSize of ['mobile', 'desktop']) { + void it(`should return the correct height when there are multiple elements on ${screenSize}`, () => { const isMobile = screenSize === 'mobile'; - expect( + assert.deepEqual( calculateApproximateBlockHeight( multipleTextElements, isMobile, ), - ).toEqual( 2 * textLineHeight + 2 * margin + defaultBlockSpacing, ); - }, - ); + }); + } }); - describe('youtube block elements', () => { - it.each([ + void describe('youtube block elements', () => { + for (const [screenSize, heightExcludingText] of [ ['mobile', 195], ['desktop', 350], - ])( - 'should return the correct height on %s', - (screenSize, heightExcludingText) => { + ] as const) { + void it(`should return the correct height on ${screenSize}`, () => { const isMobile = screenSize === 'mobile'; const margin = 12; - expect( + assert.deepEqual( calculateApproximateBlockHeight(youtubeElement, isMobile), - ).toEqual(heightExcludingText + margin + defaultBlockSpacing); - }, - ); + heightExcludingText + margin + defaultBlockSpacing, + ); + }); + } }); }); -describe('shouldDisplayAd', () => { - describe('The final block of content', () => { - it.each(['mobile', 'desktop'])( - 'should NOT display an ad if this is the final block on %s', - (screenSize) => { +void describe('shouldDisplayAd', () => { + void describe('The final block of content', () => { + for (const screenSize of ['mobile', 'desktop']) { + void it(`should NOT display an ad if this is the final block on ${screenSize}`, () => { const isMobile = screenSize === 'mobile'; const block = 5; @@ -157,15 +157,14 @@ describe('shouldDisplayAd', () => { isMobile, ); - expect(result).toBeFalsy(); - }, - ); + assert(!result); + }); + } }); - describe('Reaching the ad limit', () => { - it.each(['mobile', 'desktop'])( - 'should NOT insert another ad slot if we have reached the limit on %s.', - (screenSize) => { + void describe('Reaching the ad limit', () => { + for (const screenSize of ['mobile', 'desktop']) { + void it(`should NOT insert another ad slot if we have reached the limit on ${screenSize}.`, () => { const isMobile = screenSize === 'mobile'; const block = 5; const totalBlocks = 10; @@ -180,15 +179,14 @@ describe('shouldDisplayAd', () => { isMobile, ); - expect(result).toBeFalsy(); - }, - ); + assert(!result); + }); + } }); - describe('inserting the first ad slot', () => { - it.each(['mobile', 'desktop'])( - 'should display ad if this is the first block on %s.', - (screenSize) => { + void describe('inserting the first ad slot', () => { + for (const screenSize of ['mobile', 'desktop']) { + void it(`should display ad if this is the first block on ${screenSize}.`, () => { const isMobile = screenSize === 'mobile'; const block = 1; const totalBlocks = 10; @@ -203,18 +201,17 @@ describe('shouldDisplayAd', () => { isMobile, ); - expect(result).toBeTruthy(); - }, - ); + assert(result); + }); + } }); - describe('inserting further ad slots', () => { - it.each([ + void describe('inserting further ad slots', () => { + for (const [pixels, screenSize] of [ [1200, 'mobile'], [1500, 'desktop'], - ])( - 'should display ad if number of pixels without an ad is more than %s on %s', - (pixels, screenSize) => { + ] as const) { + void it(`should display ad if number of pixels without an ad is more than ${pixels} on ${screenSize}`, () => { const isMobile = screenSize === 'mobile'; const block = 5; const totalBlocks = 10; @@ -229,16 +226,15 @@ describe('shouldDisplayAd', () => { isMobile, ); - expect(result).toBeTruthy(); - }, - ); + assert(result); + }); + } - it.each([ + for (const [pixels, screenSize] of [ [1200, 'mobile'], [1500, 'desktop'], - ])( - 'should NOT display ad if number of pixels without an ad is less than %s on %s', - (pixels, screenSize) => { + ] as const) { + void it(`should NOT display ad if number of pixels without an ad is less than ${pixels} on ${screenSize}`, () => { const isMobile = screenSize === 'mobile'; const block = 5; const totalBlocks = 10; @@ -253,8 +249,8 @@ describe('shouldDisplayAd', () => { isMobile, ); - expect(result).toBeFalsy(); - }, - ); + assert(!result); + }); + } }); }); diff --git a/dotcom-rendering/src/lib/notification.test.ts b/dotcom-rendering/src/lib/notification.node.test.ts similarity index 88% rename from dotcom-rendering/src/lib/notification.test.ts rename to dotcom-rendering/src/lib/notification.node.test.ts index cc331b5be59..dea94ec59f0 100644 --- a/dotcom-rendering/src/lib/notification.test.ts +++ b/dotcom-rendering/src/lib/notification.node.test.ts @@ -1,7 +1,9 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; import { addNotificationsToDropdownLinks } from './notification'; -describe('addNotificationsToDropdownLinks', () => { - it('augments dropdown links with notifications', () => { +void describe('addNotificationsToDropdownLinks', () => { + void it('augments dropdown links with notifications', () => { const links = [ { id: 'account_overview', @@ -30,7 +32,7 @@ describe('addNotificationsToDropdownLinks', () => { notifications, ); - expect(linksWithNotifications).toEqual([ + assert.deepEqual(linksWithNotifications, [ { id: 'account_overview', url: `https://example.com/account_overview`, @@ -54,7 +56,7 @@ describe('addNotificationsToDropdownLinks', () => { ]); }); - it('adds multiple notification messages to a link', () => { + void it('adds multiple notification messages to a link', () => { const links = [ { id: 'account_overview', @@ -83,7 +85,7 @@ describe('addNotificationsToDropdownLinks', () => { notifications, ); - expect(linksWithNotifications).toEqual([ + assert.deepEqual(linksWithNotifications, [ { id: 'account_overview', url: `https://example.com/account_overview`, @@ -107,7 +109,7 @@ describe('addNotificationsToDropdownLinks', () => { ]); }); - it('adds new notifications if target already has notifications', () => { + void it('adds new notifications if target already has notifications', () => { const links = [ { id: 'account_overview', @@ -138,7 +140,7 @@ describe('addNotificationsToDropdownLinks', () => { notifications, ); - expect(linksWithNotifications).toEqual([ + assert.deepEqual(linksWithNotifications, [ { id: 'account_overview', url: `https://example.com/account_overview`, diff --git a/dotcom-rendering/src/lib/ophan-helpers.node.test.ts b/dotcom-rendering/src/lib/ophan-helpers.node.test.ts new file mode 100644 index 00000000000..b72b33e42c7 --- /dev/null +++ b/dotcom-rendering/src/lib/ophan-helpers.node.test.ts @@ -0,0 +1,13 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; +import { nestedOphanComponents } from './ophan-helpers'; + +void describe('Ophan helpers', () => { + void it('should handle nested values', () => { + assert.equal(nestedOphanComponents('logo'), 'logo'); + assert.equal( + nestedOphanComponents('nav', 'sub nav', 'final element'), + 'nav : sub nav : final element', + ); + }); +}); diff --git a/dotcom-rendering/src/lib/ophan-helpers.test.ts b/dotcom-rendering/src/lib/ophan-helpers.test.ts deleted file mode 100644 index e4300fea5cf..00000000000 --- a/dotcom-rendering/src/lib/ophan-helpers.test.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { nestedOphanComponents } from './ophan-helpers'; - -describe('Ophan helpers', () => { - it('should handle nested values', () => { - expect(nestedOphanComponents('logo')).toBe('logo'); - expect(nestedOphanComponents('nav', 'sub nav', 'final element')).toBe( - 'nav : sub nav : final element', - ); - }); -}); diff --git a/dotcom-rendering/src/lib/parser/parseCheckoutOutCookieData.node.test.ts b/dotcom-rendering/src/lib/parser/parseCheckoutOutCookieData.node.test.ts new file mode 100644 index 00000000000..501886b441f --- /dev/null +++ b/dotcom-rendering/src/lib/parser/parseCheckoutOutCookieData.node.test.ts @@ -0,0 +1,72 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; +import { parseCheckoutCompleteCookieData } from './parseCheckoutOutCookieData'; + +void describe('parseCheckoutCompleteCookieData', () => { + const encodeCheckoutCompleteCookieDataObj = ( + userType: string, + product: string, + ) => + encodeURIComponent(`{"userType":"${userType}","product":"${product}"}`); + + void describe('successful parse', () => { + void it('should successfully parse a url encoded json object with a valid userType and product valid field', () => { + const cookieString = encodeCheckoutCompleteCookieDataObj( + 'new', + 'SupporterPlus', + ); + assert.deepEqual(parseCheckoutCompleteCookieData(cookieString), { + userType: 'new', + product: 'SupporterPlus', + }); + }); + }); + + void describe('unsuccessful parse should return undefined', () => { + void it('invalid user type', () => { + const cookieString = encodeCheckoutCompleteCookieDataObj( + 'invalid', + 'SupporterPlus', + ); + assert.equal( + parseCheckoutCompleteCookieData(cookieString), + undefined, + ); + }); + void it('invalid product type', () => { + const cookieString = encodeCheckoutCompleteCookieDataObj( + 'new', + 'undefined', + ); + assert.equal( + parseCheckoutCompleteCookieData(cookieString), + undefined, + ); + }); + void it('invalid field', () => { + const cookieString = encodeURIComponent( + `{"invalid":"new", "product": "SupporterPlus"}`, + ); + assert.equal( + parseCheckoutCompleteCookieData(cookieString), + undefined, + ); + }); + void it('invalid json structure', () => { + const cookieString = encodeURIComponent( + `{"userType":"new", "product": "SupporterPlus"`, + ); + assert.equal( + parseCheckoutCompleteCookieData(cookieString), + undefined, + ); + }); + void it('plain string', () => { + const cookieString = `{"userType":"new", "product": "SupporterPlus"}`; + assert.equal( + parseCheckoutCompleteCookieData(cookieString), + undefined, + ); + }); + }); +}); diff --git a/dotcom-rendering/src/lib/parser/parseCheckoutOutCookieData.test.ts b/dotcom-rendering/src/lib/parser/parseCheckoutOutCookieData.test.ts deleted file mode 100644 index e4892e03f16..00000000000 --- a/dotcom-rendering/src/lib/parser/parseCheckoutOutCookieData.test.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { parseCheckoutCompleteCookieData } from './parseCheckoutOutCookieData'; - -describe('parseCheckoutCompleteCookieData', () => { - const encodeCheckoutCompleteCookieDataObj = ( - userType: string, - product: string, - ) => - encodeURIComponent(`{"userType":"${userType}","product":"${product}"}`); - - describe('successful parse', () => { - it('should successfully parse a url encoded json object with a valid userType and product valid field', () => { - const cookieString = encodeCheckoutCompleteCookieDataObj( - 'new', - 'SupporterPlus', - ); - expect(parseCheckoutCompleteCookieData(cookieString)).toStrictEqual( - { - userType: 'new', - product: 'SupporterPlus', - }, - ); - }); - }); - - describe('unsuccessful parse should return undefined', () => { - it('invalid user type', () => { - const cookieString = encodeCheckoutCompleteCookieDataObj( - 'invalid', - 'SupporterPlus', - ); - expect(parseCheckoutCompleteCookieData(cookieString)).toBe( - undefined, - ); - }); - it('invalid product type', () => { - const cookieString = encodeCheckoutCompleteCookieDataObj( - 'new', - 'undefined', - ); - expect(parseCheckoutCompleteCookieData(cookieString)).toBe( - undefined, - ); - }); - it('invalid field', () => { - const cookieString = encodeURIComponent( - `{"invalid":"new", "product": "SupporterPlus"}`, - ); - expect(parseCheckoutCompleteCookieData(cookieString)).toBe( - undefined, - ); - }); - it('invalid json structure', () => { - const cookieString = encodeURIComponent( - `{"userType":"new", "product": "SupporterPlus"`, - ); - expect(parseCheckoutCompleteCookieData(cookieString)).toBe( - undefined, - ); - }); - it('plain string', () => { - const cookieString = `{"userType":"new", "product": "SupporterPlus"}`; - expect(parseCheckoutCompleteCookieData(cookieString)).toBe( - undefined, - ); - }); - }); -}); diff --git a/dotcom-rendering/src/lib/puzzlesHubExperiment.node.test.ts b/dotcom-rendering/src/lib/puzzlesHubExperiment.node.test.ts new file mode 100644 index 00000000000..841fe44f9ea --- /dev/null +++ b/dotcom-rendering/src/lib/puzzlesHubExperiment.node.test.ts @@ -0,0 +1,42 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; +import { + isPuzzlesHubEnabled, + isPuzzlesHubVariant, +} from './puzzlesHubExperiment'; + +void describe('isPuzzlesHubVariant', () => { + const testCases = [ + ['control', { 'puzzles-new-hub': 'control' }], + ['missing', {}], + ['unknown group', { 'puzzles-new-hub': 'other' }], + ['unrelated participation', { another: 'variant' }], + ] as const; + + for (const [name, participations] of testCases) { + void it(`rejects ${name}`, () => { + assert.equal(isPuzzlesHubVariant(participations), false); + }); + } + + void it('accepts only puzzles-new-hub:variant', () => { + assert.equal( + isPuzzlesHubVariant({ 'puzzles-new-hub': 'variant' }), + true, + ); + }); +}); + +void describe('isPuzzlesHubEnabled', () => { + void it('allow local development without an experiment participation', () => { + assert.equal(isPuzzlesHubEnabled({}, true), true); + }); + + void it('requires the variant outside local development', () => { + assert.equal(isPuzzlesHubEnabled({}, false), false); + assert.equal( + isPuzzlesHubEnabled({ 'puzzles-new-hub': 'variant' }, false), + true, + ); + }); +}); diff --git a/dotcom-rendering/src/lib/puzzlesHubExperiment.test.ts b/dotcom-rendering/src/lib/puzzlesHubExperiment.test.ts deleted file mode 100644 index 1a34669908f..00000000000 --- a/dotcom-rendering/src/lib/puzzlesHubExperiment.test.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { - isPuzzlesHubEnabled, - isPuzzlesHubVariant, -} from './puzzlesHubExperiment'; - -describe('isPuzzlesHubVariant', () => { - it.each([ - ['control', { 'puzzles-new-hub': 'control' }], - ['missing', {}], - ['unknown group', { 'puzzles-new-hub': 'other' }], - ['unrelated participation', { another: 'variant' }], - ])('rejects %s', (_, participations) => { - expect(isPuzzlesHubVariant(participations)).toBe(false); - }); - - it('accepts only puzzles-new-hub:variant', () => { - expect(isPuzzlesHubVariant({ 'puzzles-new-hub': 'variant' })).toBe( - true, - ); - }); -}); - -describe('isPuzzlesHubEnabled', () => { - it('allow local development without an experiment participation', () => { - expect(isPuzzlesHubEnabled({}, true)).toBe(true); - }); - - it('requires the variant outside local development', () => { - expect(isPuzzlesHubEnabled({}, false)).toBe(false); - expect( - isPuzzlesHubEnabled({ 'puzzles-new-hub': 'variant' }, false), - ).toBe(true); - }); -}); diff --git a/dotcom-rendering/src/lib/querystring.test.ts b/dotcom-rendering/src/lib/querystring.node.test.ts similarity index 62% rename from dotcom-rendering/src/lib/querystring.test.ts rename to dotcom-rendering/src/lib/querystring.node.test.ts index cecfd9f2b3d..d243fb85183 100644 --- a/dotcom-rendering/src/lib/querystring.test.ts +++ b/dotcom-rendering/src/lib/querystring.node.test.ts @@ -1,7 +1,9 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; import { constructQuery } from './querystring'; -describe('constructQuery', () => { - it('constructs the correct query string from an object', () => { +void describe('constructQuery', () => { + void it('constructs the correct query string from an object', () => { const testParams = { sens: 'f', si: 'f', @@ -16,6 +18,6 @@ describe('constructQuery', () => { a: undefined, }; const expectedQuery = `sens=f&si=f&vl=333&cc=UK&s=sport&inskin=f&ct=article&url=%2Fsport%2F2017%2Fsep%2F30%2Ftest-article&su=0&pa=f&a=undefined`; - expect(constructQuery(testParams)).toBe(expectedQuery); + assert.equal(constructQuery(testParams), expectedQuery); }); }); diff --git a/dotcom-rendering/src/lib/result.node.test.ts b/dotcom-rendering/src/lib/result.node.test.ts new file mode 100644 index 00000000000..dc8e9269cf8 --- /dev/null +++ b/dotcom-rendering/src/lib/result.node.test.ts @@ -0,0 +1,193 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; +import { literal, safeParse } from 'valibot'; +import { error, fromValibot, ok, type Result } from './result'; + +void describe('ok', () => { + void it('creates an instance of Ok', () => { + const result = ok(3); + const value = result.getOrThrow('Expected an Ok'); + + assert.equal(result.ok, true); + assert.equal(value, 3); + }); +}); + +void describe('error', () => { + void it('creates an instance of Err', () => { + const result = error('An error'); + const err = result.getErrorOrThrow('Expected an Err'); + + assert.equal(result.ok, false); + assert.equal(err, 'An error'); + }); +}); + +void describe('flatMap', () => { + const f = (a: number): Result => ok(a + 1); + const h = (): Result => error('h error'); + + void it('runs the function and unwraps the result when both Results are Ok', () => { + const result = ok(3).flatMap(f); + const value = result.getOrThrow('Expected an Ok'); + + assert.equal(result.ok, true); + assert.equal(value, 4); + }); + + void it('passes through the Err when the first Result is Err', () => { + const result = error('error message').flatMap(f); + const err = result.getErrorOrThrow('Expected an Err'); + + assert.equal(result.ok, false); + assert.equal(err, 'error message'); + }); + + void it('passes through the Err when the second Result is Err', () => { + const result = ok(3).flatMap(h); + const err = result.getErrorOrThrow('Expected an Err'); + + assert.equal(result.ok, false); + assert.equal(err, 'h error'); + }); + + void it('passes through the first Err when both are Err', () => { + const result = error('error message').flatMap(h); + const err = result.getErrorOrThrow('Expected an Err'); + + assert.equal(result.ok, false); + assert.equal(err, 'error message'); + }); + + void it('obeys left identity law', () => { + const value = 3; + + assert.deepEqual(ok(value).flatMap(f), f(value)); + }); + + void it('obeys right identity law', () => { + const result = ok(3); + + assert.deepEqual(result.flatMap(ok), result); + }); + + void it('obeys associativity law', () => { + const result = ok(3); + const g = (a: number): Result => ok(a * 3); + + assert.deepEqual( + result.flatMap(f).flatMap(g), + result.flatMap((a) => f(a).flatMap(g)), + ); + }); +}); + +void describe('map', () => { + const f = (a: number): number => a + 1; + + void it('runs the function when Result is Ok', () => { + const result = ok(3).map(f); + const value = result.getOrThrow('Expected an Ok'); + + assert.equal(result.ok, true); + assert.equal(value, 4); + }); + + void it('passes the error through when Result is Err', () => { + const result = error('error message').map(f); + const err = result.getErrorOrThrow('Expected an Err'); + + assert.equal(result.ok, false); + assert.equal(err, 'error message'); + }); + + void it('obeys identity', () => { + const identity = (a: A): A => a; + const value = 3; + const result = ok(value); + + assert.deepEqual(result.map(identity), result); + }); + + void it('obeys composition', () => { + const g = (a: number): number => a * 3; + const result = ok(3); + + assert.deepEqual( + result.map(f).map(g), + result.map((a) => g(f(a))), + ); + }); +}); + +void describe('mapError', () => { + const f = (err: string): string => `An error: ${err}`; + + void it('produces a new error if Err', () => { + const err = error('error details'); + + assert.deepEqual(err.mapError(f), error('An error: error details')); + }); + + void it('does nothing if Ok', () => { + const result = ok(3); + + assert.deepEqual(result.mapError(f), result); + }); +}); + +void describe('getOrThrow', () => { + void it('gets the value if Ok', () => { + const value = ok(3).getOrThrow('Expected an Ok'); + + assert.equal(value, 3); + }); + + void it('throws if Err', () => { + const result = error('An error'); + + assert.throws( + () => result.getOrThrow('Expected an Ok'), + /Expected an Ok/, + ); + }); +}); + +void describe('getErrorOrThrow', () => { + void it('gets the value if Err', () => { + const err = error('An error').getErrorOrThrow('Expected an Err'); + + assert.equal(err, 'An error'); + }); + + void it('throws if Ok', () => { + const result = ok(3); + + assert.throws( + () => result.getErrorOrThrow('Expected an Err'), + /Expected an Err/, + ); + }); +}); + +void describe('fromValibot', () => { + const schema = literal('string literal'); + + void it('creates an Ok from a successful parse result', () => { + const valibotResult = safeParse(schema, 'string literal'); + + const result = fromValibot(valibotResult); + const value = result.getOrThrow('Expected an Ok'); + + assert.equal(value, 'string literal'); + }); + + void it('creates an Err from an unsuccessful parse result', () => { + const valibotResult = safeParse(schema, 'invalid literal'); + + const result = fromValibot(valibotResult); + const err = result.getErrorOrThrow('Expected an Err'); + + assert.equal(err[0].expected, '"string literal"'); + }); +}); diff --git a/dotcom-rendering/src/lib/result.test.ts b/dotcom-rendering/src/lib/result.test.ts deleted file mode 100644 index b625fc5e213..00000000000 --- a/dotcom-rendering/src/lib/result.test.ts +++ /dev/null @@ -1,185 +0,0 @@ -import { literal, safeParse } from 'valibot'; -import { error, fromValibot, ok, type Result } from './result'; - -describe('ok', () => { - it('creates an instance of Ok', () => { - const result = ok(3); - const value = result.getOrThrow('Expected an Ok'); - - expect(result.ok).toBe(true); - expect(value).toBe(3); - }); -}); - -describe('error', () => { - it('creates an instance of Err', () => { - const result = error('An error'); - const err = result.getErrorOrThrow('Expected an Err'); - - expect(result.ok).toBe(false); - expect(err).toBe('An error'); - }); -}); - -describe('flatMap', () => { - const f = (a: number): Result => ok(a + 1); - const h = (): Result => error('h error'); - - it('runs the function and unwraps the result when both Results are Ok', () => { - const result = ok(3).flatMap(f); - const value = result.getOrThrow('Expected an Ok'); - - expect(result.ok).toBe(true); - expect(value).toBe(4); - }); - - it('passes through the Err when the first Result is Err', () => { - const result = error('error message').flatMap(f); - const err = result.getErrorOrThrow('Expected an Err'); - - expect(result.ok).toBe(false); - expect(err).toBe('error message'); - }); - - it('passes through the Err when the second Result is Err', () => { - const result = ok(3).flatMap(h); - const err = result.getErrorOrThrow('Expected an Err'); - - expect(result.ok).toBe(false); - expect(err).toBe('h error'); - }); - - it('passes through the first Err when both are Err', () => { - const result = error('error message').flatMap(h); - const err = result.getErrorOrThrow('Expected an Err'); - - expect(result.ok).toBe(false); - expect(err).toBe('error message'); - }); - - it('obeys left identity law', () => { - const value = 3; - - expect(ok(value).flatMap(f)).toEqual(f(value)); - }); - - it('obeys right identity law', () => { - const result = ok(3); - - expect(result.flatMap(ok)).toEqual(result); - }); - - it('obeys associativity law', () => { - const result = ok(3); - const g = (a: number): Result => ok(a * 3); - - expect(result.flatMap(f).flatMap(g)).toEqual( - result.flatMap((a) => f(a).flatMap(g)), - ); - }); -}); - -describe('map', () => { - const f = (a: number): number => a + 1; - - it('runs the function when Result is Ok', () => { - const result = ok(3).map(f); - const value = result.getOrThrow('Expected an Ok'); - - expect(result.ok).toBe(true); - expect(value).toBe(4); - }); - - it('passes the error through when Result is Err', () => { - const result = error('error message').map(f); - const err = result.getErrorOrThrow('Expected an Err'); - - expect(result.ok).toBe(false); - expect(err).toBe('error message'); - }); - - it('obeys identity', () => { - const identity = (a: A): A => a; - const value = 3; - const result = ok(value); - - expect(result.map(identity)).toEqual(result); - }); - - it('obeys composition', () => { - const g = (a: number): number => a * 3; - const result = ok(3); - - expect(result.map(f).map(g)).toEqual(result.map((a) => g(f(a)))); - }); -}); - -describe('mapError', () => { - const f = (e: string): string => `An error: ${e}`; - - it('produces a new error if Err', () => { - const err = error('error details'); - - expect(err.mapError(f)).toEqual(error('An error: error details')); - }); - - it('does nothing if Ok', () => { - const result = ok(3); - - expect(result.mapError(f)).toEqual(result); - }); -}); - -describe('getOrThrow', () => { - it('gets the value if Ok', () => { - const value = ok(3).getOrThrow('Expected an Ok'); - - expect(value).toBe(3); - }); - - it('throws if Err', () => { - const result = error('An error'); - - expect(() => result.getOrThrow('Expected an Ok')).toThrow( - 'Expected an Ok', - ); - }); -}); - -describe('getErrorOrThrow', () => { - it('gets the value if Err', () => { - const err = error('An error').getErrorOrThrow('Expected an Err'); - - expect(err).toBe('An error'); - }); - - it('throws if Ok', () => { - const result = ok(3); - - expect(() => result.getErrorOrThrow('Expected an Err')).toThrow( - 'Expected an Err', - ); - }); -}); - -describe('fromValibot', () => { - const schema = literal('string literal'); - - it('creates an Ok from a successful parse result', () => { - const valibotResult = safeParse(schema, 'string literal'); - - const result = fromValibot(valibotResult); - const value = result.getOrThrow('Expected an Ok'); - - expect(value).toBe('string literal'); - }); - - it('creates an Err from an unsuccessful parse result', () => { - const valibotResult = safeParse(schema, 'invalid literal'); - - const result = fromValibot(valibotResult); - const err = result.getErrorOrThrow('Expected an Err'); - - expect(err[0].expected).toBe('"string literal"'); - }); -}); diff --git a/dotcom-rendering/src/lib/sendTargetingParams.apps.test.ts b/dotcom-rendering/src/lib/sendTargetingParams.apps.node.test.ts similarity index 79% rename from dotcom-rendering/src/lib/sendTargetingParams.apps.test.ts rename to dotcom-rendering/src/lib/sendTargetingParams.apps.node.test.ts index 83eb9e5a2b1..7adcf443677 100644 --- a/dotcom-rendering/src/lib/sendTargetingParams.apps.test.ts +++ b/dotcom-rendering/src/lib/sendTargetingParams.apps.node.test.ts @@ -1,7 +1,9 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; import { getTargetingParams } from './sendTargetingParams.apps'; -describe('getTargetingParams', () => { - it('extracts ad targeting params from editionCommercialProperties in the format Bridget consumes', () => { +void describe('getTargetingParams', () => { + void it('extracts ad targeting params from editionCommercialProperties in the format Bridget consumes', () => { const testEditionCommercialProperties = { adTargeting: [ { @@ -64,7 +66,8 @@ describe('getTargetingParams', () => { ['k', 'us-politics,state-of-georgia,us-crime,us-news,donaldtrump'], ]); - expect(getTargetingParams(testEditionCommercialProperties)).toEqual( + assert.deepEqual( + getTargetingParams(testEditionCommercialProperties), expectedValue, ); }); diff --git a/dotcom-rendering/src/lib/theFilter.node.test.ts b/dotcom-rendering/src/lib/theFilter.node.test.ts new file mode 100644 index 00000000000..d30986f96f6 --- /dev/null +++ b/dotcom-rendering/src/lib/theFilter.node.test.ts @@ -0,0 +1,37 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; +import { isFilterPageId } from './theFilter'; + +void describe('isFilterPageId', () => { + void it('returns true for a UK Filter article pageId', () => { + assert.equal( + isFilterPageId( + 'thefilter/2026/jul/02/jess-cartner-morleys-july-style-essentials-2026', + ), + true, + ); + }); + + void it('returns true for a US Filter article pageId', () => { + assert.equal( + isFilterPageId( + 'thefilter-us/2025/dec/27/best-wine-subscriptions-us', + ), + true, + ); + }); + + void it('returns false for a non-Filter pageId', () => { + assert.equal( + isFilterPageId('technology/2026/jan/01/some-other-article'), + false, + ); + }); + + void it('returns false for a pageId that merely contains "thefilter" mid-string', () => { + assert.equal( + isFilterPageId('lifestyle/thefilter-mentioned/some-article'), + false, + ); + }); +}); diff --git a/dotcom-rendering/src/lib/theFilter.test.ts b/dotcom-rendering/src/lib/theFilter.test.ts deleted file mode 100644 index bc2723ddaf4..00000000000 --- a/dotcom-rendering/src/lib/theFilter.test.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { isFilterPageId } from './theFilter'; - -describe('isFilterPageId', () => { - it('returns true for a UK Filter article pageId', () => { - expect( - isFilterPageId( - 'thefilter/2026/jul/02/jess-cartner-morleys-july-style-essentials-2026', - ), - ).toBe(true); - }); - - it('returns true for a US Filter article pageId', () => { - expect( - isFilterPageId( - 'thefilter-us/2025/dec/27/best-wine-subscriptions-us', - ), - ).toBe(true); - }); - - it('returns false for a non-Filter pageId', () => { - expect( - isFilterPageId('technology/2026/jan/01/some-other-article'), - ).toBe(false); - }); - - it('returns false for a pageId that merely contains "thefilter" mid-string', () => { - expect( - isFilterPageId('lifestyle/thefilter-mentioned/some-article'), - ).toBe(false); - }); -}); diff --git a/dotcom-rendering/src/lib/transparentColour.node.test.ts b/dotcom-rendering/src/lib/transparentColour.node.test.ts new file mode 100644 index 00000000000..0f0049786de --- /dev/null +++ b/dotcom-rendering/src/lib/transparentColour.node.test.ts @@ -0,0 +1,46 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; +import { transparentColour } from './transparentColour'; + +void describe('transparentColour', () => { + const validHexColours = [ + ['#000000', 'rgba(0, 0, 0, 0.5)'], + ['#C70000', 'rgba(199, 0, 0, 0.5)'], + ['#aabbcc', 'rgba(170, 187, 204, 0.5)'], + ['#ffffff', 'rgba(255, 255, 255, 0.5)'], + ] as const; + + for (const [hex, output] of validHexColours) { + void it(`For valid hex ${hex}, return ${output}`, () => { + assert.equal(transparentColour(hex), output); + }); + } + + const shortHexColours = [ + ['#000', 'rgba(0, 0, 0, 0.5)'], + ['#c00', 'rgba(204, 0, 0, 0.5)'], + ['#abc', 'rgba(170, 187, 204, 0.5)'], + ['#fff', 'rgba(255, 255, 255, 0.5)'], + ] as const; + + for (const [hex, output] of shortHexColours) { + void it(`For short hex ${hex}, return ${output}`, () => { + assert.equal(transparentColour(hex), output); + }); + } + + const invalidHexColours = [ + '---', + '#ab', + '#abcd', + '#gggggg', + '-ffffff', + 'rgb(0,0,0)', + ]; + + for (const hex of invalidHexColours) { + void it(`For invalid hex ${hex}, return rgba(127, 127, 127, 0.5)`, () => { + assert.equal(transparentColour(hex), 'rgba(127, 127, 127, 0.5)'); + }); + } +}); diff --git a/dotcom-rendering/src/lib/transparentColour.test.ts b/dotcom-rendering/src/lib/transparentColour.test.ts deleted file mode 100644 index 8cb10e6be48..00000000000 --- a/dotcom-rendering/src/lib/transparentColour.test.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { transparentColour } from './transparentColour'; - -describe('transparentColour', () => { - test.each([ - ['#000000', 'rgba(0, 0, 0, 0.5)'], - ['#C70000', 'rgba(199, 0, 0, 0.5)'], - ['#aabbcc', 'rgba(170, 187, 204, 0.5)'], - ['#ffffff', 'rgba(255, 255, 255, 0.5)'], - ])('For valid hex %s, return %s', (hex, output) => { - expect(transparentColour(hex)).toEqual(output); - }); - - test.each([ - ['#000', 'rgba(0, 0, 0, 0.5)'], - ['#c00', 'rgba(204, 0, 0, 0.5)'], - ['#abc', 'rgba(170, 187, 204, 0.5)'], - ['#fff', 'rgba(255, 255, 255, 0.5)'], - ])('For short hex %s, return %s', (hex, output) => { - expect(transparentColour(hex)).toEqual(output); - }); - - test.each(['---', '#ab', '#abcd', '#gggggg', '-ffffff', 'rgb(0,0,0)'])( - 'For invalid hex %s, return rgba(127, 127, 127, 0.5)', - (hex) => { - expect(transparentColour(hex)).toEqual('rgba(127, 127, 127, 0.5)'); - }, - ); -}); diff --git a/dotcom-rendering/src/lib/tuple.node.test.ts b/dotcom-rendering/src/lib/tuple.node.test.ts new file mode 100644 index 00000000000..f15ef9c3a9f --- /dev/null +++ b/dotcom-rendering/src/lib/tuple.node.test.ts @@ -0,0 +1,63 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; +import { isNonEmptyArray, takeFirst } from './tuple'; + +void describe('takeFirst', () => { + void it('Always returns the correct array length when the array is one less, the same as, or one more than n', () => { + const results = [ + // Format, from 1 to 12, cover length n - 1, n & n+1 + takeFirst([0, 1], 1), + takeFirst([0], 1), + takeFirst([], 1), + takeFirst([0, 1, 2], 2), + takeFirst([0, 1], 2), + takeFirst([0], 2), + takeFirst([0, 1, 2, 3], 3), + takeFirst([0, 1, 2], 3), + takeFirst([0, 1], 3), + takeFirst([0, 1, 2, 3, 4], 4), + takeFirst([0, 1, 2, 3], 4), + takeFirst([0, 1, 2], 4), + takeFirst([0, 1, 2, 3, 4, 5], 5), + takeFirst([0, 1, 2, 3, 4], 5), + takeFirst([0, 1, 2, 3], 5), + takeFirst([0, 1, 2, 3, 4, 5, 6], 6), + takeFirst([0, 1, 2, 3, 4, 5], 6), + takeFirst([0, 1, 2, 3, 4], 6), + takeFirst([0, 1, 2, 3, 4, 5, 6, 7], 7), + takeFirst([0, 1, 2, 3, 4, 5, 6], 7), + takeFirst([0, 1, 2, 3, 4, 5], 7), + takeFirst([0, 1, 2, 3, 4, 5, 6, 7, 8], 8), + takeFirst([0, 1, 2, 3, 4, 5, 6, 7], 8), + takeFirst([0, 1, 2, 3, 4, 5, 6], 8), + takeFirst([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 9), + takeFirst([0, 1, 2, 3, 4, 5, 6, 7, 8], 9), + takeFirst([0, 1, 2, 3, 4, 5, 6, 7], 9), + takeFirst([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 10), + takeFirst([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 10), + takeFirst([0, 1, 2, 3, 4, 5, 6, 7, 8], 10), + takeFirst([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], 11), + takeFirst([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 11), + takeFirst([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 11), + takeFirst([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], 12), + takeFirst([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], 12), + takeFirst([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 12), + ] as const; + + const expectedLengths = [ + 1, 1, 0, 2, 2, 1, 3, 3, 2, 4, 4, 3, 5, 5, 4, 6, 6, 5, 7, 7, 6, 8, 8, + 7, 9, 9, 8, 10, 10, 9, 11, 11, 10, 12, 12, 11, + ]; + + assert.deepEqual( + results.map((result) => result.length), + expectedLengths, + ); + }); +}); + +void it('isNonEmptyArray', () => { + assert.equal(isNonEmptyArray([]), false); + assert.equal(isNonEmptyArray([1]), true); + assert.equal(isNonEmptyArray([1, 2, 3]), true); +}); diff --git a/dotcom-rendering/src/lib/tuple.test.ts b/dotcom-rendering/src/lib/tuple.test.ts deleted file mode 100644 index 33b398afb3d..00000000000 --- a/dotcom-rendering/src/lib/tuple.test.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { isNonEmptyArray, takeFirst } from './tuple'; - -describe('takeFirst', () => { - it('Always returns the correct array length when the array is one less, the same as, or one more than n', () => { - const results = [ - // Format, from 1 to 12, cover length n - 1, n & n+1 - takeFirst([0, 1], 1), - takeFirst([0], 1), - takeFirst([], 1), - takeFirst([0, 1, 2], 2), - takeFirst([0, 1], 2), - takeFirst([0], 2), - takeFirst([0, 1, 2, 3], 3), - takeFirst([0, 1, 2], 3), - takeFirst([0, 1], 3), - takeFirst([0, 1, 2, 3, 4], 4), - takeFirst([0, 1, 2, 3], 4), - takeFirst([0, 1, 2], 4), - takeFirst([0, 1, 2, 3, 4, 5], 5), - takeFirst([0, 1, 2, 3, 4], 5), - takeFirst([0, 1, 2, 3], 5), - takeFirst([0, 1, 2, 3, 4, 5, 6], 6), - takeFirst([0, 1, 2, 3, 4, 5], 6), - takeFirst([0, 1, 2, 3, 4], 6), - takeFirst([0, 1, 2, 3, 4, 5, 6, 7], 7), - takeFirst([0, 1, 2, 3, 4, 5, 6], 7), - takeFirst([0, 1, 2, 3, 4, 5], 7), - takeFirst([0, 1, 2, 3, 4, 5, 6, 7, 8], 8), - takeFirst([0, 1, 2, 3, 4, 5, 6, 7], 8), - takeFirst([0, 1, 2, 3, 4, 5, 6], 8), - takeFirst([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 9), - takeFirst([0, 1, 2, 3, 4, 5, 6, 7, 8], 9), - takeFirst([0, 1, 2, 3, 4, 5, 6, 7], 9), - takeFirst([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 10), - takeFirst([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 10), - takeFirst([0, 1, 2, 3, 4, 5, 6, 7, 8], 10), - takeFirst([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], 11), - takeFirst([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 11), - takeFirst([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 11), - takeFirst([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], 12), - takeFirst([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], 12), - takeFirst([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 12), - ] as const; - - // Expected results from n are n, n & n-1 - expect(results[0].length).toEqual(1); - expect(results[1].length).toEqual(1); - expect(results[2].length).toEqual(0); - expect(results[3].length).toEqual(2); - expect(results[4].length).toEqual(2); - expect(results[5].length).toEqual(1); - expect(results[6].length).toEqual(3); - expect(results[7].length).toEqual(3); - expect(results[8].length).toEqual(2); - expect(results[9].length).toEqual(4); - expect(results[10].length).toEqual(4); - expect(results[11].length).toEqual(3); - expect(results[12].length).toEqual(5); - expect(results[13].length).toEqual(5); - expect(results[14].length).toEqual(4); - expect(results[15].length).toEqual(6); - expect(results[16].length).toEqual(6); - expect(results[17].length).toEqual(5); - expect(results[18].length).toEqual(7); - expect(results[19].length).toEqual(7); - expect(results[20].length).toEqual(6); - expect(results[21].length).toEqual(8); - expect(results[22].length).toEqual(8); - expect(results[23].length).toEqual(7); - expect(results[24].length).toEqual(9); - expect(results[25].length).toEqual(9); - expect(results[26].length).toEqual(8); - expect(results[27].length).toEqual(10); - expect(results[28].length).toEqual(10); - expect(results[29].length).toEqual(9); - expect(results[30].length).toEqual(11); - expect(results[31].length).toEqual(11); - expect(results[32].length).toEqual(10); - expect(results[33].length).toEqual(12); - expect(results[34].length).toEqual(12); - expect(results[35].length).toEqual(11); - }); -}); - -it('isNonEmptyArray', () => { - expect(isNonEmptyArray([])).toBe(false); - expect(isNonEmptyArray([1])).toBe(true); - expect(isNonEmptyArray([1, 2, 3])).toBe(true); -}); diff --git a/dotcom-rendering/src/lib/video.test.ts b/dotcom-rendering/src/lib/video.node.test.ts similarity index 60% rename from dotcom-rendering/src/lib/video.test.ts rename to dotcom-rendering/src/lib/video.node.test.ts index ac23ac49b5f..9fd51331b8c 100644 --- a/dotcom-rendering/src/lib/video.test.ts +++ b/dotcom-rendering/src/lib/video.node.test.ts @@ -1,3 +1,5 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; import type { FEMediaAsset } from '../frontend/feFront'; import type { VideoAssets } from '../types/content'; import type { Source } from './video'; @@ -88,18 +90,19 @@ const m3u8Src720h: Source = { hasAudio: true, }; -describe('video', () => { - describe('extractValidSourcesFromAssets', () => { - it('should drop unsupported assets', () => { +void describe('video', () => { + void describe('extractValidSourcesFromAssets', () => { + void it('should drop unsupported assets', () => { const assets = [mp4Asset480w, m3u8Asset720h, unsupportedAsset]; const expected = [mp4Src480w, m3u8Src720h]; - expect(extractValidSourcesFromAssets(assets, 'Loop')).toEqual( + assert.deepEqual( + extractValidSourcesFromAssets(assets, 'Loop'), expected, ); }); - it('should reorder sources by supportedVideoFileTypes order', () => { + void it('should reorder sources by supportedVideoFileTypes order', () => { const assets = [ m3u8Asset720h, mp4Asset480w, @@ -114,49 +117,54 @@ describe('video', () => { m3u8Src720h, m3u8Src720h, ]; - expect(extractValidSourcesFromAssets(assets, 'Loop')).toEqual( + assert.deepEqual( + extractValidSourcesFromAssets(assets, 'Loop'), expected, ); }); - it('should prefer M3U8 sources for long videos with Default video style', () => { + void it('should prefer M3U8 sources for long videos with Default video style', () => { const assets = [mp4Asset480w, m3u8Asset720h, mp4Asset720h]; const expected = [m3u8Src720h, mp4Src480w, mp4Src720h]; - expect( + assert.deepEqual( extractValidSourcesFromAssets(assets, 'Default', 37), - ).toEqual(expected); + expected, + ); }); - it('should prefer MP4 sources for short videos with Default video style', () => { + void it('should prefer MP4 sources for short videos with Default video style', () => { const assets = [mp4Asset480w, m3u8Asset720h, mp4Asset720h]; const expected = [mp4Src480w, mp4Src720h, m3u8Src720h]; - expect( + assert.deepEqual( extractValidSourcesFromAssets(assets, 'Default', 12), - ).toEqual(expected); + expected, + ); }); - it('should prefer MP4 sources with Loop video style', () => { + void it('should prefer MP4 sources with Loop video style', () => { const assets = [mp4Asset480w, m3u8Asset720h, mp4Asset720h]; const expected = [mp4Src480w, mp4Src720h, m3u8Src720h]; - expect(extractValidSourcesFromAssets(assets, 'Loop')).toEqual( + assert.deepEqual( + extractValidSourcesFromAssets(assets, 'Loop'), expected, ); }); - it('should prefer MP4 sources with Cinemagraph video style', () => { + void it('should prefer MP4 sources with Cinemagraph video style', () => { const assets = [mp4Asset480w, m3u8Asset720h, mp4Asset720h]; const expected = [mp4Src480w, mp4Src720h, m3u8Src720h]; - expect( + assert.deepEqual( extractValidSourcesFromAssets(assets, 'Cinemagraph'), - ).toEqual(expected); + expected, + ); }); }); - describe('convertFEMediaAssetsToVideoAssets', () => { + void describe('convertFEMediaAssetsToVideoAssets', () => { const feMediaAsset480w: FEMediaAsset = { id: 'https://guim-example.co.uk/atomID-1_480w.mp4', version: 1, @@ -182,41 +190,44 @@ describe('video', () => { hasAudio: true, }; - it('should convert FE media assets to video assets', () => { - expect( + void it('should convert FE media assets to video assets', () => { + assert.deepEqual( convertFEMediaAssetsToVideoAssets([ feMediaAsset480w, feMediaAsset720h, ]), - ).toEqual([ - { - url: 'https://guim-example.co.uk/atomID-1_480w.mp4', - mimeType: 'video/mp4', - dimensions: { - height: 384, - width: 480, + [ + { + url: 'https://guim-example.co.uk/atomID-1_480w.mp4', + mimeType: 'video/mp4', + aspectRatio: undefined, + dimensions: { + height: 384, + width: 480, + }, + hasAudio: true, }, - hasAudio: true, - }, - { - url: 'https://guim-example.co.uk/atomID-1_720h.mp4', - mimeType: 'video/mp4', - dimensions: { - height: 720, - width: 900, + { + url: 'https://guim-example.co.uk/atomID-1_720h.mp4', + mimeType: 'video/mp4', + aspectRatio: undefined, + dimensions: { + height: 720, + width: 900, + }, + hasAudio: true, }, - hasAudio: true, - }, - ]); + ], + ); }); - it('should return an empty array when given an empty array', () => { - expect(convertFEMediaAssetsToVideoAssets([])).toEqual([]); + void it('should return an empty array when given an empty array', () => { + assert.deepEqual(convertFEMediaAssetsToVideoAssets([]), []); }); }); - describe('getAspectRatioFromSources', () => { - it('should return the aspect ratio from the first source if it is defined', () => { + void describe('getAspectRatioFromSources', () => { + void it('should return the aspect ratio from the first source if it is defined', () => { const testSource: Source = { ...mp4Src480w, height: 720, @@ -227,12 +238,13 @@ describe('video', () => { const fiveThreeAspectRatio = 1.667; - expect(getAspectRatioFromSources([testSource])).toEqual( + assert.deepEqual( + getAspectRatioFromSources([testSource]), fiveThreeAspectRatio, ); }); - it('should calculate the aspect ratio from the width and height if aspect ratio is missing', () => { + void it('should calculate the aspect ratio from the width and height if aspect ratio is missing', () => { const testSource: Source = { ...mp4Src480w, height: 720, @@ -243,12 +255,13 @@ describe('video', () => { const twoThreeAspectRatio = 0.667; - expect(getAspectRatioFromSources([testSource])).toEqual( + assert.deepEqual( + getAspectRatioFromSources([testSource]), twoThreeAspectRatio, ); }); - it('should return the default aspect ratio if the aspect ratio is undefined and width is 0', () => { + void it('should return the default aspect ratio if the aspect ratio is undefined and width is 0', () => { const testSource: Source = { ...mp4Src480w, height: 720, @@ -256,10 +269,10 @@ describe('video', () => { aspectRatio: undefined, hasAudio: true, }; - expect(getAspectRatioFromSources([testSource])).toEqual(5 / 4); + assert.deepEqual(getAspectRatioFromSources([testSource]), 5 / 4); }); - it('should return the default aspect ratio if the aspect ratio is undefined and height is 0', () => { + void it('should return the default aspect ratio if the aspect ratio is undefined and height is 0', () => { const testSource: Source = { ...mp4Src480w, height: 0, @@ -267,11 +280,11 @@ describe('video', () => { aspectRatio: undefined, hasAudio: true, }; - expect(getAspectRatioFromSources([testSource])).toEqual(5 / 4); + assert.deepEqual(getAspectRatioFromSources([testSource]), 5 / 4); }); }); - describe('findOptimisedSourcePerMimeType', () => { + void describe('findOptimisedSourcePerMimeType', () => { const testSources: Source[] = [ mp4Src480w, mp4Src720h, @@ -279,7 +292,7 @@ describe('video', () => { m3u8Src720h, ]; - it('selects the smaller videos when there are multiple and all are larger than the screen width.', () => { + void it('selects the smaller videos when there are multiple and all are larger than the screen width.', () => { const screenWidth = 400; const sources = findOptimisedSourcePerMimeType( @@ -287,10 +300,10 @@ describe('video', () => { screenWidth, ); - expect(sources).toEqual([mp4Src480w, m3u8Src480w]); + assert.deepEqual(sources, [mp4Src480w, m3u8Src480w]); }); - it('selects the larger videos when there are two and one is larger than the screen width and one is smaller.', () => { + void it('selects the larger videos when there are two and one is larger than the screen width and one is smaller.', () => { const screenWidth = 600; const sources = findOptimisedSourcePerMimeType( @@ -298,10 +311,10 @@ describe('video', () => { screenWidth, ); - expect(sources).toEqual([mp4Src720h, m3u8Src720h]); + assert.deepEqual(sources, [mp4Src720h, m3u8Src720h]); }); - it('selects the larger videos when there are multiple and all are smaller than the screen width.', () => { + void it('selects the larger videos when there are multiple and all are smaller than the screen width.', () => { const screenWidth = 800; const sources = findOptimisedSourcePerMimeType( @@ -309,10 +322,10 @@ describe('video', () => { screenWidth, ); - expect(sources).toEqual([mp4Src720h, m3u8Src720h]); + assert.deepEqual(sources, [mp4Src720h, m3u8Src720h]); }); - it('selects the smaller videos when some are equal to the screen width and others are larger.', () => { + void it('selects the smaller videos when some are equal to the screen width and others are larger.', () => { const screenWidth = 480; const sources = findOptimisedSourcePerMimeType( @@ -320,10 +333,10 @@ describe('video', () => { screenWidth, ); - expect(sources).toEqual([mp4Src480w, m3u8Src480w]); + assert.deepEqual(sources, [mp4Src480w, m3u8Src480w]); }); - it('selects the larger videos when some are equal to the screen width and others are smaller.', () => { + void it('selects the larger videos when some are equal to the screen width and others are smaller.', () => { const screenWidth = 720; const sources = findOptimisedSourcePerMimeType( @@ -331,33 +344,34 @@ describe('video', () => { screenWidth, ); - expect(sources).toEqual([mp4Src720h, m3u8Src720h]); + assert.deepEqual(sources, [mp4Src720h, m3u8Src720h]); }); }); - describe('convertCurrentTimeToProgressPercentage', () => { - it.each([ + void describe('convertCurrentTimeToProgressPercentage', () => { + for (const testCase of [ { currentTime: 0, duration: 23, expectedPercentage: 0 }, { currentTime: 24, duration: 32, expectedPercentage: 75 }, { currentTime: 56, duration: 56, expectedPercentage: 100 }, { currentTime: 12, duration: 11, expectedPercentage: 100 }, { currentTime: -5, duration: 10, expectedPercentage: null }, { currentTime: 5, duration: -10, expectedPercentage: null }, - ])( - 'should return the correct progress percentage based on the current time and duration', - ({ currentTime, duration, expectedPercentage }) => { - expect( + ]) { + void it('should return the correct progress percentage based on the current time and duration', () => { + const { currentTime, duration, expectedPercentage } = testCase; + assert.deepEqual( convertCurrentTimeToProgressPercentage( currentTime, duration, ), - ).toEqual(expectedPercentage); - }, - ); + expectedPercentage, + ); + }); + } }); - describe('convertProgressPercentageToCurrentTime', () => { - it.each([ + void describe('convertProgressPercentageToCurrentTime', () => { + for (const testCase of [ { progressPercentage: 0, duration: 23, expectedCurrentTime: 0 }, { progressPercentage: 75, duration: 32, expectedCurrentTime: 24 }, { progressPercentage: 100, duration: 56, expectedCurrentTime: 56 }, @@ -369,21 +383,23 @@ describe('video', () => { duration: 10, expectedCurrentTime: 0, }, - ])( - 'should return the correct current time based on the progress percentage and duration', - ({ progressPercentage, duration, expectedCurrentTime }) => { - expect( + ]) { + void it('should return the correct current time based on the progress percentage and duration', () => { + const { progressPercentage, duration, expectedCurrentTime } = + testCase; + assert.deepEqual( convertProgressPercentageToCurrentTime( progressPercentage, duration, ), - ).toEqual(expectedCurrentTime); - }, - ); + expectedCurrentTime, + ); + }); + } }); - describe('formatTimeForDisplay', () => { - it.each([ + void describe('formatTimeForDisplay', () => { + for (const testCase of [ { timeInSeconds: -1.24, expectedFormattedTime: '0:00' }, { timeInSeconds: 0, expectedFormattedTime: '0:00' }, { timeInSeconds: 59, expectedFormattedTime: '0:59' }, @@ -392,28 +408,30 @@ describe('video', () => { { timeInSeconds: 92.5, expectedFormattedTime: '1:32' }, { timeInSeconds: 1000, expectedFormattedTime: '16:40' }, { timeInSeconds: 10000, expectedFormattedTime: '166:40' }, - ])( - 'should return the correct formatted time based on the time in seconds', - ({ timeInSeconds, expectedFormattedTime }) => { - expect(formatTimeForDisplay(timeInSeconds)).toEqual( + ]) { + void it('should return the correct formatted time based on the time in seconds', () => { + const { timeInSeconds, expectedFormattedTime } = testCase; + assert.deepEqual( + formatTimeForDisplay(timeInSeconds), expectedFormattedTime, ); - }, - ); + }); + } }); - describe('roundAspectRatio', () => { - it.each([ + void describe('roundAspectRatio', () => { + for (const testCase of [ { aspectRatio: 0.56938445, expectedRoundedAspectRatio: 0.569 }, { aspectRatio: 1.277777, expectedRoundedAspectRatio: 1.278 }, { aspectRatio: 1.25, expectedRoundedAspectRatio: 1.25 }, { aspectRatio: 0.8, expectedRoundedAspectRatio: 0.8 }, - ])( - 'should return the correct aspect ratio rounded to 3 decimal places', - ({ aspectRatio, expectedRoundedAspectRatio }) => { - expect(roundAspectRatio(aspectRatio)).toEqual( + ]) { + void it('should return the correct aspect ratio rounded to 3 decimal places', () => { + const { aspectRatio, expectedRoundedAspectRatio } = testCase; + assert.deepEqual( + roundAspectRatio(aspectRatio), expectedRoundedAspectRatio, ); - }, - ); + }); + } }); }); diff --git a/dotcom-rendering/src/model/article-sections.test.ts b/dotcom-rendering/src/model/article-sections.node.test.ts similarity index 87% rename from dotcom-rendering/src/model/article-sections.test.ts rename to dotcom-rendering/src/model/article-sections.node.test.ts index 079cf8feb77..e635459e027 100644 --- a/dotcom-rendering/src/model/article-sections.test.ts +++ b/dotcom-rendering/src/model/article-sections.node.test.ts @@ -1,6 +1,8 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; import { findBySubsection } from './article-sections'; -describe('returns section for each subsection', () => { +void describe('returns section for each subsection', () => { const testCases = [ [[], 'Guardian'], [['books', 'childrens-books-site'], 'Books'], @@ -89,10 +91,10 @@ describe('returns section for each subsection', () => { [['tv-and-radio'], 'TvRadio'], ] as const; - it('returns correct Section for each test case', () => { + void it('returns correct Section for each test case', () => { for (const [subsections, section] of testCases) { for (const subsection of subsections) { - expect(findBySubsection(subsection).name).toEqual(section); + assert.equal(findBySubsection(subsection).name, section); } } }); diff --git a/dotcom-rendering/src/model/buildLightboxImages.test.ts b/dotcom-rendering/src/model/buildLightboxImages.node.test.ts similarity index 67% rename from dotcom-rendering/src/model/buildLightboxImages.test.ts rename to dotcom-rendering/src/model/buildLightboxImages.node.test.ts index d114db1c2ea..a263a9bd793 100644 --- a/dotcom-rendering/src/model/buildLightboxImages.test.ts +++ b/dotcom-rendering/src/model/buildLightboxImages.node.test.ts @@ -1,3 +1,5 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; import { Standard as ExampleArticle } from '../../fixtures/generated/fe-articles/Standard'; import { images } from '../../fixtures/generated/images'; import type { Block } from '../types/blocks'; @@ -75,8 +77,8 @@ const buildBlock = (elements: FEElement[]): Block => ({ secondaryDateLine: '', }); -describe('buildLightboxImages', () => { - it("includes a product's own image when it is large enough", () => { +void describe('buildLightboxImages', () => { + void it("includes a product's own image when it is large enough", () => { const product: ProductBlockElement = { ...baseProduct, image: largeProductImage, @@ -84,17 +86,26 @@ describe('buildLightboxImages', () => { const result = buildLightboxImages(format, [buildBlock([product])], []); - expect(result).toHaveLength(1); - expect(result[0]).toMatchObject({ - masterUrl: largeProductImage.url, - elementId: product.elementId, - width: largeProductImage.width, - height: largeProductImage.height, - position: 1, - }); + assert.equal(result.length, 1); + assert.deepEqual( + { + masterUrl: result[0]?.masterUrl, + elementId: result[0]?.elementId, + width: result[0]?.width, + height: result[0]?.height, + position: result[0]?.position, + }, + { + masterUrl: largeProductImage.url, + elementId: product.elementId, + width: largeProductImage.width, + height: largeProductImage.height, + position: 1, + }, + ); }); - it("excludes a product's own image when it is too small", () => { + void it("excludes a product's own image when it is too small", () => { const product: ProductBlockElement = { ...baseProduct, image: smallProductImage, @@ -102,20 +113,20 @@ describe('buildLightboxImages', () => { const result = buildLightboxImages(format, [buildBlock([product])], []); - expect(result).toEqual([]); + assert.deepEqual(result, []); }); - it('excludes a product with no image', () => { + void it('excludes a product with no image', () => { const result = buildLightboxImages( format, [buildBlock([baseProduct])], [], ); - expect(result).toEqual([]); + assert.deepEqual(result, []); }); - it("includes images nested inside a product's content", () => { + void it("includes images nested inside a product's content", () => { const product: ProductBlockElement = { ...baseProduct, content: [largeImage], @@ -123,11 +134,11 @@ describe('buildLightboxImages', () => { const result = buildLightboxImages(format, [buildBlock([product])], []); - expect(result).toHaveLength(1); - expect(result[0]?.elementId).toEqual(largeImage.elementId); + assert.equal(result.length, 1); + assert.deepEqual(result[0]?.elementId, largeImage.elementId); }); - it('assigns positions in document order across regular and product images', () => { + void it('assigns positions in document order across regular and product images', () => { const product: ProductBlockElement = { ...baseProduct, elementId: 'product-2', @@ -137,14 +148,17 @@ describe('buildLightboxImages', () => { const result = buildLightboxImages(format, [buildBlock([product])], []); - expect(result.map((image) => image.elementId)).toEqual([ - largeImage.elementId, - product.elementId, - ]); - expect(result.map((image) => image.position)).toEqual([1, 2]); + assert.deepEqual( + result.map((image) => image.elementId), + [largeImage.elementId, product.elementId], + ); + assert.deepEqual( + result.map((image) => image.position), + [1, 2], + ); }); - it("includes a product's own CTAs on its card image", () => { + void it("includes a product's own CTAs on its card image", () => { const product: ProductBlockElement = { ...baseProduct, image: largeProductImage, @@ -153,10 +167,10 @@ describe('buildLightboxImages', () => { const result = buildLightboxImages(format, [buildBlock([product])], []); - expect(result[0]?.productCtas).toEqual(productCtas); + assert.deepEqual(result[0]?.productCtas, productCtas); }); - it('omits productCtas entirely when a product has none', () => { + void it('omits productCtas entirely when a product has none', () => { const product: ProductBlockElement = { ...baseProduct, image: largeProductImage, @@ -165,10 +179,10 @@ describe('buildLightboxImages', () => { const result = buildLightboxImages(format, [buildBlock([product])], []); - expect(result[0]?.productCtas).toBeUndefined(); + assert.equal(result[0]?.productCtas, undefined); }); - it("includes the owning product's CTAs on an image nested inside its content", () => { + void it("includes the owning product's CTAs on an image nested inside its content", () => { const product: ProductBlockElement = { ...baseProduct, content: [largeImage], @@ -177,11 +191,11 @@ describe('buildLightboxImages', () => { const result = buildLightboxImages(format, [buildBlock([product])], []); - expect(result).toHaveLength(1); - expect(result[0]?.productCtas).toEqual(productCtas); + assert.equal(result.length, 1); + assert.deepEqual(result[0]?.productCtas, productCtas); }); - it("uses the innermost product's CTAs for an image nested inside a product nested in another product's content", () => { + void it("uses the innermost product's CTAs for an image nested inside a product nested in another product's content", () => { const innerCtas: ProductCta[] = [ { url: 'https://example.com/inner', @@ -217,11 +231,11 @@ describe('buildLightboxImages', () => { [], ); - expect(result).toHaveLength(1); - expect(result[0]?.productCtas).toEqual(innerCtas); + assert.equal(result.length, 1); + assert.deepEqual(result[0]?.productCtas, innerCtas); }); - it("falls back to the product's own caption for a content image with no caption of its own", () => { + void it("falls back to the product's own caption for a content image with no caption of its own", () => { const product: ProductBlockElement = { ...baseProduct, image: largeProductImage, @@ -233,13 +247,16 @@ describe('buildLightboxImages', () => { const contentEntry = result.find( (image) => image.elementId === largeImage.elementId, ); - expect(contentEntry?.caption).toEqual(largeProductImage.caption); + assert.deepEqual(contentEntry?.caption, largeProductImage.caption); }); - it("keeps a content image's own caption instead of falling back to the product's", () => { + void it("keeps a content image's own caption instead of falling back to the product's", () => { const imageWithOwnCaption = { ...largeImage, - data: { ...largeImage.data, caption: "The image's own caption" }, + data: { + ...largeImage.data, + caption: "The image's own caption", + }, }; const product: ProductBlockElement = { ...baseProduct, @@ -252,10 +269,10 @@ describe('buildLightboxImages', () => { const contentEntry = result.find( (image) => image.elementId === largeImage.elementId, ); - expect(contentEntry?.caption).toEqual("The image's own caption"); + assert.deepEqual(contentEntry?.caption, "The image's own caption"); }); - it("uses the innermost product's caption for an image nested inside a product nested in another product's content", () => { + void it("uses the innermost product's caption for an image nested inside a product nested in another product's content", () => { const innerProductImage: ProductImage = { ...largeProductImage, caption: 'Inner product caption', @@ -286,10 +303,10 @@ describe('buildLightboxImages', () => { const contentEntry = result.find( (image) => image.elementId === largeImage.elementId, ); - expect(contentEntry?.caption).toEqual(innerProductImage.caption); + assert.deepEqual(contentEntry?.caption, innerProductImage.caption); }); - it("keeps a product's content image and card image adjacent, rather than grouping all content images before all card images", () => { + void it("keeps a product's content image and card image adjacent, rather than grouping all content images before all card images", () => { const secondImage = images[1]; const productA: ProductBlockElement = { @@ -316,16 +333,22 @@ describe('buildLightboxImages', () => { [], ); - expect(result.map((image) => image.elementId)).toEqual([ - largeImage.elementId, - productA.elementId, - secondImage.elementId, - productB.elementId, - ]); - expect(result.map((image) => image.position)).toEqual([1, 2, 3, 4]); + assert.deepEqual( + result.map((image) => image.elementId), + [ + largeImage.elementId, + productA.elementId, + secondImage.elementId, + productB.elementId, + ], + ); + assert.deepEqual( + result.map((image) => image.position), + [1, 2, 3, 4], + ); }); - it("gives every sub-image of a MultiImageBlockElement the owning product's CTAs", () => { + void it("gives every sub-image of a MultiImageBlockElement the owning product's CTAs", () => { const multiImage: MultiImageBlockElement = { _type: 'model.dotcomrendering.pageElements.MultiImageBlockElement', elementId: 'multi-1', @@ -339,8 +362,9 @@ describe('buildLightboxImages', () => { const result = buildLightboxImages(format, [buildBlock([product])], []); - expect(result).toHaveLength(2); - expect(result.every((image) => image.productCtas === productCtas)).toBe( + assert.equal(result.length, 2); + assert.equal( + result.every((image) => image.productCtas === productCtas), true, ); }); diff --git a/dotcom-rendering/src/model/enhance-ad-placeholders.test.ts b/dotcom-rendering/src/model/enhance-ad-placeholders.node.test.ts similarity index 76% rename from dotcom-rendering/src/model/enhance-ad-placeholders.test.ts rename to dotcom-rendering/src/model/enhance-ad-placeholders.node.test.ts index 09dc975a89d..596cc7c3839 100644 --- a/dotcom-rendering/src/model/enhance-ad-placeholders.test.ts +++ b/dotcom-rendering/src/model/enhance-ad-placeholders.node.test.ts @@ -1,3 +1,5 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; import { ArticleDesign, ArticleDisplay, Pillar } from '../lib/articleFormat'; import type { AdPlaceholderBlockElement, @@ -73,8 +75,8 @@ const elementIsAdPlaceholder = ( 'model.dotcomrendering.pageElements.AdPlaceholderBlockElement'; // Tests -describe('enhanceAdPlaceholders', () => { - describe('for general articles', () => { +void describe('enhanceAdPlaceholders', () => { + void describe('for general articles', () => { const testCases = [ { paragraphs: 0, expectedPositions: [] }, { paragraphs: 1, expectedPositions: [] }, @@ -107,9 +109,8 @@ describe('enhanceAdPlaceholders', () => { }, ] satisfies Array<{ paragraphs: number; expectedPositions: number[] }>; - describe.each(testCases)( - 'for $paragraphs paragraph(s) in an article', - ({ paragraphs, expectedPositions }) => { + for (const { paragraphs, expectedPositions } of testCases) { + void describe(`for ${paragraphs} paragraph(s) in an article`, () => { const elements = getTestParagraphElements(paragraphs); const expectedPlaceholders = expectedPositions.length; const input: FEElement[] = elements; @@ -123,23 +124,24 @@ describe('enhanceAdPlaceholders', () => { elementIsAdPlaceholder(el) ? [idx] : [], ); - it(`should insert ${expectedPlaceholders} ad placeholder(s)`, () => { - expect(placeholderIndices.length).toEqual( + void it(`should insert ${expectedPlaceholders} ad placeholder(s)`, () => { + assert.deepEqual( + placeholderIndices.length, expectedPlaceholders, ); }); if (expectedPlaceholders > 0) { - it(`should insert ad placeholder(s) in the expected position(s): ${expectedPositions.join( + void it(`should insert ad placeholder(s) in the expected position(s): ${expectedPositions.join( ',', )}`, () => { - expect(placeholderIndices).toEqual(expectedPositions); + assert.deepEqual(placeholderIndices, expectedPositions); }); } - }, - ); + }); + } - it('should not insert an ad placeholder before an inline image element, but can insert it after the image', () => { + void it('should not insert an ad placeholder before an inline image element, but can insert it after the image', () => { const threeParagraphs = getTestParagraphElements(3); const elements = [ @@ -157,17 +159,17 @@ describe('enhanceAdPlaceholders', () => { )(input); const outputPlaceholders = output.filter(elementIsAdPlaceholder); - expect(outputPlaceholders.length).toEqual(1); + assert.deepEqual(outputPlaceholders.length, 1); const placeholderIndices = output.flatMap((el, idx) => elementIsAdPlaceholder(el) ? [idx] : [], ); // Expect one placeholder to be present after the fourth element only - expect(placeholderIndices).toEqual([4]); + assert.deepEqual(placeholderIndices, [4]); }); - it('should not insert an ad placeholder after a thumbnail image element', () => { + void it('should not insert an ad placeholder after a thumbnail image element', () => { const threeParagraphs = getTestParagraphElements(3); const elements = [ @@ -185,17 +187,17 @@ describe('enhanceAdPlaceholders', () => { )(input); const outputPlaceholders = output.filter(elementIsAdPlaceholder); - expect(outputPlaceholders.length).toEqual(1); + assert.deepEqual(outputPlaceholders.length, 1); const placeholderIndices = output.flatMap((el, idx) => elementIsAdPlaceholder(el) ? [idx] : [], ); // Expect one placeholder to be present after the fifth element only - expect(placeholderIndices).toEqual([5]); + assert.deepEqual(placeholderIndices, [5]); }); - it('should not insert an ad placeholder after an element which is not an image or text', () => { + void it('should not insert an ad placeholder after an element which is not an image or text', () => { const threeParagraphs = getTestParagraphElements(3); const elements = [ @@ -213,17 +215,17 @@ describe('enhanceAdPlaceholders', () => { )(input); const outputPlaceholders = output.filter(elementIsAdPlaceholder); - expect(outputPlaceholders.length).toEqual(1); + assert.deepEqual(outputPlaceholders.length, 1); const placeholderIndices = output.flatMap((el, idx) => elementIsAdPlaceholder(el) ? [idx] : [], ); // Expect one placeholder to be present after the fifth element only - expect(placeholderIndices).toEqual([5]); + assert.deepEqual(placeholderIndices, [5]); }); - it('should not insert ad placeholders if shouldHideAds is true', () => { + void it('should not insert ad placeholders if shouldHideAds is true', () => { const input: FEElement[] = getTestParagraphElements(6); const output = enhanceAdPlaceholders( @@ -233,11 +235,11 @@ describe('enhanceAdPlaceholders', () => { )(input); const outputPlaceholders = output.filter(elementIsAdPlaceholder); - expect(outputPlaceholders.length).toEqual(0); + assert.deepEqual(outputPlaceholders.length, 0); }); }); - describe('for gallery articles', () => { + void describe('for gallery articles', () => { const testCases = [ { images: 0, expectedPositions: [] }, { images: 1, expectedPositions: [] }, @@ -254,9 +256,8 @@ describe('enhanceAdPlaceholders', () => { }, ] satisfies Array<{ images: number; expectedPositions: number[] }>; - describe.each(testCases)( - 'for $images images(s) in a gallery article', - ({ images, expectedPositions }) => { + for (const { images, expectedPositions } of testCases) { + void describe(`for ${images} images(s) in a gallery article`, () => { const elements = getTestImageBlockElements(images); const expectedPlaceholders = expectedPositions.length; const input: FEElement[] = elements; @@ -270,23 +271,24 @@ describe('enhanceAdPlaceholders', () => { elementIsAdPlaceholder(el) ? [idx] : [], ); - it(`should insert ${expectedPlaceholders} ad placeholder(s)`, () => { - expect(placeholderIndices.length).toEqual( + void it(`should insert ${expectedPlaceholders} ad placeholder(s)`, () => { + assert.deepEqual( + placeholderIndices.length, expectedPlaceholders, ); }); if (expectedPlaceholders > 0) { - it(`should insert ad placeholder(s) in the expected position(s): ${expectedPositions.join( + void it(`should insert ad placeholder(s) in the expected position(s): ${expectedPositions.join( ',', )}`, () => { - expect(placeholderIndices).toEqual(expectedPositions); + assert.deepEqual(placeholderIndices, expectedPositions); }); } - }, - ); + }); + } - it('should not insert ad placeholders if shouldHideAds is true', () => { + void it('should not insert ad placeholders if shouldHideAds is true', () => { const input: FEElement[] = getTestParagraphElements(6); const output = enhanceAdPlaceholders( @@ -296,10 +298,10 @@ describe('enhanceAdPlaceholders', () => { )(input); const outputPlaceholders = output.filter(elementIsAdPlaceholder); - expect(outputPlaceholders.length).toEqual(0); + assert.deepEqual(outputPlaceholders.length, 0); }); - it('should still insert ad placeholders if renderingTarget is web', () => { + void it('should still insert ad placeholders if renderingTarget is web', () => { const input: FEElement[] = getTestParagraphElements(6); const output = enhanceAdPlaceholders( @@ -309,7 +311,7 @@ describe('enhanceAdPlaceholders', () => { )(input); const outputPlaceholders = output.filter(elementIsAdPlaceholder); - expect(outputPlaceholders.length).toBeGreaterThan(0); + assert(outputPlaceholders.length > 0); }); }); }); diff --git a/dotcom-rendering/src/model/enhance-dots.test.ts b/dotcom-rendering/src/model/enhance-dots.node.test.ts similarity index 81% rename from dotcom-rendering/src/model/enhance-dots.test.ts rename to dotcom-rendering/src/model/enhance-dots.node.test.ts index 2f4720e5724..2a98da3019c 100644 --- a/dotcom-rendering/src/model/enhance-dots.test.ts +++ b/dotcom-rendering/src/model/enhance-dots.node.test.ts @@ -1,8 +1,10 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; import type { FEElement } from '../types/content'; import { enhanceDots } from './enhance-dots'; -describe('Middot Tests', () => { - it('Output should not be the same as input as dot has been replaced', () => { +void describe('Middot Tests', () => { + void it('Output should not be the same as input as dot has been replaced', () => { const input: FEElement[] = [ { _type: 'model.dotcomrendering.pageElements.TextBlockElement', @@ -29,10 +31,10 @@ describe('Middot Tests', () => { }, ]; - expect(enhanceDots(input)).not.toBe(expectedOutput); + assert.notEqual(enhanceDots(input), expectedOutput); }); - it('It does not incorrectly replace * with dot spans', () => { + void it('It does not incorrectly replace * with dot spans', () => { const input: FEElement[] = [ { _type: 'model.dotcomrendering.pageElements.TextBlockElement', @@ -70,6 +72,6 @@ describe('Middot Tests', () => { }, ]; - expect(enhanceDots(input)).toEqual(expectedOutput); + assert.deepEqual(enhanceDots(input), expectedOutput); }); }); diff --git a/dotcom-rendering/src/model/enhance-product-summary.test.ts b/dotcom-rendering/src/model/enhance-product-summary.node.test.ts similarity index 78% rename from dotcom-rendering/src/model/enhance-product-summary.test.ts rename to dotcom-rendering/src/model/enhance-product-summary.node.test.ts index e53cca12ced..e0ac850f68c 100644 --- a/dotcom-rendering/src/model/enhance-product-summary.test.ts +++ b/dotcom-rendering/src/model/enhance-product-summary.node.test.ts @@ -1,3 +1,5 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; import { enhanceProductSummary } from './enhance-product-summary'; import { findEnhancedProductSummary, @@ -5,8 +7,8 @@ import { productSummaryElement, } from './enhance-product-summary.test-helpers'; -describe('enhanceProductSummary', () => { - it('enhances product summary elements with its selected product elements', () => { +void describe('enhanceProductSummary', () => { + void it('enhances product summary elements with its selected product elements', () => { const selectedIds = ['1', '2']; const input = [ productElement( @@ -37,15 +39,16 @@ describe('enhanceProductSummary', () => { const enhancedProductSummaryElement = findEnhancedProductSummary(output); - expect(enhancedProductSummaryElement?.products).toHaveLength(2); - expect( + assert.equal(enhancedProductSummaryElement?.products.length, 2); + assert.deepEqual( enhancedProductSummaryElement?.products.map( (mapping) => mapping.productBlock.id, ), - ).toEqual(selectedIds); + selectedIds, + ); }); - it('enhances product summary elements with the correct CTA indices', () => { + void it('enhances product summary elements with the correct CTA indices', () => { const summaryProducts = [ { productId: '3', ctaIndex: 1 }, { productId: '1', ctaIndex: 0 }, @@ -77,16 +80,18 @@ describe('enhanceProductSummary', () => { const enhancedProductSummaryElement = findEnhancedProductSummary(output); - expect(enhancedProductSummaryElement?.products).toHaveLength(2); - expect( + assert.equal(enhancedProductSummaryElement?.products.length, 2); + assert.deepEqual( enhancedProductSummaryElement?.products.map( (mapping) => mapping.ctaIndex, ), - ).toEqual([1, 0]); - expect( + [1, 0], + ); + assert.deepEqual( enhancedProductSummaryElement?.products.map( (mapping) => mapping.productBlock.id, ), - ).toEqual(['3', '1']); + ['3', '1'], + ); }); }); diff --git a/dotcom-rendering/src/model/enhance-videos.test.ts b/dotcom-rendering/src/model/enhance-videos.node.test.ts similarity index 69% rename from dotcom-rendering/src/model/enhance-videos.test.ts rename to dotcom-rendering/src/model/enhance-videos.node.test.ts index d434f8da4c1..a2213b2d1c8 100644 --- a/dotcom-rendering/src/model/enhance-videos.test.ts +++ b/dotcom-rendering/src/model/enhance-videos.node.test.ts @@ -1,10 +1,12 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; import { ArticleDesign, type ArticleFormat } from '../lib/articleFormat'; import type { FEElement } from '../types/content'; import { enhanceGuVideos } from './enhance-videos'; -describe('Enhance Videos', () => { - describe('for GuVideoElement', () => { - it('sets the html of the GuVideoBlockElement', () => { +void describe('Enhance Videos', () => { + void describe('for GuVideoElement', () => { + void it('sets the html of the GuVideoBlockElement', () => { const html = ''; const videoElement: FEElement = { @@ -29,7 +31,8 @@ describe('Enhance Videos', () => { design: ArticleDesign.Video, } as unknown as ArticleFormat; - expect(enhanceGuVideos(format, html)(inputElements)).toEqual( + assert.deepEqual( + enhanceGuVideos(format, html)(inputElements), expectedOutput, ); }); diff --git a/dotcom-rendering/src/model/enhanceCommercialProperties.test.ts b/dotcom-rendering/src/model/enhanceCommercialProperties.node.test.ts similarity index 77% rename from dotcom-rendering/src/model/enhanceCommercialProperties.test.ts rename to dotcom-rendering/src/model/enhanceCommercialProperties.node.test.ts index 765f6e412a2..251c37e6da5 100644 --- a/dotcom-rendering/src/model/enhanceCommercialProperties.test.ts +++ b/dotcom-rendering/src/model/enhanceCommercialProperties.node.test.ts @@ -1,3 +1,5 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; import { Labs } from '../../fixtures/generated/fe-articles/Labs'; import { Standard } from '../../fixtures/generated/fe-articles/Standard'; import type { CommercialProperties } from '../types/commercial'; @@ -5,15 +7,16 @@ import { enhanceCommercialProperties } from './enhanceCommercialProperties'; const isNumber = (width: unknown): width is number => typeof width === 'number'; -describe('Enhance Branding', () => { - it('does not change properties if they have no branding', () => { +void describe('Enhance Branding', () => { + void it('does not change properties if they have no branding', () => { const { commercialProperties } = Standard; - expect(enhanceCommercialProperties(commercialProperties)).toEqual( + assert.deepEqual( + enhanceCommercialProperties(commercialProperties), commercialProperties, ); }); - it('should have no widths above 140', () => { + void it('should have no widths above 140', () => { const { commercialProperties: partial } = Labs; const commercialProperties: CommercialProperties = { ...partial, @@ -43,7 +46,7 @@ describe('Enhance Branding', () => { .map((p) => p.branding?.logo.dimensions.width) .filter(isNumber); - expect(Math.max(...dimensionsFail)).toBeGreaterThan(140); + assert(Math.max(...dimensionsFail) > 140); const dimensionsPass = Object.values( enhanceCommercialProperties(commercialProperties), @@ -51,6 +54,6 @@ describe('Enhance Branding', () => { .map((p) => p.branding?.logo.dimensions.width) .filter(isNumber); - expect(Math.max(...dimensionsPass)).toBeLessThanOrEqual(140); + assert(Math.max(...dimensionsPass) <= 140); }); }); diff --git a/dotcom-rendering/src/model/enhanceLists.test.ts b/dotcom-rendering/src/model/enhanceLists.node.test.ts similarity index 89% rename from dotcom-rendering/src/model/enhanceLists.test.ts rename to dotcom-rendering/src/model/enhanceLists.node.test.ts index e59476b04d3..90842a34d17 100644 --- a/dotcom-rendering/src/model/enhanceLists.test.ts +++ b/dotcom-rendering/src/model/enhanceLists.node.test.ts @@ -1,9 +1,11 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; import type { FEElement } from '../types/content'; import type { TagType } from '../types/tag'; import { enhanceLists } from './enhanceLists'; -describe('Enhance lists', () => { - it('enhances a multi-byline element correctly', () => { +void describe('Enhance lists', () => { + void it('enhances a multi-byline element correctly', () => { const elementsEnhancer = (elements: FEElement[]): FEElement[] => elements; @@ -92,7 +94,8 @@ describe('Enhance lists', () => { }, ]; - expect(enhanceLists(elementsEnhancer, tags)(inputElements)).toEqual( + assert.deepEqual( + enhanceLists(elementsEnhancer, tags)(inputElements), outputElements, ); }); diff --git a/dotcom-rendering/src/model/enhanceTags.test.ts b/dotcom-rendering/src/model/enhanceTags.node.test.ts similarity index 82% rename from dotcom-rendering/src/model/enhanceTags.test.ts rename to dotcom-rendering/src/model/enhanceTags.node.test.ts index 42d074e323f..54186af09e4 100644 --- a/dotcom-rendering/src/model/enhanceTags.test.ts +++ b/dotcom-rendering/src/model/enhanceTags.node.test.ts @@ -1,8 +1,10 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; import type { FETagType } from '../types/tag'; import { enhanceTags } from './enhanceTags'; -describe('enhanceTags', () => { - it('maps a list of FETagType to TagType', () => { +void describe('enhanceTags', () => { + void it('maps a list of FETagType to TagType', () => { const feTags: FETagType[] = [ { properties: { @@ -20,7 +22,7 @@ describe('enhanceTags', () => { const tags = enhanceTags(feTags); - expect(tags).toEqual([ + assert.deepEqual(tags, [ { id: 'profile/morwennaferrier', type: 'Contributor', diff --git a/dotcom-rendering/src/model/enhanceTimeline.test.ts b/dotcom-rendering/src/model/enhanceTimeline.node.test.ts similarity index 83% rename from dotcom-rendering/src/model/enhanceTimeline.test.ts rename to dotcom-rendering/src/model/enhanceTimeline.node.test.ts index 3db1b55acf7..6050e307ccc 100644 --- a/dotcom-rendering/src/model/enhanceTimeline.test.ts +++ b/dotcom-rendering/src/model/enhanceTimeline.node.test.ts @@ -1,4 +1,5 @@ import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; import { images } from '../../fixtures/generated/images'; import type { FEElement } from '../../src/types/content'; import { enhanceTimeline } from './enhanceTimeline'; @@ -140,8 +141,8 @@ const elementsWithMultipleSections: FEElement[] = [ }, ]; -describe('enhanceTimeline', () => { - it('keeps a main media with a role that is valid', () => { +void describe('enhanceTimeline', () => { + void it('keeps a main media with a role that is valid', () => { const enhanced = enhanceTimeline(identity)(elementsWithNoSections); assert.equal( enhanced[0]?._type, @@ -150,10 +151,10 @@ describe('enhanceTimeline', () => { const timelineEvent = enhanced[0].events[0]; assert.notEqual(timelineEvent, undefined); - expect(timelineEvent?.main).toBeDefined(); + assert.notEqual(timelineEvent?.main, undefined); }); - it('drops a main media with a role that is not valid', () => { + void it('drops a main media with a role that is not valid', () => { const enhanced = enhanceTimeline(identity)(elementsWithNoSections); assert.equal( enhanced[0]?._type, @@ -162,10 +163,10 @@ describe('enhanceTimeline', () => { const timelineEvent = enhanced[0].events[1]; assert.notEqual(timelineEvent, undefined); - expect(timelineEvent?.main).toBeUndefined(); + assert.equal(timelineEvent?.main, undefined); }); - it('keeps a main media without a role', () => { + void it('keeps a main media without a role', () => { const enhanced = enhanceTimeline(identity)(elementsWithNoSections); assert.equal( enhanced[0]?._type, @@ -174,9 +175,9 @@ describe('enhanceTimeline', () => { const timelineEvent = enhanced[0].events[2]; assert.notEqual(timelineEvent, undefined); - expect(timelineEvent?.main).toBeDefined(); + assert.notEqual(timelineEvent?.main, undefined); }); - it('keeps a body element with a role that is valid', () => { + void it('keeps a body element with a role that is valid', () => { const enhanced = enhanceTimeline(identity)(elementsWithNoSections); assert.equal( enhanced[0]?._type, @@ -185,10 +186,10 @@ describe('enhanceTimeline', () => { const timelineEvent = enhanced[0].events[3]; assert.notEqual(timelineEvent, undefined); - expect(timelineEvent?.body).toEqual([images[1]]); + assert.deepEqual(timelineEvent?.body, [images[1]]); }); - it('drops a body element with a role that is not valid', () => { + void it('drops a body element with a role that is not valid', () => { const enhanced = enhanceTimeline(identity)(elementsWithNoSections); assert.equal( enhanced[0]?._type, @@ -197,10 +198,10 @@ describe('enhanceTimeline', () => { const timelineEvent = enhanced[0].events[4]; assert.notEqual(timelineEvent, undefined); - expect(timelineEvent?.body).toEqual([]); + assert.deepEqual(timelineEvent?.body, []); }); - it('keeps a body element without a role', () => { + void it('keeps a body element without a role', () => { const enhanced = enhanceTimeline(identity)(elementsWithNoSections); assert.equal( enhanced[0]?._type, @@ -209,7 +210,7 @@ describe('enhanceTimeline', () => { const timelineEvent = enhanced[0].events[5]; assert.notEqual(timelineEvent, undefined); - expect(timelineEvent?.body).toEqual([ + assert.deepEqual(timelineEvent?.body, [ { _type: 'model.dotcomrendering.pageElements.MediaAtomBlockElement', elementId: 'mock-id', @@ -219,7 +220,7 @@ describe('enhanceTimeline', () => { ]); }); - it('enhances a timeline with one section appropriately', () => { + void it('enhances a timeline with one section appropriately', () => { const enhanced = enhanceTimeline(identity)(elementsWithOneSection); assert.equal( enhanced[0]?._type, @@ -228,10 +229,10 @@ describe('enhanceTimeline', () => { const timelineSection = enhanced[0].sections[0]; assert.notEqual(timelineSection, undefined); - expect(timelineSection?.title).toEqual('Section 1'); + assert.deepEqual(timelineSection?.title, 'Section 1'); }); - it('enhances a timeline with multiple sections appropriately', () => { + void it('enhances a timeline with multiple sections appropriately', () => { const enhanced = enhanceTimeline(identity)( elementsWithMultipleSections, ); @@ -241,6 +242,6 @@ describe('enhanceTimeline', () => { ); const timelineSections = enhanced[0].sections; - expect(timelineSections).toHaveLength(2); + assert.equal(timelineSections.length, 2); }); }); diff --git a/dotcom-rendering/src/model/extractTrendingTopics.test.ts b/dotcom-rendering/src/model/extractTrendingTopics.node.test.ts similarity index 81% rename from dotcom-rendering/src/model/extractTrendingTopics.test.ts rename to dotcom-rendering/src/model/extractTrendingTopics.node.test.ts index 1dca8ddceb0..344beb78336 100644 --- a/dotcom-rendering/src/model/extractTrendingTopics.test.ts +++ b/dotcom-rendering/src/model/extractTrendingTopics.node.test.ts @@ -1,3 +1,5 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; import type { FETagType } from '../types/tag'; import type { NarrowedFECollectionType, @@ -43,8 +45,8 @@ const tagD = tag('d'); const tagE = tag('e'); const tagF = tag('f'); -describe('extractTrendingTopics', () => { - it('returns tags in correct order with normal input', () => { +void describe('extractTrendingTopics', () => { + void it('returns tags in correct order with normal input', () => { const collection: NarrowedFECollectionType = { curated: [ card('a', [tagA]), @@ -58,12 +60,13 @@ describe('extractTrendingTopics', () => { ], }; - expect( + assert.deepEqual( extractTrendingTopicsFomFront([collection], 'au/environment'), - ).toEqual([tagA, tagB, tagC, tagD, tagE, tagF]); + [tagA, tagB, tagC, tagD, tagE, tagF], + ); }); - it('deduplicates cards', () => { + void it('deduplicates cards', () => { const collection: NarrowedFECollectionType = { curated: [ card('a', [tagA]), @@ -82,15 +85,16 @@ describe('extractTrendingTopics', () => { curated: [card('g', [tagF])], backfill: [card('g', [tagF])], }; - expect( + assert.deepEqual( extractTrendingTopicsFomFront( [collection, secondCollection], 'au/environment', ), - ).toEqual([tagA, tagB, tagC, tagD, tagE, tagF]); + [tagA, tagB, tagC, tagD, tagE, tagF], + ); }); - it('removes cards with id matching pageId', () => { + void it('removes cards with id matching pageId', () => { const tagWithPageId = tag('au/environment'); const collection: NarrowedFECollectionType = { curated: [ @@ -104,12 +108,13 @@ describe('extractTrendingTopics', () => { card('f', [tagWithPageId, tagA, tagB, tagC, tagD, tagE, tagF]), ], }; - expect( + assert.deepEqual( extractTrendingTopicsFomFront([collection], 'au/environment'), - ).toEqual([tagA, tagB, tagC, tagD, tagE, tagF]); + [tagA, tagB, tagC, tagD, tagE, tagF], + ); }); - it('removes cards without paidContentType or tagType being Keyword or Topics', () => { + void it('removes cards without paidContentType or tagType being Keyword or Topics', () => { const tagWithTopicsPaidContentType = tag( 'tagWithTopicsPaidContentType', '', @@ -148,12 +153,13 @@ describe('extractTrendingTopics', () => { ]), ], }; - expect( + assert.deepEqual( extractTrendingTopicsFomFront([collection], 'au/environment'), - ).toEqual([ - tagWithTopicsPaidContentType, - tagWithKeywordPaidContentType, - tagWithKeywordTagType, - ]); + [ + tagWithTopicsPaidContentType, + tagWithKeywordPaidContentType, + tagWithKeywordTagType, + ], + ); }); }); diff --git a/dotcom-rendering/src/model/groupTrailsByDates.test.ts b/dotcom-rendering/src/model/groupTrailsByDates.node.test.ts similarity index 64% rename from dotcom-rendering/src/model/groupTrailsByDates.test.ts rename to dotcom-rendering/src/model/groupTrailsByDates.node.test.ts index b31cf6efe71..0f72fc1b7ef 100644 --- a/dotcom-rendering/src/model/groupTrailsByDates.test.ts +++ b/dotcom-rendering/src/model/groupTrailsByDates.node.test.ts @@ -1,3 +1,5 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; import { trails } from '../../fixtures/manual/trails'; import type { DCRFrontCard } from '../types/front'; import { groupTrailsByDates } from './groupTrailsByDates'; @@ -9,8 +11,8 @@ const datesToTrails = (dates: Date[]): DCRFrontCard[] => { })); }; -describe('groupTrailsByDates', () => { - it('Will split trails into days & months depending on the frequency', () => { +void describe('groupTrailsByDates', () => { + void it('Will split trails into days & months depending on the frequency', () => { const dates = [ // SHOULD BE GROUPED BY DAY // 3 on the 23rd of June @@ -45,15 +47,15 @@ describe('groupTrailsByDates', () => { const result = groupTrailsByDates(datesToTrails(dates), 'UK'); - expect(result[0]?.day).toEqual('26'); - expect(result[1]?.day).toEqual('25'); - expect(result[2]?.day).toEqual('23'); + assert.deepEqual(result[0]?.day, '26'); + assert.deepEqual(result[1]?.day, '25'); + assert.deepEqual(result[2]?.day, '23'); - expect(result[3]?.day).toEqual(undefined); - expect(result[3]?.month).toEqual('May'); + assert.deepEqual(result[3]?.day, undefined); + assert.deepEqual(result[3]?.month, 'May'); }); - it('Will handle all editions', () => { + void it('Will handle all editions', () => { const dates = [ // The whole of the last day of June (months are 0-indexed) '2024-06-30T00:00:00Z', @@ -84,33 +86,33 @@ describe('groupTrailsByDates', () => { const uk = groupTrailsByDates(datesToTrails(dates), 'UK'); - expect(uk[1]?.day).toEqual('30'); - expect(uk[1]?.month).toEqual('June'); - expect(uk[1]?.trails).toHaveLength(23); - expect(uk[0]?.day).toBeUndefined(); - expect(uk[0]?.month).toEqual('July'); - expect(uk[0]?.trails).toHaveLength(1); + assert.deepEqual(uk[1]?.day, '30'); + assert.deepEqual(uk[1]?.month, 'June'); + assert.equal(uk[1]?.trails.length, 23); + assert.equal(uk[0]?.day, undefined); + assert.deepEqual(uk[0]?.month, 'July'); + assert.equal(uk[0]?.trails.length, 1); const au = groupTrailsByDates(datesToTrails(dates), 'AU'); - expect(au[1]?.day).toEqual('30'); - expect(au[1]?.month).toEqual('June'); - expect(au[1]?.trails).toHaveLength(14); - expect(au[0]?.day).toEqual('1'); - expect(au[0]?.month).toEqual('July'); - expect(au[0]?.trails).toHaveLength(10); + assert.deepEqual(au[1]?.day, '30'); + assert.deepEqual(au[1]?.month, 'June'); + assert.equal(au[1]?.trails.length, 14); + assert.deepEqual(au[0]?.day, '1'); + assert.deepEqual(au[0]?.month, 'July'); + assert.equal(au[0]?.trails.length, 10); const us = groupTrailsByDates(datesToTrails(dates), 'US'); - expect(us[1]?.day).toEqual('29'); - expect(us[1]?.month).toEqual('June'); - expect(us[1]?.trails).toHaveLength(4); - expect(us[0]?.day).toEqual('30'); - expect(us[0]?.month).toEqual('June'); - expect(us[0]?.trails).toHaveLength(20); + assert.deepEqual(us[1]?.day, '29'); + assert.deepEqual(us[1]?.month, 'June'); + assert.equal(us[1]?.trails.length, 4); + assert.deepEqual(us[0]?.day, '30'); + assert.deepEqual(us[0]?.month, 'June'); + assert.equal(us[0]?.trails.length, 20); }); - it('Will respect "forceDay" being set to true', () => { + void it('Will respect "forceDay" being set to true', () => { const dates = [ // This would be grouped by month if left to the pop out frequency // 1 on the 2nd of May @@ -126,9 +128,9 @@ describe('groupTrailsByDates', () => { const result = groupTrailsByDates(datesToTrails(dates), 'UK', true); - expect(result[0]?.day).toEqual('5'); - expect(result[1]?.day).toEqual('4'); - expect(result[2]?.day).toEqual('3'); - expect(result[3]?.day).toEqual('2'); + assert.deepEqual(result[0]?.day, '5'); + assert.deepEqual(result[1]?.day, '4'); + assert.deepEqual(result[2]?.day, '3'); + assert.deepEqual(result[3]?.day, '2'); }); }); diff --git a/dotcom-rendering/src/model/unwrapHtml.test.ts b/dotcom-rendering/src/model/unwrapHtml.node.test.ts similarity index 70% rename from dotcom-rendering/src/model/unwrapHtml.test.ts rename to dotcom-rendering/src/model/unwrapHtml.node.test.ts index 6ff786cb5f6..72a54fe0567 100644 --- a/dotcom-rendering/src/model/unwrapHtml.test.ts +++ b/dotcom-rendering/src/model/unwrapHtml.node.test.ts @@ -1,9 +1,11 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; import { unwrapHtml } from './unwrapHtml'; type Params = Parameters[0]; -describe('unwrapHtml', () => { - it('Returns unwrapped HTML if prefix and suffix match', () => { +void describe('unwrapHtml', () => { + void it('Returns unwrapped HTML if prefix and suffix match', () => { // Blockquote, elements inside const bqUnwrap: Params = { html: '

inner

', @@ -32,13 +34,13 @@ describe('unwrapHtml', () => { unwrapHtml(pUnwrap); // Testy test - expect(bqIsUnwrapped).toBeTruthy(); - expect(bqUnwrappedHtml).toBe('

inner

'); - expect(pIsUnwrapped).toBeTruthy(); - expect(pUnwrappedHtml).toBe('inner'); + assert(bqIsUnwrapped); + assert.equal(bqUnwrappedHtml, '

inner

'); + assert(pIsUnwrapped); + assert.equal(pUnwrappedHtml, 'inner'); }); - it('Returns non-unwrapped HTML if prefix and suffix do not match', () => { + void it('Returns non-unwrapped HTML if prefix and suffix do not match', () => { const bqUnwrap: Params = { html: '

inner

', fixes: [ @@ -50,11 +52,11 @@ describe('unwrapHtml', () => { }; const { willUnwrap: isUnwrapped, unwrappedHtml } = unwrapHtml(bqUnwrap); - expect(isUnwrapped).toBeFalsy(); - expect(unwrappedHtml).toBe(bqUnwrap.html); + assert(!isUnwrapped); + assert.equal(unwrappedHtml, bqUnwrap.html); }); - it('Returns wrapped HTML if prefix and suffix of one "fix" match from multiple options', () => { + void it('Returns wrapped HTML if prefix and suffix of one "fix" match from multiple options', () => { const bqUnwrap: Params = { html: '

inner

', fixes: [ @@ -121,16 +123,16 @@ describe('unwrapHtml', () => { unwrappedElement: ulUnwrappedElement, } = unwrapHtml(ulUnwrap); - expect(bqIsUnwrapped).toBeTruthy(); - expect(bqUnwrappedHtml).toBe('

inner

'); - expect(bqUnwrappedElement).toBe('blockquote'); + assert(bqIsUnwrapped); + assert.equal(bqUnwrappedHtml, '

inner

'); + assert.equal(bqUnwrappedElement, 'blockquote'); - expect(pIsUnwrapped).toBeTruthy(); - expect(pUnwrappedHtml).toBe('inner'); - expect(pUnwrappedElement).toBe('p'); + assert(pIsUnwrapped); + assert.equal(pUnwrappedHtml, 'inner'); + assert.equal(pUnwrappedElement, 'p'); - expect(ulIsUnwrapped).toBeTruthy(); - expect(ulUnwrappedHtml).toBe('
  • Test
  • test2
  • '); - expect(ulUnwrappedElement).toBe('ul'); + assert(ulIsUnwrapped); + assert.equal(ulUnwrappedHtml, '
  • Test
  • test2
  • '); + assert.equal(ulUnwrappedElement, 'ul'); }); }); diff --git a/dotcom-rendering/src/model/validate.test.ts b/dotcom-rendering/src/model/validate.node.test.ts similarity index 72% rename from dotcom-rendering/src/model/validate.test.ts rename to dotcom-rendering/src/model/validate.node.test.ts index 5a969f89503..f189a04d640 100644 --- a/dotcom-rendering/src/model/validate.test.ts +++ b/dotcom-rendering/src/model/validate.node.test.ts @@ -1,7 +1,5 @@ -/** - * @jest-environment node - */ - +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; import { Comment } from '../../fixtures/generated/fe-articles/Comment'; import { Feature } from '../../fixtures/generated/fe-articles/Feature'; import { Live } from '../../fixtures/generated/fe-articles/Live'; @@ -55,21 +53,21 @@ const hostedContentArticles = [ }, ]; -describe('validate', () => { - it('throws on invalid data', () => { +void describe('validate', () => { + void it('throws on invalid data', () => { const data = { foo: 'bar' }; - expect(() => validateAsFEArticle(data)).toThrow(TypeError); + assert.throws(() => validateAsFEArticle(data), TypeError); }); for (const article of articles) { - it(`validates data for a ${article.name} article`, () => { - expect(validateAsFEArticle(article.data)).toBe(article.data); + void it(`validates data for a ${article.name} article`, () => { + assert.equal(validateAsFEArticle(article.data), article.data); }); } for (const hostedItem of hostedContentArticles) { - it(`validates data for hosted ${hostedItem.name} content`, () => { - expect(validateAsFEArticle(hostedItem.data)).toBe(hostedItem.data); + void it(`validates data for hosted ${hostedItem.name} content`, () => { + assert.equal(validateAsFEArticle(hostedItem.data), hostedItem.data); }); } }); diff --git a/dotcom-rendering/src/model/validate.puzzlesPage.test.ts b/dotcom-rendering/src/model/validate.puzzlesPage.node.test.ts similarity index 73% rename from dotcom-rendering/src/model/validate.puzzlesPage.test.ts rename to dotcom-rendering/src/model/validate.puzzlesPage.node.test.ts index d28f902092b..1029111ed50 100644 --- a/dotcom-rendering/src/model/validate.puzzlesPage.test.ts +++ b/dotcom-rendering/src/model/validate.puzzlesPage.node.test.ts @@ -1,3 +1,5 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; import { validateAsPuzzlesPageType } from './validate'; const validPage = () => ({ @@ -37,25 +39,26 @@ const validPage = () => ({ }, }); -describe('validateAsPuzzlesPageType', () => { - it('accepts a valid recursive blueprint contract', () => { - expect( +void describe('validateAsPuzzlesPageType', () => { + void it('accepts a valid recursive blueprint contract', () => { + assert.equal( validateAsPuzzlesPageType(validPage()).layout.containers[0]?.id, - ).toBe('word-games'); + 'word-games', + ); }); - it('accepts enabled on featured containers and rejects it elsewhere', () => { + void it('accepts enabled on featured containers and rejects it elsewhere', () => { const featuredPage = validPage(); featuredPage.layout.containers[0]!.variant = 'featured'; (featuredPage.layout.containers[0] as { enabled?: boolean }).enabled = true; - expect(validateAsPuzzlesPageType(featuredPage)).toBeDefined(); + assert.notEqual(validateAsPuzzlesPageType(featuredPage), undefined); featuredPage.layout.containers[0]!.variant = 'standard'; - expect(() => validateAsPuzzlesPageType(featuredPage)).toThrow(); + assert.throws(() => validateAsPuzzlesPageType(featuredPage)); }); - it.each([ + for (const [name, mutate] of [ [ 'unknown card variant', (page: ReturnType) => { @@ -100,15 +103,17 @@ describe('validateAsPuzzlesPageType', () => { }); }, ], - ])('rejects %s', (_, mutate) => { - const page = validPage(); - mutate(page); - expect(() => validateAsPuzzlesPageType(page)).toThrow( - 'Unable to validate request body for puzzles page', - ); - }); + ] as const) { + void it(`rejects ${name}`, () => { + const page = validPage(); + mutate(page); + assert.throws(() => validateAsPuzzlesPageType(page), { + message: 'Unable to validate request body for puzzles page.', + }); + }); + } - it('accepts supporting content with valid puzzle references', () => { + void it('accepts supporting content with valid puzzle references', () => { const page = validPage(); page.layout.containers.push({ id: 'supporting', @@ -131,12 +136,13 @@ describe('validateAsPuzzlesPageType', () => { }, } as never); - expect(validateAsPuzzlesPageType(page).layout.containers).toHaveLength( + assert.equal( + validateAsPuzzlesPageType(page).layout.containers.length, 2, ); }); - it('rejects supporting content which references an unknown puzzle', () => { + void it('rejects supporting content which references an unknown puzzle', () => { const page = validPage(); page.layout.containers.push({ id: 'supporting', @@ -151,10 +157,10 @@ describe('validateAsPuzzlesPageType', () => { }, } as never); - expect(() => validateAsPuzzlesPageType(page)).toThrow(); + assert.throws(() => validateAsPuzzlesPageType(page)); }); - it('accepts a valid top-level ad placement and rejects one nested inside content', () => { + void it('accepts a valid top-level ad placement and rejects one nested inside content', () => { const page = validPage(); const ad = { id: 'inline-ad', @@ -164,11 +170,12 @@ describe('validateAsPuzzlesPageType', () => { content: { items: [], nestedContainers: [] }, }; page.layout.containers.push(ad as never); - expect(validateAsPuzzlesPageType(page).layout.containers).toHaveLength( + assert.equal( + validateAsPuzzlesPageType(page).layout.containers.length, 2, ); page.layout.containers.pop(); page.layout.containers[0]!.content.nestedContainers.push(ad as never); - expect(() => validateAsPuzzlesPageType(page)).toThrow(); + assert.throws(() => validateAsPuzzlesPageType(page)); }); });