-
Notifications
You must be signed in to change notification settings - Fork 515
Expand file tree
/
Copy pathevent-tracker.test.ts
More file actions
105 lines (87 loc) · 3.04 KB
/
event-tracker.test.ts
File metadata and controls
105 lines (87 loc) · 3.04 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
// @vitest-environment jsdom
import { Result } from "@stackframe/stack-shared/dist/utils/results";
import { afterEach, describe, expect, it, vi } from "vitest";
import { EventTracker } from "./event-tracker";
async function advancePastAccessTokenRefresh() {
await vi.advanceTimersByTimeAsync(10_000);
await Promise.resolve();
await vi.advanceTimersByTimeAsync(10_000);
await Promise.resolve();
}
function getSentEventTypes(sentBodies: string[]) {
const [body] = sentBodies;
const payload = JSON.parse(body);
if (typeof payload !== "object" || payload === null || !("events" in payload) || !Array.isArray(payload.events)) {
throw new Error("Expected analytics batch payload to include an events array.");
}
return payload.events.map((event) => event.event_type);
}
describe("EventTracker", () => {
afterEach(() => {
vi.useRealTimers();
});
it("captures events when browser globals are exposed as accessor descriptors", async () => {
vi.useFakeTimers();
document.body.innerHTML = "<button>Open project</button>";
const screenDescriptor = Object.getOwnPropertyDescriptor(window, "screen");
const historyDescriptor = Object.getOwnPropertyDescriptor(window, "history");
expect(screenDescriptor?.value).toBeUndefined();
expect(historyDescriptor?.value).toBeUndefined();
expect(screenDescriptor?.get).toBeTypeOf("function");
expect(historyDescriptor?.get).toBeTypeOf("function");
const sentBodies: string[] = [];
const tracker = new EventTracker({
projectId: "internal",
getAccessToken: async () => "access-token",
sendBatch: async (body) => {
sentBodies.push(body);
return Result.ok(new Response());
},
});
try {
tracker.start();
document.querySelector("button")?.dispatchEvent(new MouseEvent("click", {
bubbles: true,
clientX: 12,
clientY: 34,
}));
await advancePastAccessTokenRefresh();
expect(getSentEventTypes(sentBodies)).toMatchInlineSnapshot(`
[
"$page-view",
"$click",
]
`);
} finally {
tracker.stop();
}
});
it("captures client-side navigations when history is exposed as an accessor descriptor", async () => {
vi.useFakeTimers();
const historyDescriptor = Object.getOwnPropertyDescriptor(window, "history");
expect(historyDescriptor?.value).toBeUndefined();
expect(historyDescriptor?.get).toBeTypeOf("function");
const sentBodies: string[] = [];
const tracker = new EventTracker({
projectId: "internal",
getAccessToken: async () => "access-token",
sendBatch: async (body) => {
sentBodies.push(body);
return Result.ok(new Response());
},
});
try {
tracker.start();
window.history.pushState({}, "", "/projects/test-project");
await advancePastAccessTokenRefresh();
expect(getSentEventTypes(sentBodies)).toMatchInlineSnapshot(`
[
"$page-view",
"$page-view",
]
`);
} finally {
tracker.stop();
}
});
});