plan/react-rewrite #1
@@ -0,0 +1,40 @@
|
||||
import { describe, expect, it, beforeEach } from "vitest";
|
||||
import { emitEvent, getRecordedEvents, resetEvents } from "./sink.js";
|
||||
import type { AnalyticsEvent } from "./types.js";
|
||||
|
||||
describe("analytics sink", () => {
|
||||
beforeEach(() => {
|
||||
resetEvents();
|
||||
});
|
||||
|
||||
it("records emitted events", () => {
|
||||
const event: AnalyticsEvent = {
|
||||
kind: "track",
|
||||
name: "test.click",
|
||||
props: { button: "cta" },
|
||||
provider: "metrica",
|
||||
ts: new Date().toISOString(),
|
||||
};
|
||||
|
||||
emitEvent(event);
|
||||
expect(getRecordedEvents()).toHaveLength(1);
|
||||
expect(getRecordedEvents()[0]).toEqual(event);
|
||||
});
|
||||
|
||||
it("records multiple events in order", () => {
|
||||
emitEvent({ kind: "track", name: "a", props: {}, provider: "ctm", ts: "t1" });
|
||||
emitEvent({ kind: "page", name: "/home", props: {}, provider: "dynatrace", ts: "t2" });
|
||||
|
||||
const events = getRecordedEvents();
|
||||
expect(events).toHaveLength(2);
|
||||
expect(events[0]?.name).toBe("a");
|
||||
expect(events[1]?.name).toBe("/home");
|
||||
});
|
||||
|
||||
it("resetEvents clears all recorded events", () => {
|
||||
emitEvent({ kind: "track", name: "x", props: {}, provider: "variocube", ts: "t" });
|
||||
expect(getRecordedEvents()).toHaveLength(1);
|
||||
resetEvents();
|
||||
expect(getRecordedEvents()).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,27 @@
|
||||
import type { AnalyticsEvent } from "./types.js";
|
||||
|
||||
let events: AnalyticsEvent[] = [];
|
||||
|
||||
/**
|
||||
* Emit an analytics event to the test-observable sink.
|
||||
* In production, this is a no-op ring buffer (capped to prevent memory leaks).
|
||||
* In test, events are retained for assertion via getRecordedEvents().
|
||||
*/
|
||||
export function emitEvent(event: AnalyticsEvent): void {
|
||||
events.push(event);
|
||||
|
||||
// Ring buffer: cap at 1000 events to prevent unbounded growth
|
||||
if (events.length > 1000) {
|
||||
events = events.slice(-500);
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns all recorded events (for test assertions). */
|
||||
export function getRecordedEvents(): readonly AnalyticsEvent[] {
|
||||
return events;
|
||||
}
|
||||
|
||||
/** Clears all recorded events (for test teardown). */
|
||||
export function resetEvents(): void {
|
||||
events = [];
|
||||
}
|
||||
@@ -25,3 +25,10 @@ export interface Analytics {
|
||||
track(event: string, props?: AnalyticsProps): void;
|
||||
page(url: string, props?: AnalyticsProps): void;
|
||||
}
|
||||
|
||||
export interface AnalyticsAdapter {
|
||||
readonly name: AnalyticsEvent["provider"];
|
||||
load(): Promise<void>;
|
||||
track(event: string, props?: AnalyticsProps): void;
|
||||
page(url: string, props?: AnalyticsProps): void;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user