import { describe, it, expect, vi, beforeEach } from "vitest"; import { render, screen } from "@testing-library/react"; import { PostHogWrapper } from "#/components/providers/posthog-wrapper"; import OptionService from "#/api/option-service/option-service.api"; // Mock PostHogProvider to capture the options passed to it const mockPostHogProvider = vi.fn(); vi.mock("posthog-js/react", () => ({ PostHogProvider: (props: Record) => { mockPostHogProvider(props); return props.children; }, })); describe("PostHogWrapper", () => { beforeEach(() => { vi.clearAllMocks(); // Reset URL hash window.location.hash = ""; // Clear sessionStorage sessionStorage.clear(); // Mock the config fetch // @ts-expect-error - partial mock vi.spyOn(OptionService, "getConfig").mockResolvedValue({ posthog_client_key: "test-posthog-key", }); }); it("should initialize PostHog with bootstrap IDs from URL hash (without ph_ prefix)", async () => { // Webflow sends distinct_id and session_id without the ph_ prefix window.location.hash = "distinct_id=user-123&session_id=session-456"; render(
, ); await screen.findByTestId("child"); expect(mockPostHogProvider).toHaveBeenCalledWith( expect.objectContaining({ options: expect.objectContaining({ bootstrap: { distinctID: "user-123", sessionID: "session-456", }, }), }), ); }); it("should clean up URL hash after extracting bootstrap IDs", async () => { window.location.hash = "distinct_id=user-123&session_id=session-456"; render(
, ); await screen.findByTestId("child"); expect(window.location.hash).toBe(""); }); it("should persist bootstrap IDs to sessionStorage for OAuth survival", async () => { window.location.hash = "distinct_id=user-123&session_id=session-456"; render(
, ); await screen.findByTestId("child"); // After extracting from hash, IDs should NOT remain in sessionStorage // because they were already consumed during this page load. // But if a full-page redirect happened before PostHog init, // sessionStorage would still have them for the next load. // We verify the write happened by checking the provider received the IDs. expect(mockPostHogProvider).toHaveBeenCalledWith( expect.objectContaining({ options: expect.objectContaining({ bootstrap: { distinctID: "user-123", sessionID: "session-456", }, }), }), ); }); it("should read bootstrap IDs from sessionStorage when hash is absent (post-OAuth)", async () => { // Simulate returning from OAuth: no hash, but sessionStorage has the IDs sessionStorage.setItem( "posthog_bootstrap", JSON.stringify({ distinctID: "user-123", sessionID: "session-456" }), ); render(
, ); await screen.findByTestId("child"); expect(mockPostHogProvider).toHaveBeenCalledWith( expect.objectContaining({ options: expect.objectContaining({ bootstrap: { distinctID: "user-123", sessionID: "session-456", }, }), }), ); }); it("should clean up sessionStorage after consuming bootstrap IDs", async () => { sessionStorage.setItem( "posthog_bootstrap", JSON.stringify({ distinctID: "user-123", sessionID: "session-456" }), ); render(
, ); await screen.findByTestId("child"); expect(sessionStorage.getItem("posthog_bootstrap")).toBeNull(); }); it("should initialize PostHog with health monitoring config (web vitals, error tracking, network timing)", async () => { render(
, ); await screen.findByTestId("child"); expect(mockPostHogProvider).toHaveBeenCalledWith( expect.objectContaining({ options: expect.objectContaining({ capture_performance: { network_timing: true, web_vitals: true, }, capture_exceptions: true, }), }), ); }); it("should initialize without bootstrap when neither hash nor sessionStorage has IDs", async () => { render(
, ); await screen.findByTestId("child"); expect(mockPostHogProvider).toHaveBeenCalledWith( expect.objectContaining({ options: expect.objectContaining({ bootstrap: undefined, }), }), ); }); });