UNPKG

mixpanel-react-native

Version:

Official React Native Tracking Library for Mixpanel Analytics

902 lines (796 loc) 27.9 kB
import { MixpanelType } from "mixpanel-react-native/javascript/mixpanel-constants"; import { exp } from "react-native/Libraries/Animated/src/Easing"; import { get } from "react-native/Libraries/Utilities/PixelRatio"; jest.mock("mixpanel-react-native/javascript/mixpanel-core", () => ({ MixpanelCore: jest.fn().mockImplementation(() => ({ initialize: jest.fn(), startProcessingQueue: jest.fn(), addToMixpanelQueue: jest.fn(), flush: jest.fn(), identifyUserQueue: jest.fn(), })), })); jest.mock("mixpanel-react-native/javascript/mixpanel-network", () => ({ MixpanelNetwork: { sendRequest: jest.fn(), }, })); jest.mock("mixpanel-react-native/javascript/mixpanel-persistent", () => { return { MixpanelPersistent: { getInstance: jest.fn().mockImplementation(() => { return { initializationCompletePromise: jest.fn(), reset: jest.fn(), updateOptedOut: jest.fn(), persistOptedOut: jest.fn(), getSuperProperties: jest.fn().mockReturnValue({ company_id: [222], superProp1: "value1", superProp2: "value2", }), getDistinctId: jest.fn().mockReturnValue("distinct-id-mock"), getDeviceId: jest.fn().mockReturnValue("device-id-mock"), getUserId: jest.fn().mockReturnValue("user-id-mock"), getOptedOut: jest.fn(), getQueue: jest.fn(), saveQueue: jest.fn(), loadQueue: jest.fn(), loadDeviceId: jest.fn(), updateDeviceId: jest.fn(), persistDeviceId: jest.fn(), loadDistinctId: jest.fn(), updateDistinctId: jest.fn(), persistDistinctId: jest.fn(), loadUserId: jest.fn(), updateUserId: jest.fn(), persistUserId: jest.fn(), loadSuperProperties: jest.fn(), persistSuperProperties: jest.fn(), loadOptedOut: jest.fn(), persistOptedOut: jest.fn(), loadIdentity: jest.fn(), persistIdentity: jest.fn(), getIdentity: jest.fn(), resetIdentity: jest.fn(), getTimeEvents: jest.fn().mockReturnValue({ "test-event": Math.round(Date.now() / 1000 - 1000), }), loadTimeEvents: jest.fn(), updateTimeEvents: jest.fn(), persistTimeEvents: jest.fn(), updateSuperProperties: jest.fn(), }; }), }, }; }); jest.mock("mixpanel-react-native/javascript/mixpanel-config", () => ({ MixpanelConfig: { getInstance: jest.fn().mockReturnValue({ getFlushInterval: jest.fn().mockReturnValue(1000), getFlushBatchSize: jest.fn().mockReturnValue(50), getServerURL: jest.fn(), getUseIpAddressForGeolocation: jest.fn(), setLoggingEnabled: jest.fn(), getLoggingEnabled: jest.fn().mockReturnValue(true), setServerURL: jest.fn(), setGzipCompression: jest.fn(), }), }, })); // jest.mock("mixpanel-react-native/javascript/mixpanel-logger", () => { // return { // MixpanelLogger: { // log: jest.fn(), // }, // }; // }); const { MixpanelNetwork, } = require("mixpanel-react-native/javascript/mixpanel-network"); const { MixpanelCore, } = require("mixpanel-react-native/javascript/mixpanel-core"); const { MixpanelQueueManager, } = require("mixpanel-react-native/javascript/mixpanel-queue"); const { MixpanelPersistent, } = require("mixpanel-react-native/javascript/mixpanel-persistent"); const { MixpanelConfig, } = require("mixpanel-react-native/javascript/mixpanel-config"); describe("MixpanelMain", () => { let mixpanelMain; const token = "test-token"; beforeEach(() => { jest.clearAllMocks(); jest.isolateModules(async () => { const MixpanelMain = require("mixpanel-react-native/javascript/mixpanel-main").default; mixpanelMain = new MixpanelMain(token); MixpanelConfig.getInstance().getLoggingEnabled.mockReturnValue(true); }); jest.spyOn(console, "log").mockImplementation(() => {}); }); it("should initialize properly", async () => { const trackAutomaticEvents = false; const optOutTrackingDefault = false; const superProperties = { superProp1: "value1", superProp2: "value2" }; const serverURL = "https://api.mixpanel.com"; await mixpanelMain.initialize( token, trackAutomaticEvents, optOutTrackingDefault, superProperties, serverURL ); expect(mixpanelMain.core.initialize).toHaveBeenCalledWith(token); expect( mixpanelMain.mixpanelPersistent.updateSuperProperties ).toHaveBeenCalledWith(token, { superProp1: "value1", superProp2: "value2", company_id: [222], }); expect( mixpanelMain.mixpanelPersistent.persistSuperProperties ).toHaveBeenCalledWith(token); }); // Note: Gzip compression is only implemented in native modules, not in JavaScript fallback // This test is removed as the JavaScript implementation doesn't support gzip compression it("should not track if initialize with optOutTrackingDefault being true", async () => { const trackAutomaticEvents = false; const optOutTrackingDefault = true; const superProperties = { superProp1: "value1", superProp2: "value2" }; const serverURL = "https://api.mixpanel.com"; await mixpanelMain.initialize( token, trackAutomaticEvents, optOutTrackingDefault, superProperties, serverURL ); const eventName = "Test Event"; const eventProperties = { prop1: "value1", prop2: "value2" }; expect(mixpanelMain.mixpanelPersistent.updateOptedOut).toHaveBeenCalledWith( token, true ); mixpanelMain.mixpanelPersistent.getOptedOut.mockReturnValue(true); await mixpanelMain.track(token, eventName, eventProperties); expect(mixpanelMain.core.addToMixpanelQueue).not.toHaveBeenCalled(); }); it("should track if initialize with optOutTrackingDefault being false", async () => { const trackAutomaticEvents = false; const optOutTrackingDefault = false; const superProperties = { superProp1: "value1", superProp2: "value2" }; const serverURL = "https://api.mixpanel.com"; await mixpanelMain.initialize( token, trackAutomaticEvents, optOutTrackingDefault, superProperties, serverURL ); mixpanelMain.setLoggingEnabled(token, true); const eventName = "Test Event"; const eventProperties = { prop1: "value1", prop2: "value2" }; await mixpanelMain.track(token, eventName, eventProperties); expect(mixpanelMain.core.addToMixpanelQueue).toHaveBeenCalled(); }); it("register super properties should update properties", async () => { mixpanelMain.registerSuperProperties(token, { superProp3: "value3" }); expect( mixpanelMain.mixpanelPersistent.updateSuperProperties ).toHaveBeenCalledWith(token, { superProp1: "value1", superProp2: "value2", superProp3: "value3", company_id: [222], }); expect( mixpanelMain.mixpanelPersistent.persistSuperProperties ).toHaveBeenCalledWith(token); }); it("register super properties once should update properties only once", async () => { mixpanelMain.registerSuperPropertiesOnce(token, { superProp3: "value3" }); expect( mixpanelMain.mixpanelPersistent.updateSuperProperties ).toHaveBeenCalledWith(token, { superProp1: "value1", superProp2: "value2", superProp3: "value3", company_id: [222], }); expect( mixpanelMain.mixpanelPersistent.persistSuperProperties ).toHaveBeenCalledWith(token); mixpanelMain.registerSuperPropertiesOnce(token, { superProp3: "value4" }); expect( mixpanelMain.mixpanelPersistent.updateSuperProperties ).toHaveBeenCalledWith(token, { superProp1: "value1", superProp2: "value2", superProp3: "value3", company_id: [222], }); expect( mixpanelMain.mixpanelPersistent.persistSuperProperties ).toHaveBeenCalledWith(token); }); it("unregister super properties should update properties properly", async () => { mixpanelMain.registerSuperPropertiesOnce(token, { superProp3: "value3" }); expect( mixpanelMain.mixpanelPersistent.updateSuperProperties ).toHaveBeenCalledWith(token, { superProp1: "value1", superProp2: "value2", superProp3: "value3", company_id: [222], }); mixpanelMain.unregisterSuperProperty(token, "superProp3"); expect( mixpanelMain.mixpanelPersistent.updateSuperProperties ).toHaveBeenCalledWith(token, { superProp1: "value1", superProp2: "value2", company_id: [222], }); }); it("clear super properties should clear properties properly", async () => { mixpanelMain.clearSuperProperties(token); expect( mixpanelMain.mixpanelPersistent.updateSuperProperties ).toHaveBeenCalledWith(token, {}); expect( mixpanelMain.mixpanelPersistent.persistSuperProperties ).toHaveBeenCalledWith(token); }); it("should send correct payload on track event", async () => { const eventName = "Test Event"; const eventProperties = { prop1: "value1", prop2: "value2" }; await mixpanelMain.track(token, eventName, eventProperties); expect(mixpanelMain.core.addToMixpanelQueue).toHaveBeenCalledWith( token, MixpanelType.EVENTS, expect.objectContaining({ event: eventName, properties: expect.objectContaining({ token: token, time: expect.any(Number), prop1: "value1", prop2: "value2", $device_id: "device-id-mock", $user_id: "user-id-mock", distinct_id: "distinct-id-mock", superProp1: "value1", // include super properties superProp2: "value2", }), }) ); }); it("should trigger the flush on the flush call", async () => { mixpanelMain.flush(token); expect(mixpanelMain.core.flush).toHaveBeenCalledWith(token); }); it("setLoggingEnabled should work as expected", async () => { mixpanelMain.setLoggingEnabled(token, true); expect(mixpanelMain.config.setLoggingEnabled).toHaveBeenCalledWith( token, true ); MixpanelConfig.getInstance().getLoggingEnabled.mockReturnValueOnce(true); await mixpanelMain.track(token, "test-event"); expect(console.log).toHaveBeenCalled(); }); it("timeEvent should work as expected", async () => { mixpanelMain.timeEvent(token, "test-event"); expect( mixpanelMain.mixpanelPersistent.updateTimeEvents ).toHaveBeenCalledWith(token, { "test-event": expect.any(Number) }); expect( mixpanelMain.mixpanelPersistent.persistTimeEvents ).toHaveBeenCalledWith(token); }); it("eventElapsedTime should work as expected", async () => { mixpanelMain.timeEvent(token, "test-event"); const elapsedTime = await mixpanelMain.eventElapsedTime( token, "test-event" ); expect(elapsedTime).toBeGreaterThan(0); }); it("should update the identity properties on identify", async () => { jest.resetModules(); const newDistinctId = "new-distinct-id"; await mixpanelMain.identify(token, newDistinctId); expect( mixpanelMain.mixpanelPersistent.updateDistinctId ).toHaveBeenCalledWith(token, newDistinctId); expect(mixpanelMain.mixpanelPersistent.updateUserId).toHaveBeenCalledWith( token, newDistinctId ); }); it("should not update the identity properties if the new distinctid is the save as before", async () => { const newDistinctId = "distinct-id-mock"; await mixpanelMain.identify(token, newDistinctId); expect( mixpanelMain.mixpanelPersistent.updateDistinctId ).toHaveBeenCalledTimes(0); expect(mixpanelMain.mixpanelPersistent.updateUserId).toHaveBeenCalledTimes( 0 ); }); it("should call core.identifyUserQueue when identifying with a new distinct id", async () => { const newDistinctId = "brand-new-distinct-id"; // Ensure the previous distinctId is different so the branch is triggered mixpanelMain.mixpanelPersistent.getDistinctId .mockReturnValueOnce("old-id") .mockReturnValue("old-id"); mixpanelMain.core.identifyUserQueue.mockResolvedValue(); await mixpanelMain.identify(token, newDistinctId); expect(mixpanelMain.core.identifyUserQueue).toHaveBeenCalledWith(token); expect(mixpanelMain.core.addToMixpanelQueue).toHaveBeenCalledWith( token, MixpanelType.EVENTS, expect.any(Object) ); }); it("should NOT call core.identifyUserQueue when identifying with the same distinct id", async () => { const currentDistinctId = "distinct-id-mock"; mixpanelMain.mixpanelPersistent.getDistinctId.mockReturnValue( currentDistinctId ); await mixpanelMain.identify(token, currentDistinctId); expect(mixpanelMain.core.identifyUserQueue).not.toHaveBeenCalled(); }); it("should handle rapid identity changes correctly", async () => { // Setup different distinct IDs for concurrent calls const distinctIds = ["user1", "user2", "user3"]; let callCount = 0; // Mock getDistinctId to return different values for each call mixpanelMain.mixpanelPersistent.getDistinctId.mockImplementation(() => { if (callCount === 0) return "initial-user"; return distinctIds[callCount - 1] || "user3"; }); // Mock updateDistinctId to track the order of updates const updateCalls = []; mixpanelMain.mixpanelPersistent.updateDistinctId.mockImplementation((token, id) => { callCount++; updateCalls.push(id); }); // Execute multiple identify calls concurrently const identifyPromises = distinctIds.map(id => mixpanelMain.identify(token, id)); await Promise.all(identifyPromises); // Verify all updates were called expect(updateCalls).toEqual(distinctIds); // Verify identifyUserQueue was called for each identity change expect(mixpanelMain.core.identifyUserQueue).toHaveBeenCalledTimes(3); // Verify track was called for each $identify event expect(mixpanelMain.core.addToMixpanelQueue).toHaveBeenCalledTimes(3); // Verify final state - should be the last user expect(mixpanelMain.mixpanelPersistent.updateDistinctId).toHaveBeenLastCalledWith(token, "user3"); }); it("should send correct payload on set profile properties", async () => { const properties = { prop1: "value1", prop2: "value2" }; await mixpanelMain.set(token, properties); expect(mixpanelMain.core.addToMixpanelQueue).toHaveBeenCalledWith( token, MixpanelType.USER, expect.objectContaining({ $token: token, $time: expect.any(Number), $set: { prop1: "value1", prop2: "value2" }, $distinct_id: "distinct-id-mock", $device_id: "device-id-mock", $user_id: "user-id-mock", }) ); }); it("should send correct payload on setOnce profile properties", async () => { const properties = { prop1: "value1", prop2: "value2" }; await mixpanelMain.setOnce(token, properties); expect(mixpanelMain.core.addToMixpanelQueue).toHaveBeenCalledWith( token, MixpanelType.USER, expect.objectContaining({ $token: token, $time: expect.any(Number), $set_once: { prop1: "value1", prop2: "value2" }, $distinct_id: "distinct-id-mock", $device_id: "device-id-mock", $user_id: "user-id-mock", }) ); }); it("should send correct payload on increment profile properties", async () => { const properties = { prop1: 3 }; await mixpanelMain.increment(token, properties); expect(mixpanelMain.core.addToMixpanelQueue).toHaveBeenCalledWith( token, MixpanelType.USER, expect.objectContaining({ $token: token, $time: expect.any(Number), $add: { prop1: 3 }, $distinct_id: "distinct-id-mock", $device_id: "device-id-mock", $user_id: "user-id-mock", }) ); }); it("should send correct payload on append profile properties", async () => { const properties = { prop1: "value1" }; await mixpanelMain.append(token, properties); expect(mixpanelMain.core.addToMixpanelQueue).toHaveBeenCalledWith( token, MixpanelType.USER, expect.objectContaining({ $token: token, $time: expect.any(Number), $append: { prop1: "value1" }, $distinct_id: "distinct-id-mock", $device_id: "device-id-mock", $user_id: "user-id-mock", }) ); await mixpanelMain.append(token, "testProp", "testValue"); expect(mixpanelMain.core.addToMixpanelQueue).toHaveBeenCalledWith( token, MixpanelType.USER, expect.objectContaining({ $token: token, $time: expect.any(Number), $append: { testProp: "testValue" }, $distinct_id: "distinct-id-mock", $device_id: "device-id-mock", $user_id: "user-id-mock", }) ); }); it("should send correct payload on union profile properties", async () => { const properties = { prop1: "value1" }; await mixpanelMain.union(token, properties); expect(mixpanelMain.core.addToMixpanelQueue).toHaveBeenCalledWith( token, MixpanelType.USER, expect.objectContaining({ $token: token, $time: expect.any(Number), $union: { prop1: "value1" }, $distinct_id: "distinct-id-mock", $device_id: "device-id-mock", $user_id: "user-id-mock", }) ); await mixpanelMain.union(token, "testProp", "testValue"); expect(mixpanelMain.core.addToMixpanelQueue).toHaveBeenCalledWith( token, MixpanelType.USER, expect.objectContaining({ $token: token, $time: expect.any(Number), $union: { testProp: "testValue" }, $distinct_id: "distinct-id-mock", $device_id: "device-id-mock", $user_id: "user-id-mock", }) ); }); it("should send correct payload on remove profile properties", async () => { const properties = { prop1: "value1" }; await mixpanelMain.remove(token, properties); expect(mixpanelMain.core.addToMixpanelQueue).toHaveBeenCalledWith( token, MixpanelType.USER, expect.objectContaining({ $token: token, $time: expect.any(Number), $remove: { prop1: "value1" }, $distinct_id: "distinct-id-mock", $device_id: "device-id-mock", $user_id: "user-id-mock", }) ); await mixpanelMain.remove(token, "testProp", "testValue"); expect(mixpanelMain.core.addToMixpanelQueue).toHaveBeenCalledWith( token, MixpanelType.USER, expect.objectContaining({ $token: token, $time: expect.any(Number), $remove: { testProp: "testValue" }, $distinct_id: "distinct-id-mock", $device_id: "device-id-mock", $user_id: "user-id-mock", }) ); }); it("should send correct payload on trackCharge", async () => { const properties = { prop1: "value1" }; const charge = 100; await mixpanelMain.trackCharge(token, charge, properties); expect(mixpanelMain.core.addToMixpanelQueue).toHaveBeenCalledWith( token, MixpanelType.USER, expect.objectContaining({ $token: token, $time: expect.any(Number), $append: { $transactions: { $amount: 100, $time: expect.any(Number), prop1: "value1", }, }, $distinct_id: "distinct-id-mock", $device_id: "device-id-mock", $user_id: "user-id-mock", }) ); }); it("should send correct payload on clearCharge", async () => { await mixpanelMain.clearCharges(token); expect(mixpanelMain.core.addToMixpanelQueue).toHaveBeenCalledWith( token, MixpanelType.USER, expect.objectContaining({ $token: token, $time: expect.any(Number), $set: { $transactions: [], }, $distinct_id: "distinct-id-mock", $device_id: "device-id-mock", $user_id: "user-id-mock", }) ); }); it("should send correct payload on unset profile properties", async () => { const property = "prop1"; await mixpanelMain.unset(token, property); expect(mixpanelMain.core.addToMixpanelQueue).toHaveBeenCalledWith( token, MixpanelType.USER, expect.objectContaining({ $token: token, $time: expect.any(Number), $unset: [property], $distinct_id: "distinct-id-mock", $device_id: "device-id-mock", $user_id: "user-id-mock", }) ); }); it("should send correct payload on delete profile", async () => { await mixpanelMain.deleteUser(token); expect(mixpanelMain.core.addToMixpanelQueue).toHaveBeenCalledWith( token, MixpanelType.USER, expect.objectContaining({ $token: token, $time: expect.any(Number), $delete: "null", $distinct_id: "distinct-id-mock", $device_id: "device-id-mock", $user_id: "user-id-mock", }) ); }); it("should send correct payload on trackWithGroups", async () => { const properties = { prop1: "value1" }; const eventName = "event1"; const groups = { company_id: 111 }; await mixpanelMain.trackWithGroups(token, eventName, properties, groups); expect(mixpanelMain.core.addToMixpanelQueue).toHaveBeenCalledWith( token, MixpanelType.EVENTS, expect.objectContaining({ event: "event1", properties: expect.objectContaining({ token: token, time: expect.any(Number), prop1: "value1", $device_id: "device-id-mock", $user_id: "user-id-mock", distinct_id: "distinct-id-mock", superProp1: "value1", // include super properties superProp2: "value2", company_id: 111, }), }) ); }); it("should send correct payload on addGroup", async () => { await mixpanelMain.addGroup(token, "company_id", 111); expect(mixpanelMain.core.addToMixpanelQueue).toHaveBeenCalledWith( token, MixpanelType.USER, expect.objectContaining({ $token: token, $time: expect.any(Number), $union: { company_id: [111] }, $distinct_id: "distinct-id-mock", $device_id: "device-id-mock", $user_id: "user-id-mock", }) ); expect( mixpanelMain.mixpanelPersistent.updateSuperProperties ).toHaveBeenCalledWith(token, { company_id: [222, 111], superProp1: "value1", superProp2: "value2", }); expect( mixpanelMain.mixpanelPersistent.persistSuperProperties ).toHaveBeenCalledWith(token); }); it("should send correct payload on setGroup", async () => { await mixpanelMain.setGroup(token, "company_id", 333); expect(mixpanelMain.core.addToMixpanelQueue).toHaveBeenCalledWith( token, MixpanelType.USER, expect.objectContaining({ $token: token, $time: expect.any(Number), $set: { company_id: [333] }, $distinct_id: "distinct-id-mock", $device_id: "device-id-mock", $user_id: "user-id-mock", }) ); expect( mixpanelMain.mixpanelPersistent.updateSuperProperties ).toHaveBeenCalledWith(token, { company_id: [333], superProp1: "value1", superProp2: "value2", }); expect( mixpanelMain.mixpanelPersistent.persistSuperProperties ).toHaveBeenCalledWith(token); }); it("should send correct payload on removeGroup", async () => { await mixpanelMain.addGroup(token, "company_id", 111); // The company id has been added expect( mixpanelMain.mixpanelPersistent.updateSuperProperties ).toHaveBeenCalledWith(token, { company_id: [222, 111], superProp1: "value1", superProp2: "value2", }); expect( mixpanelMain.mixpanelPersistent.persistSuperProperties ).toHaveBeenCalledWith(token); await mixpanelMain.removeGroup(token, "company_id", 111); expect(mixpanelMain.core.addToMixpanelQueue).toHaveBeenCalledWith( token, MixpanelType.USER, expect.objectContaining({ $token: token, $time: expect.any(Number), $remove: { company_id: 111 }, $distinct_id: "distinct-id-mock", $device_id: "device-id-mock", $user_id: "user-id-mock", }) ); // The company id has been removed expect( mixpanelMain.mixpanelPersistent.updateSuperProperties ).toHaveBeenCalledWith(token, { company_id: [222], superProp1: "value1", superProp2: "value2", }); expect( mixpanelMain.mixpanelPersistent.persistSuperProperties ).toHaveBeenCalledWith(token); }); it("should send correct payload on deleteGroup", async () => { await mixpanelMain.deleteGroup(token); expect(mixpanelMain.core.addToMixpanelQueue).toHaveBeenCalledWith( token, MixpanelType.GROUPS, expect.objectContaining({ $token: token, $time: expect.any(Number), $delete: "null", }) ); }); it("should send correct payload on group set", async () => { const properties = { prop1: "value1", prop2: "value2" }; await mixpanelMain.groupSetProperties(token, "company_id", 444, properties); expect(mixpanelMain.core.addToMixpanelQueue).toHaveBeenCalledWith( token, MixpanelType.GROUPS, expect.objectContaining({ $token: token, $time: expect.any(Number), $group_id: 444, $group_key: "company_id", $set: { prop1: "value1", prop2: "value2" }, }) ); }); it("should send correct payload on group set once", async () => { const properties = { prop1: "value1", prop2: "value2" }; await mixpanelMain.groupSetPropertyOnce( token, "company_id", 444, properties ); expect(mixpanelMain.core.addToMixpanelQueue).toHaveBeenCalledWith( token, MixpanelType.GROUPS, expect.objectContaining({ $token: token, $time: expect.any(Number), $group_id: 444, $group_key: "company_id", $set_once: { prop1: "value1", prop2: "value2" }, }) ); }); it("should send correct payload on groupUnsetProperty", async () => { await mixpanelMain.groupUnsetProperty(token, "company_id", 444, "prop1"); expect(mixpanelMain.core.addToMixpanelQueue).toHaveBeenCalledWith( token, MixpanelType.GROUPS, expect.objectContaining({ $token: token, $time: expect.any(Number), $group_id: 444, $group_key: "company_id", $unset: ["prop1"], }) ); }); it("should send correct payload on groupRemovePropertyValue", async () => { await mixpanelMain.groupRemovePropertyValue( token, "company_id", 444, "prop1", "value1" ); expect(mixpanelMain.core.addToMixpanelQueue).toHaveBeenCalledWith( token, MixpanelType.GROUPS, expect.objectContaining({ $token: token, $time: expect.any(Number), $group_id: 444, $group_key: "company_id", $remove: { prop1: "value1" }, }) ); }); it("should send correct payload on groupUnionProperty", async () => { await mixpanelMain.groupUnionProperty( token, "company_id", 444, "prop1", "value1" ); expect(mixpanelMain.core.addToMixpanelQueue).toHaveBeenCalledWith( token, MixpanelType.GROUPS, expect.objectContaining({ $token: token, $time: expect.any(Number), $group_id: 444, $group_key: "company_id", $union: { prop1: "value1" }, }) ); }); });