diff --git a/packages/browser-destinations/destinations/mixpanel-web/metadata.json b/packages/browser-destinations/destinations/mixpanel-web/metadata.json index 47691af8ab9..0ca732927e9 100644 --- a/packages/browser-destinations/destinations/mixpanel-web/metadata.json +++ b/packages/browser-destinations/destinations/mixpanel-web/metadata.json @@ -25,6 +25,16 @@ "default": null, "depends_on": null }, + "sourceName": { + "label": "Source Name", + "description": "This value, if it's not blank, will be sent as segment_source_name to Mixpanel for every event, including events Mixpanel captures automatically.", + "type": "string", + "required": false, + "multiple": false, + "choices": null, + "default": null, + "depends_on": null + }, "api_host": { "label": "API Host", "description": "The Mixpanel API host to send data to.", diff --git a/packages/browser-destinations/destinations/mixpanel-web/src/__tests__/initialization.test.ts b/packages/browser-destinations/destinations/mixpanel-web/src/__tests__/initialization.test.ts index 692fc0b7eab..6ecc8a85d5a 100644 --- a/packages/browser-destinations/destinations/mixpanel-web/src/__tests__/initialization.test.ts +++ b/packages/browser-destinations/destinations/mixpanel-web/src/__tests__/initialization.test.ts @@ -51,6 +51,7 @@ describe('Mixpanel Web initialization', () => { track: jest.fn(), track_pageview: jest.fn(), identify: jest.fn(), + register: jest.fn(), alias: jest.fn(), get_group: jest.fn(), set_group: jest.fn(), @@ -99,4 +100,136 @@ describe('Mixpanel Web initialization', () => { expect(resolvedInstance).toBe(mockMixpanelInstance) expect(resolvedInstance).not.toBe(mockSnippetMixpanel) }) + + test('registers segment_source_name as a super property when sourceName is set', async () => { + const mockMixpanelInstance: Mixpanel = { + init: jest.fn(), + track: jest.fn(), + track_pageview: jest.fn(), + identify: jest.fn(), + register: jest.fn(), + alias: jest.fn(), + get_group: jest.fn(), + set_group: jest.fn(), + people: { + set: jest.fn(), + set_once: jest.fn(), + increment: jest.fn() + } + } + + const mockSnippetMixpanel: Partial = { + init: jest.fn().mockImplementation((_token, config, _name) => { + setTimeout(() => { + if (config?.loaded) { + config.loaded(mockMixpanelInstance) + } + }, 10) + }) + } + + jest.spyOn(initScriptModule, 'initScript').mockImplementation(async () => { + ;(window as any).mixpanel = mockSnippetMixpanel + }) + + const [event] = await MixpanelDestination({ + ...baseSettings, + sourceName: 'My Website Source', + subscriptions + }) + + await event.load(Context.system(), {} as Analytics) + + expect(mockMixpanelInstance.register).toHaveBeenCalledWith({ + segment_source_name: 'My Website Source' + }) + + // sourceName is a Segment side setting and is not a valid Mixpanel config option, + // so it must not be forwarded to mixpanel.init + const [, config] = (mockSnippetMixpanel.init as jest.Mock).mock.calls[0] + expect(config).not.toHaveProperty('sourceName') + }) + + test('does not register a super property when sourceName is not set', async () => { + const mockMixpanelInstance: Mixpanel = { + init: jest.fn(), + track: jest.fn(), + track_pageview: jest.fn(), + identify: jest.fn(), + register: jest.fn(), + alias: jest.fn(), + get_group: jest.fn(), + set_group: jest.fn(), + people: { + set: jest.fn(), + set_once: jest.fn(), + increment: jest.fn() + } + } + + const mockSnippetMixpanel: Partial = { + init: jest.fn().mockImplementation((_token, config, _name) => { + setTimeout(() => { + if (config?.loaded) { + config.loaded(mockMixpanelInstance) + } + }, 10) + }) + } + + jest.spyOn(initScriptModule, 'initScript').mockImplementation(async () => { + ;(window as any).mixpanel = mockSnippetMixpanel + }) + + const [event] = await MixpanelDestination({ + ...baseSettings, + subscriptions + }) + + await event.load(Context.system(), {} as Analytics) + + expect(mockMixpanelInstance.register).not.toHaveBeenCalled() + }) + + test('does not register a super property when sourceName is only whitespace', async () => { + const mockMixpanelInstance: Mixpanel = { + init: jest.fn(), + track: jest.fn(), + track_pageview: jest.fn(), + identify: jest.fn(), + register: jest.fn(), + alias: jest.fn(), + get_group: jest.fn(), + set_group: jest.fn(), + people: { + set: jest.fn(), + set_once: jest.fn(), + increment: jest.fn() + } + } + + const mockSnippetMixpanel: Partial = { + init: jest.fn().mockImplementation((_token, config, _name) => { + setTimeout(() => { + if (config?.loaded) { + config.loaded(mockMixpanelInstance) + } + }, 10) + }) + } + + jest.spyOn(initScriptModule, 'initScript').mockImplementation(async () => { + ;(window as any).mixpanel = mockSnippetMixpanel + }) + + const [event] = await MixpanelDestination({ + ...baseSettings, + sourceName: ' ', + subscriptions + }) + + await event.load(Context.system(), {} as Analytics) + + expect(mockMixpanelInstance.register).not.toHaveBeenCalled() + }) }) diff --git a/packages/browser-destinations/destinations/mixpanel-web/src/alias/__tests__/index.test.ts b/packages/browser-destinations/destinations/mixpanel-web/src/alias/__tests__/index.test.ts index 38003558f70..8d433604f2f 100644 --- a/packages/browser-destinations/destinations/mixpanel-web/src/alias/__tests__/index.test.ts +++ b/packages/browser-destinations/destinations/mixpanel-web/src/alias/__tests__/index.test.ts @@ -48,6 +48,7 @@ describe('Mixpanel.alias', () => { track_pageview: jest.fn(), track: jest.fn(), identify: jest.fn(), + register: jest.fn(), alias: jest.fn(), get_group: jest.fn().mockReturnValue(mockGroup), set_group: jest.fn(), diff --git a/packages/browser-destinations/destinations/mixpanel-web/src/generated-types.ts b/packages/browser-destinations/destinations/mixpanel-web/src/generated-types.ts index a07045e4d00..5401e62cb6f 100644 --- a/packages/browser-destinations/destinations/mixpanel-web/src/generated-types.ts +++ b/packages/browser-destinations/destinations/mixpanel-web/src/generated-types.ts @@ -9,6 +9,10 @@ export interface Settings { * The name for the new mixpanel instance that you want created. */ name?: string + /** + * This value, if it's not blank, will be sent as segment_source_name to Mixpanel for every event, including events Mixpanel captures automatically. + */ + sourceName?: string /** * The Mixpanel API host to send data to. */ diff --git a/packages/browser-destinations/destinations/mixpanel-web/src/group/__tests__/index.test.ts b/packages/browser-destinations/destinations/mixpanel-web/src/group/__tests__/index.test.ts index 11d266a81ec..6d4a1e2b3ec 100644 --- a/packages/browser-destinations/destinations/mixpanel-web/src/group/__tests__/index.test.ts +++ b/packages/browser-destinations/destinations/mixpanel-web/src/group/__tests__/index.test.ts @@ -48,6 +48,7 @@ describe('Mixpanel.group', () => { track_pageview: jest.fn(), track: jest.fn(), identify: jest.fn(), + register: jest.fn(), alias: jest.fn(), get_group: jest.fn().mockReturnValue(mockGroup), set_group: jest.fn(), diff --git a/packages/browser-destinations/destinations/mixpanel-web/src/identify/__tests__/index.test.ts b/packages/browser-destinations/destinations/mixpanel-web/src/identify/__tests__/index.test.ts index 16705110300..ff928a55517 100644 --- a/packages/browser-destinations/destinations/mixpanel-web/src/identify/__tests__/index.test.ts +++ b/packages/browser-destinations/destinations/mixpanel-web/src/identify/__tests__/index.test.ts @@ -48,6 +48,7 @@ describe('Mixpanel.identify', () => { track_pageview: jest.fn(), track: jest.fn(), identify: jest.fn(), + register: jest.fn(), alias: jest.fn(), get_group: jest.fn().mockReturnValue(mockGroup), set_group: jest.fn(), diff --git a/packages/browser-destinations/destinations/mixpanel-web/src/index.ts b/packages/browser-destinations/destinations/mixpanel-web/src/index.ts index 49d82e33a38..e43b4789dfe 100644 --- a/packages/browser-destinations/destinations/mixpanel-web/src/index.ts +++ b/packages/browser-destinations/destinations/mixpanel-web/src/index.ts @@ -29,6 +29,7 @@ export const destination: BrowserDestinationDefinition = { const { projectToken, name, + sourceName, autocapture, pageview, click, @@ -88,7 +89,14 @@ export const destination: BrowserDestinationDefinition = { } return new Promise((resolve) => { - config.loaded = (mp) => resolve(mp) + config.loaded = (mp) => { + const trimmedSourceName = sourceName?.trim() + if (trimmedSourceName && typeof mp?.register === 'function') { + // Registered as a super property so it is attached to every event + mp.register({ segment_source_name: trimmedSourceName }) + } + resolve(mp) + } if (name) { window.mixpanel.init(projectToken, config, name) diff --git a/packages/browser-destinations/destinations/mixpanel-web/src/setting-fields.ts b/packages/browser-destinations/destinations/mixpanel-web/src/setting-fields.ts index eb47932e0c2..438eec8019b 100644 --- a/packages/browser-destinations/destinations/mixpanel-web/src/setting-fields.ts +++ b/packages/browser-destinations/destinations/mixpanel-web/src/setting-fields.ts @@ -13,6 +13,12 @@ export const settingFields: Record = { description: 'The name for the new mixpanel instance that you want created.', type: 'string' }, + sourceName: { + label: 'Source Name', + description: + "This value, if it's not blank, will be sent as segment_source_name to Mixpanel for every event, including events Mixpanel captures automatically.", + type: 'string' + }, api_host: { description: 'The Mixpanel API host to send data to.', label: 'API Host', diff --git a/packages/browser-destinations/destinations/mixpanel-web/src/track/__tests__/index.test.ts b/packages/browser-destinations/destinations/mixpanel-web/src/track/__tests__/index.test.ts index 1ea2e9cbb84..129260a60d1 100644 --- a/packages/browser-destinations/destinations/mixpanel-web/src/track/__tests__/index.test.ts +++ b/packages/browser-destinations/destinations/mixpanel-web/src/track/__tests__/index.test.ts @@ -48,6 +48,7 @@ describe('Mixpanel.track', () => { track_pageview: jest.fn(), track: jest.fn(), identify: jest.fn(), + register: jest.fn(), alias: jest.fn(), get_group: jest.fn().mockReturnValue(mockGroup), set_group: jest.fn(), diff --git a/packages/browser-destinations/destinations/mixpanel-web/src/trackPageView/__tests__/index.test.ts b/packages/browser-destinations/destinations/mixpanel-web/src/trackPageView/__tests__/index.test.ts index 54b53e9ea03..59f8920ad9b 100644 --- a/packages/browser-destinations/destinations/mixpanel-web/src/trackPageView/__tests__/index.test.ts +++ b/packages/browser-destinations/destinations/mixpanel-web/src/trackPageView/__tests__/index.test.ts @@ -48,6 +48,7 @@ describe('Mixpanel.trackPageView', () => { track_pageview: jest.fn(), track: jest.fn(), identify: jest.fn(), + register: jest.fn(), alias: jest.fn(), get_group: jest.fn().mockReturnValue(mockGroup), set_group: jest.fn(), diff --git a/packages/browser-destinations/destinations/mixpanel-web/src/types.ts b/packages/browser-destinations/destinations/mixpanel-web/src/types.ts index 482585810e5..8ec5ffd351b 100644 --- a/packages/browser-destinations/destinations/mixpanel-web/src/types.ts +++ b/packages/browser-destinations/destinations/mixpanel-web/src/types.ts @@ -25,6 +25,8 @@ export interface Mixpanel { identify(unique_id?: string): void + register(properties: { [k: string]: unknown }): void + alias(alias: string, original?: string): void get_group(group_key: string, group_id: string): Group