Skip to content

Commit

Permalink
feat: gainsight PX destination (#1852) (#1889)
Browse files Browse the repository at this point in the history
* feat: gainsight PX destination (#1852)

* feat: gainsight PX destination

* feat: Gainsight PX destination
PR Feedback - changed name to use existing name

* feat: gainsight PX destination
PR feedback - Switched to use integration options instead of extra config object. Cleaned up identify

* feat: gainsight PX destination
PR feedback - Use integration config

* feat: gainsight PX destination
fixed typo on us2 datacenter URL

* fix: updating config name to productTagKey and adding test cases

* fix: added new test case

* fix: added new test case

* fix: fix imports

* fix: fix destination name

* fix: fix size limits

* fix: fix build size

* fix: fix build size

---------

Co-authored-by: Nick Wolfe <[email protected]>
  • Loading branch information
shrouti1507 and gs-nwolfe authored Oct 25, 2024
1 parent bfa9d0a commit 3a705f0
Show file tree
Hide file tree
Showing 13 changed files with 431 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const DIR_NAME = 'Gainsight_PX';
const NAME = 'GAINSIGHT_PX';
const DISPLAY_NAME = 'Gainsight PX';

const DISPLAY_NAME_TO_DIR_NAME_MAP = { [DISPLAY_NAME]: DIR_NAME };
const CNameMapping = {
[NAME]: NAME,
Gainsight_PX: NAME,
};

export { NAME, CNameMapping, DISPLAY_NAME_TO_DIR_NAME_MAP, DISPLAY_NAME, DIR_NAME };
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ const clientToServerNames = {
COMMANDBAR: 'CommandBar',
NINETAILED: 'Ninetailed',
XPIXEL: 'XPixel',
GAINSIGHT_PX: 'Gainsight PX',
};

export { clientToServerNames };
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ const configToIntNames = {
COMMANDBAR: 'CommandBar',
NINETAILED: 'Ninetailed',
XPIXEL: 'XPixel',
GAINSIGHT_PX: 'Gainsight_PX',
};

export { configToIntNames };
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,8 @@ import {
CommandBarDirectoryName,
NinetailedDisplayName,
NinetailedDirectoryName,
Gainsight_PXDisplayName,
Gainsight_PXDirectoryName,
XPixelDisplayName,
XPixelDirectoryName,
} from './destinationNames';
Expand Down Expand Up @@ -243,6 +245,7 @@ const destDisplayNamesToFileNamesMap: Record<string, string> = {
[SpotifyPixelDisplayName]: SpotifyPixelDirectoryName,
[CommandBarDisplayName]: CommandBarDirectoryName,
[NinetailedDisplayName]: NinetailedDirectoryName,
[Gainsight_PXDisplayName]: Gainsight_PXDirectoryName,
[XPixelDisplayName]: XPixelDirectoryName,
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,10 @@ export {
DISPLAY_NAME as NinetailedDisplayName,
DIR_NAME as NinetailedDirectoryName,
} from './Ninetailed/constants';
export {
DISPLAY_NAME as Gainsight_PXDisplayName,
DIR_NAME as Gainsight_PXDirectoryName,
} from './Gainsight_PX/constants';
export {
DISPLAY_NAME as XPixelDisplayName,
DIR_NAME as XPixelDirectoryName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ import { CNameMapping as SpotifyPixel } from './SpotifyPixel/constants';
import { CNameMapping as CommandBar } from './CommandBar/constants';
import { CNameMapping as Ninetailed } from './Ninetailed/constants';
import { CNameMapping as XPixel } from './XPixel/constants';
import { CNameMapping as Gainsight_PX } from './Gainsight_PX/constants';
// for sdk side native integration identification
// add a mapping from common names to index.js exported key names as identified by Rudder
const commonNames = {
Expand Down Expand Up @@ -162,6 +163,7 @@ const commonNames = {
...Sprig,
...SpotifyPixel,
...XPixel,
...Gainsight_PX
};

export { commonNames };
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
import Gainsight_PX from '../../../src/integrations/Gainsight_PX/browser';
import { loadNativeSdk } from '../../../src/integrations/Gainsight_PX/nativeSdkLoader';
import { getDestinationOptions } from '../../../src/integrations/Gainsight_PX/utils';

// Mock the external dependencies
jest.mock('../../../src/integrations/Gainsight_PX/nativeSdkLoader');
jest.mock('../../../src/integrations/Gainsight_PX/utils');

describe('Gainsight_PX', () => {
let gainsightPX;
let mockAnalytics;
let mockConfig;

beforeEach(() => {
// Reset mocks
jest.clearAllMocks();

// Mock window.aptrinsic
window.aptrinsic = jest.fn();

// Mock analytics object
mockAnalytics = {
logLevel: 'debug',
getUserId: jest.fn(),
getUserTraits: jest.fn(),
getGroupId: jest.fn(),
getGroupTraits: jest.fn(),
loadOnlyIntegrations: {},
};

// Mock config
mockConfig = {
productTagKey: 'test-product-key',
dataCenter: 'US',
};

gainsightPX = new Gainsight_PX(mockConfig, mockAnalytics);
});

describe('init', () => {
it('should load native SDK and initialize', () => {
getDestinationOptions.mockReturnValue({ someOption: 'value' });
mockAnalytics.getUserId.mockReturnValue('test-user-id');
mockAnalytics.getUserTraits.mockReturnValue({ name: 'Test User' });
mockAnalytics.getGroupId.mockReturnValue('test-group-id');
mockAnalytics.getGroupTraits.mockReturnValue({ plan: 'premium' });

gainsightPX.init();

expect(loadNativeSdk).toHaveBeenCalledWith('test-product-key', 'US', { someOption: 'value' });
expect(window.aptrinsic).toHaveBeenCalledWith(
'identify',
{ id: 'test-user-id', name: 'Test User' },
{ id: 'test-group-id', plan: 'premium' }
);
});

it('should not call identify if user ID is not present', () => {
mockAnalytics.getUserId.mockReturnValue(null);

gainsightPX.init();

expect(loadNativeSdk).toHaveBeenCalled();
expect(window.aptrinsic).not.toHaveBeenCalled();
});
});

describe('isLoaded', () => {
it('should return true when window.aptrinsic and window.aptrinsic.init exist', () => {
window.aptrinsic = { init: jest.fn() };
expect(gainsightPX.isLoaded()).toBe(true);
});

it('should return false when window.aptrinsic does not exist', () => {
delete window.aptrinsic;
expect(gainsightPX.isLoaded()).toBe(false);
});

it('should return false when window.aptrinsic exists but init is missing', () => {
window.aptrinsic = {};
expect(gainsightPX.isLoaded()).toBe(false);
});
});

describe('isReady', () => {
it('should return the same value as isLoaded', () => {
window.aptrinsic = { init: jest.fn() };
expect(gainsightPX.isReady()).toBe(gainsightPX.isLoaded());

delete window.aptrinsic;
expect(gainsightPX.isReady()).toBe(gainsightPX.isLoaded());
});
});

describe('identify', () => {
it('should call aptrinsic identify with user data', () => {
const rudderElement = {
message: {
userId: 'test-user-id',
context: {
traits: { name: 'Test User', email: '[email protected]' },
},
},
};

gainsightPX.identify(rudderElement);

expect(window.aptrinsic).toHaveBeenCalledWith(
'identify',
{ id: 'test-user-id', name: 'Test User', email: '[email protected]' },
{}
);
});

it('should not call aptrinsic identify if userId is missing', () => {
const rudderElement = {
message: {
context: {
traits: { name: 'Test User' },
},
},
};

gainsightPX.identify(rudderElement);

expect(window.aptrinsic).not.toHaveBeenCalled();
});

test('Testing identify call with ID only', () => {
// call RudderStack function
gainsightPX.identify({
message: {
userId: 'rudder01',
context: {}
}
});

// Confirm that it was translated to the appropriate PX call
expect(window.aptrinsic.mock.calls[0]).toEqual([
'identify',
{
id: 'rudder01'
},
{}
]);
});

});

describe('group', () => {
it('should call aptrinsic identify with group data', () => {
const rudderElement = {
message: {
userId: 'test-user-id',
groupId: 'test-group-id',
traits: { plan: 'premium' },
context: {
traits: { name: 'Test User' },
},
},
};

gainsightPX.group(rudderElement);

expect(window.aptrinsic).toHaveBeenCalledWith(
'identify',
{ id: 'test-user-id', name: 'Test User' },
{ id: 'test-group-id', plan: 'premium' }
);
});

it('should use anonymousId if groupId is not present', () => {
const rudderElement = {
message: {
anonymousId: 'anon-id',
traits: { plan: 'basic' },
},
};

gainsightPX.group(rudderElement);

expect(window.aptrinsic).toHaveBeenCalledWith(
'identify',
{},
{ id: 'anon-id', plan: 'basic' }
);
});
});

describe('track', () => {
it('should call aptrinsic track with event data', () => {
const rudderElement = {
message: {
event: 'Test Event',
properties: { category: 'test', value: 10 },
},
};

gainsightPX.track(rudderElement);

expect(window.aptrinsic).toHaveBeenCalledWith(
'track',
'Test Event',
{ category: 'test', value: 10 }
);
});

it('should not call aptrinsic track if event name is missing', () => {
const rudderElement = {
message: {
properties: { category: 'test' },
},
};

gainsightPX.track(rudderElement);

expect(window.aptrinsic).not.toHaveBeenCalled();
});

test('Test for empty properties', () => {
// call RudderStack function
gainsightPX.track({
message: {
context: {},
event: 'event-name',
properties: {}
}
});

// Confirm that it was translated to the appropriate PX call
expect(window.aptrinsic.mock.calls[0]).toEqual([
'track',
'event-name',
{}
]);
});

});
});
Loading

0 comments on commit 3a705f0

Please sign in to comment.