import { expect, test } from "@playwright/test"; import type { Route } from "@playwright/test"; import { join } from "path"; import type { Config } from "../types/core"; import type { Event as ClarityEvent, Payload } from "../types/data"; const snapshotEvent: ClarityEvent.Snapshot = 43; const discoverEvent: ClarityEvent.Discover = 5; const clickEvent: ClarityEvent.Click = 9; const origin = "https://clarity.test/"; const collect = `${origin}collect`; const html = ""; const builds = [ { name: "normal", file: "clarity.min.js", snapshot: false, recording: true, clicks: true, conversions: false }, { name: "extended", file: "clarity.extended.js", snapshot: false, recording: true, clicks: true, conversions: false }, { name: "performance", file: "clarity.performance.js", snapshot: false, recording: false, clicks: false, conversions: false }, { name: "Insights", file: "clarity.insight.js", snapshot: true, recording: false, clicks: true, conversions: false }, { name: "Insights conversions", file: "clarity.insight.js", snapshot: true, recording: false, clicks: true, conversions: true } ]; test.describe("build-specific snapshot responses", () => { for (const build of builds) { test(`${build.name} preserves capture after a SNAPSHOT response`, async ({ page }) => { const payloads: Payload[] = []; const errors: string[] = []; const unexpectedRequests: string[] = []; page.on("pageerror", (error: Error) => errors.push(error.message)); await page.route("**/*", async (route: Route): Promise => { const request = route.request(); if (request.url() === origin) { await route.fulfill({ status: 200, contentType: "text/html", body: html }); } else if (request.url() === collect) { payloads.push(JSON.parse(request.postData())); await route.fulfill({ status: payloads.length === 1 ? 200 : 204, contentType: "text/plain", body: payloads.length === 1 ? "SNAPSHOT\nACTION snapshot-complete" : "" }); } else { unexpectedRequests.push(request.url()); await route.abort(); } }); await page.goto(origin); await page.evaluate(() => { // Keep fixture uploads readable instead of gzip-compressed. Object.defineProperty(window, "CompressionStream", { value: undefined, configurable: true }); }); await page.addScriptTag({ path: join(__dirname, "../build", build.file) }); await page.evaluate(({ upload, conversions }: { upload: string; conversions: boolean }) => { const clarity: unknown = Reflect.get(window, "clarity"); if (typeof clarity !== "function") { throw new Error("Clarity browser API was not loaded."); } const config: Config = { projectId: "snapshot-test", track: false, lean: true, delay: 100, conversions, upload, action: (value: string): void => document.documentElement.setAttribute("data-snapshot-response", value) }; clarity("start", config); }, { upload: collect, conversions: build.conversions }); await expect(page.locator("html")).toHaveAttribute("data-snapshot-response", "snapshot-complete"); expect(payloads[0].p || []).toHaveLength(0); if (build.clicks) { await page.click("#snapshot-button"); await expect.poll(() => payloads.some(payload => payload.a.some(event => event[1] === clickEvent))).toBe(true); } await page.evaluate(() => { const clarity: unknown = Reflect.get(window, "clarity"); if (typeof clarity !== "function") { throw new Error("Clarity browser API was not loaded."); } clarity("stop"); }); await expect.poll(() => payloads.some(payload => payload.e[9] === 1)).toBe(true); const events = payloads.reduce((all, payload) => all.concat(payload.a || [], payload.p || []), []); const snapshots = events.filter(event => event[1] === snapshotEvent); expect(snapshots.length).toBe(build.snapshot ? 1 : 0); if (build.snapshot) { expect(JSON.stringify(snapshots[0])).toContain("id=snapshot-button"); } expect(events.some(event => event[1] === discoverEvent)).toBe(build.recording); expect(events.some(event => event[1] === clickEvent)).toBe(build.clicks); expect(errors).toEqual([]); expect(unexpectedRequests).toEqual([]); }); } });