analytics/tracker/test/support/mock-many-requests.ts

111 lines
3.2 KiB
TypeScript

import { Page } from '@playwright/test'
import { delay } from './test-utils'
type RequestData = Record<string, unknown>
type ShouldIgnoreRequest = (requestData?: RequestData) => boolean
const DEFAULT_RESPONSE = { status: 200, contentType: 'text/plain', body: 'ok' }
interface MockManyRequestsOptions {
/**
* Set `scopeMockToPage` to `true` if there is a need to verify that the mock request
* happens on the passed `page`.
* Default value is `false`, which means it records requests that open in new tabs as well.
* Note: You can create two mocks for the same path, one on the page and other on the browser if needed.
*/
scopeMockToPage?: boolean
page: Page
path: string
/**
* Response to fulfill the request with.
* Defaults to DEFAULT_RESPONSE. Allows overriding properties from the default one by one.
* @see DEFAULT_RESPONSE
*/
fulfill?: {
status?: number
contentType?: string
body?: string
}
/**
* When there's at least `awaitedRequestCount` unignored requests on this route,
* getRequestList resolves without waiting for `mockRequestTimeout`.
* If there's less than `awaitedRequestCount` unignored requests on this route, it
* takes `mockRequestTimeout` to resolve `getRequestList`.
* This is so as not miss unexpected requests that are sent slowly.
*/
awaitedRequestCount: number
responseDelay?: number
shouldIgnoreRequest?: ShouldIgnoreRequest | ShouldIgnoreRequest[]
mockRequestTimeout?: number
}
export async function mockManyRequests({
scopeMockToPage,
page,
path,
fulfill,
awaitedRequestCount,
responseDelay,
shouldIgnoreRequest,
mockRequestTimeout = 1000
}: MockManyRequestsOptions) {
const requestList: unknown[] = []
const scope = scopeMockToPage ? page : page.context()
await scope.route(path, async (route, request) => {
if (responseDelay) {
await delay(responseDelay)
}
const postData = request.postDataJSON()
if (shouldAllow(postData, shouldIgnoreRequest)) {
requestList.push(postData)
}
await route.fulfill({
...DEFAULT_RESPONSE,
...fulfill
})
})
const getRequestList = (): Promise<unknown[]> =>
new Promise((resolve) => {
let i = 0
const POLL_INTERVAL_MS = 10
const interval = setInterval(() => {
if (i > mockRequestTimeout / POLL_INTERVAL_MS) {
clearInterval(interval)
resolve(requestList)
} else if (requestList.length === awaitedRequestCount) {
clearInterval(interval)
resolve(requestList)
} else {
i++
}
}, POLL_INTERVAL_MS)
})
return { getRequestList }
}
function shouldAllow(
requestData: RequestData,
ignores: ShouldIgnoreRequest | ShouldIgnoreRequest[] | undefined
) {
if (Array.isArray(ignores)) {
return !ignores.some((shouldIgnore) => shouldIgnore(requestData))
} else if (ignores) {
return !ignores(requestData)
} else {
return true
}
}
export function resolveWithTimestamps(
promises: Array<Promise<unknown>>
): Promise<Array<[unknown, number]>> {
return Promise.all(
promises.map(async (mock) => {
const result = await mock
return [result, Date.now()]
})
)
}