From ce7cdad8a5b2f65d9ea01a405e3005fabdb6a858 Mon Sep 17 00:00:00 2001 From: Akinsola Lawanson Date: Thu, 16 Apr 2026 15:27:01 +0100 Subject: [PATCH 1/6] Bump @guardian/ophan-tracker-js to 2.8.0 --- package.json | 2 +- pnpm-lock.yaml | 29 +++++++++++++++--------- src/server/lib/__tests__/headers.test.ts | 2 +- src/server/lib/middleware/helmet.ts | 1 + 4 files changed, 21 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index 3aea37639..d737aaee6 100644 --- a/package.json +++ b/package.json @@ -111,7 +111,7 @@ "@faire/mjml-react": "3.5.3", "@guardian/ab-core": "9.0.0", "@guardian/libs": "31.0.0", - "@guardian/ophan-tracker-js": "2.0.3", + "@guardian/ophan-tracker-js": "2.8.0", "@guardian/source": "12.2.1", "@guardian/source-development-kitchen": "27.0.0", "@okta/jwt-verifier": "4.0.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 122c97aba..c0a289300 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -36,16 +36,16 @@ importers: version: 9.0.0(tslib@2.8.1)(typescript@5.9.3) '@guardian/libs': specifier: 31.0.0 - version: 31.0.0(@guardian/ophan-tracker-js@2.0.3)(tslib@2.8.1)(typescript@5.9.3) + version: 31.0.0(@guardian/ophan-tracker-js@2.8.0)(tslib@2.8.1)(typescript@5.9.3) '@guardian/ophan-tracker-js': - specifier: 2.0.3 - version: 2.0.3 + specifier: 2.8.0 + version: 2.8.0 '@guardian/source': specifier: 12.2.1 version: 12.2.1(@emotion/react@11.14.0(@preact/compat@18.3.1(preact@10.29.1))(@types/react@19.2.14))(@preact/compat@18.3.1(preact@10.29.1))(@types/react@19.2.14)(tslib@2.8.1)(typescript@5.9.3) '@guardian/source-development-kitchen': specifier: 27.0.0 - version: 27.0.0(@emotion/react@11.14.0(@preact/compat@18.3.1(preact@10.29.1))(@types/react@19.2.14))(@guardian/libs@31.0.0(@guardian/ophan-tracker-js@2.0.3)(tslib@2.8.1)(typescript@5.9.3))(@guardian/source@12.2.1(@emotion/react@11.14.0(@preact/compat@18.3.1(preact@10.29.1))(@types/react@19.2.14))(@preact/compat@18.3.1(preact@10.29.1))(@types/react@19.2.14)(tslib@2.8.1)(typescript@5.9.3))(@preact/compat@18.3.1(preact@10.29.1))(@types/react@19.2.14)(tslib@2.8.1)(typescript@5.9.3) + version: 27.0.0(@emotion/react@11.14.0(@preact/compat@18.3.1(preact@10.29.1))(@types/react@19.2.14))(@guardian/libs@31.0.0(@guardian/ophan-tracker-js@2.8.0)(tslib@2.8.1)(typescript@5.9.3))(@guardian/source@12.2.1(@emotion/react@11.14.0(@preact/compat@18.3.1(preact@10.29.1))(@types/react@19.2.14))(@preact/compat@18.3.1(preact@10.29.1))(@types/react@19.2.14)(tslib@2.8.1)(typescript@5.9.3))(@preact/compat@18.3.1(preact@10.29.1))(@types/react@19.2.14)(tslib@2.8.1)(typescript@5.9.3) '@okta/jwt-verifier': specifier: 4.0.2 version: 4.0.2 @@ -1016,8 +1016,8 @@ packages: typescript: optional: true - '@guardian/ophan-tracker-js@2.0.3': - resolution: {integrity: sha512-Aatn3SWsXjVA2HNkJtam6p0FPaThO5IQ/WMMdaOz/6VLUc8yWCh7kqzr5b/Yf2x7AAI3DrZm7scNNmS+g/q41Q==} + '@guardian/ophan-tracker-js@2.8.0': + resolution: {integrity: sha512-RPoyxPPKaT1em1LZiD1LKsTYzoXBG8Zjs4OzyP5dhEmfoXD99qK48JI4oGUPlq3wOOB0ZT8UrbtbWDKzeMSBHA==} engines: {node: '>=16'} '@guardian/prettier@10.0.0': @@ -1064,6 +1064,9 @@ packages: typescript: optional: true + '@guardian/tsconfig@1.0.1': + resolution: {integrity: sha512-PB24nZ6WTBB8aZ9EyxJw1vC5FYkGqwMQ/O5Oogp6P5HCQU5MN0JpUXvpYci7kwV2oXD1Az06UBnLyyVXOVMadQ==} + '@hapi/address@5.1.1': resolution: {integrity: sha512-A+po2d/dVoY7cYajycYI43ZbYMXukuopIsqCjh5QzsBCipDtdofHntljDlpccMjIfTy6UOkg+5KPriwYch2bXA==} engines: {node: '>=14.0.0'} @@ -7916,23 +7919,25 @@ snapshots: - supports-color - typescript - '@guardian/libs@31.0.0(@guardian/ophan-tracker-js@2.0.3)(tslib@2.8.1)(typescript@5.9.3)': + '@guardian/libs@31.0.0(@guardian/ophan-tracker-js@2.8.0)(tslib@2.8.1)(typescript@5.9.3)': dependencies: - '@guardian/ophan-tracker-js': 2.0.3 + '@guardian/ophan-tracker-js': 2.8.0 tslib: 2.8.1 optionalDependencies: typescript: 5.9.3 - '@guardian/ophan-tracker-js@2.0.3': {} + '@guardian/ophan-tracker-js@2.8.0': + dependencies: + '@guardian/tsconfig': 1.0.1 '@guardian/prettier@10.0.0(prettier@3.8.3)(tslib@2.8.1)': dependencies: prettier: 3.8.3 tslib: 2.8.1 - '@guardian/source-development-kitchen@27.0.0(@emotion/react@11.14.0(@preact/compat@18.3.1(preact@10.29.1))(@types/react@19.2.14))(@guardian/libs@31.0.0(@guardian/ophan-tracker-js@2.0.3)(tslib@2.8.1)(typescript@5.9.3))(@guardian/source@12.2.1(@emotion/react@11.14.0(@preact/compat@18.3.1(preact@10.29.1))(@types/react@19.2.14))(@preact/compat@18.3.1(preact@10.29.1))(@types/react@19.2.14)(tslib@2.8.1)(typescript@5.9.3))(@preact/compat@18.3.1(preact@10.29.1))(@types/react@19.2.14)(tslib@2.8.1)(typescript@5.9.3)': + '@guardian/source-development-kitchen@27.0.0(@emotion/react@11.14.0(@preact/compat@18.3.1(preact@10.29.1))(@types/react@19.2.14))(@guardian/libs@31.0.0(@guardian/ophan-tracker-js@2.8.0)(tslib@2.8.1)(typescript@5.9.3))(@guardian/source@12.2.1(@emotion/react@11.14.0(@preact/compat@18.3.1(preact@10.29.1))(@types/react@19.2.14))(@preact/compat@18.3.1(preact@10.29.1))(@types/react@19.2.14)(tslib@2.8.1)(typescript@5.9.3))(@preact/compat@18.3.1(preact@10.29.1))(@types/react@19.2.14)(tslib@2.8.1)(typescript@5.9.3)': dependencies: - '@guardian/libs': 31.0.0(@guardian/ophan-tracker-js@2.0.3)(tslib@2.8.1)(typescript@5.9.3) + '@guardian/libs': 31.0.0(@guardian/ophan-tracker-js@2.8.0)(tslib@2.8.1)(typescript@5.9.3) '@guardian/source': 12.2.1(@emotion/react@11.14.0(@preact/compat@18.3.1(preact@10.29.1))(@types/react@19.2.14))(@preact/compat@18.3.1(preact@10.29.1))(@types/react@19.2.14)(tslib@2.8.1)(typescript@5.9.3) tslib: 2.8.1 optionalDependencies: @@ -7951,6 +7956,8 @@ snapshots: react: '@preact/compat@18.3.1(preact@10.29.1)' typescript: 5.9.3 + '@guardian/tsconfig@1.0.1': {} + '@hapi/address@5.1.1': dependencies: '@hapi/hoek': 11.0.7 diff --git a/src/server/lib/__tests__/headers.test.ts b/src/server/lib/__tests__/headers.test.ts index 992fe3bed..924a12210 100644 --- a/src/server/lib/__tests__/headers.test.ts +++ b/src/server/lib/__tests__/headers.test.ts @@ -83,7 +83,7 @@ describe('Content Security Policy headers', () => { ); expect(splitCSPHeader).toContain('font-src assets.guim.co.uk'); expect(splitCSPHeader).toContain( - `connect-src 'self' consent-logs.${apiDomain} api.nextgen.guardianapps.co.uk https://api.pwnedpasswords.com localhost:1234 www.google.com`, + `connect-src 'self' consent-logs.${apiDomain} api.nextgen.guardianapps.co.uk https://api.pwnedpasswords.com localhost:1234 www.google.com ophan.theguardian.com`, ); expect(splitCSPHeader).toContain("object-src 'none'"); expect(splitCSPHeader).toContain("script-src-attr 'none'"); diff --git a/src/server/lib/middleware/helmet.ts b/src/server/lib/middleware/helmet.ts index 7f1ee6948..bfa92dd14 100644 --- a/src/server/lib/middleware/helmet.ts +++ b/src/server/lib/middleware/helmet.ts @@ -66,6 +66,7 @@ const helmetConfig: HelmetOptions = { CSP_VALID_URI.HAVEIBEENPWNED, idapiOrigin, CSP_VALID_URI.GOOGLE_RECAPTCHA, + CSP_VALID_URI.OPHAN, ], frameSrc: [CSP_VALID_URI.GOOGLE_RECAPTCHA], formAction: null, From 8217a25fb098a8d69003420af28ab706e4ffca62 Mon Sep 17 00:00:00 2001 From: Akinsola Lawanson Date: Wed, 13 May 2026 09:16:33 +0100 Subject: [PATCH 2/6] Swap Ophan interfaces to @guardian/ophan-tracker-js --- src/client/lib/ophan.ts | 7 +++---- src/shared/model/ophan.ts | 9 ++++++--- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/client/lib/ophan.ts b/src/client/lib/ophan.ts index 2f056e285..3d097541e 100644 --- a/src/client/lib/ophan.ts +++ b/src/client/lib/ophan.ts @@ -1,5 +1,5 @@ import { OphanEvent, OphanInteraction } from '@/shared/model/ophan'; -import { OphanComponentEvent } from '@guardian/libs'; +import { ComponentEvent } from '@guardian/ophan-tracker-js'; export const record = (event: OphanEvent) => { if (window.guardian?.ophan?.record) { @@ -34,6 +34,5 @@ export const trackFormFocusBlur = ( } }; -export const sendOphanComponentEvent = ( - componentEvent: OphanComponentEvent, -): void => record({ componentEvent }); +export const sendOphanComponentEvent = (componentEvent: ComponentEvent): void => + record({ componentEvent }); diff --git a/src/shared/model/ophan.ts b/src/shared/model/ophan.ts index 3ddfdd17f..8ae9b9958 100644 --- a/src/shared/model/ophan.ts +++ b/src/shared/model/ophan.ts @@ -1,4 +1,7 @@ -import { OphanABEvent, OphanComponentEvent } from '@guardian/libs'; +import { + ComponentEvent, + AbTestRegisterEntry, +} from '@guardian/ophan-tracker-js'; export interface OphanInteraction { component: string; @@ -8,10 +11,10 @@ export interface OphanInteraction { interface OphanBase { experiences?: string; - abTestRegister?: Record; + abTestRegister?: Record; } export type OphanEvent = | OphanBase | OphanInteraction - | { componentEvent: OphanComponentEvent }; + | { componentEvent: ComponentEvent }; From 85165f6a9482e0ca27256514b040b809f7c648f7 Mon Sep 17 00:00:00 2001 From: Akinsola Lawanson Date: Wed, 13 May 2026 09:16:33 +0100 Subject: [PATCH 3/6] Swap Ophan interfaces to @guardian/ophan-tracker-js and mock ophan request. --- playwright/fixtures/mockedApiRequest.ts | 6 ++++++ src/client/lib/ophan.ts | 7 +++---- src/shared/model/ophan.ts | 9 ++++++--- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/playwright/fixtures/mockedApiRequest.ts b/playwright/fixtures/mockedApiRequest.ts index c247cd521..05e7f5789 100644 --- a/playwright/fixtures/mockedApiRequest.ts +++ b/playwright/fixtures/mockedApiRequest.ts @@ -4,10 +4,12 @@ import { test as base, request as playwrightRequest, APIRequestContext, + Page, } from '@playwright/test'; type CustomFixtures = { mockApi: APIRequestContext; + page: Page; }; export const test = base.extend({ @@ -18,4 +20,8 @@ export const test = base.extend({ await use(mockApiContext); await mockApiContext.dispose(); }, + page: async ({ page }, use) => { + await page.route('**://ophan.theguardian.com/**', (route) => route.abort()); + await use(page); + }, }); diff --git a/src/client/lib/ophan.ts b/src/client/lib/ophan.ts index 2f056e285..3d097541e 100644 --- a/src/client/lib/ophan.ts +++ b/src/client/lib/ophan.ts @@ -1,5 +1,5 @@ import { OphanEvent, OphanInteraction } from '@/shared/model/ophan'; -import { OphanComponentEvent } from '@guardian/libs'; +import { ComponentEvent } from '@guardian/ophan-tracker-js'; export const record = (event: OphanEvent) => { if (window.guardian?.ophan?.record) { @@ -34,6 +34,5 @@ export const trackFormFocusBlur = ( } }; -export const sendOphanComponentEvent = ( - componentEvent: OphanComponentEvent, -): void => record({ componentEvent }); +export const sendOphanComponentEvent = (componentEvent: ComponentEvent): void => + record({ componentEvent }); diff --git a/src/shared/model/ophan.ts b/src/shared/model/ophan.ts index 3ddfdd17f..8ae9b9958 100644 --- a/src/shared/model/ophan.ts +++ b/src/shared/model/ophan.ts @@ -1,4 +1,7 @@ -import { OphanABEvent, OphanComponentEvent } from '@guardian/libs'; +import { + ComponentEvent, + AbTestRegisterEntry, +} from '@guardian/ophan-tracker-js'; export interface OphanInteraction { component: string; @@ -8,10 +11,10 @@ export interface OphanInteraction { interface OphanBase { experiences?: string; - abTestRegister?: Record; + abTestRegister?: Record; } export type OphanEvent = | OphanBase | OphanInteraction - | { componentEvent: OphanComponentEvent }; + | { componentEvent: ComponentEvent }; From b01146bf62719250994cd6a1efa3d06d20b008cf Mon Sep 17 00:00:00 2001 From: Akinsola Lawanson Date: Wed, 13 May 2026 13:35:09 +0100 Subject: [PATCH 4/6] Use e2e fixture to block abort ophan calls in test/ --- playwright/fixtures/e2eFixture.ts | 14 ++++++++++++++ playwright/tests/e2e/change_email.spec.ts | 3 ++- playwright/tests/e2e/consent_token.spec.ts | 3 ++- playwright/tests/e2e/delete.spec.ts | 3 ++- playwright/tests/e2e/jobs_terms.spec.ts | 3 ++- playwright/tests/e2e/new_account_review.spec.ts | 3 ++- playwright/tests/e2e/reauthenticate.spec.ts | 3 ++- playwright/tests/e2e/registration_1.spec.ts | 3 ++- playwright/tests/e2e/registration_2.spec.ts | 3 ++- playwright/tests/e2e/registration_3.spec.ts | 3 ++- playwright/tests/e2e/registration_4.spec.ts | 3 ++- .../tests/e2e/registration_newsletter.spec.ts | 3 ++- playwright/tests/e2e/reset_password.spec.ts | 3 ++- .../tests/e2e/reset_password_passcode.spec.ts | 3 ++- .../reset_password_passcode_active_user.spec.ts | 3 ++- ...reset_password_passcode_remaining_users.spec.ts | 3 ++- ...sword_passcode_staged_provisioned_users.spec.ts | 3 ++- playwright/tests/e2e/sign_in_1.spec.ts | 3 ++- playwright/tests/e2e/sign_in_2.spec.ts | 3 ++- .../tests/e2e/sign_in_passcode_active_user.spec.ts | 3 ++- .../e2e/sign_in_passcode_non_active_user.spec.ts | 3 ++- playwright/tests/e2e/sign_out.spec.ts | 3 ++- playwright/tests/e2e/subscription.spec.ts | 3 ++- 23 files changed, 58 insertions(+), 22 deletions(-) create mode 100644 playwright/fixtures/e2eFixture.ts diff --git a/playwright/fixtures/e2eFixture.ts b/playwright/fixtures/e2eFixture.ts new file mode 100644 index 000000000..71ea86e77 --- /dev/null +++ b/playwright/fixtures/e2eFixture.ts @@ -0,0 +1,14 @@ +/* eslint-disable no-empty-pattern -- needed to setup the mockApi function */ +/* eslint-disable react-hooks/rules-of-hooks -- stop eslint from complaining about react hooks in a non react file */ +import { test as base, Page } from '@playwright/test'; + +type CustomFixtures = { + page: Page; +}; + +export const test = base.extend({ + page: async ({ page }, use) => { + await page.route('**://ophan.theguardian.com/**', (route) => route.abort()); + await use(page); + }, +}); diff --git a/playwright/tests/e2e/change_email.spec.ts b/playwright/tests/e2e/change_email.spec.ts index b676825ea..bc41c7b1c 100644 --- a/playwright/tests/e2e/change_email.spec.ts +++ b/playwright/tests/e2e/change_email.spec.ts @@ -1,7 +1,8 @@ -import { test, expect } from '@playwright/test'; +import { expect } from '@playwright/test'; import { randomMailosaurEmail } from '../../helpers/api/idapi'; import { createTestUser, updateTestUser } from '../../helpers/api/idapi'; import { checkForEmailAndGetDetails } from '../../helpers/api/mailosaur'; +import { test } from '../../fixtures/e2eFixture'; test.describe('Change email', () => { test.describe('successful and unsuccesful flows', () => { diff --git a/playwright/tests/e2e/consent_token.spec.ts b/playwright/tests/e2e/consent_token.spec.ts index 560801487..5aeb797a8 100644 --- a/playwright/tests/e2e/consent_token.spec.ts +++ b/playwright/tests/e2e/consent_token.spec.ts @@ -1,6 +1,7 @@ -import { test, expect } from '@playwright/test'; +import { expect } from '@playwright/test'; import { createTestUser, sendConsentEmail } from '../../helpers/api/idapi'; import { checkForEmailAndGetDetails } from '../../helpers/api/mailosaur'; +import { test } from '../../fixtures/e2eFixture'; test.describe('Consent token flow', () => { test('shows the success page when supplied a valid token by a logged in user', async ({ diff --git a/playwright/tests/e2e/delete.spec.ts b/playwright/tests/e2e/delete.spec.ts index 604db76f3..e93fb9468 100644 --- a/playwright/tests/e2e/delete.spec.ts +++ b/playwright/tests/e2e/delete.spec.ts @@ -1,7 +1,8 @@ -import { test, expect, Page } from '@playwright/test'; +import { expect, Page } from '@playwright/test'; import { randomPassword, createTestUser } from '../../helpers/api/idapi'; import { checkForEmailAndGetDetails } from '../../helpers/api/mailosaur'; import { mockClientRecaptcha } from '../../helpers/network/recaptcha'; +import { test } from '../../fixtures/e2eFixture'; test.describe('Delete my account flow in Okta', () => { const signInAndVisitDeletePage = async ( diff --git a/playwright/tests/e2e/jobs_terms.spec.ts b/playwright/tests/e2e/jobs_terms.spec.ts index 364ab47cb..e0ad4a1f9 100644 --- a/playwright/tests/e2e/jobs_terms.spec.ts +++ b/playwright/tests/e2e/jobs_terms.spec.ts @@ -1,4 +1,4 @@ -import { test, expect } from '@playwright/test'; +import { expect } from '@playwright/test'; import { createTestUser } from '../../helpers/api/idapi'; import { getTestOktaUser, @@ -6,6 +6,7 @@ import { } from '../../helpers/api/okta'; import { JOBS_TOS_URI } from '@/shared/model/Configuration'; import { escapeRegExp } from '../../helpers/utils'; +import { test } from '../../fixtures/e2eFixture'; test.describe('Jobs terms and conditions flow in Okta', () => { test.describe('Shows the terms and conditions page on Sign In', () => { diff --git a/playwright/tests/e2e/new_account_review.spec.ts b/playwright/tests/e2e/new_account_review.spec.ts index fbf6fe5ac..c7e7de6d9 100644 --- a/playwright/tests/e2e/new_account_review.spec.ts +++ b/playwright/tests/e2e/new_account_review.spec.ts @@ -1,8 +1,9 @@ -import { test, expect } from '@playwright/test'; +import { expect } from '@playwright/test'; import { randomMailosaurEmail } from '../../helpers/api/idapi'; import { checkForEmailAndGetDetails } from '../../helpers/api/mailosaur'; import { JOBS_TOS_URI } from '@/shared/model/Configuration'; import { escapeRegExp } from '../../helpers/utils'; +import { test } from '../../fixtures/e2eFixture'; test.describe('New account newsletters page', () => { ['GB', 'FR', 'AU', 'US'].forEach((geoLocation) => { diff --git a/playwright/tests/e2e/reauthenticate.spec.ts b/playwright/tests/e2e/reauthenticate.spec.ts index ac73d6679..a1b3bb86e 100644 --- a/playwright/tests/e2e/reauthenticate.spec.ts +++ b/playwright/tests/e2e/reauthenticate.spec.ts @@ -1,7 +1,8 @@ -import { test, expect } from '@playwright/test'; +import { expect } from '@playwright/test'; import { createTestUser } from '../../helpers/api/idapi'; import { checkForEmailAndGetDetails } from '../../helpers/api/mailosaur'; import { getCurrentOktaSession } from '../../helpers/api/okta'; +import { test } from '../../fixtures/e2eFixture'; test.describe('Reauthenticate flow, Okta enabled, password default', () => { test('keeps User A signed in when User A attempts to reauthenticate', async ({ diff --git a/playwright/tests/e2e/registration_1.spec.ts b/playwright/tests/e2e/registration_1.spec.ts index f99a9de90..f70dba122 100644 --- a/playwright/tests/e2e/registration_1.spec.ts +++ b/playwright/tests/e2e/registration_1.spec.ts @@ -1,4 +1,4 @@ -import { test, expect } from '@playwright/test'; +import { expect } from '@playwright/test'; import { Status } from '../../../src/server/models/okta/User'; import { randomMailosaurEmail, randomPassword } from '../../helpers/api/idapi'; import { checkForEmailAndGetDetails } from '../../helpers/api/mailosaur'; @@ -6,6 +6,7 @@ import { getTestOktaUser } from '../../helpers/api/okta'; import { JOBS_TOS_URI } from '@/shared/model/Configuration'; import { escapeRegExp, incrementPasscode } from '../../helpers/utils'; import { mockClientRecaptcha } from '../../helpers/network/recaptcha'; +import { test } from '../../fixtures/e2eFixture'; test.describe('Registration flow - Split 1/4', () => { test.describe('Registering with Okta', () => { diff --git a/playwright/tests/e2e/registration_2.spec.ts b/playwright/tests/e2e/registration_2.spec.ts index ef96f5133..809ff3a50 100644 --- a/playwright/tests/e2e/registration_2.spec.ts +++ b/playwright/tests/e2e/registration_2.spec.ts @@ -1,4 +1,4 @@ -import { test, expect } from '@playwright/test'; +import { expect } from '@playwright/test'; import { Status } from '../../../src/server/models/okta/User'; import { randomMailosaurEmail, @@ -13,6 +13,7 @@ import { getTestOktaUser, } from '../../helpers/api/okta'; import { existingUserSendEmailAndValidatePasscode } from '../../helpers/register'; +import { test } from '../../fixtures/e2eFixture'; test.describe('Registration flow - Split 2/4', () => { test.describe('existing user going through registration flow', () => { diff --git a/playwright/tests/e2e/registration_3.spec.ts b/playwright/tests/e2e/registration_3.spec.ts index 3615360db..85d7d1681 100644 --- a/playwright/tests/e2e/registration_3.spec.ts +++ b/playwright/tests/e2e/registration_3.spec.ts @@ -1,4 +1,4 @@ -import { test, expect } from '@playwright/test'; +import { expect } from '@playwright/test'; import { Status } from '../../../src/server/models/okta/User'; import { randomMailosaurEmail, createTestUser } from '../../helpers/api/idapi'; import { checkForEmailAndGetDetails } from '../../helpers/api/mailosaur'; @@ -9,6 +9,7 @@ import { getTestOktaUser, } from '../../helpers/api/okta'; import { escapeRegExp } from '../../helpers/utils'; +import { test } from '../../fixtures/e2eFixture'; test.describe('Registration flow - Split 3/4', () => { test.describe('Existing users asking for an email to be resent after attempting to register with Okta - useOktaClassic', () => { diff --git a/playwright/tests/e2e/registration_4.spec.ts b/playwright/tests/e2e/registration_4.spec.ts index bd1926a1e..6ee219178 100644 --- a/playwright/tests/e2e/registration_4.spec.ts +++ b/playwright/tests/e2e/registration_4.spec.ts @@ -1,4 +1,4 @@ -import { test, expect } from '@playwright/test'; +import { expect } from '@playwright/test'; import { Status } from '../../../src/server/models/okta/User'; import { randomMailosaurEmail, @@ -14,6 +14,7 @@ import { getTestOktaUser, } from '../../helpers/api/okta'; import { escapeRegExp } from '../../helpers/utils'; +import { test } from '../../fixtures/e2eFixture'; test.describe('Registration flow - Split 4/4', () => { // a few tests to check if the Okta Classic flow is still working using the useOktaClassic flag diff --git a/playwright/tests/e2e/registration_newsletter.spec.ts b/playwright/tests/e2e/registration_newsletter.spec.ts index ec16cb7e8..c5da237cf 100644 --- a/playwright/tests/e2e/registration_newsletter.spec.ts +++ b/playwright/tests/e2e/registration_newsletter.spec.ts @@ -1,6 +1,7 @@ -import { test, expect } from '@playwright/test'; +import { expect } from '@playwright/test'; import { createTestUser } from '../../helpers/api/idapi'; import { oktaGetApps } from '../../helpers/api/okta-apps'; +import { test } from '../../fixtures/e2eFixture'; // Newsletter descriptions const RegistrationNewsletterDescriptions = { diff --git a/playwright/tests/e2e/reset_password.spec.ts b/playwright/tests/e2e/reset_password.spec.ts index d79a01e3b..846c61be8 100644 --- a/playwright/tests/e2e/reset_password.spec.ts +++ b/playwright/tests/e2e/reset_password.spec.ts @@ -1,4 +1,4 @@ -import { test, expect, Request } from '@playwright/test'; +import { expect, Request } from '@playwright/test'; import { randomMailosaurEmail, randomPassword, @@ -12,6 +12,7 @@ import { expireOktaUserPassword, } from '../../helpers/api/okta'; import { escapeRegExp } from '../../helpers/utils'; +import { test } from '../../fixtures/e2eFixture'; test.describe('Password reset recovery flows', () => { test.describe('Passcode limbo state - user does not set password after using passcode', () => { diff --git a/playwright/tests/e2e/reset_password_passcode.spec.ts b/playwright/tests/e2e/reset_password_passcode.spec.ts index 99872e028..99dce2eac 100644 --- a/playwright/tests/e2e/reset_password_passcode.spec.ts +++ b/playwright/tests/e2e/reset_password_passcode.spec.ts @@ -1,4 +1,4 @@ -import { test, expect } from '@playwright/test'; +import { expect } from '@playwright/test'; import { Status } from '../../../src/server/models/okta/User'; import { randomMailosaurEmail, @@ -11,6 +11,7 @@ import { resetOktaUserPassword, expireOktaUserPassword, } from '../../helpers/api/okta'; +import { test } from '../../fixtures/e2eFixture'; test.describe('Password reset recovery flows - with Passcodes', () => { test.describe('RECOVERY user', () => { diff --git a/playwright/tests/e2e/reset_password_passcode_active_user.spec.ts b/playwright/tests/e2e/reset_password_passcode_active_user.spec.ts index 456108b8b..e747c23dc 100644 --- a/playwright/tests/e2e/reset_password_passcode_active_user.spec.ts +++ b/playwright/tests/e2e/reset_password_passcode_active_user.spec.ts @@ -1,4 +1,4 @@ -import { test, expect } from '@playwright/test'; +import { expect } from '@playwright/test'; import { randomMailosaurEmail, randomPassword, @@ -6,6 +6,7 @@ import { } from '../../helpers/api/idapi'; import { checkForEmailAndGetDetails } from '../../helpers/api/mailosaur'; import { escapeRegExp, incrementPasscode } from '../../helpers/utils'; +import { test } from '../../fixtures/e2eFixture'; test.describe('Password reset recovery flows - with Passcodes', () => { test.describe('ACTIVE user with password', () => { diff --git a/playwright/tests/e2e/reset_password_passcode_remaining_users.spec.ts b/playwright/tests/e2e/reset_password_passcode_remaining_users.spec.ts index 99872e028..99dce2eac 100644 --- a/playwright/tests/e2e/reset_password_passcode_remaining_users.spec.ts +++ b/playwright/tests/e2e/reset_password_passcode_remaining_users.spec.ts @@ -1,4 +1,4 @@ -import { test, expect } from '@playwright/test'; +import { expect } from '@playwright/test'; import { Status } from '../../../src/server/models/okta/User'; import { randomMailosaurEmail, @@ -11,6 +11,7 @@ import { resetOktaUserPassword, expireOktaUserPassword, } from '../../helpers/api/okta'; +import { test } from '../../fixtures/e2eFixture'; test.describe('Password reset recovery flows - with Passcodes', () => { test.describe('RECOVERY user', () => { diff --git a/playwright/tests/e2e/reset_password_passcode_staged_provisioned_users.spec.ts b/playwright/tests/e2e/reset_password_passcode_staged_provisioned_users.spec.ts index dcfb2fbc0..639920425 100644 --- a/playwright/tests/e2e/reset_password_passcode_staged_provisioned_users.spec.ts +++ b/playwright/tests/e2e/reset_password_passcode_staged_provisioned_users.spec.ts @@ -1,4 +1,4 @@ -import { test, expect } from '@playwright/test'; +import { expect } from '@playwright/test'; import { Status } from '../../../src/server/models/okta/User'; import { randomMailosaurEmail, @@ -7,6 +7,7 @@ import { } from '../../helpers/api/idapi'; import { checkForEmailAndGetDetails } from '../../helpers/api/mailosaur'; import { getTestOktaUser, activateTestOktaUser } from '../../helpers/api/okta'; +import { test } from '../../fixtures/e2eFixture'; test.describe('Password reset recovery flows - with Passcodes', () => { test.describe('STAGED user', () => { diff --git a/playwright/tests/e2e/sign_in_1.spec.ts b/playwright/tests/e2e/sign_in_1.spec.ts index 7686449a7..46014e83a 100644 --- a/playwright/tests/e2e/sign_in_1.spec.ts +++ b/playwright/tests/e2e/sign_in_1.spec.ts @@ -1,4 +1,4 @@ -import { test, expect } from '@playwright/test'; +import { expect } from '@playwright/test'; import { randomMailosaurEmail, randomPassword, @@ -12,6 +12,7 @@ import { findEmailValidatedOktaGroupId, } from '../../helpers/api/okta'; import { escapeRegExp } from '../../helpers/utils'; +import { test } from '../../fixtures/e2eFixture'; const returnUrl = 'https://www.theguardian.com/world/2013/jun/09/edward-snowden-nsa-whistleblower-surveillance'; diff --git a/playwright/tests/e2e/sign_in_2.spec.ts b/playwright/tests/e2e/sign_in_2.spec.ts index 930686261..588216115 100644 --- a/playwright/tests/e2e/sign_in_2.spec.ts +++ b/playwright/tests/e2e/sign_in_2.spec.ts @@ -1,4 +1,4 @@ -import { test, expect } from '@playwright/test'; +import { expect } from '@playwright/test'; import { createTestUser } from '../../helpers/api/idapi'; import { getTestOktaUser, @@ -6,6 +6,7 @@ import { closeCurrentOktaSession, } from '../../helpers/api/okta'; import { JOBS_TOS_URI } from '@/shared/model/Configuration'; +import { test } from '../../fixtures/e2eFixture'; const returnUrl = 'https://www.theguardian.com/world/2013/jun/09/edward-snowden-nsa-whistleblower-surveillance'; diff --git a/playwright/tests/e2e/sign_in_passcode_active_user.spec.ts b/playwright/tests/e2e/sign_in_passcode_active_user.spec.ts index 952a05551..974e1fd39 100644 --- a/playwright/tests/e2e/sign_in_passcode_active_user.spec.ts +++ b/playwright/tests/e2e/sign_in_passcode_active_user.spec.ts @@ -1,4 +1,4 @@ -import { test, expect } from '@playwright/test'; +import { expect } from '@playwright/test'; import { randomMailosaurEmail, randomPassword, @@ -8,6 +8,7 @@ import { checkForEmailAndGetDetails } from '../../helpers/api/mailosaur'; import { getTestOktaUser } from '../../helpers/api/okta'; import { mockClientRecaptcha } from '../../helpers/network/recaptcha'; import { escapeRegExp, incrementPasscode } from '../../helpers/utils'; +import { test } from '../../fixtures/e2eFixture'; test.describe('Sign In flow, with passcode', () => { // set up useful variables diff --git a/playwright/tests/e2e/sign_in_passcode_non_active_user.spec.ts b/playwright/tests/e2e/sign_in_passcode_non_active_user.spec.ts index 9bf000bec..b743d272e 100644 --- a/playwright/tests/e2e/sign_in_passcode_non_active_user.spec.ts +++ b/playwright/tests/e2e/sign_in_passcode_non_active_user.spec.ts @@ -1,4 +1,4 @@ -import { test, expect } from '@playwright/test'; +import { expect } from '@playwright/test'; import { Status } from '../../../src/server/models/okta/User'; import { createTestUser } from '../../helpers/api/idapi'; import { @@ -8,6 +8,7 @@ import { getTestOktaUser, } from '../../helpers/api/okta'; import { checkForEmailAndGetDetails } from '../../helpers/api/mailosaur'; +import { test } from '../../fixtures/e2eFixture'; test.describe('Sign In flow, with passcode (part 1)', () => { const returnUrl = diff --git a/playwright/tests/e2e/sign_out.spec.ts b/playwright/tests/e2e/sign_out.spec.ts index 6603085fc..f7bb9f267 100644 --- a/playwright/tests/e2e/sign_out.spec.ts +++ b/playwright/tests/e2e/sign_out.spec.ts @@ -1,5 +1,6 @@ -import { test, expect } from '@playwright/test'; +import { expect } from '@playwright/test'; import { createTestUser } from '../../helpers/api/idapi'; +import { test } from '../../fixtures/e2eFixture'; test.describe('Sign out flow', () => { const DotComCookies = [ diff --git a/playwright/tests/e2e/subscription.spec.ts b/playwright/tests/e2e/subscription.spec.ts index db4ee7707..575b7c208 100644 --- a/playwright/tests/e2e/subscription.spec.ts +++ b/playwright/tests/e2e/subscription.spec.ts @@ -1,6 +1,7 @@ -import { test, expect, Page } from '@playwright/test'; +import { expect, Page } from '@playwright/test'; import crypto from 'crypto'; import { createTestUser } from '../../helpers/api/idapi'; +import { test } from '../../fixtures/e2eFixture'; test.describe('Unsubscribe newsletter/marketing email', () => { const newsletterId = 'today-uk'; From f997599a409a9fe217555f9630d88c0202e17044 Mon Sep 17 00:00:00 2001 From: Akinsola Lawanson Date: Fri, 5 Jun 2026 09:18:33 +0100 Subject: [PATCH 5/6] Update ophan-tracker-js --- package.json | 2 +- pnpm-lock.yaml | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index cb77d59a0..604451e1a 100644 --- a/package.json +++ b/package.json @@ -112,7 +112,7 @@ "@faire/mjml-react": "3.5.4", "@guardian/ab-core": "9.0.0", "@guardian/libs": "32.0.0", - "@guardian/ophan-tracker-js": "2.8.0", + "@guardian/ophan-tracker-js": "3.1.0", "@guardian/source": "12.2.1", "@guardian/source-development-kitchen": "28.0.0", "@okta/jwt-verifier": "4.0.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 19d51d22f..c6eb099dc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -35,16 +35,16 @@ importers: version: 9.0.0(tslib@2.8.1)(typescript@5.9.3) '@guardian/libs': specifier: 32.0.0 - version: 32.0.0(@guardian/ophan-tracker-js@2.8.0)(tslib@2.8.1)(typescript@5.9.3) + version: 32.0.0(@guardian/ophan-tracker-js@3.1.0)(tslib@2.8.1)(typescript@5.9.3) '@guardian/ophan-tracker-js': - specifier: 2.8.0 - version: 2.8.0 + specifier: 3.1.0 + version: 3.1.0 '@guardian/source': specifier: 12.2.1 version: 12.2.1(@emotion/react@11.14.0(@preact/compat@18.3.1(preact@10.29.1))(@types/react@19.2.14))(@preact/compat@18.3.1(preact@10.29.1))(@types/react@19.2.14)(tslib@2.8.1)(typescript@5.9.3) '@guardian/source-development-kitchen': specifier: 28.0.0 - version: 28.0.0(@emotion/react@11.14.0(@preact/compat@18.3.1(preact@10.29.1))(@types/react@19.2.14))(@guardian/libs@32.0.0(@guardian/ophan-tracker-js@2.8.0)(tslib@2.8.1)(typescript@5.9.3))(@guardian/source@12.2.1(@emotion/react@11.14.0(@preact/compat@18.3.1(preact@10.29.1))(@types/react@19.2.14))(@preact/compat@18.3.1(preact@10.29.1))(@types/react@19.2.14)(tslib@2.8.1)(typescript@5.9.3))(@preact/compat@18.3.1(preact@10.29.1))(@types/react@19.2.14)(tslib@2.8.1)(typescript@5.9.3) + version: 28.0.0(@emotion/react@11.14.0(@preact/compat@18.3.1(preact@10.29.1))(@types/react@19.2.14))(@guardian/libs@32.0.0(@guardian/ophan-tracker-js@3.1.0)(tslib@2.8.1)(typescript@5.9.3))(@guardian/source@12.2.1(@emotion/react@11.14.0(@preact/compat@18.3.1(preact@10.29.1))(@types/react@19.2.14))(@preact/compat@18.3.1(preact@10.29.1))(@types/react@19.2.14)(tslib@2.8.1)(typescript@5.9.3))(@preact/compat@18.3.1(preact@10.29.1))(@types/react@19.2.14)(tslib@2.8.1)(typescript@5.9.3) '@okta/jwt-verifier': specifier: 4.0.2 version: 4.0.2 @@ -1016,8 +1016,8 @@ packages: typescript: optional: true - '@guardian/ophan-tracker-js@2.8.0': - resolution: {integrity: sha512-RPoyxPPKaT1em1LZiD1LKsTYzoXBG8Zjs4OzyP5dhEmfoXD99qK48JI4oGUPlq3wOOB0ZT8UrbtbWDKzeMSBHA==} + '@guardian/ophan-tracker-js@3.1.0': + resolution: {integrity: sha512-3KCZ9leh1171Q6kdiBWRb0f+Nb8Ab5uweMehjTM7OMGvLY+9kUjATNBSeIYvHwrDlqqi1nR+657wckXK4b0MUQ==} engines: {node: '>=16'} '@guardian/prettier@10.0.0': @@ -7954,14 +7954,14 @@ snapshots: - supports-color - typescript - '@guardian/libs@32.0.0(@guardian/ophan-tracker-js@2.8.0)(tslib@2.8.1)(typescript@5.9.3)': + '@guardian/libs@32.0.0(@guardian/ophan-tracker-js@3.1.0)(tslib@2.8.1)(typescript@5.9.3)': dependencies: - '@guardian/ophan-tracker-js': 2.8.0 + '@guardian/ophan-tracker-js': 3.1.0 tslib: 2.8.1 optionalDependencies: typescript: 5.9.3 - '@guardian/ophan-tracker-js@2.8.0': + '@guardian/ophan-tracker-js@3.1.0': dependencies: '@guardian/tsconfig': 1.0.1 @@ -7970,9 +7970,9 @@ snapshots: prettier: 3.8.3 tslib: 2.8.1 - '@guardian/source-development-kitchen@28.0.0(@emotion/react@11.14.0(@preact/compat@18.3.1(preact@10.29.1))(@types/react@19.2.14))(@guardian/libs@32.0.0(@guardian/ophan-tracker-js@2.8.0)(tslib@2.8.1)(typescript@5.9.3))(@guardian/source@12.2.1(@emotion/react@11.14.0(@preact/compat@18.3.1(preact@10.29.1))(@types/react@19.2.14))(@preact/compat@18.3.1(preact@10.29.1))(@types/react@19.2.14)(tslib@2.8.1)(typescript@5.9.3))(@preact/compat@18.3.1(preact@10.29.1))(@types/react@19.2.14)(tslib@2.8.1)(typescript@5.9.3)': + '@guardian/source-development-kitchen@28.0.0(@emotion/react@11.14.0(@preact/compat@18.3.1(preact@10.29.1))(@types/react@19.2.14))(@guardian/libs@32.0.0(@guardian/ophan-tracker-js@3.1.0)(tslib@2.8.1)(typescript@5.9.3))(@guardian/source@12.2.1(@emotion/react@11.14.0(@preact/compat@18.3.1(preact@10.29.1))(@types/react@19.2.14))(@preact/compat@18.3.1(preact@10.29.1))(@types/react@19.2.14)(tslib@2.8.1)(typescript@5.9.3))(@preact/compat@18.3.1(preact@10.29.1))(@types/react@19.2.14)(tslib@2.8.1)(typescript@5.9.3)': dependencies: - '@guardian/libs': 32.0.0(@guardian/ophan-tracker-js@2.8.0)(tslib@2.8.1)(typescript@5.9.3) + '@guardian/libs': 32.0.0(@guardian/ophan-tracker-js@3.1.0)(tslib@2.8.1)(typescript@5.9.3) '@guardian/source': 12.2.1(@emotion/react@11.14.0(@preact/compat@18.3.1(preact@10.29.1))(@types/react@19.2.14))(@preact/compat@18.3.1(preact@10.29.1))(@types/react@19.2.14)(tslib@2.8.1)(typescript@5.9.3) tslib: 2.8.1 optionalDependencies: From d7259de3732fbcae0992b5608884e3179be207a2 Mon Sep 17 00:00:00 2001 From: Akinsola Lawanson Date: Fri, 5 Jun 2026 09:19:43 +0100 Subject: [PATCH 6/6] Use loadPage function in mocked tests --- playwright/fixtures/mockedApiRequest.ts | 4 --- playwright/helpers/load-page.ts | 19 ++++++++++++ playwright/tests/mocked/email_input.spec.ts | 9 +++--- .../tests/mocked/okta_change_password.spec.ts | 15 +++++----- playwright/tests/mocked/okta_register.spec.ts | 21 ++++++------- playwright/tests/mocked/okta_sign_in.spec.ts | 28 ++++++++++------- playwright/tests/mocked/rateLimit.spec.ts | 25 +++++++++------- playwright/tests/mocked/register.spec.ts | 13 ++++---- .../tests/mocked/registerController.spec.ts | 3 +- .../mocked/resendEmailController.spec.ts | 9 +++--- .../mocked/resetPasswordController.spec.ts | 29 +++++++++--------- .../tests/mocked/reset_password.spec.ts | 5 ++-- playwright/tests/mocked/set_password.spec.ts | 19 ++++++------ .../tests/mocked/signInController.spec.ts | 9 +++--- playwright/tests/mocked/sign_in.spec.ts | 13 ++++---- playwright/tests/mocked/unsubscribe.spec.ts | 30 +++++++++++++------ playwright/tests/mocked/welcome.spec.ts | 13 ++++---- 17 files changed, 158 insertions(+), 106 deletions(-) create mode 100644 playwright/helpers/load-page.ts diff --git a/playwright/fixtures/mockedApiRequest.ts b/playwright/fixtures/mockedApiRequest.ts index 05e7f5789..9983a52d7 100644 --- a/playwright/fixtures/mockedApiRequest.ts +++ b/playwright/fixtures/mockedApiRequest.ts @@ -20,8 +20,4 @@ export const test = base.extend({ await use(mockApiContext); await mockApiContext.dispose(); }, - page: async ({ page }, use) => { - await page.route('**://ophan.theguardian.com/**', (route) => route.abort()); - await use(page); - }, }); diff --git a/playwright/helpers/load-page.ts b/playwright/helpers/load-page.ts new file mode 100644 index 000000000..6c8dfdffc --- /dev/null +++ b/playwright/helpers/load-page.ts @@ -0,0 +1,19 @@ +import { Page } from '@playwright/test'; + +/** + * Loads a page and waits for the specified event to occur. + * By default, it waits for the 'load' event, which means the page has fully loaded. + * You can specify 'domcontentloaded' to wait for the DOM to be ready, + * or 'networkidle' to wait until there are no more network connections for at least 500 ms. + * + * @param {Page} page + * @param {string} url + * @param {('load' | 'domcontentloaded' | 'networkidle')} [waitUntil='load'] + */ +export const loadPage = async ( + page: Page, + url: string, + waitUntil: 'load' | 'domcontentloaded' | 'networkidle' = 'load', +) => { + await page.goto(url, { waitUntil }); +}; diff --git a/playwright/tests/mocked/email_input.spec.ts b/playwright/tests/mocked/email_input.spec.ts index 45e9a11cd..52780be78 100644 --- a/playwright/tests/mocked/email_input.spec.ts +++ b/playwright/tests/mocked/email_input.spec.ts @@ -1,10 +1,11 @@ import { test, expect } from '@playwright/test'; +import { loadPage } from '../../helpers/load-page'; test.describe('Email input component', () => { test('should show an error message when nothing submitted', async ({ page, }) => { - await page.goto('/register/email'); + await loadPage(page, '/register/email'); await page.locator('[data-cy=main-form-submit-button]').click(); await expect(page.getByText('Please enter your email.')).toBeVisible(); }); @@ -12,7 +13,7 @@ test.describe('Email input component', () => { test('should show an error message when an invalid email is submitted', async ({ page, }) => { - await page.goto('/register/email'); + await loadPage(page, '/register/email'); await page.locator('input[name="email"]').fill('invalid.email.com'); await page.locator('[data-cy=main-form-submit-button]').click(); await expect( @@ -23,7 +24,7 @@ test.describe('Email input component', () => { test('does not show an error message when a valid email is submitted', async ({ page, }) => { - await page.goto('/register/email'); + await loadPage(page, '/register/email'); await page.locator('input[name="email"]').fill('test@email.com'); await page.locator('[data-cy=main-form-submit-button]').focus(); await expect( @@ -35,7 +36,7 @@ test.describe('Email input component', () => { test('should correct error once a valid email is submitted', async ({ page, }) => { - await page.goto('/register/email'); + await loadPage(page, '/register/email'); await page.locator('input[name="email"]').fill('invalid.email.com'); await page.locator('[data-cy=main-form-submit-button]').click(); await expect( diff --git a/playwright/tests/mocked/okta_change_password.spec.ts b/playwright/tests/mocked/okta_change_password.spec.ts index a4d9df592..1451a663e 100644 --- a/playwright/tests/mocked/okta_change_password.spec.ts +++ b/playwright/tests/mocked/okta_change_password.spec.ts @@ -20,6 +20,7 @@ import { APIRequestContext, expect } from '@playwright/test'; import { test } from '../../fixtures/mockedApiRequest'; import { randomPassword } from '../../helpers/api/idapi'; +import { loadPage } from '../../helpers/load-page'; test.describe('Change password in Okta', () => { const email = 'mrtest@theguardian.com'; @@ -156,7 +157,7 @@ test.describe('Change password in Okta', () => { await mockPasswordResetInvalidStateTokenFailure(mockApi); await mockValidateRecoveryTokenFailure(mockApi); - await page.goto('/reset-password/token'); + await loadPage(page, '/reset-password/token'); await page.locator('input[name="password"]').fill(randomPassword()); await page.locator('button[type="submit"]').click(); @@ -173,7 +174,7 @@ test.describe('Change password in Okta', () => { await mockPasswordResetInvalidStateTokenFailure(mockApi); await mockValidateRecoveryTokenSuccess(mockApi); - await page.goto('/reset-password/token'); + await loadPage(page, '/reset-password/token'); await page.locator('input[name="password"]').fill(randomPassword()); await page.locator('button[type="submit"]').click(); @@ -198,7 +199,7 @@ test.describe('Change password in Okta', () => { await mockValidateRecoveryTokenSuccess(mockApi); await mockValidateRecoveryTokenFailure(mockApi); - await page.goto('/reset-password/token'); + await loadPage(page, '/reset-password/token'); await context.clearCookies({ name: 'GU_GATEWAY_STATE' }); await page.locator('input[name="password"]').fill(randomPassword()); @@ -214,7 +215,7 @@ test.describe('Change password in Okta', () => { await mockApi.post('/mock', { headers: { 'Content-Type': 'application/json', 'x-status': '500' }, }); - await page.goto('/reset-password/token'); + await loadPage(page, '/reset-password/token'); await expect(page.getByText('Link expired')).toBeVisible(); }); @@ -228,7 +229,7 @@ test.describe('Change password in Okta', () => { }); await mockValidateRecoveryTokenSuccess(mockApi); - await page.goto('/reset-password/token'); + await loadPage(page, '/reset-password/token'); await page.locator('input[name="password"]').fill(randomPassword()); await page.locator('button[type="submit"]').click(); @@ -255,7 +256,7 @@ test.describe('Change password in Okta', () => { ); await mockValidateRecoveryTokenSuccess(mockApi); - await page.goto('/reset-password/token'); + await loadPage(page, '/reset-password/token'); // even though this test is for a short password, we enter a valid password here to bypass // client-side password complexity checks in order to test the server-side response @@ -281,7 +282,7 @@ test.describe('Change password in Okta', () => { ); await mockValidateRecoveryTokenSuccess(mockApi); - await page.goto('/reset-password/token'); + await loadPage(page, '/reset-password/token'); await page.locator('input[name="password"]').fill(randomPassword()); await page.locator('button[type="submit"]').click(); diff --git a/playwright/tests/mocked/okta_register.spec.ts b/playwright/tests/mocked/okta_register.spec.ts index c0eb13e0f..b3183a5d9 100644 --- a/playwright/tests/mocked/okta_register.spec.ts +++ b/playwright/tests/mocked/okta_register.spec.ts @@ -1,13 +1,14 @@ import { BrowserContext, expect } from '@playwright/test'; import { test } from '../../fixtures/mockedApiRequest'; import { mockRequestPattern } from '../../helpers/network/mocking'; +import { loadPage } from '../../helpers/load-page'; test.describe('Okta Register flow', () => { test.beforeEach(async ({ mockApi, page, context }) => { await mockApi.get('/mock/purge'); await context.clearCookies(); // we visit the healthcheck page to make sure the cookies are cleared from the browser - await page.goto('/healthcheck'); + await loadPage(page, '/healthcheck'); }); const setIdxCookie = async (context: BrowserContext) => { @@ -41,7 +42,7 @@ test.describe('Okta Register flow', () => { await mockApi.get('/mock/purge'); await context.clearCookies(); // we visit the healthcheck page to make sure the cookies are cleared from the browser - await page.goto('/healthcheck'); + await loadPage(page, '/healthcheck'); }); test('should redirect to homepage if the idx Okta session cookie is valid', async ({ mockApi, @@ -65,7 +66,7 @@ test.describe('Okta Register flow', () => { data: {}, }); - await page.goto('/register/email'); + await loadPage(page, '/register/email'); await setIdxCookie(context); @@ -129,9 +130,9 @@ test.describe('Okta Register flow', () => { await setIdxCookie(context); // visit healthcheck to set the cookie - await page.goto('/healthcheck'); + await loadPage(page, '/healthcheck'); - await page.goto('/register'); + await loadPage(page, '/register'); await expect(page).toHaveURL(/\/reauthenticate/); @@ -146,7 +147,7 @@ test.describe('Okta Register flow', () => { await mockApi.get('/mock/purge'); await context.clearCookies(); // we visit the healthcheck page to make sure the cookies are cleared from the browser - await page.goto('/healthcheck'); + await loadPage(page, '/healthcheck'); }); test('should redirect to homepage if the idx Okta session cookie is valid', async ({ mockApi, @@ -173,9 +174,9 @@ test.describe('Okta Register flow', () => { await setIdxCookie(context); - await page.goto('/healthcheck'); + await loadPage(page, '/healthcheck'); - await page.goto('/register'); + await loadPage(page, '/register'); await expect(page.getByText('Sign in to the Guardian')).toBeVisible(); await expect(page.getByText('You are signed in with')).toBeVisible(); @@ -214,9 +215,9 @@ test.describe('Okta Register flow', () => { await setIdxCookie(context); // visit healthcheck to set the cookie - await page.goto('/healthcheck'); + await loadPage(page, '/healthcheck'); - await page.goto('/register'); + await loadPage(page, '/register'); await expect(page).toHaveURL(/\/reauthenticate/); diff --git a/playwright/tests/mocked/okta_sign_in.spec.ts b/playwright/tests/mocked/okta_sign_in.spec.ts index f107ab76f..a547d2018 100644 --- a/playwright/tests/mocked/okta_sign_in.spec.ts +++ b/playwright/tests/mocked/okta_sign_in.spec.ts @@ -2,6 +2,7 @@ import { expect } from '@playwright/test'; import { test } from '../../fixtures/mockedApiRequest'; import { JOBS_TOS_URI } from '@/shared/model/Configuration'; import { escapeRegExp } from '../../helpers/utils'; +import { loadPage } from '../../helpers/load-page'; test.describe('Sign in flow', () => { test.describe('Signing in - Okta', () => { @@ -54,7 +55,7 @@ test.describe('Sign in flow', () => { }, ]); - await page.goto('/signin'); + await loadPage(page, '/signin'); await expect(page.getByText('Sign in to the Guardian')).toBeVisible(); await expect(page.getByText('You are signed in with')).toBeVisible(); @@ -134,9 +135,10 @@ test.describe('Sign in flow', () => { }, }), ]); - await page.goto( + await loadPage( + page, '/signin?appClientId=123&fromURI=/oauth2/v1/authorize/redirect?okta_key=oktaKey', - { waitUntil: 'domcontentloaded' }, + 'domcontentloaded', ); await expect(page.getByText('Sign in to the Guardian app')).toBeVisible(); @@ -216,7 +218,8 @@ test.describe('Sign in flow', () => { }, ]); - await page.goto( + await loadPage( + page, '/signin?appClientId=456&fromURI=/oauth2/v1/authorize/redirect?okta_key=oktaKey', ); @@ -297,7 +300,8 @@ test.describe('Sign in flow', () => { }, ]); - await page.goto( + await loadPage( + page, '/signin?appClientId=456&fromURI=/oauth2/v1/authorize/redirect?okta_key=oktaKey', ); @@ -378,7 +382,8 @@ test.describe('Sign in flow', () => { }, ]); - await page.goto( + await loadPage( + page, '/signin?clientId=jobs&fromURI=/oauth2/v1/authorize/redirect?okta_key=oktaKey', ); @@ -413,7 +418,7 @@ test.describe('Sign in flow', () => { mockApi, page, }) => { - await page.goto('/signin?useOktaClassic=true'); + await loadPage(page, '/signin?useOktaClassic=true'); await page.locator('input[name="email"]').fill('example@example.com'); await page.locator('input[name="password"]').fill('password'); @@ -440,7 +445,7 @@ test.describe('Sign in flow', () => { mockApi, page, }) => { - await page.goto('/signin?useOktaClassic=true'); + await loadPage(page, '/signin?useOktaClassic=true'); await page.locator('input[name="email"]').fill('example@example.com'); await page.locator('input[name="password"]').fill('password'); await mockApi.post('/mock/permanent', { @@ -466,7 +471,7 @@ test.describe('Sign in flow', () => { mockApi, page, }) => { - await page.goto('/signin?useOktaClassic=true'); + await loadPage(page, '/signin?useOktaClassic=true'); await page.locator('input[name="email"]').fill('example@example.com'); await page.locator('input[name="password"]').fill('password'); await mockApi.post('/mock/permanent', { @@ -492,7 +497,8 @@ test.describe('Sign in flow', () => { mockApi, page, }) => { - await page.goto( + await loadPage( + page, '/signin?returnUrl=https%3A%2F%2Fwww.theguardian.com%2Fabout&useOktaClassic=true', ); await page.locator('input[name="email"]').fill('example@example.com'); @@ -559,7 +565,7 @@ test.describe('Sign in flow', () => { mockApi, page, }) => { - await page.goto('/signin?useOktaClassic=true'); + await loadPage(page, '/signin?useOktaClassic=true'); await page.locator('input[name="email"]').fill('example@example.com'); await page.locator('input[name="password"]').fill('password'); diff --git a/playwright/tests/mocked/rateLimit.spec.ts b/playwright/tests/mocked/rateLimit.spec.ts index 10acdfce8..476c3587d 100644 --- a/playwright/tests/mocked/rateLimit.spec.ts +++ b/playwright/tests/mocked/rateLimit.spec.ts @@ -1,5 +1,6 @@ import { expect } from '@playwright/test'; import { test } from '../../fixtures/mockedApiRequest'; +import { loadPage } from '../../helpers/load-page'; test.describe('POST requests return a user-facing error message when encountering a rate limit from Okta', () => { const v1Users429Response = { @@ -20,7 +21,7 @@ test.describe('POST requests return a user-facing error message when encounterin body: v1Users429Response, }, }); - await page.goto('/signin?usePasswordSignIn=true'); + await loadPage(page, '/signin?usePasswordSignIn=true'); await page.locator('input[name="email"]').fill('example@example.com'); await page.locator('input[name="password"]').fill('password'); @@ -31,7 +32,7 @@ test.describe('POST requests return a user-facing error message when encounterin }); test('Submit /reauthenticate', async ({ mockApi, page }) => { - await page.goto('/reauthenticate?usePasswordSignIn=true'); + await loadPage(page, '/reauthenticate?usePasswordSignIn=true'); await page.locator('input[name="email"]').fill('example@example.com'); await page.locator('input[name="password"]').fill('password'); @@ -49,7 +50,7 @@ test.describe('POST requests return a user-facing error message when encounterin }); test('Submit /register', async ({ mockApi, page }) => { - await page.goto('/register/email'); + await loadPage(page, '/register/email'); await page.locator('input[name="email"]').fill('example@example.com'); await mockApi.post('/mock/permanent-pattern', { @@ -66,7 +67,7 @@ test.describe('POST requests return a user-facing error message when encounterin }); test('Submit /reset-password', async ({ mockApi, page }) => { - await page.goto('/reset-password'); + await loadPage(page, '/reset-password'); await page.locator('input[name="email"]').fill('example@example.com'); await mockApi.post('/mock/permanent-pattern', { @@ -83,7 +84,7 @@ test.describe('POST requests return a user-facing error message when encounterin }); test('Submit /welcome/resend', async ({ mockApi, page }) => { - await page.goto(`/welcome/resend`); + await loadPage(page, `/welcome/resend`); await page.locator('input[name="email"]').fill('example@example.com'); await mockApi.post('/mock/permanent-pattern', { @@ -100,7 +101,7 @@ test.describe('POST requests return a user-facing error message when encounterin }); test('Submit /welcome/expired', async ({ mockApi, page }) => { - await page.goto(`/welcome/expired`); + await loadPage(page, `/welcome/expired`); await page.locator('input[name="email"]').fill('example@example.com'); await mockApi.post('/mock/permanent-pattern', { @@ -117,7 +118,7 @@ test.describe('POST requests return a user-facing error message when encounterin }); test('Submit /reset-password/resend', async ({ mockApi, page }) => { - await page.goto(`/reset-password/resend`); + await loadPage(page, `/reset-password/resend`); await page.locator('input[name="email"]').fill('example@example.com'); await mockApi.post('/mock/permanent-pattern', { @@ -134,7 +135,7 @@ test.describe('POST requests return a user-facing error message when encounterin }); test('Submit /reset-password/expired', async ({ mockApi, page }) => { - await page.goto(`/reset-password/expired`); + await loadPage(page, `/reset-password/expired`); await page.locator('input[name="email"]').fill('example@example.com'); await mockApi.post('/mock/permanent-pattern', { @@ -151,7 +152,7 @@ test.describe('POST requests return a user-facing error message when encounterin }); test('Submit /set-password/resend', async ({ mockApi, page }) => { - await page.goto(`/set-password/resend`); + await loadPage(page, `/set-password/resend`); await page.locator('input[name="email"]').fill('example@example.com'); await mockApi.post('/mock/permanent-pattern', { @@ -161,14 +162,16 @@ test.describe('POST requests return a user-facing error message when encounterin body: v1Users429Response, }, }); - await page.locator('button[type=submit]').click(); + await page.locator('button[type=submit]').click({ + noWaitAfter: true, + }); await expect( page.getByText('Sorry, something went wrong. Please try again.'), ).toBeVisible(); }); test('Submit /set-password/expired', async ({ mockApi, page }) => { - await page.goto(`/set-password/expired`); + await loadPage(page, `/set-password/expired`); await page.locator('input[name="email"]').fill('example@example.com'); await mockApi.post('/mock/permanent-pattern', { diff --git a/playwright/tests/mocked/register.spec.ts b/playwright/tests/mocked/register.spec.ts index 6018a5ba9..03a4d293a 100644 --- a/playwright/tests/mocked/register.spec.ts +++ b/playwright/tests/mocked/register.spec.ts @@ -1,6 +1,7 @@ import { expect } from '@playwright/test'; import { test } from '../../fixtures/mockedApiRequest'; import { injectAndCheckAxe } from '../../helpers/accessibility'; +import { loadPage } from '../../helpers/load-page'; test.describe('Registration flow', () => { test.beforeEach(async ({ mockApi }) => { @@ -11,9 +12,9 @@ test.describe('Registration flow', () => { test('Has no detectable a11y violations on registration page', async ({ page, }) => { - await page.goto('/register'); + await loadPage(page, '/register'); await injectAndCheckAxe(page); - await page.goto('/register/email'); + await loadPage(page, '/register/email'); await injectAndCheckAxe(page); }); @@ -21,7 +22,7 @@ test.describe('Registration flow', () => { mockApi, page, }) => { - await page.goto('/register/email'); + await loadPage(page, '/register/email'); await page.locator('input[name="email"]').fill('Invalid email'); await mockApi.post('/mock/permanent-pattern', { data: { @@ -43,7 +44,8 @@ test.describe('Registration flow', () => { await route.abort('failed'); }); - await page.goto( + await loadPage( + page, '/register/email?returnUrl=https%3A%2F%2Fwww.theguardian.com%2Fabout', ); await page.locator('input[name="email"]').fill('placeholder@example.com'); @@ -66,7 +68,8 @@ test.describe('Registration flow', () => { body: {}, }, }); - await page.goto( + await loadPage( + page, '/register/email?returnUrl=https%3A%2F%2Flocalhost%3A8861%2Fsignin', ); diff --git a/playwright/tests/mocked/registerController.spec.ts b/playwright/tests/mocked/registerController.spec.ts index 0cef3ad53..a7f76f4d7 100644 --- a/playwright/tests/mocked/registerController.spec.ts +++ b/playwright/tests/mocked/registerController.spec.ts @@ -13,6 +13,7 @@ import idxChallengeResponseEmail from '../../fixtures/okta-responses/success/idx import idxChallengeResponsePassword from '../../fixtures/okta-responses/success/idx-challenge-response-password.json'; import idxChallengeAnswerPasswordEnrollEmailResponse from '../../fixtures/okta-responses/success/idx-challenge-answer-password-enroll-email-response.json'; import { dangerouslySetPlaceholderPasswordMocks } from '../../helpers/api/placeholder-password-mock'; +import { loadPage } from '../../helpers/load-page'; const baseIdxPasscodeRegistrationMocks = async (mockApi: APIRequestContext) => { await Promise.all([ @@ -73,7 +74,7 @@ userStatuses.forEach((status) => { test.describe(`Given I am a ${status || 'nonexistent'} user`, () => { test.describe('When I submit the form on /register', () => { test.beforeEach(async ({ page }) => { - await page.goto('/register/email'); + await loadPage(page, '/register/email'); await page.locator('input[name="email"]').fill('example@example.com'); }); diff --git a/playwright/tests/mocked/resendEmailController.spec.ts b/playwright/tests/mocked/resendEmailController.spec.ts index a7a637ee0..01b454b27 100644 --- a/playwright/tests/mocked/resendEmailController.spec.ts +++ b/playwright/tests/mocked/resendEmailController.spec.ts @@ -17,6 +17,7 @@ import idxEnrollNewExistingUserResponse from '../../fixtures/okta-responses/erro import { identifyResponse } from '../../fixtures/okta-responses/success/idx-identify-response'; import idxChallengeResponseEmail from '../../fixtures/okta-responses/success/idx-challenge-response-email.json'; import { dangerouslySetPlaceholderPasswordMocks } from '../../helpers/api/placeholder-password-mock'; +import { loadPage } from '../../helpers/load-page'; // IDX passcode registration mocks const baseIdxPasscodeRegistrationMocks = async (mockApi: APIRequestContext) => { @@ -90,7 +91,7 @@ userStatuses.forEach((status) => { await setEncryptedStateCookie(context, { email: 'example@example.com', }); - await page.goto('/register/email-sent'); + await loadPage(page, '/register/email-sent'); }); switch (status) { @@ -376,7 +377,7 @@ userStatuses.forEach((status) => { await setEncryptedStateCookie(context, { email: 'example@example.com', }); - await page.goto('/welcome/email-sent'); + await loadPage(page, '/welcome/email-sent'); }); switch (status) { @@ -644,7 +645,7 @@ userStatuses.forEach((status) => { // ========================================== test.describe('When I submit the form on /welcome/resend', () => { test.beforeEach(async ({ page }) => { - await page.goto('/welcome/resend'); + await loadPage(page, '/welcome/resend'); await page.locator('input[name="email"]').fill('example@example.com'); }); @@ -814,7 +815,7 @@ userStatuses.forEach((status) => { // ========================================== test.describe('When I submit the form on /welcome/expired', () => { test.beforeEach(async ({ page }) => { - await page.goto('/welcome/expired'); + await loadPage(page, '/welcome/expired'); await page.locator('input[name="email"]').fill('example@example.com'); }); diff --git a/playwright/tests/mocked/resetPasswordController.spec.ts b/playwright/tests/mocked/resetPasswordController.spec.ts index 603235413..6610a2ded 100644 --- a/playwright/tests/mocked/resetPasswordController.spec.ts +++ b/playwright/tests/mocked/resetPasswordController.spec.ts @@ -18,6 +18,7 @@ import idxChallengeAnswerPasswordEnrollEmailResponse from '../../fixtures/okta-r import idxInteractResponse from '../../fixtures/okta-responses/success/idx-interact-response.json'; import idxIntrospectDefaultResponse from '../../fixtures/okta-responses/success/idx-introspect-default-response.json'; import { dangerouslySetPlaceholderPasswordMocks } from '../../helpers/api/placeholder-password-mock'; +import { loadPage } from '../../helpers/load-page'; // ============================================ // Helper Functions @@ -486,7 +487,7 @@ userStatuses.forEach((status) => { test.describe('When I submit the form on /reset-password', () => { test.beforeEach(async ({ page, context }) => { await setEncryptedStateCookie(context, {}); - await page.goto('/reset-password?useOktaClassic=true'); + await loadPage(page, '/reset-password?useOktaClassic=true'); await page.locator('input[name="email"]').fill('example@example.com'); }); @@ -622,7 +623,7 @@ userStatuses.forEach((status) => { await setEncryptedStateCookie(context, { email: 'example@example.com', }); - await page.goto('/reset-password/email-sent?useOktaClassic=true'); + await loadPage(page, '/reset-password/email-sent?useOktaClassic=true'); }); switch (status) { @@ -753,7 +754,7 @@ userStatuses.forEach((status) => { // ------------------------------------------ test.describe('When I submit the form on /reset-password/resend', () => { test.beforeEach(async ({ page }) => { - await page.goto('/reset-password/resend?useOktaClassic=true'); + await loadPage(page, '/reset-password/resend?useOktaClassic=true'); await page.locator('input[name="email"]').fill('example@example.com'); }); @@ -885,7 +886,7 @@ userStatuses.forEach((status) => { // ------------------------------------------ test.describe('When I submit the form on /reset-password/expired', () => { test.beforeEach(async ({ page }) => { - await page.goto('/reset-password/expired?useOktaClassic=true'); + await loadPage(page, '/reset-password/expired?useOktaClassic=true'); await page.locator('input[name="email"]').fill('example@example.com'); }); @@ -1020,7 +1021,7 @@ userStatuses.forEach((status) => { await setEncryptedStateCookie(context, { email: 'example@example.com', }); - await page.goto('/set-password/email-sent?useOktaClassic=true'); + await loadPage(page, '/set-password/email-sent?useOktaClassic=true'); }); switch (status) { @@ -1151,7 +1152,7 @@ userStatuses.forEach((status) => { // ------------------------------------------ test.describe('When I submit the form on /set-password/resend', () => { test.beforeEach(async ({ page }) => { - await page.goto('/set-password/resend?useOktaClassic=true'); + await loadPage(page, '/set-password/resend?useOktaClassic=true'); await page.locator('input[name="email"]').fill('example@example.com'); }); @@ -1283,7 +1284,7 @@ userStatuses.forEach((status) => { // ------------------------------------------ test.describe('When I submit the form on /set-password/expired', () => { test.beforeEach(async ({ page }) => { - await page.goto('/set-password/expired?useOktaClassic=true'); + await loadPage(page, '/set-password/expired?useOktaClassic=true'); await page.locator('input[name="email"]').fill('example@example.com'); }); @@ -1421,7 +1422,7 @@ userStatuses.forEach((status) => { test.describe('When I submit the form on /reset-password', () => { test.beforeEach(async ({ page, context }) => { await setEncryptedStateCookie(context, {}); - await page.goto('/reset-password'); + await loadPage(page, '/reset-password'); await page.locator('input[name="email"]').fill('example@example.com'); }); @@ -1503,7 +1504,7 @@ userStatuses.forEach((status) => { ).toISOString(), email: 'test@example.com', }); - await page.goto('/reset-password/email-sent'); + await loadPage(page, '/reset-password/email-sent'); }); switch (status) { @@ -1578,7 +1579,7 @@ userStatuses.forEach((status) => { test.describe('When I submit the form on /reset-password/resend', () => { test.beforeEach(async ({ page, context }) => { await setEncryptedStateCookie(context, {}); - await page.goto('/reset-password/resend'); + await loadPage(page, '/reset-password/resend'); await page.locator('input[name="email"]').fill('example@example.com'); }); @@ -1654,7 +1655,7 @@ userStatuses.forEach((status) => { test.describe('When I submit the form on /reset-password/expired', () => { test.beforeEach(async ({ page, context }) => { await setEncryptedStateCookie(context, {}); - await page.goto('/reset-password/expired'); + await loadPage(page, '/reset-password/expired'); await page.locator('input[name="email"]').fill('example@example.com'); }); @@ -1730,7 +1731,7 @@ userStatuses.forEach((status) => { test.describe('When I submit the form on /set-password/resend', () => { test.beforeEach(async ({ page, context }) => { await setEncryptedStateCookie(context, {}); - await page.goto('/set-password/resend'); + await loadPage(page, '/set-password/resend'); await page.locator('input[name="email"]').fill('example@example.com'); }); @@ -1806,7 +1807,7 @@ userStatuses.forEach((status) => { test.describe('When I submit the form on /set-password/expired', () => { test.beforeEach(async ({ page, context }) => { await setEncryptedStateCookie(context, {}); - await page.goto('/set-password/expired'); + await loadPage(page, '/set-password/expired'); await page.locator('input[name="email"]').fill('example@example.com'); }); @@ -1885,7 +1886,7 @@ userStatuses.forEach((status) => { mockApi, page, }) => { - await page.goto('/reset-password'); + await loadPage(page, '/reset-password'); await page.locator('input[name="email"]').fill('test@example.com'); const response = { ...userResponse.response, status }; diff --git a/playwright/tests/mocked/reset_password.spec.ts b/playwright/tests/mocked/reset_password.spec.ts index 19bda6272..e7f27fee7 100644 --- a/playwright/tests/mocked/reset_password.spec.ts +++ b/playwright/tests/mocked/reset_password.spec.ts @@ -1,6 +1,7 @@ import { expect } from '@playwright/test'; import { test } from '../../fixtures/mockedApiRequest'; import { injectAndCheckAxe } from '../../helpers/accessibility'; +import { loadPage } from '../../helpers/load-page'; const CONTENT = { ERRORS: { @@ -16,7 +17,7 @@ const emailNotRegistered = 'notregistered@example.com'; test.describe('Password reset flow', () => { test.beforeEach(async ({ mockApi, page }) => { await mockApi.get('/mock/purge'); - await page.goto('/reset-password'); + await loadPage(page, '/reset-password'); }); test.describe('A11y checks', () => { @@ -102,7 +103,7 @@ test.describe('Password reset flow', () => { * navigate again to /reset-password as the beforeEach block at the top of * this file would navigate and happen before the recaptcha network request intercept */ - await page.goto('/reset-password'); + await loadPage(page, '/reset-password'); await page.locator('input[name="email"]').fill(emailNotRegistered); await page.getByText('Request password reset').click(); diff --git a/playwright/tests/mocked/set_password.spec.ts b/playwright/tests/mocked/set_password.spec.ts index c8bdfa6a2..58f62bc40 100644 --- a/playwright/tests/mocked/set_password.spec.ts +++ b/playwright/tests/mocked/set_password.spec.ts @@ -1,6 +1,7 @@ import { expect } from '@playwright/test'; import { test } from '../../fixtures/mockedApiRequest'; import { injectAndCheckAxe } from '../../helpers/accessibility'; +import { loadPage } from '../../helpers/load-page'; test.describe('Password set/create flow', () => { test.beforeEach(async ({ mockApi }) => { @@ -19,7 +20,7 @@ test.describe('Password set/create flow', () => { body: {}, }, }); - await page.goto('/set-password/fake_token'); + await loadPage(page, '/set-password/fake_token'); await injectAndCheckAxe(page); }); @@ -34,7 +35,7 @@ test.describe('Password set/create flow', () => { body: {}, }, }); - await page.goto('/set-password/fake_token'); + await loadPage(page, '/set-password/fake_token'); await injectAndCheckAxe(page); }); @@ -49,7 +50,7 @@ test.describe('Password set/create flow', () => { body: {}, }, }); - await page.goto('/set-password/fake_token'); + await loadPage(page, '/set-password/fake_token'); await page.locator('input[name="password"]').fill('short'); await page.locator('button[type="submit"]').click(); await injectAndCheckAxe(page); @@ -69,7 +70,7 @@ test.describe('Password set/create flow', () => { const breachCheckPromise = page.waitForRequest( 'https://api.pwnedpasswords.com/range/*', ); - await page.goto('/set-password/fake_token'); + await loadPage(page, '/set-password/fake_token'); await page .locator('input[name="password"]') .fill('thisisalongandunbreachedpassword'); @@ -91,7 +92,7 @@ test.describe('Password set/create flow', () => { body: {}, }, }); - await page.goto('/set-password/fake_token'); + await loadPage(page, '/set-password/fake_token'); await expect(page.locator('input[name="password"]')).toHaveAttribute( 'type', 'password', @@ -119,7 +120,7 @@ test.describe('Password set/create flow', () => { body: {}, }, }); - await page.goto('/set-password/fake_token'); + await loadPage(page, '/set-password/fake_token'); await expect(page.getByText('This link has expired')).toBeVisible(); }); @@ -137,7 +138,7 @@ test.describe('Password set/create flow', () => { await page.route(/.*google\.com\/recaptcha\/.*/, async (route) => { await route.abort('failed'); }); - await page.goto('/set-password/fake_token'); + await loadPage(page, '/set-password/fake_token'); await expect(page.getByText('This link has expired')).toBeVisible(); await page.locator('input[name="email"]').fill('some@email.com'); await page.locator('button[type="submit"]').click(); @@ -159,7 +160,7 @@ test.describe('Password set/create flow', () => { body: {}, }, }); - await page.goto('/set-password/fake_token'); + await loadPage(page, '/set-password/fake_token'); await expect(page.getByText('This link has expired')).toBeVisible(); }); }); @@ -202,7 +203,7 @@ test.describe('Password set/create flow', () => { const breachCheckPromise = page.waitForRequest( 'https://api.pwnedpasswords.com/range/*', ); - await page.goto('/set-password/fake_token'); + await loadPage(page, '/set-password/fake_token'); await page .locator('input[name="password"]') .fill('thisisalongandunbreachedpassword'); diff --git a/playwright/tests/mocked/signInController.spec.ts b/playwright/tests/mocked/signInController.spec.ts index 31b6b39f2..89386e96d 100644 --- a/playwright/tests/mocked/signInController.spec.ts +++ b/playwright/tests/mocked/signInController.spec.ts @@ -10,6 +10,7 @@ import idxChallengeAnswerPassword401Response from '../../fixtures/okta-responses import userResponse from '../../fixtures/okta-responses/success/user.json'; import idxInteractResponse from '../../fixtures/okta-responses/success/idx-interact-response.json'; import idxIntrospectDefaultResponse from '../../fixtures/okta-responses/success/idx-introspect-default-response.json'; +import { loadPage } from '../../helpers/load-page'; const baseIdxPasscodeResetPasswordMocks = async ( mockApi: APIRequestContext, @@ -37,7 +38,7 @@ const baseIdxPasscodeResetPasswordMocks = async ( test.describe('When I submit the form on /signin - useOktaClassic', () => { test.beforeEach(async ({ mockApi, page }) => { await mockApi.get('/mock/purge'); - await page.goto('/signin?useOktaClassic=true'); + await loadPage(page, '/signin?useOktaClassic=true'); await page.locator('input[name="email"]').fill('example@example.com'); await page.locator('input[name="password"]').fill('password'); }); @@ -96,7 +97,7 @@ test.describe('When I submit the form on /signin - useOktaClassic', () => { test.describe('When I submit the form on /signin - idx api', () => { test.beforeEach(async ({ mockApi, page }) => { await mockApi.get('/mock/purge'); - await page.goto('/signin?usePasswordSignIn=true'); + await loadPage(page, '/signin?usePasswordSignIn=true'); await page.locator('input[name="email"]').fill('example@example.com'); await page.locator('input[name="password"]').fill('password'); }); @@ -222,7 +223,7 @@ test.describe('When I submit the form on /signin - idx api', () => { test.describe('When I submit the form on /reauthenticate - useOktaClassic', () => { test.beforeEach(async ({ mockApi, page }) => { await mockApi.get('/mock/purge'); - await page.goto('/reauthenticate?useOktaClassic=true'); + await loadPage(page, '/reauthenticate?useOktaClassic=true'); await page.locator('input[name="email"]').fill('example@example.com'); await page.locator('input[name="password"]').fill('password'); }); @@ -281,7 +282,7 @@ test.describe('When I submit the form on /reauthenticate - useOktaClassic', () = test.describe('When I submit the form on /reauthenticate - idx api', () => { test.beforeEach(async ({ mockApi, page }) => { await mockApi.get('/mock/purge'); - await page.goto('/reauthenticate?usePasswordSignIn=true'); + await loadPage(page, '/reauthenticate?usePasswordSignIn=true'); await page.locator('input[name="email"]').fill('example@example.com'); await page.locator('input[name="password"]').fill('password'); }); diff --git a/playwright/tests/mocked/sign_in.spec.ts b/playwright/tests/mocked/sign_in.spec.ts index 90d0d5d70..3211e05fa 100644 --- a/playwright/tests/mocked/sign_in.spec.ts +++ b/playwright/tests/mocked/sign_in.spec.ts @@ -1,6 +1,7 @@ import { expect } from '@playwright/test'; import { test } from '../../fixtures/mockedApiRequest'; import { injectAndCheckAxe } from '../../helpers/accessibility'; +import { loadPage } from '../../helpers/load-page'; test.describe('Sign in flow', () => { test.beforeEach(async ({ mockApi }) => { @@ -11,7 +12,7 @@ test.describe('Sign in flow', () => { test('Has no detectable a11y violations on sign in page', async ({ page, }) => { - await page.goto('/signin?usePasswordSignIn=true'); + await loadPage(page, '/signin?usePasswordSignIn=true'); await injectAndCheckAxe(page); }); @@ -19,7 +20,7 @@ test.describe('Sign in flow', () => { page, mockApi, }) => { - await page.goto('/signin?usePasswordSignIn=true'); + await loadPage(page, '/signin?usePasswordSignIn=true'); await page.locator('input[name="email"]').fill('Invalid email'); await page.locator('input[name="password"]').fill('Invalid password'); await mockApi.post('/mock/permanent-pattern', { @@ -42,7 +43,7 @@ test.describe('Sign in flow', () => { body: { status: 'ok', email: 'test@test.com' }, }, }); - await page.goto('/signin?encryptedEmail=bdfalrbagbgu'); + await loadPage(page, '/signin?encryptedEmail=bdfalrbagbgu'); await expect(page.locator('input[name="email"]')).toHaveValue( 'test@test.com', ); @@ -61,7 +62,8 @@ test.describe('Sign in flow', () => { await page.route(/.*google.com\/recaptcha\/.*/, async (route) => { await route.abort('failed'); }); - await page.goto( + await loadPage( + page, '/signin?returnUrl=https%3A%2F%2Fwww.theguardian.com%2Fabout&usePasswordSignIn=true', ); await page.locator('input[name="email"]').fill('placeholder@example.com'); @@ -84,7 +86,8 @@ test.describe('Sign in flow', () => { await mockApi.post('/mock/permanent-pattern', { data: { pattern: '/api/v1/users/.*', status: 500, body: {} }, }); - await page.goto( + await loadPage( + page, '/signin?returnUrl=https%3A%2F%2Flocalhost%3A8861%2Fsignin&usePasswordSignIn=true', ); await page.locator('input[name="email"]').fill('example@example.com'); diff --git a/playwright/tests/mocked/unsubscribe.spec.ts b/playwright/tests/mocked/unsubscribe.spec.ts index 6b1bb0f9a..8fcfb33e3 100644 --- a/playwright/tests/mocked/unsubscribe.spec.ts +++ b/playwright/tests/mocked/unsubscribe.spec.ts @@ -1,6 +1,7 @@ import { expect } from '@playwright/test'; import { test } from '../../fixtures/mockedApiRequest'; import { injectAndCheckAxe } from '../../helpers/accessibility'; +import { loadPage } from '../../helpers/load-page'; test.describe('Unsubscribe newsletter/marketing email', () => { test.beforeEach(async ({ mockApi }) => { @@ -19,7 +20,8 @@ test.describe('Unsubscribe newsletter/marketing email', () => { body: {}, }, }); - await page.goto( + await loadPage( + page, '/unsubscribe/newsletter/pushing-buttons%3A1000000%3A1677075570/token', ); await injectAndCheckAxe(page); @@ -53,7 +55,8 @@ test.describe('Unsubscribe newsletter/marketing email', () => { body: {}, }, }); - await page.goto( + await loadPage( + page, '/unsubscribe/newsletter/pushing-buttons%3A1000000%3A1677075570/token', ); await injectAndCheckAxe(page); @@ -72,7 +75,8 @@ test.describe('Unsubscribe newsletter/marketing email', () => { body: {}, }, }); - await page.goto( + await loadPage( + page, '/unsubscribe/newsletter/pushing-buttons%3A1000000%3A1677075570/token', ); await expect( @@ -122,7 +126,8 @@ test.describe('Unsubscribe newsletter/marketing email', () => { body: {}, }, }); - await page.goto( + await loadPage( + page, '/unsubscribe/marketing/supporter%3A1000000%3A1677075570/token', ); await expect( @@ -147,7 +152,8 @@ test.describe('Unsubscribe newsletter/marketing email', () => { test('should be able to handle a unsubscribe error if emailType is not newsletter/marketing', async ({ page, }) => { - await page.goto( + await loadPage( + page, '/unsubscribe/fake/supporter%3A1000000%3A1677075570/token', ); await expect(page.getByText('Unable to unsubscribe.')).toBeVisible(); @@ -157,7 +163,8 @@ test.describe('Unsubscribe newsletter/marketing email', () => { mockApi, page, }) => { - await page.goto( + await loadPage( + page, '/unsubscribe/newsletter/pushing-buttons%3A1000000%3A16770755abc70/token', ); @@ -180,12 +187,16 @@ test.describe('Unsubscribe newsletter/marketing email', () => { await expect(page.getByText('Unable to unsubscribe.')).toBeVisible(); - await page.goto( + await loadPage( + page, '/unsubscribe/newsletter/pushing-buttons%3A1000000%3A/token', ); await expect(page.getByText('Unable to unsubscribe.')).toBeVisible(); - await page.goto('/unsubscribe/newsletter/pushing-buttons-bad-data/token'); + await loadPage( + page, + '/unsubscribe/newsletter/pushing-buttons-bad-data/token', + ); await expect(page.getByText('Unable to unsubscribe.')).toBeVisible(); }); @@ -200,7 +211,8 @@ test.describe('Unsubscribe newsletter/marketing email', () => { body: {}, }, }); - await page.goto( + await loadPage( + page, '/unsubscribe/fake/supporter%3A1000000%3A1677075570/token', ); await expect(page.getByText('Unable to unsubscribe.')).toBeVisible(); diff --git a/playwright/tests/mocked/welcome.spec.ts b/playwright/tests/mocked/welcome.spec.ts index d39c36dad..6b6d15aa0 100644 --- a/playwright/tests/mocked/welcome.spec.ts +++ b/playwright/tests/mocked/welcome.spec.ts @@ -1,5 +1,6 @@ import { test } from '../../fixtures/mockedApiRequest'; import { injectAndCheckAxe } from '../../helpers/accessibility'; +import { loadPage } from '../../helpers/load-page'; test.describe('Welcome and set password page', () => { const defaultEmail = 'someone@theguardian.com'; @@ -29,7 +30,7 @@ test.describe('Welcome and set password page', () => { body: checkTokenSuccessResponse(), }, }); - await page.goto(`/welcome/fake_token`); + await loadPage(page, `/welcome/fake_token`); await injectAndCheckAxe(page); }); @@ -53,7 +54,7 @@ test.describe('Welcome and set password page', () => { }, }), ]); - await page.goto(`/welcome/fake_token`); + await loadPage(page, `/welcome/fake_token`); await page.locator('input[name="password"]').fill('short'); await page.locator('button[type="submit"]').click(); await injectAndCheckAxe(page); @@ -62,7 +63,7 @@ test.describe('Welcome and set password page', () => { test('has no detectable a11y violations on the resend page', async ({ page, }) => { - await page.goto(`/welcome/resend`); + await loadPage(page, `/welcome/resend`); await injectAndCheckAxe(page); }); @@ -70,7 +71,7 @@ test.describe('Welcome and set password page', () => { mockApi, page, }) => { - await page.goto(`/welcome/resend`); + await loadPage(page, `/welcome/resend`); // Mock user lookup to fail with 500 await mockApi.post('/mock/permanent-pattern', { @@ -91,7 +92,7 @@ test.describe('Welcome and set password page', () => { mockApi, page, }) => { - await page.goto(`/welcome/resend`); + await loadPage(page, `/welcome/resend`); // Mock user lookup to succeed (triggers email resend flow) await mockApi.post('/mock/permanent-pattern', { @@ -129,7 +130,7 @@ test.describe('Welcome and set password page', () => { test('has no detectable a11y violations on the email sent page without resend box', async ({ page, }) => { - await page.goto(`/welcome/email-sent`); + await loadPage(page, `/welcome/email-sent`); await injectAndCheckAxe(page); }); });