mirror of
https://github.com/stack-auth/stack.git
synced 2026-06-13 21:01:21 +08:00
Merge branch 'dev' into llm-mcp-flow
This commit is contained in:
commit
516c42439a
@ -1 +1 @@
|
||||
Please compare `dev` to `main` and ensure that all migrations are backwards compatible. In what ways could breakage occur? Report the result to me in detail.
|
||||
Please compare `dev` to `main` and ensure that all migrations are backwards compatible. In what ways could breakage occur? Report the result to me in detail. Anything else that's scary that could occur, or that we should think about while migrating?
|
||||
|
||||
@ -0,0 +1,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 as { event_type: string }[]).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();
|
||||
}
|
||||
});
|
||||
});
|
||||
@ -62,13 +62,12 @@ export class EventTracker {
|
||||
start() {
|
||||
if (this._started) return;
|
||||
if (!isBrowserLike()) return;
|
||||
const screenObject = Object.getOwnPropertyDescriptor(window, "screen")?.value;
|
||||
if (
|
||||
typeof window.addEventListener !== "function"
|
||||
|| typeof window.removeEventListener !== "function"
|
||||
|| typeof document.addEventListener !== "function"
|
||||
|| typeof document.removeEventListener !== "function"
|
||||
|| !hasScreenDimensions(screenObject)
|
||||
|| !hasScreenDimensions(window.screen)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
@ -105,7 +104,7 @@ export class EventTracker {
|
||||
}
|
||||
|
||||
private _capturePageView(entryType: "initial" | "push" | "replace" | "pop") {
|
||||
const screenObject = Object.getOwnPropertyDescriptor(window, "screen")?.value;
|
||||
const screenObject = window.screen;
|
||||
if (!hasScreenDimensions(screenObject)) {
|
||||
return;
|
||||
}
|
||||
@ -134,7 +133,7 @@ export class EventTracker {
|
||||
private _setupPageViewCapture() {
|
||||
// Fire initial page-view
|
||||
this._capturePageView("initial");
|
||||
const historyObject = Object.getOwnPropertyDescriptor(window, "history")?.value;
|
||||
const historyObject = window.history;
|
||||
if (!hasHistoryMethods(historyObject)) {
|
||||
return;
|
||||
}
|
||||
@ -246,7 +245,7 @@ export class EventTracker {
|
||||
}
|
||||
|
||||
// Restore history methods
|
||||
const historyObject = Object.getOwnPropertyDescriptor(window, "history")?.value;
|
||||
const historyObject = window.history;
|
||||
if (hasHistoryMethods(historyObject)) {
|
||||
if (this._originalPushState) {
|
||||
historyObject.pushState = this._originalPushState;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user