| import { expect } from '@playwright/test' |
| import packageJson from '../../package.json' with { type: 'json' } |
| import { mockManyRequests } from './mock-many-requests' |
|
|
| export const tracker_script_version = packageJson.tracker_script_version |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| export const expectPlausibleInAction = async function ( |
| page, |
| { |
| action, |
| expectedRequests = [], |
| refutedRequests = [], |
| pathToMock = '/api/event', |
| awaitedRequestCount, |
| expectedRequestCount, |
| responseDelay, |
| shouldIgnoreRequest, |
| mockRequestTimeout = 3000 |
| } |
| ) { |
| const requestsToExpect = expectedRequestCount |
| ? expectedRequestCount |
| : expectedRequests.length |
| const requestsToAwait = awaitedRequestCount |
| ? awaitedRequestCount |
| : requestsToExpect + refutedRequests.length |
|
|
| const { getRequestList } = await mockManyRequests({ |
| page, |
| path: pathToMock, |
| fulfill: { status: 202, contentType: 'text/plain', body: 'ok' }, |
| responseDelay, |
| shouldIgnoreRequest, |
| awaitedRequestCount: requestsToAwait, |
| mockRequestTimeout |
| }) |
| await action() |
| const requestBodies = await getRequestList() |
|
|
| const expectedButNotFoundBodySubsets = [] |
|
|
| expectedRequests.forEach((bodySubset) => { |
| const wasFound = requestBodies.some((requestBody) => { |
| return includesSubset(requestBody, bodySubset) |
| }) |
|
|
| if (!wasFound) { |
| expectedButNotFoundBodySubsets.push(bodySubset) |
| } |
| }) |
|
|
| const refutedButFoundRequestBodies = [] |
|
|
| refutedRequests.forEach((bodySubset) => { |
| const found = requestBodies.find((requestBody) => { |
| return includesSubset(requestBody, bodySubset) |
| }) |
|
|
| if (found) { |
| refutedButFoundRequestBodies.push(found) |
| } |
| }) |
|
|
| const expectedBodySubsetsErrorMessage = `The following body subsets were not found from the requests that were made:\n\n${JSON.stringify(expectedButNotFoundBodySubsets, null, 4)}\n\nReceived requests with the following bodies:\n\n${JSON.stringify(requestBodies, null, 4)}` |
| expect( |
| expectedButNotFoundBodySubsets, |
| expectedBodySubsetsErrorMessage |
| ).toHaveLength(0) |
|
|
| const refutedBodySubsetsErrorMessage = `The following requests were made, but were not expected:\n\n${JSON.stringify(refutedButFoundRequestBodies, null, 4)}` |
| expect( |
| refutedButFoundRequestBodies, |
| refutedBodySubsetsErrorMessage |
| ).toHaveLength(0) |
|
|
| const unexpectedRequestBodiesErrorMessage = `Expected ${requestsToExpect} requests, but received ${requestBodies.length}:\n\n${JSON.stringify(requestBodies, null, 4)}` |
| expect(requestBodies.length, unexpectedRequestBodiesErrorMessage).toBe( |
| requestsToExpect |
| ) |
|
|
| return requestBodies |
| } |
|
|
| export const isPageviewEvent = function (requestPostData) { |
| return requestPostData.n === 'pageview' |
| } |
|
|
| export const isEngagementEvent = function (requestPostData) { |
| return requestPostData.n === 'engagement' |
| } |
|
|
| async function toggleTabVisibility(page, hide) { |
| await page.evaluate((hide) => { |
| Object.defineProperty(document, 'visibilityState', { |
| value: hide ? 'hidden' : 'visible', |
| writable: true |
| }) |
| Object.defineProperty(document, 'hidden', { value: hide, writable: true }) |
| document.dispatchEvent(new Event('visibilitychange')) |
| }, hide) |
| } |
|
|
| export const hideCurrentTab = async function (page) { |
| return toggleTabVisibility(page, true) |
| } |
|
|
| export const showCurrentTab = async function (page) { |
| return toggleTabVisibility(page, false) |
| } |
|
|
| async function setFocus(page, focus) { |
| await page.evaluate((focus) => { |
| Object.defineProperty(document, 'hasFocus', { |
| value: () => focus, |
| writable: true |
| }) |
|
|
| const eventName = focus ? 'focus' : 'blur' |
| window.dispatchEvent(new Event(eventName)) |
| }, focus) |
| } |
|
|
| export const focus = async function (page) { |
| return setFocus(page, true) |
| } |
|
|
| export const blur = async function (page) { |
| return setFocus(page, false) |
| } |
|
|
| export const hideAndShowCurrentTab = async function (page, options = {}) { |
| await hideCurrentTab(page) |
| if (options.delay > 0) { |
| await delay(options.delay) |
| } |
| await showCurrentTab(page) |
| } |
|
|
| export const blurAndFocusPage = async function (page, options = {}) { |
| await blur(page) |
| if (options.delay > 0) { |
| await delay(options.delay) |
| } |
| await focus(page) |
| } |
|
|
| |
| export const e = { |
| stringContaining: (value) => ({ |
| expected: value, |
| __expectation__: (actual) => actual.includes(value) |
| }), |
| toBeUndefined: () => ({ |
| expected: undefined, |
| __expectation__: (actual) => actual === undefined |
| }) |
| } |
|
|
| function includesSubset(body, subset) { |
| return Object.keys(subset).every((key) => { |
| if (typeof subset[key] === 'object' && !subset[key].__expectation__) { |
| return ( |
| typeof body[key] === 'object' && |
| areFlatObjectsEqual(body[key], subset[key]) |
| ) |
| } else { |
| return checkEqual(body[key], subset[key]) |
| } |
| }) |
| } |
|
|
| |
| |
| function areFlatObjectsEqual(obj1, obj2) { |
| const keys1 = Object.keys(obj1) |
| const keys2 = Object.keys(obj2) |
|
|
| if (keys1.length !== keys2.length) return false |
|
|
| return keys1.every((key) => checkEqual(obj2[key], obj1[key])) |
| } |
|
|
| function checkEqual(a, b) { |
| if (typeof b === 'object' && b.__expectation__) { |
| return b.__expectation__(a) |
| } |
| return a === b |
| } |
|
|
| export function delay(ms) { |
| return new Promise((resolve) => setTimeout(resolve, ms)) |
| } |
|
|
| export function switchByMode(cases, mode) { |
| switch (mode) { |
| case 'web': |
| return cases.web |
| case 'esm': |
| return cases.esm |
| case 'legacy': |
| return cases.legacy |
| default: |
| throw new Error(`Unimplemented mode: ${mode}`) |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| export function ensurePlausibleInitialized(page) { |
| return page.waitForFunction(() => window.plausible?.l === true) |
| } |
|
|