Skip to main content
Glama
thoughtspot
by thoughtspot
mixpanel-tracker.spec.ts15 kB
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; import { MixpanelTracker } from "../../../src/metrics/mixpanel/mixpanel"; import { MixpanelClient } from "../../../src/metrics/mixpanel/mixpanel-client"; import type { SessionInfo } from "../../../src/thoughtspot/types"; // Mock the MixpanelClient vi.mock("../../../src/metrics/mixpanel/mixpanel-client"); describe("MixpanelTracker", () => { const mockSessionInfo: SessionInfo = { mixpanelToken: "test-mixpanel-token", clusterName: "test-cluster", clusterId: "cluster-123", userGUID: "user-123", userName: "testuser", releaseVersion: "8.0.0", currentOrgId: "org-123", privileges: ["READ", "WRITE"] }; const mockClient = { clientName: "test-client", clientId: "client-123", registrationDate: "2024-01-01" }; let tracker: MixpanelTracker; let mockMixpanelClient: any; let consoleErrorSpy: any; let consoleDebugSpy: any; beforeEach(() => { // Clear all mocks vi.clearAllMocks(); // Create mock MixpanelClient instance mockMixpanelClient = { identify: vi.fn(), register: vi.fn(), track: vi.fn().mockResolvedValue("1") }; // Mock the MixpanelClient constructor (MixpanelClient as any).mockImplementation(() => mockMixpanelClient); // Mock console methods properly consoleErrorSpy = vi.spyOn(console, "error").mockImplementation(() => { }); consoleDebugSpy = vi.spyOn(console, "debug").mockImplementation(() => { }); }); afterEach(() => { vi.restoreAllMocks(); }); describe("constructor", () => { it("should create MixpanelTracker instance", () => { tracker = new MixpanelTracker(mockSessionInfo, mockClient); expect(tracker).toBeInstanceOf(MixpanelTracker); expect(MixpanelClient).toHaveBeenCalledWith(mockSessionInfo.mixpanelToken); }); it("should initialize MixpanelClient with correct token", () => { tracker = new MixpanelTracker(mockSessionInfo, mockClient); expect(MixpanelClient).toHaveBeenCalledWith(mockSessionInfo.mixpanelToken); }); it("should call identify with userGUID", () => { tracker = new MixpanelTracker(mockSessionInfo, mockClient); expect(mockMixpanelClient.identify).toHaveBeenCalledWith(mockSessionInfo.userGUID); }); it("should register super properties with session and client info", () => { tracker = new MixpanelTracker(mockSessionInfo, mockClient); expect(mockMixpanelClient.register).toHaveBeenCalledWith({ clusterId: mockSessionInfo.clusterId, clusterName: mockSessionInfo.clusterName, releaseVersion: mockSessionInfo.releaseVersion, clientName: mockClient.clientName, clientId: mockClient.clientId, registrationDate: mockClient.registrationDate, }); }); it("should work with empty client object", () => { tracker = new MixpanelTracker(mockSessionInfo, {}); expect(mockMixpanelClient.register).toHaveBeenCalledWith({ clusterId: mockSessionInfo.clusterId, clusterName: mockSessionInfo.clusterName, releaseVersion: mockSessionInfo.releaseVersion, clientName: undefined, clientId: undefined, registrationDate: undefined, }); }); it("should work without client parameter", () => { tracker = new MixpanelTracker(mockSessionInfo); expect(mockMixpanelClient.register).toHaveBeenCalledWith({ clusterId: mockSessionInfo.clusterId, clusterName: mockSessionInfo.clusterName, releaseVersion: mockSessionInfo.releaseVersion, clientName: undefined, clientId: undefined, registrationDate: undefined, }); }); it("should handle partial client object", () => { const partialClient = { clientName: "partial-client" }; tracker = new MixpanelTracker(mockSessionInfo, partialClient); expect(mockMixpanelClient.register).toHaveBeenCalledWith({ clusterId: mockSessionInfo.clusterId, clusterName: mockSessionInfo.clusterName, releaseVersion: mockSessionInfo.releaseVersion, clientName: "partial-client", clientId: undefined, registrationDate: undefined, }); }); it("should handle null client object", () => { tracker = new MixpanelTracker(mockSessionInfo, null as any); expect(mockMixpanelClient.register).toHaveBeenCalledWith({ clusterId: mockSessionInfo.clusterId, clusterName: mockSessionInfo.clusterName, releaseVersion: mockSessionInfo.releaseVersion, clientName: undefined, clientId: undefined, registrationDate: undefined, }); }); }); describe("track", () => { beforeEach(() => { tracker = new MixpanelTracker(mockSessionInfo, mockClient); }); it("should track event successfully", async () => { const eventName = "test-event"; const props = { action: "click", page: "home" }; await tracker.track(eventName, props); expect(mockMixpanelClient.track).toHaveBeenCalledWith(eventName, props); expect(consoleDebugSpy).toHaveBeenCalledWith( "Tracked event: ", eventName, " with props: ", props ); }); it("should handle empty properties", async () => { const eventName = "test-event"; const props = {}; await tracker.track(eventName, props); expect(mockMixpanelClient.track).toHaveBeenCalledWith(eventName, props); expect(consoleDebugSpy).toHaveBeenCalledWith( "Tracked event: ", eventName, " with props: ", props ); }); it("should handle complex properties", async () => { const eventName = "test-event"; const props = { user: { id: "123", preferences: { theme: "dark", language: "en" } }, metadata: { tags: ["tag1", "tag2"], timestamp: Date.now() } }; await tracker.track(eventName, props); expect(mockMixpanelClient.track).toHaveBeenCalledWith(eventName, props); }); it("should handle special characters in event name", async () => { const eventName = "test-event-with-special-chars!@#$%^&*()"; const props = { action: "click" }; await tracker.track(eventName, props); expect(mockMixpanelClient.track).toHaveBeenCalledWith(eventName, props); }); it("should handle tracking errors gracefully", async () => { const eventName = "test-event"; const props = { action: "click" }; const error = new Error("Network error"); mockMixpanelClient.track.mockRejectedValue(error); await tracker.track(eventName, props); expect(mockMixpanelClient.track).toHaveBeenCalledWith(eventName, props); expect(consoleErrorSpy).toHaveBeenCalledWith( "Error tracking event: ", error, " for eventName: ", eventName, " and props: ", props ); expect(consoleDebugSpy).toHaveBeenCalledWith( "Tracked event: ", eventName, " with props: ", props ); }); it("should handle different types of errors", async () => { const eventName = "test-event"; const props = { action: "click" }; const error = "String error"; mockMixpanelClient.track.mockRejectedValue(error); await tracker.track(eventName, props); expect(consoleErrorSpy).toHaveBeenCalledWith( "Error tracking event: ", error, " for eventName: ", eventName, " and props: ", props ); }); it("should handle null error", async () => { const eventName = "test-event"; const props = { action: "click" }; mockMixpanelClient.track.mockRejectedValue(null); await tracker.track(eventName, props); expect(consoleErrorSpy).toHaveBeenCalledWith( "Error tracking event: ", null, " for eventName: ", eventName, " and props: ", props ); }); it("should handle undefined error", async () => { const eventName = "test-event"; const props = { action: "click" }; mockMixpanelClient.track.mockRejectedValue(undefined); await tracker.track(eventName, props); expect(consoleErrorSpy).toHaveBeenCalledWith( "Error tracking event: ", undefined, " for eventName: ", eventName, " and props: ", props ); }); it("should always log debug message even when error occurs", async () => { const eventName = "test-event"; const props = { action: "click" }; const error = new Error("Network error"); mockMixpanelClient.track.mockRejectedValue(error); await tracker.track(eventName, props); expect(consoleDebugSpy).toHaveBeenCalledWith( "Tracked event: ", eventName, " with props: ", props ); }); it("should handle multiple consecutive track calls", async () => { const events = [ { name: "event1", props: { action: "click" } }, { name: "event2", props: { action: "submit" } }, { name: "event3", props: { action: "scroll" } } ]; for (const event of events) { await tracker.track(event.name, event.props); } expect(mockMixpanelClient.track).toHaveBeenCalledTimes(3); expect(mockMixpanelClient.track).toHaveBeenNthCalledWith(1, "event1", { action: "click" }); expect(mockMixpanelClient.track).toHaveBeenNthCalledWith(2, "event2", { action: "submit" }); expect(mockMixpanelClient.track).toHaveBeenNthCalledWith(3, "event3", { action: "scroll" }); }); it("should handle async operations correctly", async () => { const eventName = "test-event"; const props = { action: "click" }; // Simulate async delay let resolved = false; mockMixpanelClient.track.mockImplementation(() => new Promise(resolve => { setTimeout(() => { resolved = true; resolve("1"); }, 10); }) ); expect(resolved).toBe(false); await tracker.track(eventName, props); expect(resolved).toBe(true); expect(mockMixpanelClient.track).toHaveBeenCalledWith(eventName, props); }); }); describe("integration with Tracker interface", () => { it("should implement Tracker interface correctly", () => { tracker = new MixpanelTracker(mockSessionInfo, mockClient); // Check that the track method exists and is callable expect(typeof tracker.track).toBe("function"); expect(tracker.track).toBeInstanceOf(Function); }); it("should handle TrackEvent enum values", async () => { tracker = new MixpanelTracker(mockSessionInfo, mockClient); // Import TrackEvent enum const { TrackEvent } = await import("../../../src/metrics"); await tracker.track(TrackEvent.CallTool, { toolName: "test-tool" }); await tracker.track(TrackEvent.Init, { version: "1.0.0" }); expect(mockMixpanelClient.track).toHaveBeenCalledTimes(2); expect(mockMixpanelClient.track).toHaveBeenNthCalledWith(1, TrackEvent.CallTool, { toolName: "test-tool" }); expect(mockMixpanelClient.track).toHaveBeenNthCalledWith(2, TrackEvent.Init, { version: "1.0.0" }); }); }); describe("error scenarios", () => { beforeEach(() => { tracker = new MixpanelTracker(mockSessionInfo, mockClient); }); it("should handle MixpanelClient constructor errors", () => { const constructorError = new Error("Failed to create client"); (MixpanelClient as any).mockImplementation(() => { throw constructorError; }); expect(() => new MixpanelTracker(mockSessionInfo, mockClient)).toThrow("Failed to create client"); }); it("should handle identify method errors", () => { mockMixpanelClient.identify.mockImplementation(() => { throw new Error("Identify failed"); }); expect(() => new MixpanelTracker(mockSessionInfo, mockClient)).toThrow("Identify failed"); }); it("should handle register method errors", () => { mockMixpanelClient.register.mockImplementation(() => { throw new Error("Register failed"); }); expect(() => new MixpanelTracker(mockSessionInfo, mockClient)).toThrow("Register failed"); }); it("should handle track method throwing synchronous errors", async () => { const syncError = new Error("Sync error"); mockMixpanelClient.track.mockImplementation(() => { throw syncError; }); await tracker.track("test-event", { action: "click" }); expect(consoleErrorSpy).toHaveBeenCalledWith( "Error tracking event: ", syncError, " for eventName: ", "test-event", " and props: ", { action: "click" } ); }); }); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/thoughtspot/mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server