diff --git a/src/Rokt-Kit.js b/src/Rokt-Kit.js index 1e11709..3912058 100644 --- a/src/Rokt-Kit.js +++ b/src/Rokt-Kit.js @@ -15,6 +15,7 @@ var name = 'Rokt'; var moduleId = 181; +var EVENT_NAME_SELECT_PLACEMENTS = 'selectplacements'; var constructor = function () { var self = this; @@ -199,10 +200,12 @@ var constructor = function () { * @param {Object} options - The options object for selecting placements containing: * - identifier {string}: The placement identifier * - attributes {Object}: Optional attributes to merge with existing attributes + * - initialAttributes {Object}: Original attributes passed by developer * @returns {Promise} A Promise resolving to the Rokt launcher's selectPlacements method with processed attributes */ function selectPlacements(options) { var attributes = (options && options.attributes) || {}; + var initialAttributes = (options && options.initialAttributes) || {}; var placementAttributes = mergeObjects(self.userAttributes, attributes); var filters = self.filters || {}; @@ -255,9 +258,46 @@ var constructor = function () { attributes: selectPlacementsAttributes, }); + // Log custom event for selectPlacements call + logSelectPlacementsEvent(initialAttributes, selectPlacementsAttributes); + return self.launcher.selectPlacements(selectPlacementsOptions); } + /** + * Logs a custom event when selectPlacements is called + * This enables visibility and troubleshooting + * @param {Object} initialAttributes - The attributes passed by the developer + * @param {Object} selectPlacementsAttributes - The final merged attributes sent to Rokt + */ + function logSelectPlacementsEvent( + initialAttributes, + selectPlacementsAttributes + ) { + if ( + !window.mParticle || + typeof window.mParticle.logEvent !== 'function' + ) { + return; + } + + var EVENT_TYPE_OTHER = window.mParticle.EventType.Other; + + // Build event attributes with both passed and final attributes as JSON strings + // initialAttributes: attributes passed by the developer + // finalAttributes: final attributes sent to selectPlacements + var eventAttributes = { + initialAttributes: stringifyIfObject(initialAttributes), + finalAttributes: stringifyIfObject(selectPlacementsAttributes), + }; + + window.mParticle.logEvent( + EVENT_NAME_SELECT_PLACEMENTS, + EVENT_TYPE_OTHER, + eventAttributes + ); + } + /** * Enables optional Integration Launcher extensions before selecting placements * @param {string} extensionName - Name of the extension to enable @@ -580,6 +620,10 @@ function isEmpty(value) { return value == null || !(Object.keys(value) || value).length; } +function stringifyIfObject(value) { + return isObject(value) ? JSON.stringify(value) : '{}'; +} + if (window && window.mParticle && window.mParticle.addForwarder) { window.mParticle.addForwarder({ name: name, diff --git a/test/src/tests.js b/test/src/tests.js index 0b75846..90a5192 100644 --- a/test/src/tests.js +++ b/test/src/tests.js @@ -88,6 +88,15 @@ describe('Rokt Forwarder', () => { mParticle.generateHash = function (input) { return 'hashed-<' + input + '>-value'; }; + // Mock for logEvent to capture custom event logging + mParticle.loggedEvents = []; + mParticle.logEvent = function (eventName, eventType, eventAttributes) { + mParticle.loggedEvents.push({ + eventName: eventName, + eventType: eventType, + eventAttributes: eventAttributes, + }); + }; // -------------------START EDITING BELOW:----------------------- var MockRoktForwarder = function () { var self = this; @@ -734,6 +743,7 @@ describe('Rokt Forwarder', () => { window.mParticle.Rokt.attachKitCalled = true; return Promise.resolve(); }; + mParticle.loggedEvents = []; window.mParticle.Rokt.setLocalSessionAttribute = function ( key, value @@ -2495,6 +2505,140 @@ describe('Rokt Forwarder', () => { ); }); }); + + describe('#logSelectPlacementsEvent', () => { + it('should log a custom event with initialAttributes and finalAttributes', async () => { + await window.mParticle.forwarder.init( + { + accountId: '123456', + }, + reportService.cb, + true, + null, + { + 'cached-user-attr': 'cached-value', + } + ); + + await window.mParticle.forwarder.selectPlacements({ + identifier: 'test-placement', + attributes: { + 'new-attr': 'new-value', + }, + initialAttributes: { + 'original-attr': 'original-value', + }, + }); + + mParticle.loggedEvents.length.should.equal(1); + mParticle.loggedEvents[0].eventName.should.equal( + 'selectplacements' + ); + mParticle.loggedEvents[0].eventType.should.equal(8); // EventType.Other + + const eventAttributes = + mParticle.loggedEvents[0].eventAttributes; + eventAttributes.should.have.property('initialAttributes'); + eventAttributes.should.have.property('finalAttributes'); + + // initialAttributes should contain original attributes from developer + const initialAttrs = JSON.parse( + eventAttributes.initialAttributes + ); + initialAttrs.should.deepEqual({ + 'original-attr': 'original-value', + }); + }); + + it('should include finalAttributes with merged user attributes, identities, and mpid', async () => { + await window.mParticle.forwarder.init( + { + accountId: '123456', + }, + reportService.cb, + true, + null, + { + 'cached-user-attr': 'cached-value', + } + ); + + await window.mParticle.forwarder.selectPlacements({ + identifier: 'test-placement', + attributes: { + 'new-attr': 'new-value', + }, + initialAttributes: { + 'new-attr': 'new-value', + }, + }); + + const eventAttributes = + mParticle.loggedEvents[0].eventAttributes; + const finalAttrs = JSON.parse(eventAttributes.finalAttributes); + + // finalAttributes should include merged attributes and mpid + finalAttrs.should.have.property('mpid', '123'); + finalAttrs.should.have.property('new-attr', 'new-value'); + finalAttrs.should.have.property( + 'cached-user-attr', + 'cached-value' + ); + }); + + it('should handle empty initialAttributes', async () => { + await window.mParticle.forwarder.init( + { + accountId: '123456', + }, + reportService.cb, + true, + null, + {} + ); + + await window.mParticle.forwarder.selectPlacements({ + identifier: 'test-placement', + attributes: { + attr: 'value', + }, + }); + + mParticle.loggedEvents.length.should.equal(1); + const eventAttributes = + mParticle.loggedEvents[0].eventAttributes; + const initialAttrs = JSON.parse( + eventAttributes.initialAttributes + ); + initialAttrs.should.deepEqual({}); + }); + + it('should skip logging when mParticle.logEvent is not available', async () => { + var originalLogEvent = window.mParticle.logEvent; + window.mParticle.logEvent = undefined; + + await window.mParticle.forwarder.init( + { + accountId: '123456', + }, + reportService.cb, + true, + null, + {} + ); + + await window.mParticle.forwarder.selectPlacements({ + identifier: 'test-placement', + attributes: { + attr: 'value', + }, + }); + + window.Rokt.selectPlacementsCalled.should.equal(true); + mParticle.loggedEvents.length.should.equal(0); + window.mParticle.logEvent = originalLogEvent; + }); + }); }); describe('#use', () => {