diff --git a/package.json b/package.json index 41e5faf..85060c6 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "lint": "eslint src/ test/src/", "lint:fix": "eslint src/ test/src/ --fix", "watch": "rollup --config rollup.config.js -w", - "watch:test": "rollup --config rollup.test.config.js -w", + "watch:tests": "rollup --config rollup.test.config.js -w", "test": "npm run build && npm run build:test && karma start test/karma.config.js", "test:debug": "npm run build && npm run build:test && DEBUG=true karma start test/karma.config.js" }, diff --git a/src/Rokt-Kit.js b/src/Rokt-Kit.js index fc1436f..17b12c4 100644 --- a/src/Rokt-Kit.js +++ b/src/Rokt-Kit.js @@ -30,6 +30,21 @@ var constructor = function () { self.filteredUser = {}; self.userAttributes = {}; + /** + * Passes attributes to the Rokt Web SDK for client-side hashing + * @see https://docs.rokt.com/developers/integration-guides/web/library/integration-launcher#hash-attributes + * @param {Object} attributes - The attributes to be hashed + * @returns {Promise} A Promise resolving to the + * hashed attributes from the launcher, or `null` if the kit is not initialized + */ + function hashAttributes(attributes) { + if (!isInitialized()) { + console.error('Rokt Kit: Not initialized'); + return null; + } + return self.launcher.hashAttributes(attributes); + } + function initForwarder( settings, _service, @@ -81,12 +96,15 @@ var constructor = function () { } } + /** + * Selects placements for Rokt Web SDK with merged attributes, filters, and experimentation options + * @see https://docs.rokt.com/developers/integration-guides/web/library/select-placements-options/ + * @param {Object} options - The options object for selecting placements containing: + * - identifier {string}: The placement identifier + * - attributes {Object}: Optional attributes to merge with existing attributes + * @returns {Promise} A Promise resolving to the Rokt launcher's selectPlacements method with processed attributes + */ function selectPlacements(options) { - // https://docs.rokt.com/developers/integration-guides/web/library/select-placements-options/ - // options should contain: - // - identifier - // - attributes - var attributes = (options && options.attributes) || {}; var placementAttributes = mergeObjects(self.userAttributes, attributes); @@ -192,9 +210,6 @@ var constructor = function () { }); } - // Called by the mParticle Rokt Manager - this.selectPlacements = selectPlacements; - // mParticle Kit Callback Methods function fetchOptimizely() { var forwarders = window.mParticle @@ -236,10 +251,27 @@ var constructor = function () { } return {}; } + + // Called by the mParticle Rokt Manager + this.selectPlacements = selectPlacements; + this.hashAttributes = hashAttributes; + + // Kit Callback Methods this.init = initForwarder; this.setUserAttribute = setUserAttribute; this.onUserIdentified = onUserIdentified; this.removeUserAttribute = removeUserAttribute; + + /** + * Checks if the kit is properly initialized and ready for use. + * Both conditions must be true: + * 1. self.isInitialized - Set after successful initialization of the kit + * 2. self.launcher - The Rokt launcher instance must be available + * @returns {boolean} Whether the kit is fully initialized + */ + function isInitialized() { + return !!(self.isInitialized && self.launcher); + } }; function getId() { diff --git a/test/src/tests.js b/test/src/tests.js index c616119..7c52ee2 100644 --- a/test/src/tests.js +++ b/test/src/tests.js @@ -189,6 +189,145 @@ describe('Rokt Forwarder', () => { }); }); + describe('#hashAttributes', () => { + beforeEach(() => { + window.Rokt = new MockRoktForwarder(); + window.mParticle.Rokt = window.Rokt; + window.mParticle.Rokt.attachKitCalled = false; + window.mParticle.Rokt.attachKit = async (kit) => { + window.mParticle.Rokt.attachKitCalled = true; + window.mParticle.Rokt.kit = kit; + Promise.resolve(); + }; + window.mParticle.forwarder.launcher = { + hashAttributes: function (attributes) { + // Mocking the hashAttributes method to show that + // the attributes will be transformed by the launcher's + // hashAttributes method. + const hashedAttributes = {}; + for (const key in attributes) { + if (attributes.hasOwnProperty(key)) { + hashedAttributes[key + '-hash'] = + 'hashed-' + attributes[key]; + } + } + window.mParticle.Rokt.hashedAttributes = hashedAttributes; + window.mParticle.Rokt.hashAttributesCalled = true; + + return Promise.resolve(hashedAttributes); + }, + }; + }); + + it('should call launcher.hashAttributes with passed through attributes when fully initialized', function () { + // Ensure both initialization conditions are met + window.mParticle.forwarder.isInitialized = true; + window.mParticle.forwarder.launcher = { + hashAttributes: function (attributes) { + window.mParticle.Rokt.hashAttributesOptions = attributes; + window.mParticle.Rokt.hashAttributesCalled = true; + return { + 'test-attribute': 'hashed-value', + }; + }, + }; + + var attributes = { + 'test-attribute': 'test-value', + }; + + window.mParticle.forwarder.hashAttributes(attributes); + + window.Rokt.hashAttributesCalled.should.equal(true); + window.Rokt.hashAttributesOptions.should.deepEqual(attributes); + }); + + it('should return null when launcher exists but kit is not initialized', function () { + // Set launcher but ensure isInitialized is false + window.mParticle.forwarder.isInitialized = false; + window.mParticle.forwarder.launcher = { + hashAttributes: function () {}, + }; + + var result = window.mParticle.forwarder.hashAttributes({ + 'test-attribute': 'test-value', + }); + + (result === null).should.equal(true); + }); + + it('should log an error when called before initialization', function () { + var errorLogged = false; + var errorMessage = null; + window.console.error = function (message) { + errorLogged = true; + errorMessage = message; + }; + + // Ensure kit is not initialized + window.mParticle.forwarder.isInitialized = false; + window.mParticle.forwarder.launcher = null; + + window.mParticle.forwarder.hashAttributes({ + 'test-attribute': 'test-value', + }); + + errorLogged.should.equal(true); + errorMessage.should.equal('Rokt Kit: Not initialized'); + }); + + it('should return null when kit is initialized but launcher is missing', function () { + // Mock isInitialized but remove launcher + window.mParticle.forwarder.isInitialized = true; + window.mParticle.forwarder.launcher = null; + + var result = window.mParticle.forwarder.hashAttributes({ + 'test-attribute': 'test-value', + }); + + (result === null).should.equal(true); + }); + + it('should log an error when kit is initialized but launcher is missing', function () { + var errorLogged = false; + var errorMessage = null; + window.console.error = function (message) { + errorLogged = true; + errorMessage = message; + }; + + window.mParticle.forwarder.isInitialized = true; + window.mParticle.forwarder.launcher = null; + + window.mParticle.forwarder.hashAttributes({ + 'test-attribute': 'test-value', + }); + + errorLogged.should.equal(true); + errorMessage.should.equal('Rokt Kit: Not initialized'); + }); + + it('should return hashed attributes from launcher', async () => { + await window.mParticle.forwarder.init( + { + accountId: '123456', + }, + reportService.cb, + true, + null, + {} + ); + + const result = await window.mParticle.forwarder.hashAttributes({ + 'test-attribute': 'test-value', + }); + + result.should.deepEqual({ + 'test-attribute-hash': 'hashed-test-value', + }); + }); + }); + describe('#selectPlacements', () => { beforeEach(() => { window.Rokt = new MockRoktForwarder();