diff --git a/src/Rokt-Kit.js b/src/Rokt-Kit.js index 17b12c4..b35fcc0 100644 --- a/src/Rokt-Kit.js +++ b/src/Rokt-Kit.js @@ -13,11 +13,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -var roktLauncherScript = 'https://apps.rokt.com/wsdk/integrations/launcher.js'; - var name = 'Rokt'; var moduleId = 181; +var ROKT_EXTENSIONS = { + 'Coupon on Signup Extension Detection': 'cos-extension-detection', + 'Experiment Monitoring': 'experiment-monitoring', + 'Sponsored Payments Apple Pay': 'sponsored-payments-apple-pay', + 'Realtime Conversion Promotion': 'realtime-conversion-promotion', +}; + var constructor = function () { var self = this; @@ -29,6 +34,20 @@ var constructor = function () { self.filters = {}; self.filteredUser = {}; self.userAttributes = {}; + self.testHelpers = null; + + /** + * Generates the Rokt launcher script URL with optional extensions + * @param {Array} extensions - List of extension query parameters to append + * @returns {string} The complete launcher script URL + */ + function generateLauncherScript(extensions) { + var baseUrl = 'https://apps.rokt.com/wsdk/integrations/launcher.js'; + if (!extensions || extensions.length === 0) { + return baseUrl; + } + return baseUrl + '?extensions=' + extensions.join(','); + } /** * Passes attributes to the Rokt Web SDK for client-side hashing @@ -53,10 +72,16 @@ var constructor = function () { filteredUserAttributes ) { var accountId = settings.accountId; + var roktExtensions = extractRoktExtensions(settings.roktExtensions); self.userAttributes = filteredUserAttributes; self.onboardingExpProvider = settings.onboardingExpProvider; if (testMode) { + // Initialize test helpers only in test mode + self.testHelpers = { + generateLauncherScript: generateLauncherScript, + extractRoktExtensions: extractRoktExtensions, + }; attachLauncher(accountId); return; } @@ -65,7 +90,7 @@ var constructor = function () { var target = document.head || document.body; var script = document.createElement('script'); script.type = 'text/javascript'; - script.src = roktLauncherScript; + script.src = generateLauncherScript(roktExtensions); script.async = true; script.crossOrigin = 'anonymous'; script.fetchPriority = 'high'; @@ -135,14 +160,11 @@ var constructor = function () { self.userAttributes = filteredAttributes; - var optimizelyAttributes = - self.onboardingExpProvider === 'Optimizely' - ? fetchOptimizely() - : {}; + var experimentAttributes = formatExperimentAttributes(attributes); var selectPlacementsAttributes = mergeObjects( filteredAttributes, - optimizelyAttributes, + experimentAttributes, { mpid: mpid, } @@ -155,6 +177,22 @@ var constructor = function () { self.launcher.selectPlacements(selectPlacementsOptions); } + /** + * Sets extension data for Rokt Web SDK + * @param {Object} partnerExtensionData - The extension data object containing: + * - [extensionName] {string}: Name of the extension + * - [extensionName].options {Object}: Key-value pairs of options for the extension + * @returns {void} Nothing is returned + */ + function setExtensionData(partnerExtensionData) { + if (!isInitialized()) { + console.error('Rokt Kit: Not initialized'); + return; + } + + window.Rokt.setExtensionData(partnerExtensionData); + } + function onUserIdentified(filteredUser) { self.filteredUser = filteredUser; self.userAttributes = filteredUser.getAllUserAttributes(); @@ -210,6 +248,22 @@ var constructor = function () { }); } + function formatExperimentAttributes(attributes) { + var PREFIX = 'rokt.partnerexperiment.'; + var EXPERIMENT_ID_KEY = PREFIX + 'experimentid'; + var BUCKET_ID_KEY = PREFIX + 'bucketid'; + var USER_ID_KEY = PREFIX + 'userid'; + + if (self.onboardingExpProvider === 'Optimizely') { + return fetchOptimizely(attributes); + } + + var result = {}; + result[PREFIX + attributes[EXPERIMENT_ID_KEY] + '.bucketid'] = + attributes[BUCKET_ID_KEY]; + result['rokt.clientcustomerid'] = attributes[USER_ID_KEY]; + return result; + } // mParticle Kit Callback Methods function fetchOptimizely() { var forwarders = window.mParticle @@ -236,14 +290,13 @@ var constructor = function () { acc, expId ) { - acc[ - 'rokt.custom.optimizely.experiment.' + - expId + - '.variationId' - ] = optimizelyState.getVariationMap()[expId].id; + acc['rokt.partnerexperiment.' + expId + '.bucketid'] = + optimizelyState.getVariationMap()[expId].id; return acc; }, {}); + var visitorId = window.optimizely.get('visitor').visitorId; + activeExperiments['rokt.clientcustomerid'] = visitorId; return activeExperiments; } } catch (error) { @@ -258,6 +311,7 @@ var constructor = function () { // Kit Callback Methods this.init = initForwarder; + this.setExtensionData = setExtensionData; this.setUserAttribute = setUserAttribute; this.onUserIdentified = onUserIdentified; this.removeUserAttribute = removeUserAttribute; @@ -325,6 +379,29 @@ function mergeObjects() { return resObj; } +function parseSettingsString(settingsString) { + try { + return JSON.parse(settingsString.replace(/"/g, '"')); + } catch (error) { + throw new Error('Settings string contains invalid JSON'); + } +} + +function extractRoktExtensions(settingsString) { + var settings = settingsString ? parseSettingsString(settingsString) : []; + + var roktExtensions = []; + for (var i = 0; i < settings.length; i++) { + var extensionName = settings[i].value; + var mappedExtension = ROKT_EXTENSIONS[extensionName]; + if (mappedExtension) { + roktExtensions.push(mappedExtension); + } + } + + return roktExtensions; +} + 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 7c52ee2..0903d8e 100644 --- a/test/src/tests.js +++ b/test/src/tests.js @@ -63,12 +63,11 @@ describe('Rokt Forwarder', () => { this.initializeCalled = false; this.isInitialized = false; - this.accountId = null; this.sandbox = null; this.integrationName = null; - this.createLauncherCalled = false; + this.createLauncher = function (options) { self.accountId = options.accountId; self.integrationName = options.integrationName; @@ -756,4 +755,98 @@ describe('Rokt Forwarder', () => { }); }); }); + + describe('#generateLauncherScript', () => { + const baseUrl = 'https://apps.rokt.com/wsdk/integrations/launcher.js'; + + beforeEach(() => { + window.mParticle.forwarder.init( + { + accountId: '123456', + }, + reportService.cb, + true + ); + }); + + it('should return base URL when no extensions are provided', () => { + const url = + window.mParticle.forwarder.testHelpers.generateLauncherScript( + [] + ); + url.should.equal(baseUrl); + }); + + it('should return base URL when extensions is null or undefined', () => { + window.mParticle.forwarder.testHelpers + .generateLauncherScript(null) + .should.equal(baseUrl); + + window.mParticle.forwarder.testHelpers + .generateLauncherScript(undefined) + .should.equal(baseUrl); + }); + + it('should correctly append a single extension', () => { + const url = + window.mParticle.forwarder.testHelpers.generateLauncherScript([ + 'cos-extension-detection', + ]); + url.should.equal(baseUrl + '?extensions=cos-extension-detection'); + }); + + it('should correctly append multiple extensions', () => { + const url = + window.mParticle.forwarder.testHelpers.generateLauncherScript([ + 'cos-extension-detection', + 'experiment-monitoring', + 'sponsored-payments-apple-pay', + ]); + url.should.equal( + baseUrl + + '?extensions=cos-extension-detection,' + + 'experiment-monitoring,' + + 'sponsored-payments-apple-pay' + ); + }); + }); + + describe('#roktExtensions', () => { + beforeEach(() => { + window.Rokt = new MockRoktForwarder(); + window.mParticle.Rokt = window.Rokt; + }); + + describe('extractRoktExtensions', () => { + it('should correctly map known extension names to their query parameters', async () => { + window.mParticle.forwarder.testHelpers + .extractRoktExtensions( + '[{"value":"Coupon on Signup Extension Detection"},' + + '{"value":"Experiment Monitoring"},' + + '{"value":"Sponsored Payments Apple Pay"},' + + '{"value":"Realtime Conversion Promotion"}]' + ) + .should.deepEqual([ + 'cos-extension-detection', + 'experiment-monitoring', + 'sponsored-payments-apple-pay', + 'realtime-conversion-promotion', + ]); + }); + + it('should ignore unknown or invalid extensions', async () => { + window.mParticle.forwarder.testHelpers + .extractRoktExtensions( + '[{"value":"Unknown Extension"},' + + '{"value":"Experiment Monitoring"},' + + '{"invalid_key":"Invalid Format"},' + + '{"value":"Sponsored Payments Apple Pay"}]' + ) + .should.deepEqual([ + 'experiment-monitoring', + 'sponsored-payments-apple-pay', + ]); + }); + }); + }); });