import { afterEach, vi } from "vitest"; import { Manager } from "../../src/Manager"; import Remote from "../../src/Remote"; import IOptions from "../../src/intefaces/IOption"; import IEventPayload from "../../src/intefaces/IEventPayload"; export const EVENTS_URL = "https://api.test/v0/client-events"; // Every page listens on window; stop the previous one so a test only sees // the events of its own page. const listening: Manager[] = []; afterEach(() => { listening.splice(0).forEach(manager => manager.stop()); }); /** * A landing page as the library sees it: a campaign token in the address, * the given markup in the body, the network and the clipboard replaced by * recorders. The redirect is recorded instead of navigating away. */ export const landingPage = (html: string, options: Partial = {}) => { const token = "e30." + btoa(JSON.stringify({ payload: { ats_company_id: "company", ats_instance_id: "instance" }, })).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "") + ".sig"; window.history.replaceState(null, "", `/verify?tk=${token}`); document.body.innerHTML = html; const sent: IEventPayload[] = []; vi.stubGlobal("fetch", vi.fn(async (url: string, init?: RequestInit) => { if (url === EVENTS_URL) { sent.push(JSON.parse(String(init?.body))); return new Response("{}", { status: 200, headers: { "content-type": "application/json" } }); } return new Response("203.0.113.7", { status: 200 }); })); const copied: string[] = []; Object.defineProperty(navigator, "clipboard", { configurable: true, value: { writeText: vi.fn(async (text: string) => { copied.push(text); }) }, }); const redirect = vi.spyOn(Manager.prototype as unknown as { performRedirect: () => void }, "performRedirect") .mockImplementation(() => undefined); const manager = new Manager(new Remote("key", EVENTS_URL, false), { apiKey: "key", url: EVENTS_URL, source: "LP", redirectUrl: "https://oops.test/", shouldRedirect: true, extraPayload: {}, ...options, }); manager.listen(); listening.push(manager); return { manager, sent, copied, redirect, events: () => sent.map(payload => payload.event), click: (selector: string): boolean => { const element = document.querySelector(selector); if (!element) { throw new Error(`No element matches ${selector}`); } return element.dispatchEvent(new MouseEvent("click", { bubbles: true, cancelable: true })); }, }; }; /** * Lets every pending send settle, so a test can assert that something was * NOT sent as well as that something was. */ export const settle = async (): Promise => { for (let i = 0; i < 20; i++) { await new Promise(resolve => setTimeout(resolve, 0)); } };