From 49138f99ef52c584d1b9e15ca4421be2b0340656 Mon Sep 17 00:00:00 2001 From: Paul D'Ambra Date: Sun, 29 Dec 2024 14:21:53 +0000 Subject: [PATCH] Extract repeated pattern --- .../session-recording/opting-out.spec.ts | 24 ++++----- .../session-recording-array-full.spec.ts | 6 +-- ...ssion-recording-ingestion-controls.spec.ts | 14 +++--- .../session-recording-linked-flags.spec.ts | 14 +++--- ...session-recording-network-recorder.spec.ts | 48 +++++++++--------- .../session-recording-sampling.spec.ts | 30 +++++------ .../session-recording.spec.ts | 50 +++++++++---------- .../utils/posthog-playwright-test-base.ts | 16 ++++++ 8 files changed, 109 insertions(+), 93 deletions(-) diff --git a/playwright/session-recording/opting-out.spec.ts b/playwright/session-recording/opting-out.spec.ts index b2debb362..752841b25 100644 --- a/playwright/session-recording/opting-out.spec.ts +++ b/playwright/session-recording/opting-out.spec.ts @@ -58,13 +58,13 @@ test.describe('Session Recording - opting out', () => { test('can start recording after starting opted out', async ({ page, context }) => { await startWith({ opt_out_capturing_by_default: true }, page, context) - const loadRecorder = page.waitForResponse('**/recorder.js*') - await page.evaluate(() => { - const ph = (window as WindowWithPostHog).posthog - ph?.opt_in_capturing() - ph?.startSessionRecording() + await page.waitingForNetworkCausedBy(['**/recorder.js*'], async () => { + await page.evaluate(() => { + const ph = (window as WindowWithPostHog).posthog + ph?.opt_in_capturing() + ph?.startSessionRecording() + }) }) - await loadRecorder expect((await page.capturedEvents()).map((x) => x.event)).toEqual(['$opt_in', '$pageview']) @@ -78,13 +78,13 @@ test.describe('Session Recording - opting out', () => { test('can start recording when starting disabled', async ({ page, context }) => { await startWith({ disable_session_recording: true }, page, context) - const loadRecorder = page.waitForResponse('**/recorder.js*') - await page.resetCapturedEvents() - await page.evaluate(() => { - const ph = (window as WindowWithPostHog).posthog - ph?.startSessionRecording() + await page.waitingForNetworkCausedBy(['**/recorder.js*'], async () => { + await page.resetCapturedEvents() + await page.evaluate(() => { + const ph = (window as WindowWithPostHog).posthog + ph?.startSessionRecording() + }) }) - await loadRecorder await page.locator('[data-cy-input]').type('hello posthog!') await pollUntilEventCaptured(page, '$snapshot') diff --git a/playwright/session-recording/session-recording-array-full.spec.ts b/playwright/session-recording/session-recording-array-full.spec.ts index 6f3922e72..8f31ffbee 100644 --- a/playwright/session-recording/session-recording-array-full.spec.ts +++ b/playwright/session-recording/session-recording-array-full.spec.ts @@ -19,9 +19,9 @@ test.describe('session recording in array.full.js', () => { test('captures session events', async ({ page, context }) => { await start(startOptions, page, context) - const responsePromise = page.waitForResponse('**/ses/*') - await page.locator('[data-cy-input]').fill('hello posthog!') - await responsePromise + await page.waitingForNetworkCausedBy(['**/ses/*'], async () => { + await page.locator('[data-cy-input]').fill('hello posthog!') + }) await page.evaluate(() => { const ph = (window as WindowWithPostHog).posthog diff --git a/playwright/session-recording/session-recording-ingestion-controls.spec.ts b/playwright/session-recording/session-recording-ingestion-controls.spec.ts index a11184657..1080cc59e 100644 --- a/playwright/session-recording/session-recording-ingestion-controls.spec.ts +++ b/playwright/session-recording/session-recording-ingestion-controls.spec.ts @@ -31,14 +31,14 @@ test.describe('Session recording - multiple ingestion controls', () => { }) test('respects sampling when overriding linked flag', async ({ page }) => { - const loadRecorder = page.waitForResponse('**/recorder.js*') - await page.evaluate(() => { - const ph = (window as WindowWithPostHog).posthog - ph?.opt_in_capturing() - // this won't start recording because of the linked flag and sample rate - ph?.startSessionRecording() + await page.waitingForNetworkCausedBy(['**/recorder.js*'], async () => { + await page.evaluate(() => { + const ph = (window as WindowWithPostHog).posthog + ph?.opt_in_capturing() + // this won't start recording because of the linked flag and sample rate + ph?.startSessionRecording() + }) }) - await loadRecorder expect((await page.capturedEvents()).map((x) => x.event)).toEqual(['$opt_in', '$pageview']) diff --git a/playwright/session-recording/session-recording-linked-flags.spec.ts b/playwright/session-recording/session-recording-linked-flags.spec.ts index ff15687c3..71ceafceb 100644 --- a/playwright/session-recording/session-recording-linked-flags.spec.ts +++ b/playwright/session-recording/session-recording-linked-flags.spec.ts @@ -29,14 +29,14 @@ test.describe('Session recording - linked flags', () => { }) test('can opt in and override linked flag', async ({ page }) => { - const loadRecorder = page.waitForResponse('**/recorder.js*') - await page.evaluate(() => { - const ph = (window as WindowWithPostHog).posthog - ph?.opt_in_capturing() - // starting does not begin recording because of the linked flag - ph?.startSessionRecording() + await page.waitingForNetworkCausedBy(['**/recorder.js*'], async () => { + await page.evaluate(() => { + const ph = (window as WindowWithPostHog).posthog + ph?.opt_in_capturing() + // starting does not begin recording because of the linked flag + ph?.startSessionRecording() + }) }) - await loadRecorder expect((await page.capturedEvents()).map((x) => x.event)).toEqual(['$opt_in', '$pageview']) await page.resetCapturedEvents() diff --git a/playwright/session-recording/session-recording-network-recorder.spec.ts b/playwright/session-recording/session-recording-network-recorder.spec.ts index 904d23d08..62b771c86 100644 --- a/playwright/session-recording/session-recording-network-recorder.spec.ts +++ b/playwright/session-recording/session-recording-network-recorder.spec.ts @@ -59,27 +59,28 @@ test.beforeEach(async ({ context }) => { }) } - await start( - { - options: { - session_recording: {}, - }, - decideResponseOverrides: { - sessionRecording: { - endpoint: '/ses/', - networkPayloadCapture: { recordBody: true, recordHeaders: true }, + await page.waitingForNetworkCausedBy(['**/recorder.js*'], async () => { + await start( + { + options: { + session_recording: {}, + }, + decideResponseOverrides: { + sessionRecording: { + endpoint: '/ses/', + networkPayloadCapture: { recordBody: true, recordHeaders: true }, + }, + capturePerformance: true, + autocapture_opt_out: true, }, - capturePerformance: true, - autocapture_opt_out: true, + url: './playground/cypress/index.html', + runBeforePostHogInit: wrapInPageContext, + runAfterPostHogInit: wrapInPageContext, }, - url: './playground/cypress/index.html', - runBeforePostHogInit: wrapInPageContext, - runAfterPostHogInit: wrapInPageContext, - }, - page, - context - ) - await page.waitForResponse('**/recorder.js*') + page, + context + ) + }) // also wrap after posthog is loaded await page.evaluate((isBadlyBehaved) => { @@ -98,12 +99,9 @@ test.beforeEach(async ({ context }) => { }) ;['fetch', 'xhr'].forEach((networkType) => { test('it captures ' + networkType, async ({ page, browserName }) => { - const exampleComResponse = page.waitForResponse('https://example.com/') - const sessionRecordingDataSent = page.waitForResponse('**/ses/*') - await page.click(`[data-cy-${networkType}-call-button]`) - await exampleComResponse - await sessionRecordingDataSent - + await page.waitingForNetworkCausedBy(['**/ses/*', 'https://example.com/'], async () => { + await page.click(`[data-cy-${networkType}-call-button]`) + }) const capturedEvents = await page.capturedEvents() const snapshots = capturedEvents.filter((c) => c.event === '$snapshot') diff --git a/playwright/session-recording/session-recording-sampling.spec.ts b/playwright/session-recording/session-recording-sampling.spec.ts index f6b681a14..296d1f229 100644 --- a/playwright/session-recording/session-recording-sampling.spec.ts +++ b/playwright/session-recording/session-recording-sampling.spec.ts @@ -27,9 +27,10 @@ test.describe('Session recording - sampling', () => { }, } test.beforeEach(async ({ page, context }) => { - await start(startOptions, page, context) + await page.waitingForNetworkCausedBy(['**/recorder.js*'], async () => { + await start(startOptions, page, context) + }) - await page.waitForResponse('**/recorder.js*') const capturedEvents = await page.evaluate(() => (window as WindowWithPostHog).capturedEvents || []) expect(capturedEvents.map((x) => x.event)).toEqual(['$pageview']) await page.resetCapturedEvents() @@ -58,18 +59,19 @@ test.describe('Session recording - sampling', () => { await page.resetCapturedEvents() await page.reload() - await start( - { - ...sampleZeroStartOptions, - type: 'reload', - }, - page, - context - ) - await page.waitForResponse('**/recorder.js*') - const responsePromise = page.waitForResponse('**/ses/*') - await page.locator('[data-cy-input]').fill('hello posthog!') - await responsePromise + await page.waitingForNetworkCausedBy(['**/recorder.js*'], async () => { + await start( + { + ...sampleZeroStartOptions, + type: 'reload', + }, + page, + context + ) + }) + await page.waitingForNetworkCausedBy(['**/ses/*'], async () => { + await page.locator('[data-cy-input]').fill('hello posthog!') + }) const afterReloadCapturedEvents = await page.capturedEvents() const lastCaptured = afterReloadCapturedEvents[afterReloadCapturedEvents.length - 1] diff --git a/playwright/session-recording/session-recording.spec.ts b/playwright/session-recording/session-recording.spec.ts index 1e4a0d7b1..93a2812c5 100644 --- a/playwright/session-recording/session-recording.spec.ts +++ b/playwright/session-recording/session-recording.spec.ts @@ -146,22 +146,22 @@ test.describe('Session recording - array.js', () => { const capturedEvents = await page.capturedEvents() expect(new Set(capturedEvents.map((c) => c['properties']['$session_id']))).toEqual(new Set([firstSessionId])) - const waitForRecorder = page.waitForResponse('**/recorder.js*') - await start( - { - ...startOptions, - type: 'reload', - }, - page, - page.context() - ) - - await page.resetCapturedEvents() - await waitForRecorder + await page.waitingForNetworkCausedBy(['**/recorder.js*'], async () => { + await start( + { + ...startOptions, + type: 'reload', + }, + page, + page.context() + ) + + await page.resetCapturedEvents() + }) - const moreResponsePromise = page.waitForResponse('**/ses/*') - await page.locator('[data-cy-input]').type('hello posthog!') - await moreResponsePromise + await page.waitingForNetworkCausedBy(['**/ses/*'], async () => { + await page.locator('[data-cy-input]').type('hello posthog!') + }) const capturedAfterActivity = await page.capturedEvents() expect(capturedAfterActivity.map((x) => x.event)).toEqual(['$snapshot']) @@ -194,9 +194,9 @@ test.describe('Session recording - array.js', () => { ph?.reset() }) - const responsePromise = page.waitForResponse('**/ses/*') - await page.locator('[data-cy-input]').fill('hello posthog!') - await responsePromise + await page.waitingForNetworkCausedBy(['**/ses/*'], async () => { + await page.locator('[data-cy-input]').fill('hello posthog!') + }) const capturedEvents = await page.capturedEvents() const postResetSessionIds = new Set(capturedEvents.map((c) => c['properties']['$session_id'])) @@ -207,9 +207,9 @@ test.describe('Session recording - array.js', () => { }) test('rotates sessions after 24 hours', async ({ page }) => { - const responsePromise = page.waitForResponse('**/ses/*') - await page.locator('[data-cy-input]').fill('hello posthog!') - await responsePromise + await page.waitingForNetworkCausedBy(['**/ses/*'], async () => { + await page.locator('[data-cy-input]').fill('hello posthog!') + }) await page.evaluate(() => { const ph = (window as WindowWithPostHog).posthog @@ -239,10 +239,10 @@ test.describe('Session recording - array.js', () => { ph.sessionManager['_sessionStartTimestamp'] = startTs - timeout - 1000 }) - const anotherResponsePromise = page.waitForResponse('**/ses/*') - // using fill here means the session id doesn't rotate, must need some kind of user interaction - await page.locator('[data-cy-input]').type('hello posthog!') - await anotherResponsePromise + await page.waitingForNetworkCausedBy(['**/ses/*'], async () => { + // using fill here means the session id doesn't rotate, must need some kind of user interaction + await page.locator('[data-cy-input]').type('hello posthog!') + }) await page.evaluate(() => { const ph = (window as WindowWithPostHog).posthog diff --git a/playwright/utils/posthog-playwright-test-base.ts b/playwright/utils/posthog-playwright-test-base.ts index ea6828d6e..ebc24de33 100644 --- a/playwright/utils/posthog-playwright-test-base.ts +++ b/playwright/utils/posthog-playwright-test-base.ts @@ -27,7 +27,10 @@ declare module '@playwright/test' { */ interface Page { resetCapturedEvents(): Promise + capturedEvents(): Promise + + waitingForNetworkCausedBy: (urlPatterns: (string | RegExp)[], action: () => Promise) => Promise } } @@ -44,6 +47,19 @@ export const test = base.extend<{ mockStaticAssets: void; page: Page }>({ return (window as WindowWithPostHog).capturedEvents || [] }) } + page.waitingForNetworkCausedBy = async function ( + urlPatterns: (string | RegExp)[], + action: () => Promise + ) { + const responsePromises = urlPatterns.map((urlPattern) => { + return this.waitForResponse(urlPattern) + }) + + await action() + + // eslint-disable-next-line compat/compat + await Promise.allSettled(responsePromises) + } // Pass the extended page to the test await use(page)