From b03569d7626f767e82b247444b4ad593bb361253 Mon Sep 17 00:00:00 2001 From: Artur Pata Date: Tue, 16 Sep 2025 08:39:17 +0300 Subject: [PATCH] Update tests transformRequest tests (#5687) --- tracker/.prettierignore | 1 + tracker/test/outbound-links.spec.ts | 28 ++- tracker/test/tagged-events.spec.ts | 30 ++- ...uest.spec.ts => transform-request.spec.ts} | 179 +++++++++++++++++- 4 files changed, 223 insertions(+), 15 deletions(-) rename tracker/test/{transform_request.spec.ts => transform-request.spec.ts} (59%) diff --git a/tracker/.prettierignore b/tracker/.prettierignore index 39b9daca06..7a3a848c23 100644 --- a/tracker/.prettierignore +++ b/tracker/.prettierignore @@ -1,3 +1,4 @@ src/p.js src/plausible.js +npm_package/plausible.js node_modules/ diff --git a/tracker/test/outbound-links.spec.ts b/tracker/test/outbound-links.spec.ts index 632bad9142..cc733db173 100644 --- a/tracker/test/outbound-links.spec.ts +++ b/tracker/test/outbound-links.spec.ts @@ -31,7 +31,7 @@ for (const mode of ['web', 'esm']) { fulfill: { status: 200, contentType: 'text/html', - body: 'other pageother page' + body: OTHER_PAGE_BODY }, awaitedRequestCount: 1 }) @@ -78,7 +78,7 @@ for (const mode of ['web', 'esm']) { fulfill: { status: 200, contentType: 'text/html', - body: 'other pageother page' + body: OTHER_PAGE_BODY }, awaitedRequestCount: 1 }) @@ -151,7 +151,7 @@ for (const mode of ['legacy', 'web']) fulfill: { status: 200, contentType: 'text/html', - body: 'other pageother page' + body: OTHER_PAGE_BODY }, awaitedRequestCount: 1 }) @@ -201,7 +201,7 @@ for (const mode of ['legacy', 'web']) fulfill: { status: 200, contentType: 'text/html', - body: 'other pageother page' + body: OTHER_PAGE_BODY }, awaitedRequestCount: 1 }) @@ -253,7 +253,7 @@ for (const mode of ['legacy', 'web']) fulfill: { status: 200, contentType: 'text/html', - body: 'other pageother page' + body: OTHER_PAGE_BODY }, awaitedRequestCount: 1 }) @@ -340,7 +340,7 @@ test.describe('outbound links feature when using legacy .compat extension', () = fulfill: { status: 200, contentType: 'text/html', - body: 'other pageother page' + body: OTHER_PAGE_BODY }, awaitedRequestCount: 2, mockRequestTimeout: 2000 @@ -396,7 +396,7 @@ test.describe('outbound links feature when using legacy .compat extension', () = fulfill: { status: 200, contentType: 'text/html', - body: 'other pageother page' + body: OTHER_PAGE_BODY }, awaitedRequestCount: 1 }) @@ -448,7 +448,7 @@ test.describe('outbound links feature when using legacy .compat extension', () = fulfill: { status: 200, contentType: 'text/html', - body: 'other pageother page' + body: OTHER_PAGE_BODY }, awaitedRequestCount: 1 }) @@ -478,7 +478,7 @@ test.describe('outbound links feature when using legacy .compat extension', () = fulfill: { status: 200, contentType: 'text/html', - body: 'other pageother page' + body: OTHER_PAGE_BODY }, awaitedRequestCount: 2, mockRequestTimeout: 2000 @@ -522,3 +522,13 @@ test.describe('outbound links feature when using legacy .compat extension', () = }) }) }) + +const OTHER_PAGE_BODY = /* HTML */ ` + + + other page + + + other page + + ` diff --git a/tracker/test/tagged-events.spec.ts b/tracker/test/tagged-events.spec.ts index ae60605568..c1e2744f65 100644 --- a/tracker/test/tagged-events.spec.ts +++ b/tracker/test/tagged-events.spec.ts @@ -26,7 +26,15 @@ test.beforeEach(async ({ page }) => { await route.fulfill({ status: 200, contentType: 'text/html', - body: 'mocked pagemocked page' + body: /* HTML */ ` + + + mocked page + + + mocked page + + ` }) }) }) @@ -40,7 +48,7 @@ for (const mode of ['web', 'esm']) { testId, scriptConfig: switchByMode( { - web: { ...DEFAULT_CONFIG }, + web: config, esm: `` @@ -457,8 +465,12 @@ for (const mode of ['legacy', 'web']) { action: () => page.click('circle'), expectedRequests: [ { - n: 'link click' - // bug with p.url, can't assert + n: 'link click', + p: { + expected: { url: {} }, + __expectation__: (actual) => + actual && JSON.stringify(actual) === '{"url":{}}' + } } ], shouldIgnoreRequest: [isPageviewEvent, isEngagementEvent] @@ -568,7 +580,15 @@ test.describe('tagged events feature when using legacy .compat extension', () => fulfill: { status: 200, contentType: 'text/html', - body: 'other pageother page' + body: /* HTML */ ` + + + other page + + + other page + + ` }, awaitedRequestCount: 2, mockRequestTimeout: 2000 diff --git a/tracker/test/transform_request.spec.ts b/tracker/test/transform-request.spec.ts similarity index 59% rename from tracker/test/transform_request.spec.ts rename to tracker/test/transform-request.spec.ts index 7823142e8c..8e02b997db 100644 --- a/tracker/test/transform_request.spec.ts +++ b/tracker/test/transform-request.spec.ts @@ -6,12 +6,14 @@ import { e, expectPlausibleInAction, hideAndShowCurrentTab, + isPageviewEvent, isEngagementEvent, switchByMode } from './support/test-utils' -import { test } from '@playwright/test' +import { test, expect } from '@playwright/test' import { ScriptConfig } from './support/types' import { LOCAL_SERVER_ADDR } from './support/server' + const DEFAULT_CONFIG: ScriptConfig = { domain: 'example.com', endpoint: `${LOCAL_SERVER_ADDR}/api/event`, @@ -263,3 +265,178 @@ for (const mode of ['web', 'esm']) { }) }) } + +test.describe(`transformRequest examples from /docs work`, () => { + test.beforeEach(async ({ page }) => { + await page + .context() + .route(new RegExp('(http|https)://example\\.com.*'), async (route) => { + await route.fulfill({ + status: 200, + contentType: 'text/html', + body: /* HTML */ ` + + + mocked page + + + mocked page + + ` + }) + }) + }) + + test('you can omit automatically tracked url property from tagged link clicks', async ({ + page + }, { testId }) => { + function omitAutomaticUrlProperty(payload) { + if (payload.p && payload.p.url) { + delete payload.p.url + } + return payload + } + const config = { + ...DEFAULT_CONFIG, + transformRequest: omitAutomaticUrlProperty + } + const { url } = await initializePageDynamically(page, { + testId, + scriptConfig: config, + bodyContent: /* HTML */ `Purchase` + }) + + await expectPlausibleInAction(page, { + action: async () => { + await page.goto(url) + await page.click('a') + }, + expectedRequests: [ + { + n: 'Purchase', + p: { discounted: 'true' } // <-- no url property + } + ], + shouldIgnoreRequest: [isPageviewEvent, isEngagementEvent] + }) + await expect(page.getByText('mocked page')).toBeVisible() + }) + + for (const { hashBasedRouting, urlSuffix, expectedUrlSuffix } of [ + { + hashBasedRouting: true, + urlSuffix: + '?utm_source=example&utm_medium=referral&utm_campaign=test#fragment', + expectedUrlSuffix: '#fragment' + }, + { + hashBasedRouting: false, + urlSuffix: '?utm_source=example&utm_medium=referral&utm_campaign=test', + expectedUrlSuffix: '' + } + ]) { + test(`you can omit UTM properties from pageview urls (hashBasedRouting: ${hashBasedRouting})`, async ({ + page + }, { testId }) => { + function omitUTMProperties(payload) { + const parts = payload.u.split('?') + let urlWithoutQuery = parts.shift() + + if (payload.h) { + const fragment = parts.join('?').split('#')[1] + urlWithoutQuery = + typeof fragment === 'string' + ? urlWithoutQuery + '#' + fragment + : urlWithoutQuery + } + + payload.u = urlWithoutQuery + return payload + } + + const config = { + ...DEFAULT_CONFIG, + hashBasedRouting, + transformRequest: omitUTMProperties + } + + // the star path is needed for the dynamic page to load when accessing it with query params + const path = '*' + const { url } = await initializePageDynamically(page, { + testId, + path, + scriptConfig: config, + bodyContent: '' + }) + + const [actualUrl] = url.split('*') + + await expectPlausibleInAction(page, { + action: async () => { + await page.goto(`${actualUrl}${urlSuffix}`) + // await page.click('a') + }, + expectedRequests: [ + { + n: 'pageview', + u: `${LOCAL_SERVER_ADDR}${actualUrl}${expectedUrlSuffix}` + } + ], + shouldIgnoreRequest: [isEngagementEvent] + }) + }) + } + + test('you can track pages using their canonical url', async ({ page }, { + testId + }) => { + function rewriteUrlToCanonicalUrl(payload) { + // Get the canonical URL element + const canonicalMeta = document.querySelector('link[rel="canonical"]') + // Use the canonical URL if it exists, falling back on the regular URL when it doesn't. + if (canonicalMeta) { + // @ts-expect-error - canonicalMeta definitely has the href attribute + payload.u = canonicalMeta.href + window.location.search + } + return payload + } + + // the star path is needed for the dynamic page to load when accessing it with query params + const nonCanonicalPath = '/products/clothes/shoes/banana-leather-shoe*' + const { url } = await initializePageDynamically(page, { + testId, + path: nonCanonicalPath, + scriptConfig: /* HTML */ ` + + + `, + bodyContent: '' + }) + const [actualUrl] = url.split('*') + + await expectPlausibleInAction(page, { + action: async () => { + await page.goto(`${actualUrl}?utm_source=example`) + }, + expectedRequests: [ + { + n: 'pageview', + u: `${LOCAL_SERVER_ADDR}/products/banana-leather-shoe?utm_source=example` + } + ], + shouldIgnoreRequest: [isEngagementEvent] + }) + }) +})