@@ -6,12 +6,14 @@ import {
66 e ,
77 expectPlausibleInAction ,
88 hideAndShowCurrentTab ,
9+ isPageviewEvent ,
910 isEngagementEvent ,
1011 switchByMode
1112} from './support/test-utils'
12- import { test } from '@playwright/test'
13+ import { test , expect } from '@playwright/test'
1314import { ScriptConfig } from './support/types'
1415import { LOCAL_SERVER_ADDR } from './support/server'
16+
1517const DEFAULT_CONFIG : ScriptConfig = {
1618 domain : 'example.com' ,
1719 endpoint : `${ LOCAL_SERVER_ADDR } /api/event` ,
@@ -263,3 +265,178 @@ for (const mode of ['web', 'esm']) {
263265 } )
264266 } )
265267}
268+
269+ test . describe ( `transformRequest examples from /docs work` , ( ) => {
270+ test . beforeEach ( async ( { page } ) => {
271+ await page
272+ . context ( )
273+ . route ( new RegExp ( '(http|https)://example\\.com.*' ) , async ( route ) => {
274+ await route . fulfill ( {
275+ status : 200 ,
276+ contentType : 'text/html' ,
277+ body : /* HTML */ `<!DOCTYPE html>
278+ <html>
279+ <head>
280+ <title>mocked page</title>
281+ </head>
282+ <body>
283+ mocked page
284+ </body>
285+ </html>`
286+ } )
287+ } )
288+ } )
289+
290+ test ( 'you can omit automatically tracked url property from tagged link clicks' , async ( {
291+ page
292+ } , { testId } ) => {
293+ function omitAutomaticUrlProperty ( payload ) {
294+ if ( payload . p && payload . p . url ) {
295+ delete payload . p . url
296+ }
297+ return payload
298+ }
299+ const config = {
300+ ...DEFAULT_CONFIG ,
301+ transformRequest : omitAutomaticUrlProperty
302+ }
303+ const { url } = await initializePageDynamically ( page , {
304+ testId,
305+ scriptConfig : config ,
306+ bodyContent : /* HTML */ `<a
307+ class="plausible-event-name=Purchase plausible-event-discounted=true"
308+ href="https://example.com/target?user=sensitive"
309+ >Purchase</a
310+ >`
311+ } )
312+
313+ await expectPlausibleInAction ( page , {
314+ action : async ( ) => {
315+ await page . goto ( url )
316+ await page . click ( 'a' )
317+ } ,
318+ expectedRequests : [
319+ {
320+ n : 'Purchase' ,
321+ p : { discounted : 'true' } // <-- no url property
322+ }
323+ ] ,
324+ shouldIgnoreRequest : [ isPageviewEvent , isEngagementEvent ]
325+ } )
326+ await expect ( page . getByText ( 'mocked page' ) ) . toBeVisible ( )
327+ } )
328+
329+ for ( const { hashBasedRouting, urlSuffix, expectedUrlSuffix } of [
330+ {
331+ hashBasedRouting : true ,
332+ urlSuffix :
333+ '?utm_source=example&utm_medium=referral&utm_campaign=test#fragment' ,
334+ expectedUrlSuffix : '#fragment'
335+ } ,
336+ {
337+ hashBasedRouting : false ,
338+ urlSuffix : '?utm_source=example&utm_medium=referral&utm_campaign=test' ,
339+ expectedUrlSuffix : ''
340+ }
341+ ] ) {
342+ test ( `you can omit UTM properties from pageview urls (hashBasedRouting: ${ hashBasedRouting } )` , async ( {
343+ page
344+ } , { testId } ) => {
345+ function omitUTMProperties ( payload ) {
346+ const parts = payload . u . split ( '?' )
347+ let urlWithoutQuery = parts . shift ( )
348+
349+ if ( payload . h ) {
350+ const fragment = parts . join ( '?' ) . split ( '#' ) [ 1 ]
351+ urlWithoutQuery =
352+ typeof fragment === 'string'
353+ ? urlWithoutQuery + '#' + fragment
354+ : urlWithoutQuery
355+ }
356+
357+ payload . u = urlWithoutQuery
358+ return payload
359+ }
360+
361+ const config = {
362+ ...DEFAULT_CONFIG ,
363+ hashBasedRouting,
364+ transformRequest : omitUTMProperties
365+ }
366+
367+ // the star path is needed for the dynamic page to load when accessing it with query params
368+ const path = '*'
369+ const { url } = await initializePageDynamically ( page , {
370+ testId,
371+ path,
372+ scriptConfig : config ,
373+ bodyContent : ''
374+ } )
375+
376+ const [ actualUrl ] = url . split ( '*' )
377+
378+ await expectPlausibleInAction ( page , {
379+ action : async ( ) => {
380+ await page . goto ( `${ actualUrl } ${ urlSuffix } ` )
381+ // await page.click('a')
382+ } ,
383+ expectedRequests : [
384+ {
385+ n : 'pageview' ,
386+ u : `${ LOCAL_SERVER_ADDR } ${ actualUrl } ${ expectedUrlSuffix } `
387+ }
388+ ] ,
389+ shouldIgnoreRequest : [ isEngagementEvent ]
390+ } )
391+ } )
392+ }
393+
394+ test ( 'you can track pages using their canonical url' , async ( { page } , {
395+ testId
396+ } ) => {
397+ function rewriteUrlToCanonicalUrl ( payload ) {
398+ // Get the canonical URL element
399+ const canonicalMeta = document . querySelector ( 'link[rel="canonical"]' )
400+ // Use the canonical URL if it exists, falling back on the regular URL when it doesn't.
401+ if ( canonicalMeta ) {
402+ // @ts -expect-error - canonicalMeta definitely has the href attribute
403+ payload . u = canonicalMeta . href + window . location . search
404+ }
405+ return payload
406+ }
407+
408+ // the star path is needed for the dynamic page to load when accessing it with query params
409+ const nonCanonicalPath = '/products/clothes/shoes/banana-leather-shoe*'
410+ const { url } = await initializePageDynamically ( page , {
411+ testId,
412+ path : nonCanonicalPath ,
413+ scriptConfig : /* HTML */ `
414+ <link rel="canonical" href="/products/banana-leather-shoe" />
415+ <script type="module">
416+ import { init, track } from '/tracker/js/npm_package/plausible.js'
417+ init(
418+ ${ serializeWithFunctions ( {
419+ ...DEFAULT_CONFIG ,
420+ transformRequest : rewriteUrlToCanonicalUrl
421+ } ) }
422+ )
423+ </script>
424+ ` ,
425+ bodyContent : ''
426+ } )
427+ const [ actualUrl ] = url . split ( '*' )
428+
429+ await expectPlausibleInAction ( page , {
430+ action : async ( ) => {
431+ await page . goto ( `${ actualUrl } ?utm_source=example` )
432+ } ,
433+ expectedRequests : [
434+ {
435+ n : 'pageview' ,
436+ u : `${ LOCAL_SERVER_ADDR } /products/banana-leather-shoe?utm_source=example`
437+ }
438+ ] ,
439+ shouldIgnoreRequest : [ isEngagementEvent ]
440+ } )
441+ } )
442+ } )
0 commit comments