@ably/ui 18.2.0 → 18.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/core/hooks/use-themed-scrollpoints.test.js +1 -1
- package/core/hooks/use-themed-scrollpoints.test.js.map +1 -1
- package/core/insights/command-queue.js +1 -1
- package/core/insights/command-queue.js.map +1 -1
- package/core/insights/index.js +1 -1
- package/core/insights/index.js.map +1 -1
- package/core/insights/index.test.js +1 -1
- package/core/insights/index.test.js.map +1 -1
- package/core/insights/mixpanel.js +1 -1
- package/core/insights/mixpanel.js.map +1 -1
- package/core/insights/mixpanel.test.js +1 -1
- package/core/insights/mixpanel.test.js.map +1 -1
- package/core/insights/posthog.js +1 -1
- package/core/insights/posthog.js.map +1 -1
- package/core/insights/posthog.test.js +1 -1
- package/core/insights/posthog.test.js.map +1 -1
- package/core/insights/service.js +1 -1
- package/core/insights/service.js.map +1 -1
- package/core/insights/types.js.map +1 -1
- package/index.d.ts +4 -87
- package/package.json +11 -10
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{describe,it,expect,beforeEach,afterEach,vi}from"vitest";import{renderHook,act}from"@testing-library/react";import{useThemedScrollpoints}from"./use-themed-scrollpoints";describe("useThemedScrollpoints",()=>{let observerCallback;let mockObserve;let mockUnobserve;let mockDisconnect;let originalIntersectionObserver;let originalRequestAnimationFrame;beforeEach(()=>{vi.useFakeTimers();originalIntersectionObserver=global.IntersectionObserver;originalRequestAnimationFrame=global.requestAnimationFrame;mockObserve=vi.fn();mockUnobserve=vi.fn();mockDisconnect=vi.fn();global.IntersectionObserver=vi.fn(callback
|
|
1
|
+
import{describe,it,expect,beforeEach,afterEach,vi}from"vitest";import{renderHook,act}from"@testing-library/react";import{useThemedScrollpoints}from"./use-themed-scrollpoints";describe("useThemedScrollpoints",()=>{let observerCallback;let mockObserve;let mockUnobserve;let mockDisconnect;let originalIntersectionObserver;let originalRequestAnimationFrame;beforeEach(()=>{vi.useFakeTimers();originalIntersectionObserver=global.IntersectionObserver;originalRequestAnimationFrame=global.requestAnimationFrame;mockObserve=vi.fn();mockUnobserve=vi.fn();mockDisconnect=vi.fn();global.IntersectionObserver=vi.fn(function(callback){observerCallback=callback;return{observe:mockObserve,unobserve:mockUnobserve,disconnect:mockDisconnect}});global.requestAnimationFrame=vi.fn(cb=>{cb(0);return 0})});afterEach(()=>{vi.clearAllMocks();vi.useRealTimers();document.body.innerHTML="";global.IntersectionObserver=originalIntersectionObserver;global.requestAnimationFrame=originalRequestAnimationFrame});it("returns first scrollpoint className on mount",()=>{const elem1=document.createElement("div");elem1.id="zone1";elem1.getBoundingClientRect=vi.fn().mockReturnValue({top:0,bottom:200});const elem2=document.createElement("div");elem2.id="zone2";elem2.getBoundingClientRect=vi.fn().mockReturnValue({top:200,bottom:400});document.body.appendChild(elem1);document.body.appendChild(elem2);const scrollpoints=[{id:"zone1",className:"theme-light"},{id:"zone2",className:"theme-dark"}];const{result}=renderHook(()=>useThemedScrollpoints(scrollpoints));expect(result.current).toBe("");act(()=>{vi.runAllTimers()});expect(result.current).toBe("theme-light")});it("returns empty string when no scrollpoints provided",()=>{const{result}=renderHook(()=>useThemedScrollpoints([]));expect(result.current).toBe("")});it("clears activeClassName when scrollpoints changes from non-empty to empty",()=>{const elem=document.createElement("div");elem.id="zone1";elem.getBoundingClientRect=vi.fn().mockReturnValue({top:0,bottom:200});document.body.appendChild(elem);const{result,rerender}=renderHook(({scrollpoints})=>useThemedScrollpoints(scrollpoints),{initialProps:{scrollpoints:[{id:"zone1",className:"theme-light"}]}});act(()=>{vi.runAllTimers()});expect(result.current).toBe("theme-light");rerender({scrollpoints:[]});expect(result.current).toBe("")});it("does not create IntersectionObserver when no scrollpoints provided",()=>{renderHook(()=>useThemedScrollpoints([]));expect(IntersectionObserver).not.toHaveBeenCalled()});it("creates IntersectionObserver with correct config",()=>{const scrollpoints=[{id:"zone1",className:"theme-light"}];const elem=document.createElement("div");elem.id="zone1";document.body.appendChild(elem);renderHook(()=>useThemedScrollpoints(scrollpoints));expect(IntersectionObserver).toHaveBeenCalledWith(expect.any(Function),expect.objectContaining({rootMargin:"-64px 0px 0px 0px",threshold:0}))});it("observes all scrollpoint elements that exist in DOM",()=>{const elem1=document.createElement("div");elem1.id="zone1";const elem2=document.createElement("div");elem2.id="zone2";document.body.appendChild(elem1);document.body.appendChild(elem2);const scrollpoints=[{id:"zone1",className:"theme-light"},{id:"zone2",className:"theme-dark"}];renderHook(()=>useThemedScrollpoints(scrollpoints));expect(mockObserve).toHaveBeenCalledTimes(2);expect(mockObserve).toHaveBeenCalledWith(elem1);expect(mockObserve).toHaveBeenCalledWith(elem2)});it("logs warning for missing DOM elements",()=>{const consoleWarn=vi.spyOn(console,"warn").mockImplementation(()=>{});const scrollpoints=[{id:"non-existent",className:"theme"}];renderHook(()=>useThemedScrollpoints(scrollpoints));expect(consoleWarn).toHaveBeenCalledWith(expect.stringContaining('Element with id "non-existent" not found'));consoleWarn.mockRestore()});it("observes existing elements and warns about missing ones",()=>{const consoleWarn=vi.spyOn(console,"warn").mockImplementation(()=>{});const elem=document.createElement("div");elem.id="zone1";document.body.appendChild(elem);const scrollpoints=[{id:"zone1",className:"theme-light"},{id:"missing",className:"theme-dark"}];renderHook(()=>useThemedScrollpoints(scrollpoints));expect(mockObserve).toHaveBeenCalledTimes(1);expect(mockObserve).toHaveBeenCalledWith(elem);expect(consoleWarn).toHaveBeenCalledWith(expect.stringContaining('Element with id "missing" not found'));consoleWarn.mockRestore()});it("updates className when intersection occurs",()=>{const elem1=document.createElement("div");elem1.id="zone1";elem1.getBoundingClientRect=vi.fn().mockReturnValue({top:0,bottom:200});const elem2=document.createElement("div");elem2.id="zone2";elem2.getBoundingClientRect=vi.fn().mockReturnValue({top:50,bottom:250});document.body.appendChild(elem1);document.body.appendChild(elem2);const scrollpoints=[{id:"zone1",className:"theme-light"},{id:"zone2",className:"theme-dark"}];const{result}=renderHook(()=>useThemedScrollpoints(scrollpoints));act(()=>{vi.runAllTimers()});expect(result.current).toBe("theme-dark");act(()=>{observerCallback([{target:elem1,isIntersecting:true,boundingClientRect:{top:60,bottom:260,left:0,right:0,x:0,y:60,width:0,height:200}},{target:elem2,isIntersecting:true,boundingClientRect:{top:100,bottom:300,left:0,right:0,x:0,y:100,width:0,height:200}}],{})});expect(result.current).toBe("theme-light")});it("does not update className if same scrollpoint intersects again",()=>{const elem=document.createElement("div");elem.id="zone1";elem.getBoundingClientRect=vi.fn().mockReturnValue({top:0,bottom:200});document.body.appendChild(elem);const scrollpoints=[{id:"zone1",className:"theme-light"}];const{result}=renderHook(()=>useThemedScrollpoints(scrollpoints));act(()=>{vi.runAllTimers()});expect(result.current).toBe("theme-light");const renderCount=result.current;observerCallback([{target:elem,isIntersecting:true,boundingClientRect:{top:0,bottom:200,left:0,right:0,x:0,y:0,width:0,height:200}}],{});expect(result.current).toBe(renderCount)});it("ignores non-intersecting entries",()=>{const elem1=document.createElement("div");elem1.id="zone1";elem1.getBoundingClientRect=vi.fn().mockReturnValue({top:0,bottom:200});const elem2=document.createElement("div");elem2.id="zone2";elem2.getBoundingClientRect=vi.fn().mockReturnValue({top:200,bottom:400});document.body.appendChild(elem1);document.body.appendChild(elem2);const scrollpoints=[{id:"zone1",className:"theme-light"},{id:"zone2",className:"theme-dark"}];const{result}=renderHook(()=>useThemedScrollpoints(scrollpoints));act(()=>{vi.runAllTimers()});expect(result.current).toBe("theme-light");observerCallback([{target:elem2,isIntersecting:false}],{});expect(result.current).toBe("theme-light")});it("uses closest element to header when multiple entries intersect",()=>{const elem1=document.createElement("div");elem1.id="zone1";elem1.getBoundingClientRect=vi.fn().mockReturnValue({top:0,bottom:50});const elem2=document.createElement("div");elem2.id="zone2";elem2.getBoundingClientRect=vi.fn().mockReturnValue({top:60,bottom:200});const elem3=document.createElement("div");elem3.id="zone3";elem3.getBoundingClientRect=vi.fn().mockReturnValue({top:10,bottom:100});document.body.appendChild(elem1);document.body.appendChild(elem2);document.body.appendChild(elem3);const scrollpoints=[{id:"zone1",className:"theme-light"},{id:"zone2",className:"theme-dark"},{id:"zone3",className:"theme-blue"}];const{result}=renderHook(()=>useThemedScrollpoints(scrollpoints));act(()=>{vi.runAllTimers()});expect(result.current).toBe("theme-dark");act(()=>{observerCallback([{target:elem1,isIntersecting:false,boundingClientRect:{top:0,bottom:50,left:0,right:0,x:0,y:0,width:0,height:50}},{target:elem2,isIntersecting:true,boundingClientRect:{top:100,bottom:300,left:0,right:0,x:0,y:100,width:0,height:200}},{target:elem3,isIntersecting:true,boundingClientRect:{top:62,bottom:152,left:0,right:0,x:0,y:62,width:0,height:90}}],{})});expect(result.current).toBe("theme-blue")});it("disconnects observer on unmount",()=>{const elem=document.createElement("div");elem.id="zone1";document.body.appendChild(elem);const scrollpoints=[{id:"zone1",className:"theme-light"}];const{unmount}=renderHook(()=>useThemedScrollpoints(scrollpoints));unmount();expect(mockDisconnect).toHaveBeenCalled()});it("recreates observer when scrollpoints change",()=>{const elem1=document.createElement("div");elem1.id="zone1";const elem2=document.createElement("div");elem2.id="zone2";document.body.appendChild(elem1);document.body.appendChild(elem2);const{rerender}=renderHook(({scrollpoints})=>useThemedScrollpoints(scrollpoints),{initialProps:{scrollpoints:[{id:"zone1",className:"theme-light"}]}});expect(IntersectionObserver).toHaveBeenCalledTimes(1);expect(mockObserve).toHaveBeenCalledTimes(1);rerender({scrollpoints:[{id:"zone1",className:"theme-light"},{id:"zone2",className:"theme-dark"}]});expect(mockDisconnect).toHaveBeenCalledTimes(1);expect(IntersectionObserver).toHaveBeenCalledTimes(2);expect(mockObserve).toHaveBeenCalledTimes(3)});it("uses requestAnimationFrame for state updates",()=>{const elem=document.createElement("div");elem.id="zone1";document.body.appendChild(elem);const scrollpoints=[{id:"zone1",className:"theme-light"},{id:"zone2",className:"theme-dark"}];renderHook(()=>useThemedScrollpoints(scrollpoints));const rafSpy=vi.spyOn(global,"requestAnimationFrame");observerCallback([{target:elem,isIntersecting:true}],{});expect(rafSpy).toHaveBeenCalled();rafSpy.mockRestore()})});
|
|
2
2
|
//# sourceMappingURL=use-themed-scrollpoints.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/core/hooks/use-themed-scrollpoints.test.ts"],"sourcesContent":["/**\n * @vitest-environment jsdom\n */\n\nimport { describe, it, expect, beforeEach, afterEach, vi } from \"vitest\";\nimport { renderHook, act } from \"@testing-library/react\";\nimport { useThemedScrollpoints } from \"./use-themed-scrollpoints\";\n\ndescribe(\"useThemedScrollpoints\", () => {\n let observerCallback: IntersectionObserverCallback;\n let mockObserve: ReturnType<typeof vi.fn>;\n let mockUnobserve: ReturnType<typeof vi.fn>;\n let mockDisconnect: ReturnType<typeof vi.fn>;\n let originalIntersectionObserver: typeof IntersectionObserver;\n let originalRequestAnimationFrame: typeof requestAnimationFrame;\n\n beforeEach(() => {\n vi.useFakeTimers();\n\n // Save originals to prevent test pollution\n originalIntersectionObserver = global.IntersectionObserver;\n originalRequestAnimationFrame = global.requestAnimationFrame;\n\n // Mock IntersectionObserver\n mockObserve = vi.fn();\n mockUnobserve = vi.fn();\n mockDisconnect = vi.fn();\n\n global.IntersectionObserver = vi.fn((callback) => {\n observerCallback = callback;\n return {\n observe: mockObserve,\n unobserve: mockUnobserve,\n disconnect: mockDisconnect,\n };\n }) as unknown as typeof IntersectionObserver;\n\n // Mock requestAnimationFrame\n global.requestAnimationFrame = vi.fn((cb) => {\n cb(0);\n return 0;\n });\n });\n\n afterEach(() => {\n vi.clearAllMocks();\n vi.useRealTimers();\n document.body.innerHTML = \"\";\n // Restore originals to prevent test pollution\n global.IntersectionObserver = originalIntersectionObserver;\n global.requestAnimationFrame = originalRequestAnimationFrame;\n });\n\n it(\"returns first scrollpoint className on mount\", () => {\n // Create DOM elements\n const elem1 = document.createElement(\"div\");\n elem1.id = \"zone1\";\n elem1.getBoundingClientRect = vi\n .fn()\n .mockReturnValue({ top: 0, bottom: 200 });\n const elem2 = document.createElement(\"div\");\n elem2.id = \"zone2\";\n elem2.getBoundingClientRect = vi\n .fn()\n .mockReturnValue({ top: 200, bottom: 400 });\n document.body.appendChild(elem1);\n document.body.appendChild(elem2);\n\n const scrollpoints = [\n { id: \"zone1\", className: \"theme-light\" },\n { id: \"zone2\", className: \"theme-dark\" },\n ];\n\n const { result } = renderHook(() => useThemedScrollpoints(scrollpoints));\n\n // Initial state is empty\n expect(result.current).toBe(\"\");\n\n // Advance timer to trigger initial check\n act(() => {\n vi.runAllTimers();\n });\n\n // Should now have the first scrollpoint's className\n expect(result.current).toBe(\"theme-light\");\n });\n\n it(\"returns empty string when no scrollpoints provided\", () => {\n const { result } = renderHook(() => useThemedScrollpoints([]));\n expect(result.current).toBe(\"\");\n });\n\n it(\"clears activeClassName when scrollpoints changes from non-empty to empty\", () => {\n const elem = document.createElement(\"div\");\n elem.id = \"zone1\";\n elem.getBoundingClientRect = vi\n .fn()\n .mockReturnValue({ top: 0, bottom: 200 });\n document.body.appendChild(elem);\n\n const { result, rerender } = renderHook(\n ({ scrollpoints }) => useThemedScrollpoints(scrollpoints),\n {\n initialProps: {\n scrollpoints: [{ id: \"zone1\", className: \"theme-light\" }],\n },\n },\n );\n\n // Wait for initial check\n act(() => {\n vi.runAllTimers();\n });\n expect(result.current).toBe(\"theme-light\");\n\n // Change to empty scrollpoints\n rerender({ scrollpoints: [] });\n\n // Should clear the className\n expect(result.current).toBe(\"\");\n });\n\n it(\"does not create IntersectionObserver when no scrollpoints provided\", () => {\n renderHook(() => useThemedScrollpoints([]));\n\n expect(IntersectionObserver).not.toHaveBeenCalled();\n });\n\n it(\"creates IntersectionObserver with correct config\", () => {\n const scrollpoints = [{ id: \"zone1\", className: \"theme-light\" }];\n\n // Create DOM element\n const elem = document.createElement(\"div\");\n elem.id = \"zone1\";\n document.body.appendChild(elem);\n\n renderHook(() => useThemedScrollpoints(scrollpoints));\n\n expect(IntersectionObserver).toHaveBeenCalledWith(\n expect.any(Function),\n expect.objectContaining({\n rootMargin: \"-64px 0px 0px 0px\",\n threshold: 0,\n }),\n );\n });\n\n it(\"observes all scrollpoint elements that exist in DOM\", () => {\n // Create DOM elements\n const elem1 = document.createElement(\"div\");\n elem1.id = \"zone1\";\n const elem2 = document.createElement(\"div\");\n elem2.id = \"zone2\";\n document.body.appendChild(elem1);\n document.body.appendChild(elem2);\n\n const scrollpoints = [\n { id: \"zone1\", className: \"theme-light\" },\n { id: \"zone2\", className: \"theme-dark\" },\n ];\n\n renderHook(() => useThemedScrollpoints(scrollpoints));\n\n expect(mockObserve).toHaveBeenCalledTimes(2);\n expect(mockObserve).toHaveBeenCalledWith(elem1);\n expect(mockObserve).toHaveBeenCalledWith(elem2);\n });\n\n it(\"logs warning for missing DOM elements\", () => {\n const consoleWarn = vi.spyOn(console, \"warn\").mockImplementation(() => {});\n\n const scrollpoints = [{ id: \"non-existent\", className: \"theme\" }];\n\n renderHook(() => useThemedScrollpoints(scrollpoints));\n\n expect(consoleWarn).toHaveBeenCalledWith(\n expect.stringContaining('Element with id \"non-existent\" not found'),\n );\n\n consoleWarn.mockRestore();\n });\n\n it(\"observes existing elements and warns about missing ones\", () => {\n const consoleWarn = vi.spyOn(console, \"warn\").mockImplementation(() => {});\n\n // Create only one element\n const elem = document.createElement(\"div\");\n elem.id = \"zone1\";\n document.body.appendChild(elem);\n\n const scrollpoints = [\n { id: \"zone1\", className: \"theme-light\" },\n { id: \"missing\", className: \"theme-dark\" },\n ];\n\n renderHook(() => useThemedScrollpoints(scrollpoints));\n\n expect(mockObserve).toHaveBeenCalledTimes(1);\n expect(mockObserve).toHaveBeenCalledWith(elem);\n expect(consoleWarn).toHaveBeenCalledWith(\n expect.stringContaining('Element with id \"missing\" not found'),\n );\n\n consoleWarn.mockRestore();\n });\n\n it(\"updates className when intersection occurs\", () => {\n // Create DOM elements\n const elem1 = document.createElement(\"div\");\n elem1.id = \"zone1\";\n elem1.getBoundingClientRect = vi\n .fn()\n .mockReturnValue({ top: 0, bottom: 200 });\n const elem2 = document.createElement(\"div\");\n elem2.id = \"zone2\";\n elem2.getBoundingClientRect = vi\n .fn()\n .mockReturnValue({ top: 50, bottom: 250 });\n document.body.appendChild(elem1);\n document.body.appendChild(elem2);\n\n const scrollpoints = [\n { id: \"zone1\", className: \"theme-light\" },\n { id: \"zone2\", className: \"theme-dark\" },\n ];\n\n const { result } = renderHook(() => useThemedScrollpoints(scrollpoints));\n\n // Wait for initial check\n // elem2 (top=50) is closer to header (64) than elem1 (top=0)\n // distance for elem1: |0 - 64| = 64\n // distance for elem2: |50 - 64| = 14\n act(() => {\n vi.runAllTimers();\n });\n expect(result.current).toBe(\"theme-dark\");\n\n // Simulate scrolling - elem1 moves closer to header position\n act(() => {\n observerCallback(\n [\n {\n target: elem1,\n isIntersecting: true,\n boundingClientRect: {\n top: 60,\n bottom: 260,\n left: 0,\n right: 0,\n x: 0,\n y: 60,\n width: 0,\n height: 200,\n },\n } as unknown as IntersectionObserverEntry,\n {\n target: elem2,\n isIntersecting: true,\n boundingClientRect: {\n top: 100,\n bottom: 300,\n left: 0,\n right: 0,\n x: 0,\n y: 100,\n width: 0,\n height: 200,\n },\n } as unknown as IntersectionObserverEntry,\n ],\n {} as IntersectionObserver,\n );\n });\n\n // elem1 is now closer to header (distance=4 vs elem2 distance=36)\n expect(result.current).toBe(\"theme-light\");\n });\n\n it(\"does not update className if same scrollpoint intersects again\", () => {\n const elem = document.createElement(\"div\");\n elem.id = \"zone1\";\n elem.getBoundingClientRect = vi\n .fn()\n .mockReturnValue({ top: 0, bottom: 200 });\n document.body.appendChild(elem);\n\n const scrollpoints = [{ id: \"zone1\", className: \"theme-light\" }];\n\n const { result } = renderHook(() => useThemedScrollpoints(scrollpoints));\n\n // Wait for initial check\n act(() => {\n vi.runAllTimers();\n });\n expect(result.current).toBe(\"theme-light\");\n\n // Simulate intersection with same element\n const renderCount = result.current;\n observerCallback(\n [\n {\n target: elem,\n isIntersecting: true,\n boundingClientRect: {\n top: 0,\n bottom: 200,\n left: 0,\n right: 0,\n x: 0,\n y: 0,\n width: 0,\n height: 200,\n },\n } as unknown as IntersectionObserverEntry,\n ],\n {} as IntersectionObserver,\n );\n\n // Should not trigger re-render (className unchanged)\n expect(result.current).toBe(renderCount);\n });\n\n it(\"ignores non-intersecting entries\", () => {\n const elem1 = document.createElement(\"div\");\n elem1.id = \"zone1\";\n elem1.getBoundingClientRect = vi\n .fn()\n .mockReturnValue({ top: 0, bottom: 200 });\n const elem2 = document.createElement(\"div\");\n elem2.id = \"zone2\";\n elem2.getBoundingClientRect = vi\n .fn()\n .mockReturnValue({ top: 200, bottom: 400 });\n document.body.appendChild(elem1);\n document.body.appendChild(elem2);\n\n const scrollpoints = [\n { id: \"zone1\", className: \"theme-light\" },\n { id: \"zone2\", className: \"theme-dark\" },\n ];\n\n const { result } = renderHook(() => useThemedScrollpoints(scrollpoints));\n\n // Wait for initial check\n act(() => {\n vi.runAllTimers();\n });\n expect(result.current).toBe(\"theme-light\");\n\n // Simulate non-intersecting entry\n observerCallback(\n [\n {\n target: elem2,\n isIntersecting: false,\n } as unknown as IntersectionObserverEntry,\n ],\n {} as IntersectionObserver,\n );\n\n // Should remain unchanged\n expect(result.current).toBe(\"theme-light\");\n });\n\n it(\"uses closest element to header when multiple entries intersect\", () => {\n const elem1 = document.createElement(\"div\");\n elem1.id = \"zone1\";\n elem1.getBoundingClientRect = vi\n .fn()\n .mockReturnValue({ top: 0, bottom: 50 });\n const elem2 = document.createElement(\"div\");\n elem2.id = \"zone2\";\n // zone2 top is at 60, which is closer to HEADER_HEIGHT (64) than zone3\n elem2.getBoundingClientRect = vi\n .fn()\n .mockReturnValue({ top: 60, bottom: 200 });\n const elem3 = document.createElement(\"div\");\n elem3.id = \"zone3\";\n elem3.getBoundingClientRect = vi\n .fn()\n .mockReturnValue({ top: 10, bottom: 100 });\n document.body.appendChild(elem1);\n document.body.appendChild(elem2);\n document.body.appendChild(elem3);\n\n const scrollpoints = [\n { id: \"zone1\", className: \"theme-light\" },\n { id: \"zone2\", className: \"theme-dark\" },\n { id: \"zone3\", className: \"theme-blue\" },\n ];\n\n const { result } = renderHook(() => useThemedScrollpoints(scrollpoints));\n\n // Wait for initial check\n // elem2 (top=60) is closest to header (64) with distance=4\n // elem3 (top=10) has distance=54\n // elem1 (top=0) doesn't contain header (bottom=50 < 64)\n act(() => {\n vi.runAllTimers();\n });\n expect(result.current).toBe(\"theme-dark\");\n\n // Simulate scrolling where elem3 becomes closest to header\n act(() => {\n observerCallback(\n [\n {\n target: elem1,\n isIntersecting: false,\n boundingClientRect: {\n top: 0,\n bottom: 50,\n left: 0,\n right: 0,\n x: 0,\n y: 0,\n width: 0,\n height: 50,\n },\n } as unknown as IntersectionObserverEntry,\n {\n target: elem2,\n isIntersecting: true,\n boundingClientRect: {\n top: 100,\n bottom: 300,\n left: 0,\n right: 0,\n x: 0,\n y: 100,\n width: 0,\n height: 200,\n },\n } as unknown as IntersectionObserverEntry,\n {\n target: elem3,\n isIntersecting: true,\n boundingClientRect: {\n top: 62,\n bottom: 152,\n left: 0,\n right: 0,\n x: 0,\n y: 62,\n width: 0,\n height: 90,\n },\n } as unknown as IntersectionObserverEntry,\n ],\n {} as IntersectionObserver,\n );\n });\n\n // elem3 is now closest to HEADER_HEIGHT (64)\n // distance for elem2: |100 - 64| = 36\n // distance for elem3: |62 - 64| = 2\n expect(result.current).toBe(\"theme-blue\");\n });\n\n it(\"disconnects observer on unmount\", () => {\n const elem = document.createElement(\"div\");\n elem.id = \"zone1\";\n document.body.appendChild(elem);\n\n const scrollpoints = [{ id: \"zone1\", className: \"theme-light\" }];\n\n const { unmount } = renderHook(() => useThemedScrollpoints(scrollpoints));\n\n unmount();\n\n expect(mockDisconnect).toHaveBeenCalled();\n });\n\n it(\"recreates observer when scrollpoints change\", () => {\n const elem1 = document.createElement(\"div\");\n elem1.id = \"zone1\";\n const elem2 = document.createElement(\"div\");\n elem2.id = \"zone2\";\n document.body.appendChild(elem1);\n document.body.appendChild(elem2);\n\n const { rerender } = renderHook(\n ({ scrollpoints }) => useThemedScrollpoints(scrollpoints),\n {\n initialProps: {\n scrollpoints: [{ id: \"zone1\", className: \"theme-light\" }],\n },\n },\n );\n\n expect(IntersectionObserver).toHaveBeenCalledTimes(1);\n expect(mockObserve).toHaveBeenCalledTimes(1);\n\n // Change scrollpoints\n rerender({\n scrollpoints: [\n { id: \"zone1\", className: \"theme-light\" },\n { id: \"zone2\", className: \"theme-dark\" },\n ],\n });\n\n // Should disconnect old observer and create new one\n expect(mockDisconnect).toHaveBeenCalledTimes(1);\n expect(IntersectionObserver).toHaveBeenCalledTimes(2);\n expect(mockObserve).toHaveBeenCalledTimes(3); // 1 from first render + 2 from second\n });\n\n it(\"uses requestAnimationFrame for state updates\", () => {\n const elem = document.createElement(\"div\");\n elem.id = \"zone1\";\n document.body.appendChild(elem);\n\n const scrollpoints = [\n { id: \"zone1\", className: \"theme-light\" },\n { id: \"zone2\", className: \"theme-dark\" },\n ];\n\n renderHook(() => useThemedScrollpoints(scrollpoints));\n\n const rafSpy = vi.spyOn(global, \"requestAnimationFrame\");\n\n // Simulate intersection\n observerCallback(\n [\n {\n target: elem,\n isIntersecting: true,\n } as unknown as IntersectionObserverEntry,\n ],\n {} as IntersectionObserver,\n );\n\n expect(rafSpy).toHaveBeenCalled();\n\n rafSpy.mockRestore();\n });\n});\n"],"names":["describe","it","expect","beforeEach","afterEach","vi","renderHook","act","useThemedScrollpoints","observerCallback","mockObserve","mockUnobserve","mockDisconnect","originalIntersectionObserver","originalRequestAnimationFrame","useFakeTimers","global","IntersectionObserver","requestAnimationFrame","fn","callback","observe","unobserve","disconnect","cb","clearAllMocks","useRealTimers","document","body","innerHTML","elem1","createElement","id","getBoundingClientRect","mockReturnValue","top","bottom","elem2","appendChild","scrollpoints","className","result","current","toBe","runAllTimers","elem","rerender","initialProps","not","toHaveBeenCalled","toHaveBeenCalledWith","any","Function","objectContaining","rootMargin","threshold","toHaveBeenCalledTimes","consoleWarn","spyOn","console","mockImplementation","stringContaining","mockRestore","target","isIntersecting","boundingClientRect","left","right","x","y","width","height","renderCount","elem3","unmount","rafSpy"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/core/hooks/use-themed-scrollpoints.test.ts"],"sourcesContent":["/**\n * @vitest-environment jsdom\n */\n\nimport { describe, it, expect, beforeEach, afterEach, vi } from \"vitest\";\nimport { renderHook, act } from \"@testing-library/react\";\nimport { useThemedScrollpoints } from \"./use-themed-scrollpoints\";\n\ndescribe(\"useThemedScrollpoints\", () => {\n let observerCallback: IntersectionObserverCallback;\n let mockObserve: ReturnType<typeof vi.fn>;\n let mockUnobserve: ReturnType<typeof vi.fn>;\n let mockDisconnect: ReturnType<typeof vi.fn>;\n let originalIntersectionObserver: typeof IntersectionObserver;\n let originalRequestAnimationFrame: typeof requestAnimationFrame;\n\n beforeEach(() => {\n vi.useFakeTimers();\n\n // Save originals to prevent test pollution\n originalIntersectionObserver = global.IntersectionObserver;\n originalRequestAnimationFrame = global.requestAnimationFrame;\n\n // Mock IntersectionObserver\n mockObserve = vi.fn();\n mockUnobserve = vi.fn();\n mockDisconnect = vi.fn();\n\n // Vitest 4: constructor mocks must use `function`/`class`, not arrows —\n // arrows have no [[Construct]] and `new` against them throws.\n global.IntersectionObserver = vi.fn(function (\n callback: IntersectionObserverCallback,\n ) {\n observerCallback = callback;\n return {\n observe: mockObserve,\n unobserve: mockUnobserve,\n disconnect: mockDisconnect,\n };\n }) as unknown as typeof IntersectionObserver;\n\n // Mock requestAnimationFrame\n global.requestAnimationFrame = vi.fn((cb) => {\n cb(0);\n return 0;\n });\n });\n\n afterEach(() => {\n vi.clearAllMocks();\n vi.useRealTimers();\n document.body.innerHTML = \"\";\n // Restore originals to prevent test pollution\n global.IntersectionObserver = originalIntersectionObserver;\n global.requestAnimationFrame = originalRequestAnimationFrame;\n });\n\n it(\"returns first scrollpoint className on mount\", () => {\n // Create DOM elements\n const elem1 = document.createElement(\"div\");\n elem1.id = \"zone1\";\n elem1.getBoundingClientRect = vi\n .fn()\n .mockReturnValue({ top: 0, bottom: 200 });\n const elem2 = document.createElement(\"div\");\n elem2.id = \"zone2\";\n elem2.getBoundingClientRect = vi\n .fn()\n .mockReturnValue({ top: 200, bottom: 400 });\n document.body.appendChild(elem1);\n document.body.appendChild(elem2);\n\n const scrollpoints = [\n { id: \"zone1\", className: \"theme-light\" },\n { id: \"zone2\", className: \"theme-dark\" },\n ];\n\n const { result } = renderHook(() => useThemedScrollpoints(scrollpoints));\n\n // Initial state is empty\n expect(result.current).toBe(\"\");\n\n // Advance timer to trigger initial check\n act(() => {\n vi.runAllTimers();\n });\n\n // Should now have the first scrollpoint's className\n expect(result.current).toBe(\"theme-light\");\n });\n\n it(\"returns empty string when no scrollpoints provided\", () => {\n const { result } = renderHook(() => useThemedScrollpoints([]));\n expect(result.current).toBe(\"\");\n });\n\n it(\"clears activeClassName when scrollpoints changes from non-empty to empty\", () => {\n const elem = document.createElement(\"div\");\n elem.id = \"zone1\";\n elem.getBoundingClientRect = vi\n .fn()\n .mockReturnValue({ top: 0, bottom: 200 });\n document.body.appendChild(elem);\n\n const { result, rerender } = renderHook(\n ({ scrollpoints }) => useThemedScrollpoints(scrollpoints),\n {\n initialProps: {\n scrollpoints: [{ id: \"zone1\", className: \"theme-light\" }],\n },\n },\n );\n\n // Wait for initial check\n act(() => {\n vi.runAllTimers();\n });\n expect(result.current).toBe(\"theme-light\");\n\n // Change to empty scrollpoints\n rerender({ scrollpoints: [] });\n\n // Should clear the className\n expect(result.current).toBe(\"\");\n });\n\n it(\"does not create IntersectionObserver when no scrollpoints provided\", () => {\n renderHook(() => useThemedScrollpoints([]));\n\n expect(IntersectionObserver).not.toHaveBeenCalled();\n });\n\n it(\"creates IntersectionObserver with correct config\", () => {\n const scrollpoints = [{ id: \"zone1\", className: \"theme-light\" }];\n\n // Create DOM element\n const elem = document.createElement(\"div\");\n elem.id = \"zone1\";\n document.body.appendChild(elem);\n\n renderHook(() => useThemedScrollpoints(scrollpoints));\n\n expect(IntersectionObserver).toHaveBeenCalledWith(\n expect.any(Function),\n expect.objectContaining({\n rootMargin: \"-64px 0px 0px 0px\",\n threshold: 0,\n }),\n );\n });\n\n it(\"observes all scrollpoint elements that exist in DOM\", () => {\n // Create DOM elements\n const elem1 = document.createElement(\"div\");\n elem1.id = \"zone1\";\n const elem2 = document.createElement(\"div\");\n elem2.id = \"zone2\";\n document.body.appendChild(elem1);\n document.body.appendChild(elem2);\n\n const scrollpoints = [\n { id: \"zone1\", className: \"theme-light\" },\n { id: \"zone2\", className: \"theme-dark\" },\n ];\n\n renderHook(() => useThemedScrollpoints(scrollpoints));\n\n expect(mockObserve).toHaveBeenCalledTimes(2);\n expect(mockObserve).toHaveBeenCalledWith(elem1);\n expect(mockObserve).toHaveBeenCalledWith(elem2);\n });\n\n it(\"logs warning for missing DOM elements\", () => {\n const consoleWarn = vi.spyOn(console, \"warn\").mockImplementation(() => {});\n\n const scrollpoints = [{ id: \"non-existent\", className: \"theme\" }];\n\n renderHook(() => useThemedScrollpoints(scrollpoints));\n\n expect(consoleWarn).toHaveBeenCalledWith(\n expect.stringContaining('Element with id \"non-existent\" not found'),\n );\n\n consoleWarn.mockRestore();\n });\n\n it(\"observes existing elements and warns about missing ones\", () => {\n const consoleWarn = vi.spyOn(console, \"warn\").mockImplementation(() => {});\n\n // Create only one element\n const elem = document.createElement(\"div\");\n elem.id = \"zone1\";\n document.body.appendChild(elem);\n\n const scrollpoints = [\n { id: \"zone1\", className: \"theme-light\" },\n { id: \"missing\", className: \"theme-dark\" },\n ];\n\n renderHook(() => useThemedScrollpoints(scrollpoints));\n\n expect(mockObserve).toHaveBeenCalledTimes(1);\n expect(mockObserve).toHaveBeenCalledWith(elem);\n expect(consoleWarn).toHaveBeenCalledWith(\n expect.stringContaining('Element with id \"missing\" not found'),\n );\n\n consoleWarn.mockRestore();\n });\n\n it(\"updates className when intersection occurs\", () => {\n // Create DOM elements\n const elem1 = document.createElement(\"div\");\n elem1.id = \"zone1\";\n elem1.getBoundingClientRect = vi\n .fn()\n .mockReturnValue({ top: 0, bottom: 200 });\n const elem2 = document.createElement(\"div\");\n elem2.id = \"zone2\";\n elem2.getBoundingClientRect = vi\n .fn()\n .mockReturnValue({ top: 50, bottom: 250 });\n document.body.appendChild(elem1);\n document.body.appendChild(elem2);\n\n const scrollpoints = [\n { id: \"zone1\", className: \"theme-light\" },\n { id: \"zone2\", className: \"theme-dark\" },\n ];\n\n const { result } = renderHook(() => useThemedScrollpoints(scrollpoints));\n\n // Wait for initial check\n // elem2 (top=50) is closer to header (64) than elem1 (top=0)\n // distance for elem1: |0 - 64| = 64\n // distance for elem2: |50 - 64| = 14\n act(() => {\n vi.runAllTimers();\n });\n expect(result.current).toBe(\"theme-dark\");\n\n // Simulate scrolling - elem1 moves closer to header position\n act(() => {\n observerCallback(\n [\n {\n target: elem1,\n isIntersecting: true,\n boundingClientRect: {\n top: 60,\n bottom: 260,\n left: 0,\n right: 0,\n x: 0,\n y: 60,\n width: 0,\n height: 200,\n },\n } as unknown as IntersectionObserverEntry,\n {\n target: elem2,\n isIntersecting: true,\n boundingClientRect: {\n top: 100,\n bottom: 300,\n left: 0,\n right: 0,\n x: 0,\n y: 100,\n width: 0,\n height: 200,\n },\n } as unknown as IntersectionObserverEntry,\n ],\n {} as IntersectionObserver,\n );\n });\n\n // elem1 is now closer to header (distance=4 vs elem2 distance=36)\n expect(result.current).toBe(\"theme-light\");\n });\n\n it(\"does not update className if same scrollpoint intersects again\", () => {\n const elem = document.createElement(\"div\");\n elem.id = \"zone1\";\n elem.getBoundingClientRect = vi\n .fn()\n .mockReturnValue({ top: 0, bottom: 200 });\n document.body.appendChild(elem);\n\n const scrollpoints = [{ id: \"zone1\", className: \"theme-light\" }];\n\n const { result } = renderHook(() => useThemedScrollpoints(scrollpoints));\n\n // Wait for initial check\n act(() => {\n vi.runAllTimers();\n });\n expect(result.current).toBe(\"theme-light\");\n\n // Simulate intersection with same element\n const renderCount = result.current;\n observerCallback(\n [\n {\n target: elem,\n isIntersecting: true,\n boundingClientRect: {\n top: 0,\n bottom: 200,\n left: 0,\n right: 0,\n x: 0,\n y: 0,\n width: 0,\n height: 200,\n },\n } as unknown as IntersectionObserverEntry,\n ],\n {} as IntersectionObserver,\n );\n\n // Should not trigger re-render (className unchanged)\n expect(result.current).toBe(renderCount);\n });\n\n it(\"ignores non-intersecting entries\", () => {\n const elem1 = document.createElement(\"div\");\n elem1.id = \"zone1\";\n elem1.getBoundingClientRect = vi\n .fn()\n .mockReturnValue({ top: 0, bottom: 200 });\n const elem2 = document.createElement(\"div\");\n elem2.id = \"zone2\";\n elem2.getBoundingClientRect = vi\n .fn()\n .mockReturnValue({ top: 200, bottom: 400 });\n document.body.appendChild(elem1);\n document.body.appendChild(elem2);\n\n const scrollpoints = [\n { id: \"zone1\", className: \"theme-light\" },\n { id: \"zone2\", className: \"theme-dark\" },\n ];\n\n const { result } = renderHook(() => useThemedScrollpoints(scrollpoints));\n\n // Wait for initial check\n act(() => {\n vi.runAllTimers();\n });\n expect(result.current).toBe(\"theme-light\");\n\n // Simulate non-intersecting entry\n observerCallback(\n [\n {\n target: elem2,\n isIntersecting: false,\n } as unknown as IntersectionObserverEntry,\n ],\n {} as IntersectionObserver,\n );\n\n // Should remain unchanged\n expect(result.current).toBe(\"theme-light\");\n });\n\n it(\"uses closest element to header when multiple entries intersect\", () => {\n const elem1 = document.createElement(\"div\");\n elem1.id = \"zone1\";\n elem1.getBoundingClientRect = vi\n .fn()\n .mockReturnValue({ top: 0, bottom: 50 });\n const elem2 = document.createElement(\"div\");\n elem2.id = \"zone2\";\n // zone2 top is at 60, which is closer to HEADER_HEIGHT (64) than zone3\n elem2.getBoundingClientRect = vi\n .fn()\n .mockReturnValue({ top: 60, bottom: 200 });\n const elem3 = document.createElement(\"div\");\n elem3.id = \"zone3\";\n elem3.getBoundingClientRect = vi\n .fn()\n .mockReturnValue({ top: 10, bottom: 100 });\n document.body.appendChild(elem1);\n document.body.appendChild(elem2);\n document.body.appendChild(elem3);\n\n const scrollpoints = [\n { id: \"zone1\", className: \"theme-light\" },\n { id: \"zone2\", className: \"theme-dark\" },\n { id: \"zone3\", className: \"theme-blue\" },\n ];\n\n const { result } = renderHook(() => useThemedScrollpoints(scrollpoints));\n\n // Wait for initial check\n // elem2 (top=60) is closest to header (64) with distance=4\n // elem3 (top=10) has distance=54\n // elem1 (top=0) doesn't contain header (bottom=50 < 64)\n act(() => {\n vi.runAllTimers();\n });\n expect(result.current).toBe(\"theme-dark\");\n\n // Simulate scrolling where elem3 becomes closest to header\n act(() => {\n observerCallback(\n [\n {\n target: elem1,\n isIntersecting: false,\n boundingClientRect: {\n top: 0,\n bottom: 50,\n left: 0,\n right: 0,\n x: 0,\n y: 0,\n width: 0,\n height: 50,\n },\n } as unknown as IntersectionObserverEntry,\n {\n target: elem2,\n isIntersecting: true,\n boundingClientRect: {\n top: 100,\n bottom: 300,\n left: 0,\n right: 0,\n x: 0,\n y: 100,\n width: 0,\n height: 200,\n },\n } as unknown as IntersectionObserverEntry,\n {\n target: elem3,\n isIntersecting: true,\n boundingClientRect: {\n top: 62,\n bottom: 152,\n left: 0,\n right: 0,\n x: 0,\n y: 62,\n width: 0,\n height: 90,\n },\n } as unknown as IntersectionObserverEntry,\n ],\n {} as IntersectionObserver,\n );\n });\n\n // elem3 is now closest to HEADER_HEIGHT (64)\n // distance for elem2: |100 - 64| = 36\n // distance for elem3: |62 - 64| = 2\n expect(result.current).toBe(\"theme-blue\");\n });\n\n it(\"disconnects observer on unmount\", () => {\n const elem = document.createElement(\"div\");\n elem.id = \"zone1\";\n document.body.appendChild(elem);\n\n const scrollpoints = [{ id: \"zone1\", className: \"theme-light\" }];\n\n const { unmount } = renderHook(() => useThemedScrollpoints(scrollpoints));\n\n unmount();\n\n expect(mockDisconnect).toHaveBeenCalled();\n });\n\n it(\"recreates observer when scrollpoints change\", () => {\n const elem1 = document.createElement(\"div\");\n elem1.id = \"zone1\";\n const elem2 = document.createElement(\"div\");\n elem2.id = \"zone2\";\n document.body.appendChild(elem1);\n document.body.appendChild(elem2);\n\n const { rerender } = renderHook(\n ({ scrollpoints }) => useThemedScrollpoints(scrollpoints),\n {\n initialProps: {\n scrollpoints: [{ id: \"zone1\", className: \"theme-light\" }],\n },\n },\n );\n\n expect(IntersectionObserver).toHaveBeenCalledTimes(1);\n expect(mockObserve).toHaveBeenCalledTimes(1);\n\n // Change scrollpoints\n rerender({\n scrollpoints: [\n { id: \"zone1\", className: \"theme-light\" },\n { id: \"zone2\", className: \"theme-dark\" },\n ],\n });\n\n // Should disconnect old observer and create new one\n expect(mockDisconnect).toHaveBeenCalledTimes(1);\n expect(IntersectionObserver).toHaveBeenCalledTimes(2);\n expect(mockObserve).toHaveBeenCalledTimes(3); // 1 from first render + 2 from second\n });\n\n it(\"uses requestAnimationFrame for state updates\", () => {\n const elem = document.createElement(\"div\");\n elem.id = \"zone1\";\n document.body.appendChild(elem);\n\n const scrollpoints = [\n { id: \"zone1\", className: \"theme-light\" },\n { id: \"zone2\", className: \"theme-dark\" },\n ];\n\n renderHook(() => useThemedScrollpoints(scrollpoints));\n\n const rafSpy = vi.spyOn(global, \"requestAnimationFrame\");\n\n // Simulate intersection\n observerCallback(\n [\n {\n target: elem,\n isIntersecting: true,\n } as unknown as IntersectionObserverEntry,\n ],\n {} as IntersectionObserver,\n );\n\n expect(rafSpy).toHaveBeenCalled();\n\n rafSpy.mockRestore();\n });\n});\n"],"names":["describe","it","expect","beforeEach","afterEach","vi","renderHook","act","useThemedScrollpoints","observerCallback","mockObserve","mockUnobserve","mockDisconnect","originalIntersectionObserver","originalRequestAnimationFrame","useFakeTimers","global","IntersectionObserver","requestAnimationFrame","fn","callback","observe","unobserve","disconnect","cb","clearAllMocks","useRealTimers","document","body","innerHTML","elem1","createElement","id","getBoundingClientRect","mockReturnValue","top","bottom","elem2","appendChild","scrollpoints","className","result","current","toBe","runAllTimers","elem","rerender","initialProps","not","toHaveBeenCalled","toHaveBeenCalledWith","any","Function","objectContaining","rootMargin","threshold","toHaveBeenCalledTimes","consoleWarn","spyOn","console","mockImplementation","stringContaining","mockRestore","target","isIntersecting","boundingClientRect","left","right","x","y","width","height","renderCount","elem3","unmount","rafSpy"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
function _define_property(obj,key,value){if(key in obj){Object.defineProperty(obj,key,{value:value,enumerable:true,configurable:true,writable:true})}else{obj[key]=value}return obj}import{InsightsService}from"./service";import*as logger from"./logger";export class InsightsCommandQueue{executeInitInsights(config){this.debugMode=!!config.debug;if(this.debugMode){logger.debug("Initializing insights")}this.realImplementation=new InsightsService;this.realImplementation.initInsights(config);this.isInitialized=true;this.processQueue()}processQueue(){if(this.debugMode){logger.debug(`Processing ${this.queue.length} queued commands`)}while(this.queue.length>0){const cmd=this.queue.shift();if(cmd&&this.realImplementation&&typeof this.realImplementation[cmd.methodName]==="function"){try{if(this.debugMode){logger.debug(`Executing queued command: ${cmd.methodName}`,cmd.args)}const fn=this.realImplementation[cmd.methodName];fn.apply(this.realImplementation,cmd.args)}catch(e){if(this.debugMode){logger.error(`Error executing queued command: ${cmd.methodName}`,e)}}}}}initInsights(_config){}enableDebugMode(){}disableDebugMode(){}identify(_identity){}trackPageView(_options){}track(_event,_properties){}
|
|
1
|
+
function _define_property(obj,key,value){if(key in obj){Object.defineProperty(obj,key,{value:value,enumerable:true,configurable:true,writable:true})}else{obj[key]=value}return obj}import{InsightsService}from"./service";import*as logger from"./logger";export class InsightsCommandQueue{executeInitInsights(config){this.debugMode=!!config.debug;if(this.debugMode){logger.debug("Initializing insights")}this.realImplementation=new InsightsService;this.realImplementation.initInsights(config);this.isInitialized=true;this.processQueue()}processQueue(){if(this.debugMode){logger.debug(`Processing ${this.queue.length} queued commands`)}while(this.queue.length>0){const cmd=this.queue.shift();if(cmd&&this.realImplementation&&typeof this.realImplementation[cmd.methodName]==="function"){try{if(this.debugMode){logger.debug(`Executing queued command: ${cmd.methodName}`,cmd.args)}const fn=this.realImplementation[cmd.methodName];fn.apply(this.realImplementation,cmd.args)}catch(e){if(this.debugMode){logger.error(`Error executing queued command: ${cmd.methodName}`,e)}}}}}initInsights(_config){}enableDebugMode(){}disableDebugMode(){}identify(_identity){}trackPageView(_options){}track(_event,_properties){}setupObserver(){return()=>{}}constructor(){_define_property(this,"isInitialized",false);_define_property(this,"queue",[]);_define_property(this,"debugMode",false);_define_property(this,"realImplementation",null);return new Proxy(this,{get:(target,prop)=>{if(prop in target&&typeof target[prop]!=="function"){return target[prop]}return(...args)=>{if(!target.isInitialized||!target.realImplementation){if(prop==="initInsights"){target.executeInitInsights(args[0]);return}if(target.debugMode){logger.debug(`Queuing method call: ${String(prop)}`,args)}target.queue.push({methodName:prop,args});return}if(typeof target.realImplementation[prop]==="function"){return target.realImplementation[prop](...args)}}}})}}
|
|
2
2
|
//# sourceMappingURL=command-queue.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/core/insights/command-queue.ts"],"sourcesContent":["import {\n AnalyticsService,\n Command,\n InsightsConfig,\n InsightsIdentity,\n TrackPageViewOptions,\n} from \"./types\";\nimport { InsightsService } from \"./service\";\nimport * as logger from \"./logger\";\n\n// Queue handler that will collect commands before initialization\nexport class InsightsCommandQueue implements AnalyticsService {\n private isInitialized: boolean = false;\n private queue: Command[] = [];\n private debugMode: boolean = false;\n private realImplementation: InsightsService | null = null;\n\n constructor() {\n // Create a proxy that will either queue commands or execute them directly\n return new Proxy(this, {\n get: (target: InsightsCommandQueue, prop: keyof InsightsCommandQueue) => {\n // Return actual properties of the queue\n if (prop in target && typeof target[prop] !== \"function\") {\n return target[prop];\n }\n\n // Return a function that either queues or executes the method call\n return (...args: unknown[]) => {\n if (!target.isInitialized || !target.realImplementation) {\n // Queue the command for later execution\n if (prop === \"initInsights\") {\n // Special handling for initInsights - execute it right away\n target.executeInitInsights(args[0] as InsightsConfig);\n return;\n }\n\n // For debug logging\n if (target.debugMode) {\n logger.debug(`Queuing method call: ${String(prop)}`, args);\n }\n\n target.queue.push({\n methodName: prop,\n args,\n });\n\n return;\n }\n\n // Execute the command immediately on the real implementation\n if (typeof target.realImplementation[prop] === \"function\") {\n return (\n target.realImplementation[prop] as (...args: unknown[]) => unknown\n )(...args);\n }\n };\n },\n });\n }\n\n // Special handling for init since it needs to happen right away\n private executeInitInsights(config: InsightsConfig): void {\n this.debugMode = !!config.debug;\n\n if (this.debugMode) {\n logger.debug(\"Initializing insights\");\n }\n\n // Create and initialize the real implementation\n this.realImplementation = new InsightsService();\n this.realImplementation.initInsights(config);\n\n // Mark as initialized and process the queue\n this.isInitialized = true;\n this.processQueue();\n }\n\n // Process all queued commands\n private processQueue(): void {\n if (this.debugMode) {\n logger.debug(`Processing ${this.queue.length} queued commands`);\n }\n\n while (this.queue.length > 0) {\n const cmd = this.queue.shift();\n if (\n cmd &&\n this.realImplementation &&\n typeof this.realImplementation[cmd.methodName] === \"function\"\n ) {\n try {\n if (this.debugMode) {\n logger.debug(\n `Executing queued command: ${cmd.methodName}`,\n cmd.args,\n );\n }\n\n const fn = this.realImplementation[cmd.methodName] as (\n ...args: unknown[]\n ) => unknown;\n\n // Execute the command with the real implementation\n fn.apply(this.realImplementation, cmd.args as unknown[]);\n } catch (e) {\n if (this.debugMode) {\n logger.error(\n `Error executing queued command: ${cmd.methodName}`,\n e,\n );\n }\n }\n }\n }\n }\n\n // Implement all methods required by AnalyticsService to satisfy TypeScript\n // (These won't be called directly due to the proxy)\n initInsights(_config: InsightsConfig): void {}\n enableDebugMode(): void {}\n disableDebugMode(): void {}\n identify(_identity: InsightsIdentity): void {}\n trackPageView(_options?: TrackPageViewOptions): void {}\n track(_event: string, _properties?: Record<string, unknown>): void {}\n
|
|
1
|
+
{"version":3,"sources":["../../../src/core/insights/command-queue.ts"],"sourcesContent":["import {\n AnalyticsService,\n Command,\n InsightsConfig,\n InsightsIdentity,\n TrackPageViewOptions,\n} from \"./types\";\nimport { InsightsService } from \"./service\";\nimport * as logger from \"./logger\";\n\n// Queue handler that will collect commands before initialization\nexport class InsightsCommandQueue implements AnalyticsService {\n private isInitialized: boolean = false;\n private queue: Command[] = [];\n private debugMode: boolean = false;\n private realImplementation: InsightsService | null = null;\n\n constructor() {\n // Create a proxy that will either queue commands or execute them directly\n return new Proxy(this, {\n get: (target: InsightsCommandQueue, prop: keyof InsightsCommandQueue) => {\n // Return actual properties of the queue\n if (prop in target && typeof target[prop] !== \"function\") {\n return target[prop];\n }\n\n // Return a function that either queues or executes the method call\n return (...args: unknown[]) => {\n if (!target.isInitialized || !target.realImplementation) {\n // Queue the command for later execution\n if (prop === \"initInsights\") {\n // Special handling for initInsights - execute it right away\n target.executeInitInsights(args[0] as InsightsConfig);\n return;\n }\n\n // For debug logging\n if (target.debugMode) {\n logger.debug(`Queuing method call: ${String(prop)}`, args);\n }\n\n target.queue.push({\n methodName: prop,\n args,\n });\n\n return;\n }\n\n // Execute the command immediately on the real implementation\n if (typeof target.realImplementation[prop] === \"function\") {\n return (\n target.realImplementation[prop] as (...args: unknown[]) => unknown\n )(...args);\n }\n };\n },\n });\n }\n\n // Special handling for init since it needs to happen right away\n private executeInitInsights(config: InsightsConfig): void {\n this.debugMode = !!config.debug;\n\n if (this.debugMode) {\n logger.debug(\"Initializing insights\");\n }\n\n // Create and initialize the real implementation\n this.realImplementation = new InsightsService();\n this.realImplementation.initInsights(config);\n\n // Mark as initialized and process the queue\n this.isInitialized = true;\n this.processQueue();\n }\n\n // Process all queued commands\n private processQueue(): void {\n if (this.debugMode) {\n logger.debug(`Processing ${this.queue.length} queued commands`);\n }\n\n while (this.queue.length > 0) {\n const cmd = this.queue.shift();\n if (\n cmd &&\n this.realImplementation &&\n typeof this.realImplementation[cmd.methodName] === \"function\"\n ) {\n try {\n if (this.debugMode) {\n logger.debug(\n `Executing queued command: ${cmd.methodName}`,\n cmd.args,\n );\n }\n\n const fn = this.realImplementation[cmd.methodName] as (\n ...args: unknown[]\n ) => unknown;\n\n // Execute the command with the real implementation\n fn.apply(this.realImplementation, cmd.args as unknown[]);\n } catch (e) {\n if (this.debugMode) {\n logger.error(\n `Error executing queued command: ${cmd.methodName}`,\n e,\n );\n }\n }\n }\n }\n }\n\n // Implement all methods required by AnalyticsService to satisfy TypeScript\n // (These won't be called directly due to the proxy)\n initInsights(_config: InsightsConfig): void {}\n enableDebugMode(): void {}\n disableDebugMode(): void {}\n identify(_identity: InsightsIdentity): void {}\n trackPageView(_options?: TrackPageViewOptions): void {}\n track(_event: string, _properties?: Record<string, unknown>): void {}\n setupObserver(): () => void {\n return () => {};\n }\n}\n"],"names":["InsightsService","logger","InsightsCommandQueue","executeInitInsights","config","debugMode","debug","realImplementation","initInsights","isInitialized","processQueue","queue","length","cmd","shift","methodName","args","fn","apply","e","error","_config","enableDebugMode","disableDebugMode","identify","_identity","trackPageView","_options","track","_event","_properties","setupObserver","Proxy","get","target","prop","String","push"],"mappings":"oLAOA,OAASA,eAAe,KAAQ,WAAY,AAC5C,WAAYC,WAAY,UAAW,AAGnC,QAAO,MAAMC,qBAkDX,AAAQC,oBAAoBC,MAAsB,CAAQ,CACxD,IAAI,CAACC,SAAS,CAAG,CAAC,CAACD,OAAOE,KAAK,CAE/B,GAAI,IAAI,CAACD,SAAS,CAAE,CAClBJ,OAAOK,KAAK,CAAC,wBACf,CAGA,IAAI,CAACC,kBAAkB,CAAG,IAAIP,gBAC9B,IAAI,CAACO,kBAAkB,CAACC,YAAY,CAACJ,OAGrC,CAAA,IAAI,CAACK,aAAa,CAAG,KACrB,IAAI,CAACC,YAAY,EACnB,CAGA,AAAQA,cAAqB,CAC3B,GAAI,IAAI,CAACL,SAAS,CAAE,CAClBJ,OAAOK,KAAK,CAAC,CAAC,WAAW,EAAE,IAAI,CAACK,KAAK,CAACC,MAAM,CAAC,gBAAgB,CAAC,CAChE,CAEA,MAAO,IAAI,CAACD,KAAK,CAACC,MAAM,CAAG,EAAG,CAC5B,MAAMC,IAAM,IAAI,CAACF,KAAK,CAACG,KAAK,GAC5B,GACED,KACA,IAAI,CAACN,kBAAkB,EACvB,OAAO,IAAI,CAACA,kBAAkB,CAACM,IAAIE,UAAU,CAAC,GAAK,WACnD,CACA,GAAI,CACF,GAAI,IAAI,CAACV,SAAS,CAAE,CAClBJ,OAAOK,KAAK,CACV,CAAC,0BAA0B,EAAEO,IAAIE,UAAU,CAAC,CAAC,CAC7CF,IAAIG,IAAI,CAEZ,CAEA,MAAMC,GAAK,IAAI,CAACV,kBAAkB,CAACM,IAAIE,UAAU,CAAC,CAKlDE,GAAGC,KAAK,CAAC,IAAI,CAACX,kBAAkB,CAAEM,IAAIG,IAAI,CAC5C,CAAE,MAAOG,EAAG,CACV,GAAI,IAAI,CAACd,SAAS,CAAE,CAClBJ,OAAOmB,KAAK,CACV,CAAC,gCAAgC,EAAEP,IAAIE,UAAU,CAAC,CAAC,CACnDI,EAEJ,CACF,CACF,CACF,CACF,CAIAX,aAAaa,OAAuB,CAAQ,CAAC,CAC7CC,iBAAwB,CAAC,CACzBC,kBAAyB,CAAC,CAC1BC,SAASC,SAA2B,CAAQ,CAAC,CAC7CC,cAAcC,QAA+B,CAAQ,CAAC,CACtDC,MAAMC,MAAc,CAAEC,WAAqC,CAAQ,CAAC,CACpEC,eAA4B,CAC1B,MAAO,KAAO,CAChB,CA7GA,aAAc,CALd,sBAAQtB,gBAAyB,OACjC,sBAAQE,QAAmB,EAAE,EAC7B,sBAAQN,YAAqB,OAC7B,sBAAQE,qBAA6C,MAInD,OAAO,IAAIyB,MAAM,IAAI,CAAE,CACrBC,IAAK,CAACC,OAA8BC,QAElC,GAAIA,QAAQD,QAAU,OAAOA,MAAM,CAACC,KAAK,GAAK,WAAY,CACxD,OAAOD,MAAM,CAACC,KAAK,AACrB,CAGA,MAAO,CAAC,GAAGnB,QACT,GAAI,CAACkB,OAAOzB,aAAa,EAAI,CAACyB,OAAO3B,kBAAkB,CAAE,CAEvD,GAAI4B,OAAS,eAAgB,CAE3BD,OAAO/B,mBAAmB,CAACa,IAAI,CAAC,EAAE,EAClC,MACF,CAGA,GAAIkB,OAAO7B,SAAS,CAAE,CACpBJ,OAAOK,KAAK,CAAC,CAAC,qBAAqB,EAAE8B,OAAOD,MAAM,CAAC,CAAEnB,KACvD,CAEAkB,OAAOvB,KAAK,CAAC0B,IAAI,CAAC,CAChBtB,WAAYoB,KACZnB,IACF,GAEA,MACF,CAGA,GAAI,OAAOkB,OAAO3B,kBAAkB,CAAC4B,KAAK,GAAK,WAAY,CACzD,OAAO,AACLD,OAAO3B,kBAAkB,CAAC4B,KAAK,IAC5BnB,KACP,CACF,CACF,CACF,EACF,CAqEF"}
|
package/core/insights/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{InsightsCommandQueue}from"./command-queue";const insights=new InsightsCommandQueue;export const initInsights=config=>insights.initInsights(config);export const enableDebugMode=()=>insights.enableDebugMode();export const disableDebugMode=()=>insights.disableDebugMode();export const identify=identity=>insights.identify(identity);export const trackPageView=options=>insights.trackPageView(options);export const track=(event,properties)=>insights.track(event,properties);export const
|
|
1
|
+
import{InsightsCommandQueue}from"./command-queue";const insights=new InsightsCommandQueue;export const initInsights=config=>insights.initInsights(config);export const enableDebugMode=()=>insights.enableDebugMode();export const disableDebugMode=()=>insights.disableDebugMode();export const identify=identity=>insights.identify(identity);export const trackPageView=options=>insights.trackPageView(options);export const track=(event,properties)=>insights.track(event,properties);export const setupObserver=()=>insights.setupObserver();
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/core/insights/index.ts"],"sourcesContent":["import {\n InsightsConfig,\n InsightsIdentity,\n TrackPageViewOptions,\n} from \"./types\";\nimport { InsightsCommandQueue } from \"./command-queue\";\nexport type { InsightsConfig };\n\n// Hi and welcome 👋\n//\n// The insights code is written using a Command Queue, or Deferred Execution pattern.\n// This pattern is useful when you need to queue up actions that should wait until\n// some initialization is complete. In this case, we want to queue up all the analytics\n// commands until the analytics service is initialized. This way, we can ensure that\n// no analytics events are lost during the initialization process. It looks wildly\n// different than other parts of Ably UI, but if you squint you realise it looks very\n// much like the services it's wrapping.\n//\n// There are three pieces working together here:\n// - The `AnalyticsService` interface, which defines the public methods that the insights\n// service will expose.\n// - The `InsightsCommandQueue` class, which is the main entry point for the insights\n// service. It acts as a proxy that will either queue up commands or execute\n// them directly on the real implementation.\n// - The `InsightsService` class, which is the real implementation that will be used\n// after initialization. It's responsible for initializing the underlying analytics\n// services (Mixpanel, Posthog & the data layer) and executing the queued commands.\n\n// Create the singleton instance with the command queue pattern\nconst insights = new InsightsCommandQueue();\n\n// Export the methods with the same interface as before\nexport const initInsights = (config: InsightsConfig) =>\n insights.initInsights(config);\nexport const enableDebugMode = () => insights.enableDebugMode();\nexport const disableDebugMode = () => insights.disableDebugMode();\nexport const identify = (identity: InsightsIdentity) =>\n insights.identify(identity);\nexport const trackPageView = (options?: TrackPageViewOptions) =>\n insights.trackPageView(options);\nexport const track = (event: string, properties?: Record<string, unknown>) =>\n insights.track(event, properties);\nexport const
|
|
1
|
+
{"version":3,"sources":["../../../src/core/insights/index.ts"],"sourcesContent":["import {\n InsightsConfig,\n InsightsIdentity,\n TrackPageViewOptions,\n} from \"./types\";\nimport { InsightsCommandQueue } from \"./command-queue\";\nexport type { InsightsConfig };\n\n// Hi and welcome 👋\n//\n// The insights code is written using a Command Queue, or Deferred Execution pattern.\n// This pattern is useful when you need to queue up actions that should wait until\n// some initialization is complete. In this case, we want to queue up all the analytics\n// commands until the analytics service is initialized. This way, we can ensure that\n// no analytics events are lost during the initialization process. It looks wildly\n// different than other parts of Ably UI, but if you squint you realise it looks very\n// much like the services it's wrapping.\n//\n// There are three pieces working together here:\n// - The `AnalyticsService` interface, which defines the public methods that the insights\n// service will expose.\n// - The `InsightsCommandQueue` class, which is the main entry point for the insights\n// service. It acts as a proxy that will either queue up commands or execute\n// them directly on the real implementation.\n// - The `InsightsService` class, which is the real implementation that will be used\n// after initialization. It's responsible for initializing the underlying analytics\n// services (Mixpanel, Posthog & the data layer) and executing the queued commands.\n\n// Create the singleton instance with the command queue pattern\nconst insights = new InsightsCommandQueue();\n\n// Export the methods with the same interface as before\nexport const initInsights = (config: InsightsConfig) =>\n insights.initInsights(config);\nexport const enableDebugMode = () => insights.enableDebugMode();\nexport const disableDebugMode = () => insights.disableDebugMode();\nexport const identify = (identity: InsightsIdentity) =>\n insights.identify(identity);\nexport const trackPageView = (options?: TrackPageViewOptions) =>\n insights.trackPageView(options);\nexport const track = (event: string, properties?: Record<string, unknown>) =>\n insights.track(event, properties);\nexport const setupObserver = () => insights.setupObserver();\n"],"names":["InsightsCommandQueue","insights","initInsights","config","enableDebugMode","disableDebugMode","identify","identity","trackPageView","options","track","event","properties","setupObserver"],"mappings":"AAKA,OAASA,oBAAoB,KAAQ,iBAAkB,CAwBvD,MAAMC,SAAW,IAAID,oBAGrB,QAAO,MAAME,aAAe,AAACC,QAC3BF,SAASC,YAAY,CAACC,OAAQ,AAChC,QAAO,MAAMC,gBAAkB,IAAMH,SAASG,eAAe,EAAG,AAChE,QAAO,MAAMC,iBAAmB,IAAMJ,SAASI,gBAAgB,EAAG,AAClE,QAAO,MAAMC,SAAW,AAACC,UACvBN,SAASK,QAAQ,CAACC,SAAU,AAC9B,QAAO,MAAMC,cAAgB,AAACC,SAC5BR,SAASO,aAAa,CAACC,QAAS,AAClC,QAAO,MAAMC,MAAQ,CAACC,MAAeC,aACnCX,SAASS,KAAK,CAACC,MAAOC,WAAY,AACpC,QAAO,MAAMC,cAAgB,IAAMZ,SAASY,aAAa,EAAG"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{describe,expect,beforeEach,afterEach,it,vi}from"vitest";import*as datalayer from"./datalayer";import*as mixpanel from"./mixpanel";import*as posthog from"./posthog";import*as logger from"./logger";import*as insights from"./index";vi.mock("./datalayer",()=>({track:vi.fn(),trackPageView:vi.fn()}));vi.mock("./mixpanel",()=>({initMixpanel:vi.fn(),enableDebugMode:vi.fn(),disableDebugMode:vi.fn(),identify:vi.fn(),trackPageView:vi.fn(),track:vi.fn(),startSessionRecording:vi.fn(),stopSessionRecording:vi.fn()}));vi.mock("./posthog",()=>({initPosthog:vi.fn(),enableDebugMode:vi.fn(),disableDebugMode:vi.fn(),identify:vi.fn(),trackPageView:vi.fn(),track:vi.fn(),startSessionRecording:vi.fn(),stopSessionRecording:vi.fn()}));vi.mock("./logger",()=>({debug:vi.fn(),info:vi.fn(),warn:vi.fn(),error:vi.fn()}));describe("Insights Command Queue",()=>{const testConfig={debug:true,mixpanelToken:"test-token",mixpanelAutoCapture:false,mixpanelRecordSessionsPercent:10,posthogApiKey:"test-key",posthogApiHost:"test-host"};const testIdentity={userId:"user-123",accountId:"account-456",organisationId:"org-789",email:"test@example.com",name:"Test User"};beforeEach(()=>{vi.clearAllMocks();vi.resetModules()});afterEach(()=>{document.body.replaceWith(document.body.cloneNode(true))});describe("Pre-initialization Queueing",()=>{it("should queue methods called before initialization",async()=>{insights.track("early_event",{early:true});insights.identify(testIdentity);insights.trackPageView();expect(mixpanel.track).not.toHaveBeenCalled();expect(posthog.track).not.toHaveBeenCalled();expect(datalayer.track).not.toHaveBeenCalled();expect(mixpanel.identify).not.toHaveBeenCalled();expect(posthog.identify).not.toHaveBeenCalled();expect(mixpanel.trackPageView).not.toHaveBeenCalled();expect(posthog.trackPageView).not.toHaveBeenCalled();expect(datalayer.trackPageView).not.toHaveBeenCalled();insights.initInsights(testConfig);expect(mixpanel.initMixpanel).toHaveBeenCalledWith(testConfig.mixpanelToken,testConfig.mixpanelAutoCapture,testConfig.debug,testConfig.mixpanelRecordSessionsPercent);expect(posthog.initPosthog).toHaveBeenCalledWith(testConfig.posthogApiKey,testConfig.posthogApiHost);expect(mixpanel.track).toHaveBeenCalledWith("early_event",{early:true});expect(posthog.track).toHaveBeenCalledWith("early_event",{early:true});expect(datalayer.track).toHaveBeenCalledWith("early_event",{early:true});expect(mixpanel.identify).toHaveBeenCalledWith(testIdentity);expect(posthog.identify).toHaveBeenCalledWith(testIdentity);expect(mixpanel.trackPageView).toHaveBeenCalled();expect(posthog.trackPageView).toHaveBeenCalled();expect(datalayer.trackPageView).not.toHaveBeenCalled()});it("should handle errors in queued methods gracefully",async()=>{mixpanel.track.mockImplementationOnce(()=>{throw new Error("Mixpanel error")});insights.track("error_event",{error:true});insights.trackPageView();insights.initInsights(testConfig);expect(logger.error).toHaveBeenCalledWith(expect.stringContaining("Failed to track event in Mixpanel"),expect.any(Error));expect(posthog.track).toHaveBeenCalledWith("error_event",{error:true});expect(datalayer.track).toHaveBeenCalledWith("error_event",{error:true});expect(mixpanel.trackPageView).toHaveBeenCalled();expect(posthog.trackPageView).toHaveBeenCalled();expect(datalayer.trackPageView).not.toHaveBeenCalled()});it("should report page view to GTM as well when includeDataLayer is true",()=>{insights.trackPageView({includeDataLayer:true});expect(mixpanel.trackPageView).toHaveBeenCalled();expect(posthog.trackPageView).toHaveBeenCalled();expect(datalayer.trackPageView).toHaveBeenCalled()})});describe("Post-initialization Direct Execution",()=>{beforeEach(()=>{insights.initInsights(testConfig);vi.clearAllMocks()});it("should directly call methods after initialization",()=>{insights.track("post_init_event",{post:true});expect(mixpanel.track).toHaveBeenCalledWith("post_init_event",{post:true});expect(posthog.track).toHaveBeenCalledWith("post_init_event",{post:true});expect(datalayer.track).toHaveBeenCalledWith("post_init_event",{post:true})});it("should handle all exported methods correctly",()=>{insights.identify(testIdentity);expect(mixpanel.identify).toHaveBeenCalledWith(testIdentity);expect(posthog.identify).toHaveBeenCalledWith(testIdentity);insights.trackPageView();expect(mixpanel.trackPageView).toHaveBeenCalled();expect(posthog.trackPageView).toHaveBeenCalled();expect(datalayer.trackPageView).not.toHaveBeenCalled();insights.startSessionRecording();expect(mixpanel.startSessionRecording).toHaveBeenCalled();expect(posthog.startSessionRecording).toHaveBeenCalled();insights.stopSessionRecording();expect(mixpanel.stopSessionRecording).toHaveBeenCalled();expect(posthog.stopSessionRecording).toHaveBeenCalled();insights.enableDebugMode();expect(mixpanel.enableDebugMode).toHaveBeenCalled();expect(posthog.enableDebugMode).toHaveBeenCalled();insights.disableDebugMode();expect(mixpanel.disableDebugMode).toHaveBeenCalled();expect(posthog.disableDebugMode).toHaveBeenCalled()});it("should report page view to GTM as well when includeDataLayer is true",()=>{insights.trackPageView({includeDataLayer:true});expect(mixpanel.trackPageView).toHaveBeenCalled();expect(posthog.trackPageView).toHaveBeenCalled();expect(datalayer.trackPageView).toHaveBeenCalled()})});describe("Observer Setup",()=>{beforeEach(()=>{insights.initInsights(testConfig);vi.clearAllMocks()});it("should set up click event observer and track clicks",()=>{const cleanup=insights.setupObserver();const testElement=document.createElement("button");testElement.setAttribute("data-insight-event","button_clicked");testElement.setAttribute("data-insight-button-id","test-123");document.body.appendChild(testElement);testElement.click();expect(mixpanel.track).toHaveBeenCalledWith("button_clicked",{buttonId:"test-123"});expect(posthog.track).toHaveBeenCalledWith("button_clicked",{buttonId:"test-123"});expect(datalayer.track).toHaveBeenCalledWith("button_clicked",{buttonId:"test-123"});cleanup();vi.clearAllMocks();testElement.click();expect(mixpanel.track).not.toHaveBeenCalled()});it("should handle nested elements correctly",()=>{insights.setupObserver();const parentElement=document.createElement("div");parentElement.setAttribute("data-insight-event","container_clicked");parentElement.setAttribute("data-insight-container-id","parent-container");const childElement=document.createElement("span");childElement.textContent="Click me";parentElement.appendChild(childElement);document.body.appendChild(parentElement);childElement.click();expect(mixpanel.track).toHaveBeenCalledWith("container_clicked",{containerId:"parent-container"})})});describe("Error Handling",()=>{it("should handle initialization errors gracefully",()=>{mixpanel.initMixpanel.mockImplementationOnce(()=>{throw new Error("Mixpanel init error")});expect(()=>{insights.initInsights(testConfig)}).not.toThrow();expect(logger.error).toHaveBeenCalledWith(expect.stringContaining("Failed to initialize Mixpanel"),expect.any(Error))});it("should handle runtime errors in methods",()=>{insights.initInsights(testConfig);vi.clearAllMocks();mixpanel.track.mockImplementationOnce(()=>{throw new Error("Mixpanel track error")});posthog.track.mockImplementationOnce(()=>{throw new Error("Posthog track error")});expect(()=>{insights.track("error_test",{test:true})}).not.toThrow();expect(logger.error).toHaveBeenCalledWith(expect.stringContaining("Failed to track event in Mixpanel"),expect.any(Error));expect(logger.error).toHaveBeenCalledWith(expect.stringContaining("Failed to track event in Posthog"),expect.any(Error));expect(datalayer.track).toHaveBeenCalledWith("error_test",{test:true})})});describe("Debug Mode",()=>{it("should respect debug flag in config",()=>{insights.initInsights(testConfig);expect(logger.debug).toHaveBeenCalledWith(expect.stringContaining("Initializing insights"));vi.clearAllMocks();insights.track("debug_test",{debug:true});expect(logger.info).toHaveBeenCalledWith(expect.stringContaining("Tracking event"),expect.objectContaining({event:"debug_test",properties:{debug:true}}))});it("should not log debug info when debug is false",()=>{insights.initInsights({...testConfig,debug:false});vi.clearAllMocks();insights.track("no_debug_test",{debug:false});expect(logger.info).not.toHaveBeenCalled()})});describe("Arbitrary Properties",()=>{const customPageViewProperties={customProperty:"custom-value",anotherProperty:123,nestedObject:{foo:"bar"}};const customPageViewPropertiesWithDataLayer={customProperty:"custom-value",anotherProperty:456};const customPageViewPropertiesWithExcludeIds={customProperty:"test",count:789};const customIdentityProperties={customProperty:"user-custom-value",userSegment:"premium",signupDate:"2024-01-01"};const customMinimalIdentityProperties={customField1:"value1",customField2:999,customField3:{nested:true}};beforeEach(()=>{insights.initInsights(testConfig);vi.clearAllMocks()});it("should pass arbitrary properties through trackPageView",()=>{insights.trackPageView(customPageViewProperties);expect(mixpanel.trackPageView).toHaveBeenCalledWith(customPageViewProperties);expect(posthog.trackPageView).toHaveBeenCalledWith(customPageViewProperties);expect(datalayer.trackPageView).not.toHaveBeenCalled()});it("should pass arbitrary properties through trackPageView with includeDataLayer",()=>{insights.trackPageView({includeDataLayer:true,...customPageViewPropertiesWithDataLayer});expect(mixpanel.trackPageView).toHaveBeenCalledWith(customPageViewPropertiesWithDataLayer);expect(posthog.trackPageView).toHaveBeenCalledWith(customPageViewPropertiesWithDataLayer);expect(datalayer.trackPageView).toHaveBeenCalledWith(customPageViewPropertiesWithDataLayer)});it("should pass arbitrary properties through trackPageView with excludeIds",()=>{const excludeIds=["id1","id2"];insights.trackPageView({excludeIds,...customPageViewPropertiesWithExcludeIds});expect(mixpanel.trackPageView).toHaveBeenCalledWith({excludeIds,...customPageViewPropertiesWithExcludeIds});expect(posthog.trackPageView).toHaveBeenCalledWith(customPageViewPropertiesWithExcludeIds)});it("should pass arbitrary properties through identify",()=>{const identityWithCustomProps={...testIdentity,...customIdentityProperties};insights.identify(identityWithCustomProps);expect(mixpanel.identify).toHaveBeenCalledWith(identityWithCustomProps);expect(posthog.identify).toHaveBeenCalledWith(identityWithCustomProps)});it("should pass arbitrary properties through identify with minimal identity",()=>{const minimalIdentity={userId:"minimal-user",accountId:"minimal-account",...customMinimalIdentityProperties};insights.identify(minimalIdentity);expect(mixpanel.identify).toHaveBeenCalledWith(minimalIdentity);expect(posthog.identify).toHaveBeenCalledWith(minimalIdentity)})})});
|
|
1
|
+
import{describe,expect,beforeEach,afterEach,it,vi}from"vitest";import*as datalayer from"./datalayer";import*as mixpanel from"./mixpanel";import*as posthog from"./posthog";import*as logger from"./logger";import*as insights from"./index";vi.mock("./datalayer",()=>({track:vi.fn(),trackPageView:vi.fn()}));vi.mock("./mixpanel",()=>({initMixpanel:vi.fn(),enableDebugMode:vi.fn(),disableDebugMode:vi.fn(),identify:vi.fn(),trackPageView:vi.fn(),track:vi.fn()}));vi.mock("./posthog",()=>({initPosthog:vi.fn(),enableDebugMode:vi.fn(),disableDebugMode:vi.fn(),identify:vi.fn(),trackPageView:vi.fn(),track:vi.fn()}));vi.mock("./logger",()=>({debug:vi.fn(),info:vi.fn(),warn:vi.fn(),error:vi.fn()}));describe("Insights Command Queue",()=>{const testConfig={debug:true,mixpanelToken:"test-token",mixpanelAutoCapture:false,posthogApiKey:"test-key",posthogApiHost:"test-host"};const testIdentity={userId:"user-123",accountId:"account-456",organisationId:"org-789",email:"test@example.com",name:"Test User"};beforeEach(()=>{vi.clearAllMocks();vi.resetModules()});afterEach(()=>{document.body.replaceWith(document.body.cloneNode(true))});describe("Pre-initialization Queueing",()=>{it("should queue methods called before initialization",async()=>{insights.track("early_event",{early:true});insights.identify(testIdentity);insights.trackPageView();expect(mixpanel.track).not.toHaveBeenCalled();expect(posthog.track).not.toHaveBeenCalled();expect(datalayer.track).not.toHaveBeenCalled();expect(mixpanel.identify).not.toHaveBeenCalled();expect(posthog.identify).not.toHaveBeenCalled();expect(mixpanel.trackPageView).not.toHaveBeenCalled();expect(posthog.trackPageView).not.toHaveBeenCalled();expect(datalayer.trackPageView).not.toHaveBeenCalled();insights.initInsights(testConfig);expect(mixpanel.initMixpanel).toHaveBeenCalledWith(testConfig.mixpanelToken,testConfig.mixpanelAutoCapture,testConfig.debug,undefined);expect(posthog.initPosthog).toHaveBeenCalledWith(testConfig.posthogApiKey,testConfig.posthogApiHost);expect(mixpanel.track).toHaveBeenCalledWith("early_event",{early:true});expect(posthog.track).toHaveBeenCalledWith("early_event",{early:true});expect(datalayer.track).toHaveBeenCalledWith("early_event",{early:true});expect(mixpanel.identify).toHaveBeenCalledWith(testIdentity);expect(posthog.identify).toHaveBeenCalledWith(testIdentity);expect(mixpanel.trackPageView).toHaveBeenCalled();expect(posthog.trackPageView).toHaveBeenCalled();expect(datalayer.trackPageView).not.toHaveBeenCalled()});it("should handle errors in queued methods gracefully",async()=>{mixpanel.track.mockImplementationOnce(()=>{throw new Error("Mixpanel error")});insights.track("error_event",{error:true});insights.trackPageView();insights.initInsights(testConfig);expect(logger.error).toHaveBeenCalledWith(expect.stringContaining("Failed to track event in Mixpanel"),expect.any(Error));expect(posthog.track).toHaveBeenCalledWith("error_event",{error:true});expect(datalayer.track).toHaveBeenCalledWith("error_event",{error:true});expect(mixpanel.trackPageView).toHaveBeenCalled();expect(posthog.trackPageView).toHaveBeenCalled();expect(datalayer.trackPageView).not.toHaveBeenCalled()});it("should report page view to GTM as well when includeDataLayer is true",()=>{insights.trackPageView({includeDataLayer:true});expect(mixpanel.trackPageView).toHaveBeenCalled();expect(posthog.trackPageView).toHaveBeenCalled();expect(datalayer.trackPageView).toHaveBeenCalled()})});describe("Post-initialization Direct Execution",()=>{beforeEach(()=>{insights.initInsights(testConfig);vi.clearAllMocks()});it("should directly call methods after initialization",()=>{insights.track("post_init_event",{post:true});expect(mixpanel.track).toHaveBeenCalledWith("post_init_event",{post:true});expect(posthog.track).toHaveBeenCalledWith("post_init_event",{post:true});expect(datalayer.track).toHaveBeenCalledWith("post_init_event",{post:true})});it("should handle all exported methods correctly",()=>{insights.identify(testIdentity);expect(mixpanel.identify).toHaveBeenCalledWith(testIdentity);expect(posthog.identify).toHaveBeenCalledWith(testIdentity);insights.trackPageView();expect(mixpanel.trackPageView).toHaveBeenCalled();expect(posthog.trackPageView).toHaveBeenCalled();expect(datalayer.trackPageView).not.toHaveBeenCalled();insights.enableDebugMode();expect(mixpanel.enableDebugMode).toHaveBeenCalled();expect(posthog.enableDebugMode).toHaveBeenCalled();insights.disableDebugMode();expect(mixpanel.disableDebugMode).toHaveBeenCalled();expect(posthog.disableDebugMode).toHaveBeenCalled()});it("should report page view to GTM as well when includeDataLayer is true",()=>{insights.trackPageView({includeDataLayer:true});expect(mixpanel.trackPageView).toHaveBeenCalled();expect(posthog.trackPageView).toHaveBeenCalled();expect(datalayer.trackPageView).toHaveBeenCalled()})});describe("Observer Setup",()=>{beforeEach(()=>{insights.initInsights(testConfig);vi.clearAllMocks()});it("should set up click event observer and track clicks",()=>{const cleanup=insights.setupObserver();const testElement=document.createElement("button");testElement.setAttribute("data-insight-event","button_clicked");testElement.setAttribute("data-insight-button-id","test-123");document.body.appendChild(testElement);testElement.click();expect(mixpanel.track).toHaveBeenCalledWith("button_clicked",{buttonId:"test-123"});expect(posthog.track).toHaveBeenCalledWith("button_clicked",{buttonId:"test-123"});expect(datalayer.track).toHaveBeenCalledWith("button_clicked",{buttonId:"test-123"});cleanup();vi.clearAllMocks();testElement.click();expect(mixpanel.track).not.toHaveBeenCalled()});it("should handle nested elements correctly",()=>{insights.setupObserver();const parentElement=document.createElement("div");parentElement.setAttribute("data-insight-event","container_clicked");parentElement.setAttribute("data-insight-container-id","parent-container");const childElement=document.createElement("span");childElement.textContent="Click me";parentElement.appendChild(childElement);document.body.appendChild(parentElement);childElement.click();expect(mixpanel.track).toHaveBeenCalledWith("container_clicked",{containerId:"parent-container"})})});describe("Error Handling",()=>{it("should handle initialization errors gracefully",()=>{mixpanel.initMixpanel.mockImplementationOnce(()=>{throw new Error("Mixpanel init error")});expect(()=>{insights.initInsights(testConfig)}).not.toThrow();expect(logger.error).toHaveBeenCalledWith(expect.stringContaining("Failed to initialize Mixpanel"),expect.any(Error))});it("should handle runtime errors in methods",()=>{insights.initInsights(testConfig);vi.clearAllMocks();mixpanel.track.mockImplementationOnce(()=>{throw new Error("Mixpanel track error")});posthog.track.mockImplementationOnce(()=>{throw new Error("Posthog track error")});expect(()=>{insights.track("error_test",{test:true})}).not.toThrow();expect(logger.error).toHaveBeenCalledWith(expect.stringContaining("Failed to track event in Mixpanel"),expect.any(Error));expect(logger.error).toHaveBeenCalledWith(expect.stringContaining("Failed to track event in Posthog"),expect.any(Error));expect(datalayer.track).toHaveBeenCalledWith("error_test",{test:true})})});describe("Debug Mode",()=>{it("should respect debug flag in config",()=>{insights.initInsights(testConfig);expect(logger.debug).toHaveBeenCalledWith(expect.stringContaining("Initializing insights"));vi.clearAllMocks();insights.track("debug_test",{debug:true});expect(logger.info).toHaveBeenCalledWith(expect.stringContaining("Tracking event"),expect.objectContaining({event:"debug_test",properties:{debug:true}}))});it("should not log debug info when debug is false",()=>{insights.initInsights({...testConfig,debug:false});vi.clearAllMocks();insights.track("no_debug_test",{debug:false});expect(logger.info).not.toHaveBeenCalled()})});describe("Arbitrary Properties",()=>{const customPageViewProperties={customProperty:"custom-value",anotherProperty:123,nestedObject:{foo:"bar"}};const customPageViewPropertiesWithDataLayer={customProperty:"custom-value",anotherProperty:456};const customPageViewPropertiesWithExcludeIds={customProperty:"test",count:789};const customIdentityProperties={customProperty:"user-custom-value",userSegment:"premium",signupDate:"2024-01-01"};const customMinimalIdentityProperties={customField1:"value1",customField2:999,customField3:{nested:true}};beforeEach(()=>{insights.initInsights(testConfig);vi.clearAllMocks()});it("should pass arbitrary properties through trackPageView",()=>{insights.trackPageView(customPageViewProperties);expect(mixpanel.trackPageView).toHaveBeenCalledWith(customPageViewProperties);expect(posthog.trackPageView).toHaveBeenCalledWith(customPageViewProperties);expect(datalayer.trackPageView).not.toHaveBeenCalled()});it("should pass arbitrary properties through trackPageView with includeDataLayer",()=>{insights.trackPageView({includeDataLayer:true,...customPageViewPropertiesWithDataLayer});expect(mixpanel.trackPageView).toHaveBeenCalledWith(customPageViewPropertiesWithDataLayer);expect(posthog.trackPageView).toHaveBeenCalledWith(customPageViewPropertiesWithDataLayer);expect(datalayer.trackPageView).toHaveBeenCalledWith(customPageViewPropertiesWithDataLayer)});it("should pass arbitrary properties through trackPageView with excludeIds",()=>{const excludeIds=["id1","id2"];insights.trackPageView({excludeIds,...customPageViewPropertiesWithExcludeIds});expect(mixpanel.trackPageView).toHaveBeenCalledWith({excludeIds,...customPageViewPropertiesWithExcludeIds});expect(posthog.trackPageView).toHaveBeenCalledWith(customPageViewPropertiesWithExcludeIds)});it("should pass arbitrary properties through identify",()=>{const identityWithCustomProps={...testIdentity,...customIdentityProperties};insights.identify(identityWithCustomProps);expect(mixpanel.identify).toHaveBeenCalledWith(identityWithCustomProps);expect(posthog.identify).toHaveBeenCalledWith(identityWithCustomProps)});it("should pass arbitrary properties through identify with minimal identity",()=>{const minimalIdentity={userId:"minimal-user",accountId:"minimal-account",...customMinimalIdentityProperties};insights.identify(minimalIdentity);expect(mixpanel.identify).toHaveBeenCalledWith(minimalIdentity);expect(posthog.identify).toHaveBeenCalledWith(minimalIdentity)})})});
|
|
2
2
|
//# sourceMappingURL=index.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/core/insights/index.test.ts"],"sourcesContent":["/**\n * @vitest-environment jsdom\n */\n\nimport { describe, expect, beforeEach, afterEach, it, vi, Mock } from \"vitest\";\n\nimport * as datalayer from \"./datalayer\";\nimport * as mixpanel from \"./mixpanel\";\nimport * as posthog from \"./posthog\";\nimport * as logger from \"./logger\";\nimport * as insights from \"./index\";\n\n// Mock the dependencies\nvi.mock(\"./datalayer\", () => ({\n track: vi.fn(),\n trackPageView: vi.fn(),\n}));\n\nvi.mock(\"./mixpanel\", () => ({\n initMixpanel: vi.fn(),\n enableDebugMode: vi.fn(),\n disableDebugMode: vi.fn(),\n identify: vi.fn(),\n trackPageView: vi.fn(),\n track: vi.fn(),\n startSessionRecording: vi.fn(),\n stopSessionRecording: vi.fn(),\n}));\n\nvi.mock(\"./posthog\", () => ({\n initPosthog: vi.fn(),\n enableDebugMode: vi.fn(),\n disableDebugMode: vi.fn(),\n identify: vi.fn(),\n trackPageView: vi.fn(),\n track: vi.fn(),\n startSessionRecording: vi.fn(),\n stopSessionRecording: vi.fn(),\n}));\n\nvi.mock(\"./logger\", () => ({\n debug: vi.fn(),\n info: vi.fn(),\n warn: vi.fn(),\n error: vi.fn(),\n}));\n\ndescribe(\"Insights Command Queue\", () => {\n const testConfig = {\n debug: true,\n mixpanelToken: \"test-token\",\n mixpanelAutoCapture: false,\n mixpanelRecordSessionsPercent: 10,\n posthogApiKey: \"test-key\",\n posthogApiHost: \"test-host\",\n };\n\n const testIdentity = {\n userId: \"user-123\",\n accountId: \"account-456\",\n organisationId: \"org-789\",\n email: \"test@example.com\",\n name: \"Test User\",\n };\n\n beforeEach(() => {\n // Clear all mocks before each test\n vi.clearAllMocks();\n\n // Reset the module to clear any internal state\n vi.resetModules();\n });\n\n afterEach(() => {\n // Cleanup document event listeners\n document.body.replaceWith(document.body.cloneNode(true));\n });\n\n describe(\"Pre-initialization Queueing\", () => {\n it(\"should queue methods called before initialization\", async () => {\n // Call methods before initialization\n insights.track(\"early_event\", { early: true });\n insights.identify(testIdentity);\n insights.trackPageView();\n\n // Verify nothing has been called yet on the underlying services\n expect(mixpanel.track).not.toHaveBeenCalled();\n expect(posthog.track).not.toHaveBeenCalled();\n expect(datalayer.track).not.toHaveBeenCalled();\n expect(mixpanel.identify).not.toHaveBeenCalled();\n expect(posthog.identify).not.toHaveBeenCalled();\n expect(mixpanel.trackPageView).not.toHaveBeenCalled();\n expect(posthog.trackPageView).not.toHaveBeenCalled();\n expect(datalayer.trackPageView).not.toHaveBeenCalled();\n\n // Now initialize\n insights.initInsights(testConfig);\n\n // Initialize should be called immediately\n expect(mixpanel.initMixpanel).toHaveBeenCalledWith(\n testConfig.mixpanelToken,\n testConfig.mixpanelAutoCapture,\n testConfig.debug,\n testConfig.mixpanelRecordSessionsPercent,\n );\n expect(posthog.initPosthog).toHaveBeenCalledWith(\n testConfig.posthogApiKey,\n testConfig.posthogApiHost,\n );\n\n // Queued methods should now be called in the correct order\n expect(mixpanel.track).toHaveBeenCalledWith(\"early_event\", {\n early: true,\n });\n expect(posthog.track).toHaveBeenCalledWith(\"early_event\", {\n early: true,\n });\n expect(datalayer.track).toHaveBeenCalledWith(\"early_event\", {\n early: true,\n });\n\n expect(mixpanel.identify).toHaveBeenCalledWith(testIdentity);\n expect(posthog.identify).toHaveBeenCalledWith(testIdentity);\n\n expect(mixpanel.trackPageView).toHaveBeenCalled();\n expect(posthog.trackPageView).toHaveBeenCalled();\n expect(datalayer.trackPageView).not.toHaveBeenCalled();\n });\n\n it(\"should handle errors in queued methods gracefully\", async () => {\n // Setup an error for one of the methods\n (mixpanel.track as Mock).mockImplementationOnce(() => {\n throw new Error(\"Mixpanel error\");\n });\n\n // Call methods before initialization\n insights.track(\"error_event\", { error: true });\n insights.trackPageView();\n\n // Now initialize\n insights.initInsights(testConfig);\n\n // Should have logged the error but continued processing the queue\n expect(logger.error).toHaveBeenCalledWith(\n expect.stringContaining(\"Failed to track event in Mixpanel\"),\n expect.any(Error),\n );\n\n // The other methods should still be called\n expect(posthog.track).toHaveBeenCalledWith(\"error_event\", {\n error: true,\n });\n expect(datalayer.track).toHaveBeenCalledWith(\"error_event\", {\n error: true,\n });\n expect(mixpanel.trackPageView).toHaveBeenCalled();\n expect(posthog.trackPageView).toHaveBeenCalled();\n expect(datalayer.trackPageView).not.toHaveBeenCalled();\n });\n\n it(\"should report page view to GTM as well when includeDataLayer is true\", () => {\n insights.trackPageView({ includeDataLayer: true });\n expect(mixpanel.trackPageView).toHaveBeenCalled();\n expect(posthog.trackPageView).toHaveBeenCalled();\n expect(datalayer.trackPageView).toHaveBeenCalled();\n });\n });\n\n describe(\"Post-initialization Direct Execution\", () => {\n beforeEach(() => {\n // Initialize first\n insights.initInsights(testConfig);\n // Clear the mocks to focus on post-init behavior\n vi.clearAllMocks();\n });\n\n it(\"should directly call methods after initialization\", () => {\n // Call methods after initialization\n insights.track(\"post_init_event\", { post: true });\n\n // Should be called immediately\n expect(mixpanel.track).toHaveBeenCalledWith(\"post_init_event\", {\n post: true,\n });\n expect(posthog.track).toHaveBeenCalledWith(\"post_init_event\", {\n post: true,\n });\n expect(datalayer.track).toHaveBeenCalledWith(\"post_init_event\", {\n post: true,\n });\n });\n\n it(\"should handle all exported methods correctly\", () => {\n // Test each exported method\n insights.identify(testIdentity);\n expect(mixpanel.identify).toHaveBeenCalledWith(testIdentity);\n expect(posthog.identify).toHaveBeenCalledWith(testIdentity);\n\n insights.trackPageView();\n expect(mixpanel.trackPageView).toHaveBeenCalled();\n expect(posthog.trackPageView).toHaveBeenCalled();\n expect(datalayer.trackPageView).not.toHaveBeenCalled();\n\n insights.startSessionRecording();\n expect(mixpanel.startSessionRecording).toHaveBeenCalled();\n expect(posthog.startSessionRecording).toHaveBeenCalled();\n\n insights.stopSessionRecording();\n expect(mixpanel.stopSessionRecording).toHaveBeenCalled();\n expect(posthog.stopSessionRecording).toHaveBeenCalled();\n\n insights.enableDebugMode();\n expect(mixpanel.enableDebugMode).toHaveBeenCalled();\n expect(posthog.enableDebugMode).toHaveBeenCalled();\n\n insights.disableDebugMode();\n expect(mixpanel.disableDebugMode).toHaveBeenCalled();\n expect(posthog.disableDebugMode).toHaveBeenCalled();\n });\n\n it(\"should report page view to GTM as well when includeDataLayer is true\", () => {\n insights.trackPageView({ includeDataLayer: true });\n expect(mixpanel.trackPageView).toHaveBeenCalled();\n expect(posthog.trackPageView).toHaveBeenCalled();\n expect(datalayer.trackPageView).toHaveBeenCalled();\n });\n });\n\n describe(\"Observer Setup\", () => {\n beforeEach(() => {\n insights.initInsights(testConfig);\n vi.clearAllMocks();\n });\n\n it(\"should set up click event observer and track clicks\", () => {\n // Setup observer\n const cleanup = insights.setupObserver();\n\n // Create a test element with insight attributes\n const testElement = document.createElement(\"button\");\n testElement.setAttribute(\"data-insight-event\", \"button_clicked\");\n testElement.setAttribute(\"data-insight-button-id\", \"test-123\");\n document.body.appendChild(testElement);\n\n // Simulate click\n testElement.click();\n\n // Should track the event\n expect(mixpanel.track).toHaveBeenCalledWith(\"button_clicked\", {\n buttonId: \"test-123\",\n });\n expect(posthog.track).toHaveBeenCalledWith(\"button_clicked\", {\n buttonId: \"test-123\",\n });\n expect(datalayer.track).toHaveBeenCalledWith(\"button_clicked\", {\n buttonId: \"test-123\",\n });\n\n // Test cleanup\n cleanup();\n\n // Reset tracking mocks\n vi.clearAllMocks();\n\n // Click again - should not track\n testElement.click();\n expect(mixpanel.track).not.toHaveBeenCalled();\n });\n\n it(\"should handle nested elements correctly\", () => {\n // Setup observer\n insights.setupObserver();\n\n // Create parent element with insight attributes\n const parentElement = document.createElement(\"div\");\n parentElement.setAttribute(\"data-insight-event\", \"container_clicked\");\n parentElement.setAttribute(\n \"data-insight-container-id\",\n \"parent-container\",\n );\n\n // Create child element without insights\n const childElement = document.createElement(\"span\");\n childElement.textContent = \"Click me\";\n\n // Nest elements\n parentElement.appendChild(childElement);\n document.body.appendChild(parentElement);\n\n // Click the child element\n childElement.click();\n\n // Should find and use the parent's insight attributes\n expect(mixpanel.track).toHaveBeenCalledWith(\"container_clicked\", {\n containerId: \"parent-container\",\n });\n });\n });\n\n describe(\"Error Handling\", () => {\n it(\"should handle initialization errors gracefully\", () => {\n // Setup an error in initialization\n (mixpanel.initMixpanel as Mock).mockImplementationOnce(() => {\n throw new Error(\"Mixpanel init error\");\n });\n\n // Should not throw when initializing\n expect(() => {\n insights.initInsights(testConfig);\n }).not.toThrow();\n\n // Should log the error\n expect(logger.error).toHaveBeenCalledWith(\n expect.stringContaining(\"Failed to initialize Mixpanel\"),\n expect.any(Error),\n );\n });\n\n it(\"should handle runtime errors in methods\", () => {\n // Initialize first\n insights.initInsights(testConfig);\n vi.clearAllMocks();\n\n // Setup errors in tracking\n (mixpanel.track as Mock).mockImplementationOnce(() => {\n throw new Error(\"Mixpanel track error\");\n });\n\n (posthog.track as Mock).mockImplementationOnce(() => {\n throw new Error(\"Posthog track error\");\n });\n\n // Should not throw when tracking\n expect(() => {\n insights.track(\"error_test\", { test: true });\n }).not.toThrow();\n\n // Should log the errors\n expect(logger.error).toHaveBeenCalledWith(\n expect.stringContaining(\"Failed to track event in Mixpanel\"),\n expect.any(Error),\n );\n\n expect(logger.error).toHaveBeenCalledWith(\n expect.stringContaining(\"Failed to track event in Posthog\"),\n expect.any(Error),\n );\n\n // Should still try to track with datalayer\n expect(datalayer.track).toHaveBeenCalledWith(\"error_test\", {\n test: true,\n });\n });\n });\n\n describe(\"Debug Mode\", () => {\n it(\"should respect debug flag in config\", () => {\n // Initialize with debug: true\n insights.initInsights(testConfig);\n\n // Should log debug messages\n expect(logger.debug).toHaveBeenCalledWith(\n expect.stringContaining(\"Initializing insights\"),\n );\n\n // Clear mocks and test tracking\n vi.clearAllMocks();\n insights.track(\"debug_test\", { debug: true });\n\n // Should log info about tracking\n expect(logger.info).toHaveBeenCalledWith(\n expect.stringContaining(\"Tracking event\"),\n expect.objectContaining({\n event: \"debug_test\",\n properties: { debug: true },\n }),\n );\n });\n\n it(\"should not log debug info when debug is false\", () => {\n // Initialize with debug: false\n insights.initInsights({\n ...testConfig,\n debug: false,\n });\n\n // Clear mocks and test tracking\n vi.clearAllMocks();\n insights.track(\"no_debug_test\", { debug: false });\n\n // Should not log info about tracking\n expect(logger.info).not.toHaveBeenCalled();\n });\n });\n\n describe(\"Arbitrary Properties\", () => {\n const customPageViewProperties = {\n customProperty: \"custom-value\",\n anotherProperty: 123,\n nestedObject: { foo: \"bar\" },\n };\n\n const customPageViewPropertiesWithDataLayer = {\n customProperty: \"custom-value\",\n anotherProperty: 456,\n };\n\n const customPageViewPropertiesWithExcludeIds = {\n customProperty: \"test\",\n count: 789,\n };\n\n const customIdentityProperties = {\n customProperty: \"user-custom-value\",\n userSegment: \"premium\",\n signupDate: \"2024-01-01\",\n };\n\n const customMinimalIdentityProperties = {\n customField1: \"value1\",\n customField2: 999,\n customField3: { nested: true },\n };\n\n beforeEach(() => {\n insights.initInsights(testConfig);\n vi.clearAllMocks();\n });\n\n it(\"should pass arbitrary properties through trackPageView\", () => {\n // Call trackPageView with custom properties\n insights.trackPageView(customPageViewProperties);\n\n // Verify mixpanel received all properties\n expect(mixpanel.trackPageView).toHaveBeenCalledWith(\n customPageViewProperties,\n );\n\n // Verify posthog received all properties\n expect(posthog.trackPageView).toHaveBeenCalledWith(\n customPageViewProperties,\n );\n\n // Verify datalayer did not receive anything (includeDataLayer not set)\n expect(datalayer.trackPageView).not.toHaveBeenCalled();\n });\n\n it(\"should pass arbitrary properties through trackPageView with includeDataLayer\", () => {\n // Call trackPageView with custom properties and includeDataLayer\n insights.trackPageView({\n includeDataLayer: true,\n ...customPageViewPropertiesWithDataLayer,\n });\n\n // Verify mixpanel received custom properties (includeDataLayer filtered out)\n expect(mixpanel.trackPageView).toHaveBeenCalledWith(\n customPageViewPropertiesWithDataLayer,\n );\n\n // Verify posthog received custom properties (includeDataLayer filtered out)\n expect(posthog.trackPageView).toHaveBeenCalledWith(\n customPageViewPropertiesWithDataLayer,\n );\n\n // Verify datalayer received custom properties (includeDataLayer filtered out)\n expect(datalayer.trackPageView).toHaveBeenCalledWith(\n customPageViewPropertiesWithDataLayer,\n );\n });\n\n it(\"should pass arbitrary properties through trackPageView with excludeIds\", () => {\n const excludeIds = [\"id1\", \"id2\"];\n\n // Call trackPageView with custom properties and excludeIds\n insights.trackPageView({\n excludeIds,\n ...customPageViewPropertiesWithExcludeIds,\n });\n\n // Verify mixpanel received excludeIds and custom properties\n expect(mixpanel.trackPageView).toHaveBeenCalledWith({\n excludeIds,\n ...customPageViewPropertiesWithExcludeIds,\n });\n\n // Verify posthog received only custom properties (excludeIds filtered out)\n expect(posthog.trackPageView).toHaveBeenCalledWith(\n customPageViewPropertiesWithExcludeIds,\n );\n });\n\n it(\"should pass arbitrary properties through identify\", () => {\n const identityWithCustomProps = {\n ...testIdentity,\n ...customIdentityProperties,\n };\n\n // Call identify with custom properties\n insights.identify(identityWithCustomProps);\n\n // Verify mixpanel received all properties\n expect(mixpanel.identify).toHaveBeenCalledWith(identityWithCustomProps);\n\n // Verify posthog received all properties\n expect(posthog.identify).toHaveBeenCalledWith(identityWithCustomProps);\n });\n\n it(\"should pass arbitrary properties through identify with minimal identity\", () => {\n const minimalIdentity = {\n userId: \"minimal-user\",\n accountId: \"minimal-account\",\n ...customMinimalIdentityProperties,\n };\n\n // Call identify with only required fields plus custom properties\n insights.identify(minimalIdentity);\n\n // Verify mixpanel received all properties\n expect(mixpanel.identify).toHaveBeenCalledWith(minimalIdentity);\n\n // Verify posthog received all properties\n expect(posthog.identify).toHaveBeenCalledWith(minimalIdentity);\n });\n });\n});\n"],"names":["describe","expect","beforeEach","afterEach","it","vi","datalayer","mixpanel","posthog","logger","insights","mock","track","fn","trackPageView","initMixpanel","enableDebugMode","disableDebugMode","identify","startSessionRecording","stopSessionRecording","initPosthog","debug","info","warn","error","testConfig","mixpanelToken","mixpanelAutoCapture","mixpanelRecordSessionsPercent","posthogApiKey","posthogApiHost","testIdentity","userId","accountId","organisationId","email","name","clearAllMocks","resetModules","document","body","replaceWith","cloneNode","early","not","toHaveBeenCalled","initInsights","toHaveBeenCalledWith","mockImplementationOnce","Error","stringContaining","any","includeDataLayer","post","cleanup","setupObserver","testElement","createElement","setAttribute","appendChild","click","buttonId","parentElement","childElement","textContent","containerId","toThrow","test","objectContaining","event","properties","customPageViewProperties","customProperty","anotherProperty","nestedObject","foo","customPageViewPropertiesWithDataLayer","customPageViewPropertiesWithExcludeIds","count","customIdentityProperties","userSegment","signupDate","customMinimalIdentityProperties","customField1","customField2","customField3","nested","excludeIds","identityWithCustomProps","minimalIdentity"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/core/insights/index.test.ts"],"sourcesContent":["/**\n * @vitest-environment jsdom\n */\n\nimport { describe, expect, beforeEach, afterEach, it, vi, Mock } from \"vitest\";\n\nimport * as datalayer from \"./datalayer\";\nimport * as mixpanel from \"./mixpanel\";\nimport * as posthog from \"./posthog\";\nimport * as logger from \"./logger\";\nimport * as insights from \"./index\";\n\n// Mock the dependencies\nvi.mock(\"./datalayer\", () => ({\n track: vi.fn(),\n trackPageView: vi.fn(),\n}));\n\nvi.mock(\"./mixpanel\", () => ({\n initMixpanel: vi.fn(),\n enableDebugMode: vi.fn(),\n disableDebugMode: vi.fn(),\n identify: vi.fn(),\n trackPageView: vi.fn(),\n track: vi.fn(),\n}));\n\nvi.mock(\"./posthog\", () => ({\n initPosthog: vi.fn(),\n enableDebugMode: vi.fn(),\n disableDebugMode: vi.fn(),\n identify: vi.fn(),\n trackPageView: vi.fn(),\n track: vi.fn(),\n}));\n\nvi.mock(\"./logger\", () => ({\n debug: vi.fn(),\n info: vi.fn(),\n warn: vi.fn(),\n error: vi.fn(),\n}));\n\ndescribe(\"Insights Command Queue\", () => {\n const testConfig = {\n debug: true,\n mixpanelToken: \"test-token\",\n mixpanelAutoCapture: false,\n posthogApiKey: \"test-key\",\n posthogApiHost: \"test-host\",\n };\n\n const testIdentity = {\n userId: \"user-123\",\n accountId: \"account-456\",\n organisationId: \"org-789\",\n email: \"test@example.com\",\n name: \"Test User\",\n };\n\n beforeEach(() => {\n // Clear all mocks before each test\n vi.clearAllMocks();\n\n // Reset the module to clear any internal state\n vi.resetModules();\n });\n\n afterEach(() => {\n // Cleanup document event listeners\n document.body.replaceWith(document.body.cloneNode(true));\n });\n\n describe(\"Pre-initialization Queueing\", () => {\n it(\"should queue methods called before initialization\", async () => {\n // Call methods before initialization\n insights.track(\"early_event\", { early: true });\n insights.identify(testIdentity);\n insights.trackPageView();\n\n // Verify nothing has been called yet on the underlying services\n expect(mixpanel.track).not.toHaveBeenCalled();\n expect(posthog.track).not.toHaveBeenCalled();\n expect(datalayer.track).not.toHaveBeenCalled();\n expect(mixpanel.identify).not.toHaveBeenCalled();\n expect(posthog.identify).not.toHaveBeenCalled();\n expect(mixpanel.trackPageView).not.toHaveBeenCalled();\n expect(posthog.trackPageView).not.toHaveBeenCalled();\n expect(datalayer.trackPageView).not.toHaveBeenCalled();\n\n // Now initialize\n insights.initInsights(testConfig);\n\n // Initialize should be called immediately\n expect(mixpanel.initMixpanel).toHaveBeenCalledWith(\n testConfig.mixpanelToken,\n testConfig.mixpanelAutoCapture,\n testConfig.debug,\n undefined,\n );\n expect(posthog.initPosthog).toHaveBeenCalledWith(\n testConfig.posthogApiKey,\n testConfig.posthogApiHost,\n );\n\n // Queued methods should now be called in the correct order\n expect(mixpanel.track).toHaveBeenCalledWith(\"early_event\", {\n early: true,\n });\n expect(posthog.track).toHaveBeenCalledWith(\"early_event\", {\n early: true,\n });\n expect(datalayer.track).toHaveBeenCalledWith(\"early_event\", {\n early: true,\n });\n\n expect(mixpanel.identify).toHaveBeenCalledWith(testIdentity);\n expect(posthog.identify).toHaveBeenCalledWith(testIdentity);\n\n expect(mixpanel.trackPageView).toHaveBeenCalled();\n expect(posthog.trackPageView).toHaveBeenCalled();\n expect(datalayer.trackPageView).not.toHaveBeenCalled();\n });\n\n it(\"should handle errors in queued methods gracefully\", async () => {\n // Setup an error for one of the methods\n (mixpanel.track as Mock).mockImplementationOnce(() => {\n throw new Error(\"Mixpanel error\");\n });\n\n // Call methods before initialization\n insights.track(\"error_event\", { error: true });\n insights.trackPageView();\n\n // Now initialize\n insights.initInsights(testConfig);\n\n // Should have logged the error but continued processing the queue\n expect(logger.error).toHaveBeenCalledWith(\n expect.stringContaining(\"Failed to track event in Mixpanel\"),\n expect.any(Error),\n );\n\n // The other methods should still be called\n expect(posthog.track).toHaveBeenCalledWith(\"error_event\", {\n error: true,\n });\n expect(datalayer.track).toHaveBeenCalledWith(\"error_event\", {\n error: true,\n });\n expect(mixpanel.trackPageView).toHaveBeenCalled();\n expect(posthog.trackPageView).toHaveBeenCalled();\n expect(datalayer.trackPageView).not.toHaveBeenCalled();\n });\n\n it(\"should report page view to GTM as well when includeDataLayer is true\", () => {\n insights.trackPageView({ includeDataLayer: true });\n expect(mixpanel.trackPageView).toHaveBeenCalled();\n expect(posthog.trackPageView).toHaveBeenCalled();\n expect(datalayer.trackPageView).toHaveBeenCalled();\n });\n });\n\n describe(\"Post-initialization Direct Execution\", () => {\n beforeEach(() => {\n // Initialize first\n insights.initInsights(testConfig);\n // Clear the mocks to focus on post-init behavior\n vi.clearAllMocks();\n });\n\n it(\"should directly call methods after initialization\", () => {\n // Call methods after initialization\n insights.track(\"post_init_event\", { post: true });\n\n // Should be called immediately\n expect(mixpanel.track).toHaveBeenCalledWith(\"post_init_event\", {\n post: true,\n });\n expect(posthog.track).toHaveBeenCalledWith(\"post_init_event\", {\n post: true,\n });\n expect(datalayer.track).toHaveBeenCalledWith(\"post_init_event\", {\n post: true,\n });\n });\n\n it(\"should handle all exported methods correctly\", () => {\n // Test each exported method\n insights.identify(testIdentity);\n expect(mixpanel.identify).toHaveBeenCalledWith(testIdentity);\n expect(posthog.identify).toHaveBeenCalledWith(testIdentity);\n\n insights.trackPageView();\n expect(mixpanel.trackPageView).toHaveBeenCalled();\n expect(posthog.trackPageView).toHaveBeenCalled();\n expect(datalayer.trackPageView).not.toHaveBeenCalled();\n\n insights.enableDebugMode();\n expect(mixpanel.enableDebugMode).toHaveBeenCalled();\n expect(posthog.enableDebugMode).toHaveBeenCalled();\n\n insights.disableDebugMode();\n expect(mixpanel.disableDebugMode).toHaveBeenCalled();\n expect(posthog.disableDebugMode).toHaveBeenCalled();\n });\n\n it(\"should report page view to GTM as well when includeDataLayer is true\", () => {\n insights.trackPageView({ includeDataLayer: true });\n expect(mixpanel.trackPageView).toHaveBeenCalled();\n expect(posthog.trackPageView).toHaveBeenCalled();\n expect(datalayer.trackPageView).toHaveBeenCalled();\n });\n });\n\n describe(\"Observer Setup\", () => {\n beforeEach(() => {\n insights.initInsights(testConfig);\n vi.clearAllMocks();\n });\n\n it(\"should set up click event observer and track clicks\", () => {\n // Setup observer\n const cleanup = insights.setupObserver();\n\n // Create a test element with insight attributes\n const testElement = document.createElement(\"button\");\n testElement.setAttribute(\"data-insight-event\", \"button_clicked\");\n testElement.setAttribute(\"data-insight-button-id\", \"test-123\");\n document.body.appendChild(testElement);\n\n // Simulate click\n testElement.click();\n\n // Should track the event\n expect(mixpanel.track).toHaveBeenCalledWith(\"button_clicked\", {\n buttonId: \"test-123\",\n });\n expect(posthog.track).toHaveBeenCalledWith(\"button_clicked\", {\n buttonId: \"test-123\",\n });\n expect(datalayer.track).toHaveBeenCalledWith(\"button_clicked\", {\n buttonId: \"test-123\",\n });\n\n // Test cleanup\n cleanup();\n\n // Reset tracking mocks\n vi.clearAllMocks();\n\n // Click again - should not track\n testElement.click();\n expect(mixpanel.track).not.toHaveBeenCalled();\n });\n\n it(\"should handle nested elements correctly\", () => {\n // Setup observer\n insights.setupObserver();\n\n // Create parent element with insight attributes\n const parentElement = document.createElement(\"div\");\n parentElement.setAttribute(\"data-insight-event\", \"container_clicked\");\n parentElement.setAttribute(\n \"data-insight-container-id\",\n \"parent-container\",\n );\n\n // Create child element without insights\n const childElement = document.createElement(\"span\");\n childElement.textContent = \"Click me\";\n\n // Nest elements\n parentElement.appendChild(childElement);\n document.body.appendChild(parentElement);\n\n // Click the child element\n childElement.click();\n\n // Should find and use the parent's insight attributes\n expect(mixpanel.track).toHaveBeenCalledWith(\"container_clicked\", {\n containerId: \"parent-container\",\n });\n });\n });\n\n describe(\"Error Handling\", () => {\n it(\"should handle initialization errors gracefully\", () => {\n // Setup an error in initialization\n (mixpanel.initMixpanel as Mock).mockImplementationOnce(() => {\n throw new Error(\"Mixpanel init error\");\n });\n\n // Should not throw when initializing\n expect(() => {\n insights.initInsights(testConfig);\n }).not.toThrow();\n\n // Should log the error\n expect(logger.error).toHaveBeenCalledWith(\n expect.stringContaining(\"Failed to initialize Mixpanel\"),\n expect.any(Error),\n );\n });\n\n it(\"should handle runtime errors in methods\", () => {\n // Initialize first\n insights.initInsights(testConfig);\n vi.clearAllMocks();\n\n // Setup errors in tracking\n (mixpanel.track as Mock).mockImplementationOnce(() => {\n throw new Error(\"Mixpanel track error\");\n });\n\n (posthog.track as Mock).mockImplementationOnce(() => {\n throw new Error(\"Posthog track error\");\n });\n\n // Should not throw when tracking\n expect(() => {\n insights.track(\"error_test\", { test: true });\n }).not.toThrow();\n\n // Should log the errors\n expect(logger.error).toHaveBeenCalledWith(\n expect.stringContaining(\"Failed to track event in Mixpanel\"),\n expect.any(Error),\n );\n\n expect(logger.error).toHaveBeenCalledWith(\n expect.stringContaining(\"Failed to track event in Posthog\"),\n expect.any(Error),\n );\n\n // Should still try to track with datalayer\n expect(datalayer.track).toHaveBeenCalledWith(\"error_test\", {\n test: true,\n });\n });\n });\n\n describe(\"Debug Mode\", () => {\n it(\"should respect debug flag in config\", () => {\n // Initialize with debug: true\n insights.initInsights(testConfig);\n\n // Should log debug messages\n expect(logger.debug).toHaveBeenCalledWith(\n expect.stringContaining(\"Initializing insights\"),\n );\n\n // Clear mocks and test tracking\n vi.clearAllMocks();\n insights.track(\"debug_test\", { debug: true });\n\n // Should log info about tracking\n expect(logger.info).toHaveBeenCalledWith(\n expect.stringContaining(\"Tracking event\"),\n expect.objectContaining({\n event: \"debug_test\",\n properties: { debug: true },\n }),\n );\n });\n\n it(\"should not log debug info when debug is false\", () => {\n // Initialize with debug: false\n insights.initInsights({\n ...testConfig,\n debug: false,\n });\n\n // Clear mocks and test tracking\n vi.clearAllMocks();\n insights.track(\"no_debug_test\", { debug: false });\n\n // Should not log info about tracking\n expect(logger.info).not.toHaveBeenCalled();\n });\n });\n\n describe(\"Arbitrary Properties\", () => {\n const customPageViewProperties = {\n customProperty: \"custom-value\",\n anotherProperty: 123,\n nestedObject: { foo: \"bar\" },\n };\n\n const customPageViewPropertiesWithDataLayer = {\n customProperty: \"custom-value\",\n anotherProperty: 456,\n };\n\n const customPageViewPropertiesWithExcludeIds = {\n customProperty: \"test\",\n count: 789,\n };\n\n const customIdentityProperties = {\n customProperty: \"user-custom-value\",\n userSegment: \"premium\",\n signupDate: \"2024-01-01\",\n };\n\n const customMinimalIdentityProperties = {\n customField1: \"value1\",\n customField2: 999,\n customField3: { nested: true },\n };\n\n beforeEach(() => {\n insights.initInsights(testConfig);\n vi.clearAllMocks();\n });\n\n it(\"should pass arbitrary properties through trackPageView\", () => {\n // Call trackPageView with custom properties\n insights.trackPageView(customPageViewProperties);\n\n // Verify mixpanel received all properties\n expect(mixpanel.trackPageView).toHaveBeenCalledWith(\n customPageViewProperties,\n );\n\n // Verify posthog received all properties\n expect(posthog.trackPageView).toHaveBeenCalledWith(\n customPageViewProperties,\n );\n\n // Verify datalayer did not receive anything (includeDataLayer not set)\n expect(datalayer.trackPageView).not.toHaveBeenCalled();\n });\n\n it(\"should pass arbitrary properties through trackPageView with includeDataLayer\", () => {\n // Call trackPageView with custom properties and includeDataLayer\n insights.trackPageView({\n includeDataLayer: true,\n ...customPageViewPropertiesWithDataLayer,\n });\n\n // Verify mixpanel received custom properties (includeDataLayer filtered out)\n expect(mixpanel.trackPageView).toHaveBeenCalledWith(\n customPageViewPropertiesWithDataLayer,\n );\n\n // Verify posthog received custom properties (includeDataLayer filtered out)\n expect(posthog.trackPageView).toHaveBeenCalledWith(\n customPageViewPropertiesWithDataLayer,\n );\n\n // Verify datalayer received custom properties (includeDataLayer filtered out)\n expect(datalayer.trackPageView).toHaveBeenCalledWith(\n customPageViewPropertiesWithDataLayer,\n );\n });\n\n it(\"should pass arbitrary properties through trackPageView with excludeIds\", () => {\n const excludeIds = [\"id1\", \"id2\"];\n\n // Call trackPageView with custom properties and excludeIds\n insights.trackPageView({\n excludeIds,\n ...customPageViewPropertiesWithExcludeIds,\n });\n\n // Verify mixpanel received excludeIds and custom properties\n expect(mixpanel.trackPageView).toHaveBeenCalledWith({\n excludeIds,\n ...customPageViewPropertiesWithExcludeIds,\n });\n\n // Verify posthog received only custom properties (excludeIds filtered out)\n expect(posthog.trackPageView).toHaveBeenCalledWith(\n customPageViewPropertiesWithExcludeIds,\n );\n });\n\n it(\"should pass arbitrary properties through identify\", () => {\n const identityWithCustomProps = {\n ...testIdentity,\n ...customIdentityProperties,\n };\n\n // Call identify with custom properties\n insights.identify(identityWithCustomProps);\n\n // Verify mixpanel received all properties\n expect(mixpanel.identify).toHaveBeenCalledWith(identityWithCustomProps);\n\n // Verify posthog received all properties\n expect(posthog.identify).toHaveBeenCalledWith(identityWithCustomProps);\n });\n\n it(\"should pass arbitrary properties through identify with minimal identity\", () => {\n const minimalIdentity = {\n userId: \"minimal-user\",\n accountId: \"minimal-account\",\n ...customMinimalIdentityProperties,\n };\n\n // Call identify with only required fields plus custom properties\n insights.identify(minimalIdentity);\n\n // Verify mixpanel received all properties\n expect(mixpanel.identify).toHaveBeenCalledWith(minimalIdentity);\n\n // Verify posthog received all properties\n expect(posthog.identify).toHaveBeenCalledWith(minimalIdentity);\n });\n });\n});\n"],"names":["describe","expect","beforeEach","afterEach","it","vi","datalayer","mixpanel","posthog","logger","insights","mock","track","fn","trackPageView","initMixpanel","enableDebugMode","disableDebugMode","identify","initPosthog","debug","info","warn","error","testConfig","mixpanelToken","mixpanelAutoCapture","posthogApiKey","posthogApiHost","testIdentity","userId","accountId","organisationId","email","name","clearAllMocks","resetModules","document","body","replaceWith","cloneNode","early","not","toHaveBeenCalled","initInsights","toHaveBeenCalledWith","undefined","mockImplementationOnce","Error","stringContaining","any","includeDataLayer","post","cleanup","setupObserver","testElement","createElement","setAttribute","appendChild","click","buttonId","parentElement","childElement","textContent","containerId","toThrow","test","objectContaining","event","properties","customPageViewProperties","customProperty","anotherProperty","nestedObject","foo","customPageViewPropertiesWithDataLayer","customPageViewPropertiesWithExcludeIds","count","customIdentityProperties","userSegment","signupDate","customMinimalIdentityProperties","customField1","customField2","customField3","nested","excludeIds","identityWithCustomProps","minimalIdentity"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import mixpanel from"mixpanel-browser";export const initMixpanel=(token,autoCapture=false,debug=false,
|
|
1
|
+
import mixpanel from"mixpanel-browser";export const MIXPANEL_EU_API_HOST="https://api-eu.mixpanel.com";export const initMixpanel=(token,autoCapture=false,debug=false,apiHost=MIXPANEL_EU_API_HOST)=>{const blockSelectors=["[data-mask]"];if(!token){console.warn("Mixpanel token not provided, skipping initialization");return}mixpanel.init(token,{api_host:apiHost,debug:debug,persistence:"localStorage",autocapture:autoCapture?{block_selectors:blockSelectors,capture_text_content:true,pageview:false}:false,track_pageview:false,record_sessions_percent:0})};export const enableDebugMode=()=>{mixpanel.set_config({debug:true})};export const disableDebugMode=()=>{mixpanel.set_config({debug:false})};export const identify=({userId,accountId,organisationId,email,name,...properties})=>{if(!userId){return}mixpanel.identify(userId.toString());const peopleProperties={...properties};if(email||name){peopleProperties.$email=email;peopleProperties.$name=name}if(organisationId){peopleProperties.organization_id=[organisationId.toString()]}if(Object.keys(peopleProperties).length>0){mixpanel.people.set(peopleProperties)}if(accountId){mixpanel.people.union({accounts:[accountId.toString()]})}};const redactUrlSegments=excludeIds=>{const pathSegments=window.location.pathname.split("/");const redactedSegments=pathSegments.map(segment=>{if(/^\d+$/.test(segment)||excludeIds.some(id=>id&&id!==""&&segment===id)){return"{redacted}"}return segment});const url=new URL(window.location.href);url.pathname=redactedSegments.join("/");return decodeURI(url.toString()).toLowerCase()};export const trackPageView=properties=>{const{excludeIds,...rest}=properties??{};mixpanel.track_pageview({redacted_path:redactUrlSegments(excludeIds??[]),...rest})};export const track=(event,properties)=>{mixpanel.track(event,properties)};
|
|
2
2
|
//# sourceMappingURL=mixpanel.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/core/insights/mixpanel.ts"],"sourcesContent":["import mixpanel from \"mixpanel-browser\";\n\nimport { InsightsIdentity, TrackPageViewOptions } from \"./types\";\n\nexport const initMixpanel = (\n token: string,\n autoCapture: boolean = false,\n debug: boolean = false,\n
|
|
1
|
+
{"version":3,"sources":["../../../src/core/insights/mixpanel.ts"],"sourcesContent":["import mixpanel from \"mixpanel-browser\";\n\nimport { InsightsIdentity, TrackPageViewOptions } from \"./types\";\n\nexport const MIXPANEL_EU_API_HOST = \"https://api-eu.mixpanel.com\";\n\nexport const initMixpanel = (\n token: string,\n autoCapture: boolean = false,\n debug: boolean = false,\n apiHost: string = MIXPANEL_EU_API_HOST,\n) => {\n const blockSelectors = [\"[data-mask]\"];\n if (!token) {\n console.warn(\"Mixpanel token not provided, skipping initialization\");\n return;\n }\n\n mixpanel.init(token, {\n api_host: apiHost,\n debug: debug,\n persistence: \"localStorage\",\n autocapture: autoCapture\n ? {\n block_selectors: blockSelectors,\n capture_text_content: true,\n pageview: false, // We'll track page views manually\n }\n : false,\n track_pageview: false, // We'll track page views manually\n record_sessions_percent: 0,\n });\n};\n\nexport const enableDebugMode = () => {\n mixpanel.set_config({ debug: true });\n};\n\nexport const disableDebugMode = () => {\n mixpanel.set_config({ debug: false });\n};\n\nexport const identify = ({\n userId,\n accountId,\n organisationId,\n email,\n name,\n ...properties\n}: InsightsIdentity) => {\n // In very rare cases we might have a user without an account, so we'll\n // let null/undefined/blank strings through on that one\n if (!userId) {\n return;\n }\n\n mixpanel.identify(userId.toString());\n\n const peopleProperties: Record<string, unknown> = { ...properties };\n\n if (email || name) {\n peopleProperties.$email = email;\n peopleProperties.$name = name;\n }\n\n if (organisationId) {\n peopleProperties.organization_id = [organisationId.toString()];\n }\n\n if (Object.keys(peopleProperties).length > 0) {\n mixpanel.people.set(peopleProperties);\n }\n\n if (accountId) {\n mixpanel.people.union({ accounts: [accountId.toString()] });\n }\n};\n\n// Simple function to replace all digits and IDs in a URL path with {redacted},\n// purely to make reporting based on aggregates easier\nconst redactUrlSegments = (excludeIds: string[]) => {\n const pathSegments = window.location.pathname.split(\"/\");\n\n const redactedSegments = pathSegments.map((segment) => {\n // Redact if the segment contains only digits or matches any of the excluded IDs\n if (\n /^\\d+$/.test(segment) ||\n excludeIds.some((id) => id && id !== \"\" && segment === id)\n ) {\n return \"{redacted}\";\n }\n\n return segment;\n });\n\n // Join the segments back together\n const url = new URL(window.location.href);\n url.pathname = redactedSegments.join(\"/\");\n\n return decodeURI(url.toString()).toLowerCase();\n};\n\nexport const trackPageView = (properties?: TrackPageViewOptions) => {\n const { excludeIds, ...rest } = properties ?? {};\n\n // Add the redacted URL to the page view event for reporting\n mixpanel.track_pageview({\n redacted_path: redactUrlSegments(excludeIds ?? []),\n ...rest,\n });\n};\n\nexport const track = (event: string, properties?: Record<string, unknown>) => {\n mixpanel.track(event, properties);\n};\n"],"names":["mixpanel","MIXPANEL_EU_API_HOST","initMixpanel","token","autoCapture","debug","apiHost","blockSelectors","console","warn","init","api_host","persistence","autocapture","block_selectors","capture_text_content","pageview","track_pageview","record_sessions_percent","enableDebugMode","set_config","disableDebugMode","identify","userId","accountId","organisationId","email","name","properties","toString","peopleProperties","$email","$name","organization_id","Object","keys","length","people","set","union","accounts","redactUrlSegments","excludeIds","pathSegments","window","location","pathname","split","redactedSegments","map","segment","test","some","id","url","URL","href","join","decodeURI","toLowerCase","trackPageView","rest","redacted_path","track","event"],"mappings":"AAAA,OAAOA,aAAc,kBAAmB,AAIxC,QAAO,MAAMC,qBAAuB,6BAA8B,AAElE,QAAO,MAAMC,aAAe,CAC1BC,MACAC,YAAuB,KAAK,CAC5BC,MAAiB,KAAK,CACtBC,QAAkBL,oBAAoB,IAEtC,MAAMM,eAAiB,CAAC,cAAc,CACtC,GAAI,CAACJ,MAAO,CACVK,QAAQC,IAAI,CAAC,wDACb,MACF,CAEAT,SAASU,IAAI,CAACP,MAAO,CACnBQ,SAAUL,QACVD,MAAOA,MACPO,YAAa,eACbC,YAAaT,YACT,CACEU,gBAAiBP,eACjBQ,qBAAsB,KACtBC,SAAU,KACZ,EACA,MACJC,eAAgB,MAChBC,wBAAyB,CAC3B,EACF,CAAE,AAEF,QAAO,MAAMC,gBAAkB,KAC7BnB,SAASoB,UAAU,CAAC,CAAEf,MAAO,IAAK,EACpC,CAAE,AAEF,QAAO,MAAMgB,iBAAmB,KAC9BrB,SAASoB,UAAU,CAAC,CAAEf,MAAO,KAAM,EACrC,CAAE,AAEF,QAAO,MAAMiB,SAAW,CAAC,CACvBC,MAAM,CACNC,SAAS,CACTC,cAAc,CACdC,KAAK,CACLC,IAAI,CACJ,GAAGC,WACc,IAGjB,GAAI,CAACL,OAAQ,CACX,MACF,CAEAvB,SAASsB,QAAQ,CAACC,OAAOM,QAAQ,IAEjC,MAAMC,iBAA4C,CAAE,GAAGF,UAAU,AAAC,EAElE,GAAIF,OAASC,KAAM,CACjBG,iBAAiBC,MAAM,CAAGL,KAC1BI,CAAAA,iBAAiBE,KAAK,CAAGL,IAC3B,CAEA,GAAIF,eAAgB,CAClBK,iBAAiBG,eAAe,CAAG,CAACR,eAAeI,QAAQ,GAAG,AAChE,CAEA,GAAIK,OAAOC,IAAI,CAACL,kBAAkBM,MAAM,CAAG,EAAG,CAC5CpC,SAASqC,MAAM,CAACC,GAAG,CAACR,iBACtB,CAEA,GAAIN,UAAW,CACbxB,SAASqC,MAAM,CAACE,KAAK,CAAC,CAAEC,SAAU,CAAChB,UAAUK,QAAQ,GAAG,AAAC,EAC3D,CACF,CAAE,CAIF,MAAMY,kBAAoB,AAACC,aACzB,MAAMC,aAAeC,OAAOC,QAAQ,CAACC,QAAQ,CAACC,KAAK,CAAC,KAEpD,MAAMC,iBAAmBL,aAAaM,GAAG,CAAC,AAACC,UAEzC,GACE,QAAQC,IAAI,CAACD,UACbR,WAAWU,IAAI,CAAC,AAACC,IAAOA,IAAMA,KAAO,IAAMH,UAAYG,IACvD,CACA,MAAO,YACT,CAEA,OAAOH,OACT,GAGA,MAAMI,IAAM,IAAIC,IAAIX,OAAOC,QAAQ,CAACW,IAAI,CACxCF,CAAAA,IAAIR,QAAQ,CAAGE,iBAAiBS,IAAI,CAAC,KAErC,OAAOC,UAAUJ,IAAIzB,QAAQ,IAAI8B,WAAW,EAC9C,CAEA,QAAO,MAAMC,cAAgB,AAAChC,aAC5B,KAAM,CAAEc,UAAU,CAAE,GAAGmB,KAAM,CAAGjC,YAAc,CAAC,EAG/C5B,SAASiB,cAAc,CAAC,CACtB6C,cAAerB,kBAAkBC,YAAc,EAAE,EACjD,GAAGmB,IAAI,AACT,EACF,CAAE,AAEF,QAAO,MAAME,MAAQ,CAACC,MAAepC,cACnC5B,SAAS+D,KAAK,CAACC,MAAOpC,WACxB,CAAE"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{describe,expect,beforeEach,it,vi}from"vitest";import mixpanel from"mixpanel-browser";import{initMixpanel}from"./mixpanel";vi.mock("mixpanel-browser",()=>({default:{init:vi.fn()}}));describe("initMixpanel",()=>{beforeEach(()=>{vi.clearAllMocks()});it("does not initialize without a token",()=>{const consoleSpy=vi.spyOn(console,"warn").mockImplementation(()=>{});initMixpanel("");expect(mixpanel.init).not.toHaveBeenCalled();consoleSpy.mockRestore()});it("blocks [data-mask] elements in autocapture",()=>{initMixpanel("test-token",true);const config=mixpanel.init.mock.calls[0][1];expect(config.autocapture.block_selectors).toEqual(["[data-mask]"])});it("
|
|
1
|
+
import{describe,expect,beforeEach,it,vi}from"vitest";import mixpanel from"mixpanel-browser";import{initMixpanel,MIXPANEL_EU_API_HOST}from"./mixpanel";vi.mock("mixpanel-browser",()=>({default:{init:vi.fn()}}));describe("initMixpanel",()=>{beforeEach(()=>{vi.clearAllMocks()});it("does not initialize without a token",()=>{const consoleSpy=vi.spyOn(console,"warn").mockImplementation(()=>{});initMixpanel("");expect(mixpanel.init).not.toHaveBeenCalled();consoleSpy.mockRestore()});it("blocks [data-mask] elements in autocapture",()=>{initMixpanel("test-token",true);const config=mixpanel.init.mock.calls[0][1];expect(config.autocapture.block_selectors).toEqual(["[data-mask]"])});it("does not record sessions",()=>{initMixpanel("test-token",true);const config=mixpanel.init.mock.calls[0][1];expect(config.record_sessions_percent).toBe(0)});it("defaults api_host to the EU ingestion endpoint",()=>{initMixpanel("test-token");const config=mixpanel.init.mock.calls[0][1];expect(config.api_host).toBe(MIXPANEL_EU_API_HOST);expect(MIXPANEL_EU_API_HOST).toBe("https://api-eu.mixpanel.com")});it("allows overriding api_host",()=>{initMixpanel("test-token",false,false,"https://api.mixpanel.com");const config=mixpanel.init.mock.calls[0][1];expect(config.api_host).toBe("https://api.mixpanel.com")});it("falls back to the EU default when api_host is undefined",()=>{initMixpanel("test-token",false,false,undefined);const config=mixpanel.init.mock.calls[0][1];expect(config.api_host).toBe(MIXPANEL_EU_API_HOST)})});
|
|
2
2
|
//# sourceMappingURL=mixpanel.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/core/insights/mixpanel.test.ts"],"sourcesContent":["/**\n * @vitest-environment jsdom\n */\n\nimport { describe, expect, beforeEach, it, vi } from \"vitest\";\nimport mixpanel from \"mixpanel-browser\";\n\nimport { initMixpanel } from \"./mixpanel\";\n\nvi.mock(\"mixpanel-browser\", () => ({\n default: {\n init: vi.fn(),\n },\n}));\n\ndescribe(\"initMixpanel\", () => {\n beforeEach(() => {\n vi.clearAllMocks();\n });\n\n it(\"does not initialize without a token\", () => {\n const consoleSpy = vi.spyOn(console, \"warn\").mockImplementation(() => {});\n initMixpanel(\"\");\n expect(mixpanel.init).not.toHaveBeenCalled();\n consoleSpy.mockRestore();\n });\n\n it(\"blocks [data-mask] elements in autocapture\", () => {\n initMixpanel(\"test-token\", true);\n\n const config = (mixpanel.init as ReturnType<typeof vi.fn>).mock.calls[0][1];\n expect(config.autocapture.block_selectors).toEqual([\"[data-mask]\"]);\n });\n\n it(\"
|
|
1
|
+
{"version":3,"sources":["../../../src/core/insights/mixpanel.test.ts"],"sourcesContent":["/**\n * @vitest-environment jsdom\n */\n\nimport { describe, expect, beforeEach, it, vi } from \"vitest\";\nimport mixpanel from \"mixpanel-browser\";\n\nimport { initMixpanel, MIXPANEL_EU_API_HOST } from \"./mixpanel\";\n\nvi.mock(\"mixpanel-browser\", () => ({\n default: {\n init: vi.fn(),\n },\n}));\n\ndescribe(\"initMixpanel\", () => {\n beforeEach(() => {\n vi.clearAllMocks();\n });\n\n it(\"does not initialize without a token\", () => {\n const consoleSpy = vi.spyOn(console, \"warn\").mockImplementation(() => {});\n initMixpanel(\"\");\n expect(mixpanel.init).not.toHaveBeenCalled();\n consoleSpy.mockRestore();\n });\n\n it(\"blocks [data-mask] elements in autocapture\", () => {\n initMixpanel(\"test-token\", true);\n\n const config = (mixpanel.init as ReturnType<typeof vi.fn>).mock.calls[0][1];\n expect(config.autocapture.block_selectors).toEqual([\"[data-mask]\"]);\n });\n\n it(\"does not record sessions\", () => {\n initMixpanel(\"test-token\", true);\n\n const config = (mixpanel.init as ReturnType<typeof vi.fn>).mock.calls[0][1];\n expect(config.record_sessions_percent).toBe(0);\n });\n\n it(\"defaults api_host to the EU ingestion endpoint\", () => {\n initMixpanel(\"test-token\");\n\n const config = (mixpanel.init as ReturnType<typeof vi.fn>).mock.calls[0][1];\n expect(config.api_host).toBe(MIXPANEL_EU_API_HOST);\n expect(MIXPANEL_EU_API_HOST).toBe(\"https://api-eu.mixpanel.com\");\n });\n\n it(\"allows overriding api_host\", () => {\n initMixpanel(\"test-token\", false, false, \"https://api.mixpanel.com\");\n\n const config = (mixpanel.init as ReturnType<typeof vi.fn>).mock.calls[0][1];\n expect(config.api_host).toBe(\"https://api.mixpanel.com\");\n });\n\n it(\"falls back to the EU default when api_host is undefined\", () => {\n initMixpanel(\"test-token\", false, false, undefined);\n\n const config = (mixpanel.init as ReturnType<typeof vi.fn>).mock.calls[0][1];\n expect(config.api_host).toBe(MIXPANEL_EU_API_HOST);\n });\n});\n"],"names":["describe","expect","beforeEach","it","vi","mixpanel","initMixpanel","MIXPANEL_EU_API_HOST","mock","default","init","fn","clearAllMocks","consoleSpy","spyOn","console","mockImplementation","not","toHaveBeenCalled","mockRestore","config","calls","autocapture","block_selectors","toEqual","record_sessions_percent","toBe","api_host","undefined"],"mappings":"AAIA,OAASA,QAAQ,CAAEC,MAAM,CAAEC,UAAU,CAAEC,EAAE,CAAEC,EAAE,KAAQ,QAAS,AAC9D,QAAOC,aAAc,kBAAmB,AAExC,QAASC,YAAY,CAAEC,oBAAoB,KAAQ,YAAa,CAEhEH,GAAGI,IAAI,CAAC,mBAAoB,IAAO,CAAA,CACjCC,QAAS,CACPC,KAAMN,GAAGO,EAAE,EACb,CACF,CAAA,GAEAX,SAAS,eAAgB,KACvBE,WAAW,KACTE,GAAGQ,aAAa,EAClB,GAEAT,GAAG,sCAAuC,KACxC,MAAMU,WAAaT,GAAGU,KAAK,CAACC,QAAS,QAAQC,kBAAkB,CAAC,KAAO,GACvEV,aAAa,IACbL,OAAOI,SAASK,IAAI,EAAEO,GAAG,CAACC,gBAAgB,GAC1CL,WAAWM,WAAW,EACxB,GAEAhB,GAAG,6CAA8C,KAC/CG,aAAa,aAAc,MAE3B,MAAMc,OAAS,AAACf,SAASK,IAAI,CAA8BF,IAAI,CAACa,KAAK,CAAC,EAAE,CAAC,EAAE,CAC3EpB,OAAOmB,OAAOE,WAAW,CAACC,eAAe,EAAEC,OAAO,CAAC,CAAC,cAAc,CACpE,GAEArB,GAAG,2BAA4B,KAC7BG,aAAa,aAAc,MAE3B,MAAMc,OAAS,AAACf,SAASK,IAAI,CAA8BF,IAAI,CAACa,KAAK,CAAC,EAAE,CAAC,EAAE,CAC3EpB,OAAOmB,OAAOK,uBAAuB,EAAEC,IAAI,CAAC,EAC9C,GAEAvB,GAAG,iDAAkD,KACnDG,aAAa,cAEb,MAAMc,OAAS,AAACf,SAASK,IAAI,CAA8BF,IAAI,CAACa,KAAK,CAAC,EAAE,CAAC,EAAE,CAC3EpB,OAAOmB,OAAOO,QAAQ,EAAED,IAAI,CAACnB,sBAC7BN,OAAOM,sBAAsBmB,IAAI,CAAC,8BACpC,GAEAvB,GAAG,6BAA8B,KAC/BG,aAAa,aAAc,MAAO,MAAO,4BAEzC,MAAMc,OAAS,AAACf,SAASK,IAAI,CAA8BF,IAAI,CAACa,KAAK,CAAC,EAAE,CAAC,EAAE,CAC3EpB,OAAOmB,OAAOO,QAAQ,EAAED,IAAI,CAAC,2BAC/B,GAEAvB,GAAG,0DAA2D,KAC5DG,aAAa,aAAc,MAAO,MAAOsB,WAEzC,MAAMR,OAAS,AAACf,SAASK,IAAI,CAA8BF,IAAI,CAACa,KAAK,CAAC,EAAE,CAAC,EAAE,CAC3EpB,OAAOmB,OAAOO,QAAQ,EAAED,IAAI,CAACnB,qBAC/B,EACF"}
|
package/core/insights/posthog.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import posthog from"posthog-js";export const initPosthog=(apiKey,apiHost)=>{if(!apiKey){return}posthog.init(apiKey,{api_host:apiHost,capture_pageview:false,
|
|
1
|
+
import posthog from"posthog-js";export const initPosthog=(apiKey,apiHost)=>{if(!apiKey){return}posthog.init(apiKey,{api_host:apiHost,capture_pageview:false,disable_session_recording:true})};export const enableDebugMode=()=>{posthog.debug()};export const disableDebugMode=()=>{posthog.debug(false)};export const identify=({userId,accountId,organisationId,email,name,...properties})=>{if(!userId){return}if(userId!==posthog.get_distinct_id()){posthog.identify(userId,{email,name,...properties})}if(accountId){posthog.group("account",accountId)}if(organisationId){posthog.group("organisation",organisationId)}};export const trackPageView=properties=>{posthog.capture("$pageview",properties)};export const track=(event,properties)=>{posthog.capture(event,properties)};
|
|
2
2
|
//# sourceMappingURL=posthog.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/core/insights/posthog.ts"],"sourcesContent":["import posthog from \"posthog-js\";\n\nimport { InsightsIdentity } from \"./types\";\n\nexport const initPosthog = (apiKey: string, apiHost: string) => {\n if (!apiKey) {\n return;\n }\n\n posthog.init(apiKey, {\n api_host: apiHost,\n capture_pageview: false,\n
|
|
1
|
+
{"version":3,"sources":["../../../src/core/insights/posthog.ts"],"sourcesContent":["import posthog from \"posthog-js\";\n\nimport { InsightsIdentity } from \"./types\";\n\nexport const initPosthog = (apiKey: string, apiHost: string) => {\n if (!apiKey) {\n return;\n }\n\n posthog.init(apiKey, {\n api_host: apiHost,\n capture_pageview: false,\n disable_session_recording: true,\n });\n};\n\nexport const enableDebugMode = () => {\n posthog.debug();\n};\n\nexport const disableDebugMode = () => {\n posthog.debug(false);\n};\n\nexport const identify = ({\n userId,\n accountId,\n organisationId,\n email,\n name,\n ...properties\n}: InsightsIdentity) => {\n // In very rare cases we might have a user without an account, so we'll\n // let null/undefined/blank strings through on that one\n if (!userId) {\n return;\n }\n\n if (userId !== posthog.get_distinct_id()) {\n posthog.identify(userId, { email, name, ...properties });\n }\n\n // Associate all events in this session with this account\n if (accountId) {\n posthog.group(\"account\", accountId);\n }\n\n // Associate all events in this session with this organisation (if available)\n if (organisationId) {\n posthog.group(\"organisation\", organisationId);\n }\n};\n\nexport const trackPageView = (properties?: Record<string, unknown>) => {\n posthog.capture(\"$pageview\", properties);\n};\n\nexport const track = (event: string, properties?: Record<string, unknown>) => {\n posthog.capture(event, properties);\n};\n"],"names":["posthog","initPosthog","apiKey","apiHost","init","api_host","capture_pageview","disable_session_recording","enableDebugMode","debug","disableDebugMode","identify","userId","accountId","organisationId","email","name","properties","get_distinct_id","group","trackPageView","capture","track","event"],"mappings":"AAAA,OAAOA,YAAa,YAAa,AAIjC,QAAO,MAAMC,YAAc,CAACC,OAAgBC,WAC1C,GAAI,CAACD,OAAQ,CACX,MACF,CAEAF,QAAQI,IAAI,CAACF,OAAQ,CACnBG,SAAUF,QACVG,iBAAkB,MAClBC,0BAA2B,IAC7B,EACF,CAAE,AAEF,QAAO,MAAMC,gBAAkB,KAC7BR,QAAQS,KAAK,EACf,CAAE,AAEF,QAAO,MAAMC,iBAAmB,KAC9BV,QAAQS,KAAK,CAAC,MAChB,CAAE,AAEF,QAAO,MAAME,SAAW,CAAC,CACvBC,MAAM,CACNC,SAAS,CACTC,cAAc,CACdC,KAAK,CACLC,IAAI,CACJ,GAAGC,WACc,IAGjB,GAAI,CAACL,OAAQ,CACX,MACF,CAEA,GAAIA,SAAWZ,QAAQkB,eAAe,GAAI,CACxClB,QAAQW,QAAQ,CAACC,OAAQ,CAAEG,MAAOC,KAAM,GAAGC,UAAU,AAAC,EACxD,CAGA,GAAIJ,UAAW,CACbb,QAAQmB,KAAK,CAAC,UAAWN,UAC3B,CAGA,GAAIC,eAAgB,CAClBd,QAAQmB,KAAK,CAAC,eAAgBL,eAChC,CACF,CAAE,AAEF,QAAO,MAAMM,cAAgB,AAACH,aAC5BjB,QAAQqB,OAAO,CAAC,YAAaJ,WAC/B,CAAE,AAEF,QAAO,MAAMK,MAAQ,CAACC,MAAeN,cACnCjB,QAAQqB,OAAO,CAACE,MAAON,WACzB,CAAE"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{describe,expect,beforeEach,it,vi}from"vitest";import posthog from"posthog-js";import{initPosthog}from"./posthog";vi.mock("posthog-js",()=>({default:{init:vi.fn()}}));describe("initPosthog",()=>{beforeEach(()=>{vi.clearAllMocks()});it("does not initialize without an API key",()=>{initPosthog("","https://app.posthog.com");expect(posthog.init).not.toHaveBeenCalled()});it("initializes with session recording
|
|
1
|
+
import{describe,expect,beforeEach,it,vi}from"vitest";import posthog from"posthog-js";import{initPosthog}from"./posthog";vi.mock("posthog-js",()=>({default:{init:vi.fn()}}));describe("initPosthog",()=>{beforeEach(()=>{vi.clearAllMocks()});it("does not initialize without an API key",()=>{initPosthog("","https://app.posthog.com");expect(posthog.init).not.toHaveBeenCalled()});it("initializes with session recording disabled",()=>{initPosthog("phc_test","https://app.posthog.com");expect(posthog.init).toHaveBeenCalledWith("phc_test",{api_host:"https://app.posthog.com",capture_pageview:false,disable_session_recording:true})})});
|
|
2
2
|
//# sourceMappingURL=posthog.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/core/insights/posthog.test.ts"],"sourcesContent":["/**\n * @vitest-environment jsdom\n */\n\nimport { describe, expect, beforeEach, it, vi } from \"vitest\";\nimport posthog from \"posthog-js\";\n\nimport { initPosthog } from \"./posthog\";\n\nvi.mock(\"posthog-js\", () => ({\n default: {\n init: vi.fn(),\n },\n}));\n\ndescribe(\"initPosthog\", () => {\n beforeEach(() => {\n vi.clearAllMocks();\n });\n\n it(\"does not initialize without an API key\", () => {\n initPosthog(\"\", \"https://app.posthog.com\");\n expect(posthog.init).not.toHaveBeenCalled();\n });\n\n it(\"initializes with session recording
|
|
1
|
+
{"version":3,"sources":["../../../src/core/insights/posthog.test.ts"],"sourcesContent":["/**\n * @vitest-environment jsdom\n */\n\nimport { describe, expect, beforeEach, it, vi } from \"vitest\";\nimport posthog from \"posthog-js\";\n\nimport { initPosthog } from \"./posthog\";\n\nvi.mock(\"posthog-js\", () => ({\n default: {\n init: vi.fn(),\n },\n}));\n\ndescribe(\"initPosthog\", () => {\n beforeEach(() => {\n vi.clearAllMocks();\n });\n\n it(\"does not initialize without an API key\", () => {\n initPosthog(\"\", \"https://app.posthog.com\");\n expect(posthog.init).not.toHaveBeenCalled();\n });\n\n it(\"initializes with session recording disabled\", () => {\n initPosthog(\"phc_test\", \"https://app.posthog.com\");\n\n expect(posthog.init).toHaveBeenCalledWith(\"phc_test\", {\n api_host: \"https://app.posthog.com\",\n capture_pageview: false,\n disable_session_recording: true,\n });\n });\n});\n"],"names":["describe","expect","beforeEach","it","vi","posthog","initPosthog","mock","default","init","fn","clearAllMocks","not","toHaveBeenCalled","toHaveBeenCalledWith","api_host","capture_pageview","disable_session_recording"],"mappings":"AAIA,OAASA,QAAQ,CAAEC,MAAM,CAAEC,UAAU,CAAEC,EAAE,CAAEC,EAAE,KAAQ,QAAS,AAC9D,QAAOC,YAAa,YAAa,AAEjC,QAASC,WAAW,KAAQ,WAAY,CAExCF,GAAGG,IAAI,CAAC,aAAc,IAAO,CAAA,CAC3BC,QAAS,CACPC,KAAML,GAAGM,EAAE,EACb,CACF,CAAA,GAEAV,SAAS,cAAe,KACtBE,WAAW,KACTE,GAAGO,aAAa,EAClB,GAEAR,GAAG,yCAA0C,KAC3CG,YAAY,GAAI,2BAChBL,OAAOI,QAAQI,IAAI,EAAEG,GAAG,CAACC,gBAAgB,EAC3C,GAEAV,GAAG,8CAA+C,KAChDG,YAAY,WAAY,2BAExBL,OAAOI,QAAQI,IAAI,EAAEK,oBAAoB,CAAC,WAAY,CACpDC,SAAU,0BACVC,iBAAkB,MAClBC,0BAA2B,IAC7B,EACF,EACF"}
|
package/core/insights/service.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
function _define_property(obj,key,value){if(key in obj){Object.defineProperty(obj,key,{value:value,enumerable:true,configurable:true,writable:true})}else{obj[key]=value}return obj}import*as datalayer from"./datalayer";import*as mixpanel from"./mixpanel";import*as posthog from"./posthog";import*as logger from"./logger";export class InsightsService{initInsights({mixpanelToken,mixpanelAutoCapture,
|
|
1
|
+
function _define_property(obj,key,value){if(key in obj){Object.defineProperty(obj,key,{value:value,enumerable:true,configurable:true,writable:true})}else{obj[key]=value}return obj}import*as datalayer from"./datalayer";import*as mixpanel from"./mixpanel";import*as posthog from"./posthog";import*as logger from"./logger";export class InsightsService{initInsights({mixpanelToken,mixpanelAutoCapture,mixpanelApiHost,posthogApiKey,posthogApiHost,debug=false}){this.debugMode=!!debug;if(this.debugMode){logger.debug("InsightService: Initializing insights")}try{mixpanel.initMixpanel(mixpanelToken,mixpanelAutoCapture,this.debugMode,mixpanelApiHost)}catch(e){if(this.debugMode){logger.error("Failed to initialize Mixpanel",e)}}try{posthog.initPosthog(posthogApiKey,posthogApiHost)}catch(e){if(this.debugMode){logger.error("Failed to initialize Posthog",e)}}}enableDebugMode(){this.debugMode=true;logger.debug("Enabling debug mode");try{mixpanel.enableDebugMode();posthog.enableDebugMode()}catch(e){logger.error("Failed to enable debug mode",e)}}disableDebugMode(){this.debugMode=false;logger.debug("Disabling debug mode");try{mixpanel.disableDebugMode();posthog.disableDebugMode()}catch(e){logger.error("Failed to disable debug mode",e)}}identify(identity){const{userId,accountId,organisationId,email,name,...properties}=identity;if(!userId){if(this.debugMode){logger.warn("User ID not provided, skipping identify")}return}if(this.debugMode){logger.info("Identifying user",{userId,accountId,organisationId,email,name,...properties})}try{mixpanel.identify({userId,accountId,organisationId,email,name,...properties})}catch(e){if(this.debugMode){logger.error("Failed to identify user in Mixpanel",e)}}try{posthog.identify({userId,accountId,organisationId,email,name,...properties})}catch(e){if(this.debugMode){logger.error("Failed to identify user in Posthog",e)}}}trackPageView(options){const{excludeIds,includeDataLayer,...properties}=options??{};if(this.debugMode){logger.info("Tracking page view")}try{mixpanel.trackPageView({excludeIds,...properties})}catch(e){if(this.debugMode){logger.error("Failed to track page view in Mixpanel",e)}}try{posthog.trackPageView(properties)}catch(e){if(this.debugMode){logger.error("Failed to track page view in Posthog",e)}}if(includeDataLayer){try{datalayer.trackPageView(properties)}catch(e){if(this.debugMode){logger.error("Failed to track page view in GTM",e)}}}}track(event,properties){if(this.debugMode){logger.info("Tracking event",{event,properties})}try{mixpanel.track(event,properties)}catch(e){if(this.debugMode){logger.error("Failed to track event in Mixpanel",e)}}try{posthog.track(event,properties)}catch(e){if(this.debugMode){logger.error("Failed to track event in Posthog",e)}}try{datalayer.track(event,properties)}catch(e){if(this.debugMode){logger.error("Failed to track event in Datalayer",e)}}}setupObserver(){const getInsightAttributes=element=>{const MAX_ATTRIBUTES=10;let count=0;const attributes={};for(const attr of Array.from(element.attributes)){if(count>=MAX_ATTRIBUTES)break;if(attr.name.startsWith("data-insight-")){if(!/^data-insight-[a-zA-Z0-9-]+$/.test(attr.name))continue;if(typeof attr.value!=="string"||attr.value.length>100)continue;const key=attr.name.replace("data-insight-","").split("-").map((part,index)=>index===0?part:part.charAt(0).toUpperCase()+part.slice(1)).join("");attributes[key]=attr.value;count++}}return attributes};const findClosestElementWithInsights=element=>{let current=element;while(current&¤t!==document.body){const insights=getInsightAttributes(current);if(Object.keys(insights).length>0){return insights}current=current.parentElement}return null};const handleClick=event=>{if(!(event.target instanceof HTMLElement))return;const insights=findClosestElementWithInsights(event.target);if(insights){const{event:eventName,...properties}=insights;this.track(eventName||"element_clicked",properties)}};document.body.addEventListener("click",handleClick);return()=>{document.body.removeEventListener("click",handleClick)}}constructor(){_define_property(this,"debugMode",false)}}
|
|
2
2
|
//# sourceMappingURL=service.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/core/insights/service.ts"],"sourcesContent":["import type {\n AnalyticsService,\n InsightsConfig,\n InsightsIdentity,\n TrackPageViewOptions,\n} from \"./types\";\nimport * as datalayer from \"./datalayer\";\nimport * as mixpanel from \"./mixpanel\";\nimport * as posthog from \"./posthog\";\nimport * as logger from \"./logger\";\n\n// The real implementation that will be used after initialization\nexport class InsightsService implements AnalyticsService {\n private debugMode: boolean = false;\n\n initInsights({\n mixpanelToken,\n mixpanelAutoCapture,\n mixpanelRecordSessionsPercent = 1,\n posthogApiKey,\n posthogApiHost,\n debug = false,\n }: InsightsConfig): void {\n this.debugMode = !!debug;\n\n if (this.debugMode) {\n logger.debug(\"InsightService: Initializing insights\");\n }\n\n try {\n mixpanel.initMixpanel(\n mixpanelToken,\n mixpanelAutoCapture,\n this.debugMode,\n mixpanelRecordSessionsPercent,\n );\n } catch (e) {\n if (this.debugMode) {\n logger.error(\"Failed to initialize Mixpanel\", e);\n }\n }\n\n try {\n posthog.initPosthog(posthogApiKey, posthogApiHost);\n } catch (e) {\n if (this.debugMode) {\n logger.error(\"Failed to initialize Posthog\", e);\n }\n }\n }\n\n enableDebugMode(): void {\n this.debugMode = true;\n logger.debug(\"Enabling debug mode\");\n\n try {\n mixpanel.enableDebugMode();\n posthog.enableDebugMode();\n } catch (e) {\n logger.error(\"Failed to enable debug mode\", e);\n }\n }\n\n disableDebugMode(): void {\n this.debugMode = false;\n logger.debug(\"Disabling debug mode\");\n\n try {\n mixpanel.disableDebugMode();\n posthog.disableDebugMode();\n } catch (e) {\n logger.error(\"Failed to disable debug mode\", e);\n }\n }\n\n identify(identity: InsightsIdentity): void {\n const { userId, accountId, organisationId, email, name, ...properties } =\n identity;\n\n // In very rare cases we might have a user without an account, so we'll\n // let null/undefined/blank strings through on that one\n if (!userId) {\n if (this.debugMode) {\n logger.warn(\"User ID not provided, skipping identify\");\n }\n return;\n }\n\n if (this.debugMode) {\n logger.info(\"Identifying user\", {\n userId,\n accountId,\n organisationId,\n email,\n name,\n ...properties,\n });\n }\n\n try {\n mixpanel.identify({\n userId,\n accountId,\n organisationId,\n email,\n name,\n ...properties,\n });\n } catch (e) {\n if (this.debugMode) {\n logger.error(\"Failed to identify user in Mixpanel\", e);\n }\n }\n\n try {\n posthog.identify({\n userId,\n accountId,\n organisationId,\n email,\n name,\n ...properties,\n });\n } catch (e) {\n if (this.debugMode) {\n logger.error(\"Failed to identify user in Posthog\", e);\n }\n }\n }\n\n trackPageView(options?: TrackPageViewOptions): void {\n const { excludeIds, includeDataLayer, ...properties } = options ?? {};\n\n if (this.debugMode) {\n logger.info(\"Tracking page view\");\n }\n\n try {\n mixpanel.trackPageView({ excludeIds, ...properties });\n } catch (e) {\n if (this.debugMode) {\n logger.error(\"Failed to track page view in Mixpanel\", e);\n }\n }\n\n try {\n posthog.trackPageView(properties);\n } catch (e) {\n if (this.debugMode) {\n logger.error(\"Failed to track page view in Posthog\", e);\n }\n }\n\n if (includeDataLayer) {\n try {\n datalayer.trackPageView(properties);\n } catch (e) {\n if (this.debugMode) {\n logger.error(\"Failed to track page view in GTM\", e);\n }\n }\n }\n }\n\n track(event: string, properties?: Record<string, unknown>): void {\n if (this.debugMode) {\n logger.info(\"Tracking event\", { event, properties });\n }\n\n try {\n mixpanel.track(event, properties);\n } catch (e) {\n if (this.debugMode) {\n logger.error(\"Failed to track event in Mixpanel\", e);\n }\n }\n\n try {\n posthog.track(event, properties);\n } catch (e) {\n if (this.debugMode) {\n logger.error(\"Failed to track event in Posthog\", e);\n }\n }\n\n try {\n datalayer.track(event, properties);\n } catch (e) {\n if (this.debugMode) {\n logger.error(\"Failed to track event in Datalayer\", e);\n }\n }\n }\n\n startSessionRecording(): void {\n if (this.debugMode) {\n logger.info(\"Starting session recording\");\n }\n\n try {\n mixpanel.startSessionRecording();\n } catch (e) {\n if (this.debugMode) {\n logger.error(\"Failed to start session recording in Mixpanel\", e);\n }\n }\n\n try {\n posthog.startSessionRecording();\n } catch (e) {\n if (this.debugMode) {\n logger.error(\"Failed to start session recording in Posthog\", e);\n }\n }\n }\n\n stopSessionRecording(): void {\n if (this.debugMode) {\n logger.info(\"Stopping session recording\");\n }\n\n try {\n mixpanel.stopSessionRecording();\n } catch (e) {\n if (this.debugMode) {\n logger.error(\"Failed to stop session recording in Mixpanel\", e);\n }\n }\n\n try {\n posthog.stopSessionRecording();\n } catch (e) {\n if (this.debugMode) {\n logger.error(\"Failed to stop session recording in Posthog\", e);\n }\n }\n }\n\n setupObserver(): () => void {\n // Helper to get all data-insight-* attributes from an element\n const getInsightAttributes = (\n element: HTMLElement,\n ): { event?: string; [key: string]: string | undefined } => {\n // limit how many data attributes we'll process\n const MAX_ATTRIBUTES = 10;\n let count = 0;\n\n const attributes: { event?: string; [key: string]: string | undefined } =\n {};\n\n for (const attr of Array.from(element.attributes)) {\n if (count >= MAX_ATTRIBUTES) break;\n if (attr.name.startsWith(\"data-insight-\")) {\n // Validate attribute name format\n if (!/^data-insight-[a-zA-Z0-9-]+$/.test(attr.name)) continue;\n\n // Sanitize attribute value\n if (typeof attr.value !== \"string\" || attr.value.length > 100)\n continue;\n\n // Convert data-insight-event-name to eventName\n const key = attr.name\n .replace(\"data-insight-\", \"\")\n .split(\"-\")\n .map((part: string, index: number) =>\n index === 0 ? part : part.charAt(0).toUpperCase() + part.slice(1),\n )\n .join(\"\");\n attributes[key] = attr.value;\n count++;\n }\n }\n return attributes;\n };\n\n // Helper to find closest element with data-insight attributes\n const findClosestElementWithInsights = (element: HTMLElement) => {\n let current = element;\n while (current && current !== document.body) {\n const insights = getInsightAttributes(current);\n if (Object.keys(insights).length > 0) {\n return insights;\n }\n\n current = current.parentElement as HTMLElement;\n }\n return null;\n };\n\n // Global click handler\n const handleClick = (event: MouseEvent): void => {\n if (!(event.target instanceof HTMLElement)) return;\n const insights = findClosestElementWithInsights(event.target);\n if (insights) {\n // Extract special properties if they exist\n const { event: eventName, ...properties } = insights;\n this.track(eventName || \"element_clicked\", properties);\n }\n };\n\n // Add listener to document body to catch all clicks\n document.body.addEventListener(\"click\", handleClick);\n\n // Return cleanup function in case it's needed\n return () => {\n document.body.removeEventListener(\"click\", handleClick);\n };\n }\n}\n"],"names":["datalayer","mixpanel","posthog","logger","InsightsService","initInsights","mixpanelToken","mixpanelAutoCapture","mixpanelRecordSessionsPercent","posthogApiKey","posthogApiHost","debug","debugMode","initMixpanel","e","error","initPosthog","enableDebugMode","disableDebugMode","identify","identity","userId","accountId","organisationId","email","name","properties","warn","info","trackPageView","options","excludeIds","includeDataLayer","track","event","startSessionRecording","stopSessionRecording","setupObserver","getInsightAttributes","element","MAX_ATTRIBUTES","count","attributes","attr","Array","from","startsWith","test","value","length","key","replace","split","map","part","index","charAt","toUpperCase","slice","join","findClosestElementWithInsights","current","document","body","insights","Object","keys","parentElement","handleClick","target","HTMLElement","eventName","addEventListener","removeEventListener"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/core/insights/service.ts"],"sourcesContent":["import type {\n AnalyticsService,\n InsightsConfig,\n InsightsIdentity,\n TrackPageViewOptions,\n} from \"./types\";\nimport * as datalayer from \"./datalayer\";\nimport * as mixpanel from \"./mixpanel\";\nimport * as posthog from \"./posthog\";\nimport * as logger from \"./logger\";\n\n// The real implementation that will be used after initialization\nexport class InsightsService implements AnalyticsService {\n private debugMode: boolean = false;\n\n initInsights({\n mixpanelToken,\n mixpanelAutoCapture,\n mixpanelApiHost,\n posthogApiKey,\n posthogApiHost,\n debug = false,\n }: InsightsConfig): void {\n this.debugMode = !!debug;\n\n if (this.debugMode) {\n logger.debug(\"InsightService: Initializing insights\");\n }\n\n try {\n mixpanel.initMixpanel(\n mixpanelToken,\n mixpanelAutoCapture,\n this.debugMode,\n mixpanelApiHost,\n );\n } catch (e) {\n if (this.debugMode) {\n logger.error(\"Failed to initialize Mixpanel\", e);\n }\n }\n\n try {\n posthog.initPosthog(posthogApiKey, posthogApiHost);\n } catch (e) {\n if (this.debugMode) {\n logger.error(\"Failed to initialize Posthog\", e);\n }\n }\n }\n\n enableDebugMode(): void {\n this.debugMode = true;\n logger.debug(\"Enabling debug mode\");\n\n try {\n mixpanel.enableDebugMode();\n posthog.enableDebugMode();\n } catch (e) {\n logger.error(\"Failed to enable debug mode\", e);\n }\n }\n\n disableDebugMode(): void {\n this.debugMode = false;\n logger.debug(\"Disabling debug mode\");\n\n try {\n mixpanel.disableDebugMode();\n posthog.disableDebugMode();\n } catch (e) {\n logger.error(\"Failed to disable debug mode\", e);\n }\n }\n\n identify(identity: InsightsIdentity): void {\n const { userId, accountId, organisationId, email, name, ...properties } =\n identity;\n\n // In very rare cases we might have a user without an account, so we'll\n // let null/undefined/blank strings through on that one\n if (!userId) {\n if (this.debugMode) {\n logger.warn(\"User ID not provided, skipping identify\");\n }\n return;\n }\n\n if (this.debugMode) {\n logger.info(\"Identifying user\", {\n userId,\n accountId,\n organisationId,\n email,\n name,\n ...properties,\n });\n }\n\n try {\n mixpanel.identify({\n userId,\n accountId,\n organisationId,\n email,\n name,\n ...properties,\n });\n } catch (e) {\n if (this.debugMode) {\n logger.error(\"Failed to identify user in Mixpanel\", e);\n }\n }\n\n try {\n posthog.identify({\n userId,\n accountId,\n organisationId,\n email,\n name,\n ...properties,\n });\n } catch (e) {\n if (this.debugMode) {\n logger.error(\"Failed to identify user in Posthog\", e);\n }\n }\n }\n\n trackPageView(options?: TrackPageViewOptions): void {\n const { excludeIds, includeDataLayer, ...properties } = options ?? {};\n\n if (this.debugMode) {\n logger.info(\"Tracking page view\");\n }\n\n try {\n mixpanel.trackPageView({ excludeIds, ...properties });\n } catch (e) {\n if (this.debugMode) {\n logger.error(\"Failed to track page view in Mixpanel\", e);\n }\n }\n\n try {\n posthog.trackPageView(properties);\n } catch (e) {\n if (this.debugMode) {\n logger.error(\"Failed to track page view in Posthog\", e);\n }\n }\n\n if (includeDataLayer) {\n try {\n datalayer.trackPageView(properties);\n } catch (e) {\n if (this.debugMode) {\n logger.error(\"Failed to track page view in GTM\", e);\n }\n }\n }\n }\n\n track(event: string, properties?: Record<string, unknown>): void {\n if (this.debugMode) {\n logger.info(\"Tracking event\", { event, properties });\n }\n\n try {\n mixpanel.track(event, properties);\n } catch (e) {\n if (this.debugMode) {\n logger.error(\"Failed to track event in Mixpanel\", e);\n }\n }\n\n try {\n posthog.track(event, properties);\n } catch (e) {\n if (this.debugMode) {\n logger.error(\"Failed to track event in Posthog\", e);\n }\n }\n\n try {\n datalayer.track(event, properties);\n } catch (e) {\n if (this.debugMode) {\n logger.error(\"Failed to track event in Datalayer\", e);\n }\n }\n }\n\n setupObserver(): () => void {\n // Helper to get all data-insight-* attributes from an element\n const getInsightAttributes = (\n element: HTMLElement,\n ): { event?: string; [key: string]: string | undefined } => {\n // limit how many data attributes we'll process\n const MAX_ATTRIBUTES = 10;\n let count = 0;\n\n const attributes: { event?: string; [key: string]: string | undefined } =\n {};\n\n for (const attr of Array.from(element.attributes)) {\n if (count >= MAX_ATTRIBUTES) break;\n if (attr.name.startsWith(\"data-insight-\")) {\n // Validate attribute name format\n if (!/^data-insight-[a-zA-Z0-9-]+$/.test(attr.name)) continue;\n\n // Sanitize attribute value\n if (typeof attr.value !== \"string\" || attr.value.length > 100)\n continue;\n\n // Convert data-insight-event-name to eventName\n const key = attr.name\n .replace(\"data-insight-\", \"\")\n .split(\"-\")\n .map((part: string, index: number) =>\n index === 0 ? part : part.charAt(0).toUpperCase() + part.slice(1),\n )\n .join(\"\");\n attributes[key] = attr.value;\n count++;\n }\n }\n return attributes;\n };\n\n // Helper to find closest element with data-insight attributes\n const findClosestElementWithInsights = (element: HTMLElement) => {\n let current = element;\n while (current && current !== document.body) {\n const insights = getInsightAttributes(current);\n if (Object.keys(insights).length > 0) {\n return insights;\n }\n\n current = current.parentElement as HTMLElement;\n }\n return null;\n };\n\n // Global click handler\n const handleClick = (event: MouseEvent): void => {\n if (!(event.target instanceof HTMLElement)) return;\n const insights = findClosestElementWithInsights(event.target);\n if (insights) {\n // Extract special properties if they exist\n const { event: eventName, ...properties } = insights;\n this.track(eventName || \"element_clicked\", properties);\n }\n };\n\n // Add listener to document body to catch all clicks\n document.body.addEventListener(\"click\", handleClick);\n\n // Return cleanup function in case it's needed\n return () => {\n document.body.removeEventListener(\"click\", handleClick);\n };\n }\n}\n"],"names":["datalayer","mixpanel","posthog","logger","InsightsService","initInsights","mixpanelToken","mixpanelAutoCapture","mixpanelApiHost","posthogApiKey","posthogApiHost","debug","debugMode","initMixpanel","e","error","initPosthog","enableDebugMode","disableDebugMode","identify","identity","userId","accountId","organisationId","email","name","properties","warn","info","trackPageView","options","excludeIds","includeDataLayer","track","event","setupObserver","getInsightAttributes","element","MAX_ATTRIBUTES","count","attributes","attr","Array","from","startsWith","test","value","length","key","replace","split","map","part","index","charAt","toUpperCase","slice","join","findClosestElementWithInsights","current","document","body","insights","Object","keys","parentElement","handleClick","target","HTMLElement","eventName","addEventListener","removeEventListener"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/core/insights/types.ts"],"sourcesContent":["export type InsightsConfig = {\n debug: boolean;\n mixpanelToken: string;\n mixpanelAutoCapture: boolean;\n
|
|
1
|
+
{"version":3,"sources":["../../../src/core/insights/types.ts"],"sourcesContent":["export type InsightsConfig = {\n debug: boolean;\n mixpanelToken: string;\n mixpanelAutoCapture: boolean;\n mixpanelApiHost?: string;\n posthogApiKey: string;\n posthogApiHost: string;\n};\n\n// Define the interface for our analytics service\nexport interface AnalyticsService {\n initInsights: (config: InsightsConfig) => void;\n enableDebugMode: () => void;\n disableDebugMode: () => void;\n identify: (identity: InsightsIdentity) => void;\n trackPageView: (options?: TrackPageViewOptions) => void;\n track: (event: string, properties?: Record<string, unknown>) => void;\n setupObserver: () => () => void;\n}\n\n// Command type for our queue\nexport type Command = {\n methodName: keyof AnalyticsService;\n args: unknown[];\n};\n\nexport type InsightsIdentity = {\n userId: string;\n accountId: string;\n organisationId?: string;\n email?: string;\n name?: string;\n} & Record<string, unknown>;\n\nexport type TrackPageViewOptions = {\n includeDataLayer?: boolean;\n excludeIds?: string[];\n} & Record<string, unknown>;\n"],"names":[],"mappings":"AAkCA,QAG4B"}
|
package/index.d.ts
CHANGED
|
@@ -304,14 +304,6 @@ export default Button;
|
|
|
304
304
|
//# sourceMappingURL=Button.d.ts.map
|
|
305
305
|
}
|
|
306
306
|
|
|
307
|
-
declare module '@ably/ui/core/Code/Code.test' {
|
|
308
|
-
/**
|
|
309
|
-
* @vitest-environment jsdom
|
|
310
|
-
*/
|
|
311
|
-
export {};
|
|
312
|
-
//# sourceMappingURL=Code.test.d.ts.map
|
|
313
|
-
}
|
|
314
|
-
|
|
315
307
|
declare module '@ably/ui/core/Code/component' {
|
|
316
308
|
export default highlightEl;
|
|
317
309
|
function highlightEl(el: any): void;
|
|
@@ -348,14 +340,6 @@ export default ApiKeySelector;
|
|
|
348
340
|
//# sourceMappingURL=ApiKeySelector.d.ts.map
|
|
349
341
|
}
|
|
350
342
|
|
|
351
|
-
declare module '@ably/ui/core/CodeSnippet/CodeSnippet.test' {
|
|
352
|
-
/**
|
|
353
|
-
* @vitest-environment jsdom
|
|
354
|
-
*/
|
|
355
|
-
export {};
|
|
356
|
-
//# sourceMappingURL=CodeSnippet.test.d.ts.map
|
|
357
|
-
}
|
|
358
|
-
|
|
359
343
|
declare module '@ably/ui/core/CodeSnippet/CopyButton' {
|
|
360
344
|
type CopyButtonProps = {
|
|
361
345
|
onCopy: () => void;
|
|
@@ -430,11 +414,6 @@ export default languages;
|
|
|
430
414
|
//# sourceMappingURL=languages.d.ts.map
|
|
431
415
|
}
|
|
432
416
|
|
|
433
|
-
declare module '@ably/ui/core/CodeSnippet/languages.test' {
|
|
434
|
-
export {};
|
|
435
|
-
//# sourceMappingURL=languages.test.d.ts.map
|
|
436
|
-
}
|
|
437
|
-
|
|
438
417
|
declare module '@ably/ui/core/CodeSnippet' {
|
|
439
418
|
import React from "react";
|
|
440
419
|
import { SDKType } from "@ably/ui/core/CodeSnippet/languages";
|
|
@@ -6321,14 +6300,6 @@ export function useThemedScrollpoints(scrollpoints: ThemedScrollpoint[]): string
|
|
|
6321
6300
|
//# sourceMappingURL=use-themed-scrollpoints.d.ts.map
|
|
6322
6301
|
}
|
|
6323
6302
|
|
|
6324
|
-
declare module '@ably/ui/core/hooks/use-themed-scrollpoints.test' {
|
|
6325
|
-
/**
|
|
6326
|
-
* @vitest-environment jsdom
|
|
6327
|
-
*/
|
|
6328
|
-
export {};
|
|
6329
|
-
//# sourceMappingURL=use-themed-scrollpoints.test.d.ts.map
|
|
6330
|
-
}
|
|
6331
|
-
|
|
6332
6303
|
declare module '@ably/ui/core/insights/command-queue' {
|
|
6333
6304
|
import { AnalyticsService, InsightsConfig, InsightsIdentity, TrackPageViewOptions } from "@ably/ui/core/types";
|
|
6334
6305
|
export class InsightsCommandQueue implements AnalyticsService {
|
|
@@ -6345,8 +6316,6 @@ export class InsightsCommandQueue implements AnalyticsService {
|
|
|
6345
6316
|
identify(_identity: InsightsIdentity): void;
|
|
6346
6317
|
trackPageView(_options?: TrackPageViewOptions): void;
|
|
6347
6318
|
track(_event: string, _properties?: Record<string, unknown>): void;
|
|
6348
|
-
startSessionRecording(): void;
|
|
6349
|
-
stopSessionRecording(): void;
|
|
6350
6319
|
setupObserver(): () => void;
|
|
6351
6320
|
}
|
|
6352
6321
|
//# sourceMappingURL=command-queue.d.ts.map
|
|
@@ -6372,20 +6341,10 @@ export const disableDebugMode: () => void;
|
|
|
6372
6341
|
export const identify: (identity: InsightsIdentity) => void;
|
|
6373
6342
|
export const trackPageView: (options?: TrackPageViewOptions) => void;
|
|
6374
6343
|
export const track: (event: string, properties?: Record<string, unknown>) => void;
|
|
6375
|
-
export const startSessionRecording: () => void;
|
|
6376
|
-
export const stopSessionRecording: () => void;
|
|
6377
6344
|
export const setupObserver: () => () => void;
|
|
6378
6345
|
//# sourceMappingURL=index.d.ts.map
|
|
6379
6346
|
}
|
|
6380
6347
|
|
|
6381
|
-
declare module '@ably/ui/core/insights/index.test' {
|
|
6382
|
-
/**
|
|
6383
|
-
* @vitest-environment jsdom
|
|
6384
|
-
*/
|
|
6385
|
-
export {};
|
|
6386
|
-
//# sourceMappingURL=index.test.d.ts.map
|
|
6387
|
-
}
|
|
6388
|
-
|
|
6389
6348
|
declare module '@ably/ui/core/insights/logger' {
|
|
6390
6349
|
export const debug: (...args: unknown[]) => void;
|
|
6391
6350
|
export const info: (...args: unknown[]) => void;
|
|
@@ -6396,25 +6355,16 @@ export const error: (...args: unknown[]) => void;
|
|
|
6396
6355
|
|
|
6397
6356
|
declare module '@ably/ui/core/insights/mixpanel' {
|
|
6398
6357
|
import { InsightsIdentity, TrackPageViewOptions } from "@ably/ui/core/types";
|
|
6399
|
-
export const
|
|
6358
|
+
export const MIXPANEL_EU_API_HOST = "https://api-eu.mixpanel.com";
|
|
6359
|
+
export const initMixpanel: (token: string, autoCapture?: boolean, debug?: boolean, apiHost?: string) => void;
|
|
6400
6360
|
export const enableDebugMode: () => void;
|
|
6401
6361
|
export const disableDebugMode: () => void;
|
|
6402
6362
|
export const identify: ({ userId, accountId, organisationId, email, name, ...properties }: InsightsIdentity) => void;
|
|
6403
6363
|
export const trackPageView: (properties?: TrackPageViewOptions) => void;
|
|
6404
6364
|
export const track: (event: string, properties?: Record<string, unknown>) => void;
|
|
6405
|
-
export const startSessionRecording: () => void;
|
|
6406
|
-
export const stopSessionRecording: () => void;
|
|
6407
6365
|
//# sourceMappingURL=mixpanel.d.ts.map
|
|
6408
6366
|
}
|
|
6409
6367
|
|
|
6410
|
-
declare module '@ably/ui/core/insights/mixpanel.test' {
|
|
6411
|
-
/**
|
|
6412
|
-
* @vitest-environment jsdom
|
|
6413
|
-
*/
|
|
6414
|
-
export {};
|
|
6415
|
-
//# sourceMappingURL=mixpanel.test.d.ts.map
|
|
6416
|
-
}
|
|
6417
|
-
|
|
6418
6368
|
declare module '@ably/ui/core/insights/posthog' {
|
|
6419
6369
|
import { InsightsIdentity } from "@ably/ui/core/types";
|
|
6420
6370
|
export const initPosthog: (apiKey: string, apiHost: string) => void;
|
|
@@ -6423,31 +6373,19 @@ export const disableDebugMode: () => void;
|
|
|
6423
6373
|
export const identify: ({ userId, accountId, organisationId, email, name, ...properties }: InsightsIdentity) => void;
|
|
6424
6374
|
export const trackPageView: (properties?: Record<string, unknown>) => void;
|
|
6425
6375
|
export const track: (event: string, properties?: Record<string, unknown>) => void;
|
|
6426
|
-
export const startSessionRecording: () => void;
|
|
6427
|
-
export const stopSessionRecording: () => void;
|
|
6428
6376
|
//# sourceMappingURL=posthog.d.ts.map
|
|
6429
6377
|
}
|
|
6430
6378
|
|
|
6431
|
-
declare module '@ably/ui/core/insights/posthog.test' {
|
|
6432
|
-
/**
|
|
6433
|
-
* @vitest-environment jsdom
|
|
6434
|
-
*/
|
|
6435
|
-
export {};
|
|
6436
|
-
//# sourceMappingURL=posthog.test.d.ts.map
|
|
6437
|
-
}
|
|
6438
|
-
|
|
6439
6379
|
declare module '@ably/ui/core/insights/service' {
|
|
6440
6380
|
import type { AnalyticsService, InsightsConfig, InsightsIdentity, TrackPageViewOptions } from "@ably/ui/core/types";
|
|
6441
6381
|
export class InsightsService implements AnalyticsService {
|
|
6442
6382
|
private debugMode;
|
|
6443
|
-
initInsights({ mixpanelToken, mixpanelAutoCapture,
|
|
6383
|
+
initInsights({ mixpanelToken, mixpanelAutoCapture, mixpanelApiHost, posthogApiKey, posthogApiHost, debug, }: InsightsConfig): void;
|
|
6444
6384
|
enableDebugMode(): void;
|
|
6445
6385
|
disableDebugMode(): void;
|
|
6446
6386
|
identify(identity: InsightsIdentity): void;
|
|
6447
6387
|
trackPageView(options?: TrackPageViewOptions): void;
|
|
6448
6388
|
track(event: string, properties?: Record<string, unknown>): void;
|
|
6449
|
-
startSessionRecording(): void;
|
|
6450
|
-
stopSessionRecording(): void;
|
|
6451
6389
|
setupObserver(): () => void;
|
|
6452
6390
|
}
|
|
6453
6391
|
//# sourceMappingURL=service.d.ts.map
|
|
@@ -6458,7 +6396,7 @@ export type InsightsConfig = {
|
|
|
6458
6396
|
debug: boolean;
|
|
6459
6397
|
mixpanelToken: string;
|
|
6460
6398
|
mixpanelAutoCapture: boolean;
|
|
6461
|
-
|
|
6399
|
+
mixpanelApiHost?: string;
|
|
6462
6400
|
posthogApiKey: string;
|
|
6463
6401
|
posthogApiHost: string;
|
|
6464
6402
|
};
|
|
@@ -6469,8 +6407,6 @@ export interface AnalyticsService {
|
|
|
6469
6407
|
identify: (identity: InsightsIdentity) => void;
|
|
6470
6408
|
trackPageView: (options?: TrackPageViewOptions) => void;
|
|
6471
6409
|
track: (event: string, properties?: Record<string, unknown>) => void;
|
|
6472
|
-
startSessionRecording: () => void;
|
|
6473
|
-
stopSessionRecording: () => void;
|
|
6474
6410
|
setupObserver: () => () => void;
|
|
6475
6411
|
}
|
|
6476
6412
|
export type Command = {
|
|
@@ -6646,25 +6582,6 @@ export const sanitizeMarketingHtml: (input: string | null | undefined) => string
|
|
|
6646
6582
|
//# sourceMappingURL=sanitize-html.d.ts.map
|
|
6647
6583
|
}
|
|
6648
6584
|
|
|
6649
|
-
declare module '@ably/ui/core/utils/sanitize-html.test' {
|
|
6650
|
-
/**
|
|
6651
|
-
* @vitest-environment jsdom
|
|
6652
|
-
*
|
|
6653
|
-
* Adversarial test corpus for the sanitize-html primitives. Cases are sourced
|
|
6654
|
-
* from public XSS cheatsheets (OWASP, PortSwigger, html5sec.org) and from
|
|
6655
|
-
* past DOMPurify CVEs / advisories. They exercise the wrappers, not DOMPurify
|
|
6656
|
-
* itself — the assertion in every case is "the rendered string does not
|
|
6657
|
-
* carry an executable payload through to a DOM that would fire on hydration".
|
|
6658
|
-
*
|
|
6659
|
-
* The shape of each assertion is intentionally strict: we don't trust that
|
|
6660
|
-
* "alert" or "javascript" not appearing as a substring means safety, so most
|
|
6661
|
-
* checks combine "tag/attr stripped" + "no live-handler attribute survives"
|
|
6662
|
-
* + "raw text content may survive but is rendered as text".
|
|
6663
|
-
*/
|
|
6664
|
-
export {};
|
|
6665
|
-
//# sourceMappingURL=sanitize-html.test.d.ts.map
|
|
6666
|
-
}
|
|
6667
|
-
|
|
6668
6585
|
declare module '@ably/ui/core/utils/syntax-highlighter-registry' {
|
|
6669
6586
|
export default registry;
|
|
6670
6587
|
const registry: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ably/ui",
|
|
3
|
-
"version": "18.
|
|
3
|
+
"version": "18.3.1",
|
|
4
4
|
"description": "Home of the Ably design system library ([design.ably.com](https://design.ably.com)). It provides a showcase, development/test environment and a publishing pipeline for different distributables.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -37,11 +37,12 @@
|
|
|
37
37
|
"@types/react": "^18.3.1",
|
|
38
38
|
"@types/react-dom": "^18.3.0",
|
|
39
39
|
"@types/svg-sprite": "^0.0.39",
|
|
40
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
41
|
-
"@typescript-eslint/parser": "^8.
|
|
40
|
+
"@typescript-eslint/eslint-plugin": "^8.61.0",
|
|
41
|
+
"@typescript-eslint/parser": "^8.61.0",
|
|
42
42
|
"@vitejs/plugin-react-swc": "^4.3.0",
|
|
43
|
-
"@vitest/browser": "
|
|
44
|
-
"@vitest/
|
|
43
|
+
"@vitest/browser": "^4.1.8",
|
|
44
|
+
"@vitest/browser-playwright": "^4.1.8",
|
|
45
|
+
"@vitest/coverage-v8": "^4.1.8",
|
|
45
46
|
"autoprefixer": "^10.5.0",
|
|
46
47
|
"eslint": "^8.57.1",
|
|
47
48
|
"eslint-config-prettier": "^10.1.8",
|
|
@@ -49,15 +50,15 @@
|
|
|
49
50
|
"eslint-plugin-react": "^7.35.0",
|
|
50
51
|
"eslint-plugin-react-hooks": "^7.1.1",
|
|
51
52
|
"eslint-plugin-react-perf": "^3.3.3",
|
|
52
|
-
"eslint-plugin-storybook": "^10.
|
|
53
|
+
"eslint-plugin-storybook": "^10.4.0",
|
|
53
54
|
"heroicons": "^2.2.0",
|
|
54
55
|
"http-server": "14.1.1",
|
|
55
56
|
"jsdom": "^29.1.0",
|
|
56
57
|
"mixpanel-browser": "^2.79.0",
|
|
57
58
|
"msw": "2.14.3",
|
|
58
59
|
"msw-storybook-addon": "^2.0.7",
|
|
59
|
-
"playwright": "^1.
|
|
60
|
-
"posthog-js": "1.
|
|
60
|
+
"playwright": "^1.60.0",
|
|
61
|
+
"posthog-js": "1.383.3",
|
|
61
62
|
"prettier": "^3.8.0",
|
|
62
63
|
"storybook": "^10.4.0",
|
|
63
64
|
"svg-sprite": "^2.0.4",
|
|
@@ -65,7 +66,7 @@
|
|
|
65
66
|
"ts-node": "^10.9.2",
|
|
66
67
|
"typescript": "6.0.2",
|
|
67
68
|
"vite": "^7.3.2",
|
|
68
|
-
"vitest": "^
|
|
69
|
+
"vitest": "^4.1.8"
|
|
69
70
|
},
|
|
70
71
|
"dependencies": {
|
|
71
72
|
"@heroicons/react": "^2.2.0",
|
|
@@ -78,7 +79,7 @@
|
|
|
78
79
|
"@radix-ui/react-tooltip": "^1.2.8",
|
|
79
80
|
"array-flat-polyfill": "^1.0.1",
|
|
80
81
|
"clsx": "^2.1.1",
|
|
81
|
-
"dompurify": "^3.4.
|
|
82
|
+
"dompurify": "^3.4.9",
|
|
82
83
|
"embla-carousel": "^8.6.0",
|
|
83
84
|
"embla-carousel-autoplay": "^8.6.0",
|
|
84
85
|
"embla-carousel-react": "^8.6.0",
|