From ea403209b687c3c7d6931916571ee2f544308b5b Mon Sep 17 00:00:00 2001 From: Jaissica Date: Tue, 25 Nov 2025 16:16:42 -0500 Subject: [PATCH 01/10] fix: indentation in FacebookEventForwarder.js (#58) --- src/FacebookEventForwarder.js | 736 +++++++++++++++++----------------- 1 file changed, 368 insertions(+), 368 deletions(-) diff --git a/src/FacebookEventForwarder.js b/src/FacebookEventForwarder.js index 782a8ae..7a4bd60 100755 --- a/src/FacebookEventForwarder.js +++ b/src/FacebookEventForwarder.js @@ -12,443 +12,443 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. - var isobject = require('isobject'); - - var name = 'Facebook', - moduleId = 45, - MessageType = { - SessionStart: 1, - SessionEnd: 2, - PageView: 3, - PageEvent: 4, - CrashReport: 5, - OptOut: 6, - Commerce: 16, - }, - IdentityType = { - Other: 0, - CustomerId: 1, - Facebook: 2, - Twitter: 3, - Google: 4, - Microsoft: 5, - Yahoo: 6, - Email: 7, - FacebookCustomAudienceId: 9, - Other2: 10, - Other3: 11, - Other4: 12, - Other5: 13, - Other6: 14, - Other7: 15, - Other8: 16, - Other9: 17, - Other10: 18, - MobileNumber: 19, - PhoneNumber2: 20, - PhoneNumber3: 21, - }, - SupportedCommerceTypes = [], - // Standard FB Event Names from https://developers.facebook.com/docs/facebook-pixel/reference#standard-events - ADD_TO_CART_EVENT_NAME = 'AddToCart', - ADD_TO_WISHLIST_EVENT_NAME = 'AddToWishlist', - CHECKOUT_EVENT_NAME = 'InitiateCheckout', - PAGE_VIEW_EVENT_NAME = 'PageView', - PURCHASE_EVENT_NAME = 'Purchase', - REMOVE_FROM_CART_EVENT_NAME = 'RemoveFromCart', - VIEW_CONTENT_EVENT_NAME = 'ViewContent', - constructor = function () { - var self = this, - isInitialized = false, - reportingService = null, - settings, - productAttributeMapping; - - self.name = name; - - function initForwarder(forwarderSettings, service, testMode, trackerId, userAttributes, userIdentities) { - settings = forwarderSettings; - reportingService = service; - - SupportedCommerceTypes = [ - mParticle.ProductActionType.Checkout, - mParticle.ProductActionType.Purchase, - mParticle.ProductActionType.AddToCart, - mParticle.ProductActionType.RemoveFromCart, - mParticle.ProductActionType.AddToWishlist, - mParticle.ProductActionType.ViewDetail - ]; - - try { - if (!testMode) { - !function (f, b, e, v, n, t, s) { - if (f.fbq) return; n = f.fbq = function () { n.callMethod ? n.callMethod.apply(n, arguments) : n.queue.push(arguments); }; if (!f._fbq) f._fbq = n; - n.push = n; n.loaded = !0; n.version = '2.0'; n.queue = []; t = b.createElement(e); t.async = !0; t.src = v; s = b.getElementsByTagName(e)[0]; - s.parentNode.insertBefore(t, s); - } (window, document, 'script', 'https://connect.facebook.net/en_US/fbevents.js'); - - var visitorData = {}; - - if(settings.externalUserIdentityType && userIdentities && userIdentities.length > 0) { - var selectedType = IdentityType[settings.externalUserIdentityType]; - var selectedIdentity = userIdentities.filter(function (identityElement) { - if (identityElement.Type === selectedType) { - return identityElement.Identity; - } - }) - - if (selectedIdentity.length > 0) { - visitorData['external_id'] = selectedIdentity[0].Identity; +var isobject = require('isobject'); + +var name = 'Facebook', + moduleId = 45, + MessageType = { + SessionStart: 1, + SessionEnd: 2, + PageView: 3, + PageEvent: 4, + CrashReport: 5, + OptOut: 6, + Commerce: 16, + }, + IdentityType = { + Other: 0, + CustomerId: 1, + Facebook: 2, + Twitter: 3, + Google: 4, + Microsoft: 5, + Yahoo: 6, + Email: 7, + FacebookCustomAudienceId: 9, + Other2: 10, + Other3: 11, + Other4: 12, + Other5: 13, + Other6: 14, + Other7: 15, + Other8: 16, + Other9: 17, + Other10: 18, + MobileNumber: 19, + PhoneNumber2: 20, + PhoneNumber3: 21, + }, + SupportedCommerceTypes = [], + // Standard FB Event Names from https://developers.facebook.com/docs/facebook-pixel/reference#standard-events + ADD_TO_CART_EVENT_NAME = 'AddToCart', + ADD_TO_WISHLIST_EVENT_NAME = 'AddToWishlist', + CHECKOUT_EVENT_NAME = 'InitiateCheckout', + PAGE_VIEW_EVENT_NAME = 'PageView', + PURCHASE_EVENT_NAME = 'Purchase', + REMOVE_FROM_CART_EVENT_NAME = 'RemoveFromCart', + VIEW_CONTENT_EVENT_NAME = 'ViewContent', + constructor = function () { + var self = this, + isInitialized = false, + reportingService = null, + settings, + productAttributeMapping; + + self.name = name; + + function initForwarder(forwarderSettings, service, testMode, trackerId, userAttributes, userIdentities) { + settings = forwarderSettings; + reportingService = service; + + SupportedCommerceTypes = [ + mParticle.ProductActionType.Checkout, + mParticle.ProductActionType.Purchase, + mParticle.ProductActionType.AddToCart, + mParticle.ProductActionType.RemoveFromCart, + mParticle.ProductActionType.AddToWishlist, + mParticle.ProductActionType.ViewDetail + ]; + + try { + if (!testMode) { + !function (f, b, e, v, n, t, s) { + if (f.fbq) return; n = f.fbq = function () { n.callMethod ? n.callMethod.apply(n, arguments) : n.queue.push(arguments); }; if (!f._fbq) f._fbq = n; + n.push = n; n.loaded = !0; n.version = '2.0'; n.queue = []; t = b.createElement(e); t.async = !0; t.src = v; s = b.getElementsByTagName(e)[0]; + s.parentNode.insertBefore(t, s); + } (window, document, 'script', 'https://connect.facebook.net/en_US/fbevents.js'); + + var visitorData = {}; + + if(settings.externalUserIdentityType && userIdentities && userIdentities.length > 0) { + var selectedType = IdentityType[settings.externalUserIdentityType]; + var selectedIdentity = userIdentities.filter(function (identityElement) { + if (identityElement.Type === selectedType) { + return identityElement.Identity; } + }) + + if (selectedIdentity.length > 0) { + visitorData['external_id'] = selectedIdentity[0].Identity; } } + } - if (settings.disablePushState === 'True') { - // Facebook will automatically track page views whenever a new state is pushed to the HTML 5 History State API - // this option can be disabled to prevent duplicate page views - // https://developers.facebook.com/docs/facebook-pixel/implementation/tag_spa/#tagging-single-page-applications - fbq.disablePushState = true; - } - fbq('init', settings.pixelId, visitorData); - - loadMappings(); + if (settings.disablePushState === 'True') { + // Facebook will automatically track page views whenever a new state is pushed to the HTML 5 History State API + // this option can be disabled to prevent duplicate page views + // https://developers.facebook.com/docs/facebook-pixel/implementation/tag_spa/#tagging-single-page-applications + fbq.disablePushState = true; + } + fbq('init', settings.pixelId, visitorData); + + loadMappings(); - isInitialized = true; + isInitialized = true; - return 'Successfully initialized: ' + name; + return 'Successfully initialized: ' + name; - } - catch (e) { - return 'Can\'t initialize forwarder: ' + name + ':' + e; - } } - - function loadMappings() { - productAttributeMapping = settings.productAttributeMapping - ? JSON.parse(settings.productAttributeMapping.replace(/"/g, '"')) - : []; + catch (e) { + return 'Can\'t initialize forwarder: ' + name + ':' + e; } + } - function processEvent(event) { - var reportEvent = false; + function loadMappings() { + productAttributeMapping = settings.productAttributeMapping + ? JSON.parse(settings.productAttributeMapping.replace(/"/g, '"')) + : []; + } - if (!isInitialized) { - return 'Can\'t send forwarder ' + name + ', not initialized'; - } + function processEvent(event) { + var reportEvent = false; - try { - if (event.EventDataType == MessageType.PageView) { - reportEvent = true; - logPageView(event); - } - else if (event.EventDataType == MessageType.PageEvent) { - reportEvent = true; - logPageEvent(event); - } - else if (event.EventDataType == MessageType.Commerce) { - reportEvent = logCommerceEvent(event); - } - - if (reportEvent && reportingService) { - reportingService(self, event); - } + if (!isInitialized) { + return 'Can\'t send forwarder ' + name + ', not initialized'; + } - return 'Successfully sent to forwarder ' + name; + try { + if (event.EventDataType == MessageType.PageView) { + reportEvent = true; + logPageView(event); + } + else if (event.EventDataType == MessageType.PageEvent) { + reportEvent = true; + logPageEvent(event); } - catch (error) { - return 'Can\'t send to forwarder: ' + name + ' ' + error; + else if (event.EventDataType == MessageType.Commerce) { + reportEvent = logCommerceEvent(event); + } + + if (reportEvent && reportingService) { + reportingService(self, event); } + + return 'Successfully sent to forwarder ' + name; + } + catch (error) { + return 'Can\'t send to forwarder: ' + name + ' ' + error; } + } + + function logCommerceEvent(event) { + if (event.ProductAction && + event.ProductAction.ProductList && + event.ProductAction.ProductActionType && + SupportedCommerceTypes.indexOf(event.ProductAction.ProductActionType) > -1) { - function logCommerceEvent(event) { - if (event.ProductAction && - event.ProductAction.ProductList && - event.ProductAction.ProductActionType && - SupportedCommerceTypes.indexOf(event.ProductAction.ProductActionType) > -1) { + var eventName, + totalValue, + params = cloneEventAttributes(event), + eventID = createEventId(event); + params['currency'] = event.CurrencyCode || 'USD'; - var eventName, - totalValue, - params = cloneEventAttributes(event), - eventID = createEventId(event); - params['currency'] = event.CurrencyCode || 'USD'; + if (event.EventName) { + params['content_name'] = event.EventName; + } - if (event.EventName) { - params['content_name'] = event.EventName; + var productSkus = event.ProductAction.ProductList.reduce(function (arr, curr) { + if (curr.Sku) { + arr.push(curr.Sku); } + return arr; + }, []); - var productSkus = event.ProductAction.ProductList.reduce(function (arr, curr) { - if (curr.Sku) { - arr.push(curr.Sku); - } - return arr; - }, []); + if (productSkus && productSkus.length > 0) { + params['content_ids'] = productSkus; + } - if (productSkus && productSkus.length > 0) { - params['content_ids'] = productSkus; + if (event.ProductAction.ProductActionType == mParticle.ProductActionType.AddToWishlist || + event.ProductAction.ProductActionType == mParticle.ProductActionType.Checkout) { + var eventCategory = getEventCategoryString(event); + if (eventCategory) { + params['content_category'] = eventCategory; } + if (event.ProductAction.ProductActionType == mParticle.ProductActionType.Checkout && event.ProductAction.CheckoutStep) { + params['checkout_step'] = event.ProductAction.CheckoutStep; + } + } - if (event.ProductAction.ProductActionType == mParticle.ProductActionType.AddToWishlist || - event.ProductAction.ProductActionType == mParticle.ProductActionType.Checkout) { - var eventCategory = getEventCategoryString(event); - if (eventCategory) { - params['content_category'] = eventCategory; - } - if (event.ProductAction.ProductActionType == mParticle.ProductActionType.Checkout && event.ProductAction.CheckoutStep) { - params['checkout_step'] = event.ProductAction.CheckoutStep; + if (event.ProductAction.ProductActionType == mParticle.ProductActionType.AddToCart || + event.ProductAction.ProductActionType == mParticle.ProductActionType.AddToWishlist || + event.ProductAction.ProductActionType == mParticle.ProductActionType.ViewDetail) { + + totalValue = event.ProductAction.ProductList.reduce(function(sum, product){ + if (isNumeric(product.Price) && isNumeric(product.Quantity)) { + sum += product.Price * product.Quantity; } - } + return sum; + }, 0); - if (event.ProductAction.ProductActionType == mParticle.ProductActionType.AddToCart || - event.ProductAction.ProductActionType == mParticle.ProductActionType.AddToWishlist || - event.ProductAction.ProductActionType == mParticle.ProductActionType.ViewDetail) { + params['value'] = totalValue; - totalValue = event.ProductAction.ProductList.reduce(function(sum, product){ - if (isNumeric(product.Price) && isNumeric(product.Quantity)) { - sum += product.Price * product.Quantity; - } - return sum; - }, 0); + if (event.ProductAction.ProductActionType == mParticle.ProductActionType.AddToWishlist){ + eventName = ADD_TO_WISHLIST_EVENT_NAME; + } + else if (event.ProductAction.ProductActionType == mParticle.ProductActionType.AddToCart){ + eventName = ADD_TO_CART_EVENT_NAME; + } + else{ + eventName = VIEW_CONTENT_EVENT_NAME; + } - params['value'] = totalValue; + } + else if (event.ProductAction.ProductActionType == mParticle.ProductActionType.Checkout || + event.ProductAction.ProductActionType == mParticle.ProductActionType.Purchase) { - if (event.ProductAction.ProductActionType == mParticle.ProductActionType.AddToWishlist){ - eventName = ADD_TO_WISHLIST_EVENT_NAME; - } - else if (event.ProductAction.ProductActionType == mParticle.ProductActionType.AddToCart){ - eventName = ADD_TO_CART_EVENT_NAME; - } - else{ - eventName = VIEW_CONTENT_EVENT_NAME; - } + eventName = event.ProductAction.ProductActionType == mParticle.ProductActionType.Checkout ? CHECKOUT_EVENT_NAME : PURCHASE_EVENT_NAME; + if (event.ProductAction.TotalAmount) { + params['value'] = event.ProductAction.TotalAmount; } - else if (event.ProductAction.ProductActionType == mParticle.ProductActionType.Checkout || - event.ProductAction.ProductActionType == mParticle.ProductActionType.Purchase) { - - eventName = event.ProductAction.ProductActionType == mParticle.ProductActionType.Checkout ? CHECKOUT_EVENT_NAME : PURCHASE_EVENT_NAME; - if (event.ProductAction.TotalAmount) { - params['value'] = event.ProductAction.TotalAmount; + var num_items = event.ProductAction.ProductList.reduce(function(sum, product){ + if (isNumeric(product.Quantity)) { + sum += product.Quantity; } + return sum; + }, 0); + params['num_items'] = num_items; + + if (event.ProductAction.TransactionId) { + params['order_id'] = event.ProductAction.TransactionId; + } - var num_items = event.ProductAction.ProductList.reduce(function(sum, product){ - if (isNumeric(product.Quantity)) { - sum += product.Quantity; + // Build contents array for Purchase events + if (event.ProductAction.ProductActionType == mParticle.ProductActionType.Purchase) { + var contents = buildProductContents(event.ProductAction.ProductList); + if (contents && contents.length > 0) { + params['contents'] = contents; + } + } + } + else if (event.ProductAction.ProductActionType == mParticle.ProductActionType.RemoveFromCart) { + eventName = REMOVE_FROM_CART_EVENT_NAME; + + // remove from cart can be performed in 1 of 2 ways: + // 1. mParticle.eCommerce.logProductEvent(), which contains event.ProductAction.TotalAmount + // 2. mParticle.eCommerce.Cart.remove(), which does not contain event.ProductAction.TotalAmount + // when there is no TotalAmount, a manual calculation must be done + if (event.ProductAction.TotalAmount) { + totalValue = event.ProductAction.TotalAmount; + } else { + totalValue = event.ProductAction.ProductList.reduce(function(sum, product) { + if (isNumeric(product.TotalAmount)) { + sum += product.TotalAmount; } return sum; }, 0); - params['num_items'] = num_items; - - if (event.ProductAction.TransactionId) { - params['order_id'] = event.ProductAction.TransactionId; - } - - // Build contents array for Purchase events - if (event.ProductAction.ProductActionType == mParticle.ProductActionType.Purchase) { - var contents = buildProductContents(event.ProductAction.ProductList); - if (contents && contents.length > 0) { - params['contents'] = contents; - } - } - } - else if (event.ProductAction.ProductActionType == mParticle.ProductActionType.RemoveFromCart) { - eventName = REMOVE_FROM_CART_EVENT_NAME; - - // remove from cart can be performed in 1 of 2 ways: - // 1. mParticle.eCommerce.logProductEvent(), which contains event.ProductAction.TotalAmount - // 2. mParticle.eCommerce.Cart.remove(), which does not contain event.ProductAction.TotalAmount - // when there is no TotalAmount, a manual calculation must be done - if (event.ProductAction.TotalAmount) { - totalValue = event.ProductAction.TotalAmount; - } else { - totalValue = event.ProductAction.ProductList.reduce(function(sum, product) { - if (isNumeric(product.TotalAmount)) { - sum += product.TotalAmount; - } - return sum; - }, 0); - } - - params['value'] = totalValue; - - fbq('trackCustom', eventName || 'customEvent', params, eventID); - return true; } - if (eventName) { - fbq('track', eventName, params, eventID); - } - else { - return false; - } + params['value'] = totalValue; + fbq('trackCustom', eventName || 'customEvent', params, eventID); return true; } - return false; - } + if (eventName) { + fbq('track', eventName, params, eventID); + } + else { + return false; + } - function logPageView(event) { - logPageEvent(event, PAGE_VIEW_EVENT_NAME); + return true; } - function logPageEvent(event, eventName) { - var params = cloneEventAttributes(event); - var eventID = createEventId(event); + return false; + } - eventName = eventName || event.EventName; - if (event.EventName) { - params['content_name'] = event.EventName; - } + function logPageView(event) { + logPageEvent(event, PAGE_VIEW_EVENT_NAME); + } - fbq('trackCustom', eventName || 'customEvent', params, eventID); - } + function logPageEvent(event, eventName) { + var params = cloneEventAttributes(event); + var eventID = createEventId(event); - function cloneEventAttributes(event) { - var attr = {}; - if (event && event.EventAttributes) { - try { - attr = JSON.parse(JSON.stringify(event.EventAttributes)); - } - catch (e) { - // - } - } - return attr; + eventName = eventName || event.EventName; + if (event.EventName) { + params['content_name'] = event.EventName; } - function isNumeric(n) { - return !isNaN(parseFloat(n)) && isFinite(n); - } - - function getEventCategoryString(event) { + fbq('trackCustom', eventName || 'customEvent', params, eventID); + } - var enumTypeValues; - var enumValue; - if (event.EventDataType == MessageType.Commerce) { - enumTypeValues = event.EventCategory ? mParticle.CommerceEventType : mParticle.ProductActionType; - enumValue = event.EventCategory || event.ProductAction.ProductActionType; + function cloneEventAttributes(event) { + var attr = {}; + if (event && event.EventAttributes) { + try { + attr = JSON.parse(JSON.stringify(event.EventAttributes)); } - else { - enumTypeValues = mParticle.EventType; - enumValue = event.EventCategory; + catch (e) { + // } + } + return attr; + } - if (enumTypeValues && enumValue) { + function isNumeric(n) { + return !isNaN(parseFloat(n)) && isFinite(n); + } - for (var category in enumTypeValues) { - if (enumValue == enumTypeValues[category]) { - return category; - } - } - } + function getEventCategoryString(event) { - return null; + var enumTypeValues; + var enumValue; + if (event.EventDataType == MessageType.Commerce) { + enumTypeValues = event.EventCategory ? mParticle.CommerceEventType : mParticle.ProductActionType; + enumValue = event.EventCategory || event.ProductAction.ProductActionType; } - - /** - * Builds contents array for Facebook Pixel commerce events. - * Creates a nested array of content items with product details. - * - * @param {Array} productList - Array of products from event.ProductAction.ProductList - * @returns {Array} Array of content objects for Facebook Pixel - */ - function buildProductContents(productList) { - if (!productList || productList.length === 0) { - return []; - } - - return productList - .filter(function(product) { - return product && product.Sku; - }) - .map(function(product) { - var contentItem = { - id: product.Sku, - quantity: isNumeric(product.Quantity) ? product.Quantity : 1, - name: product.Name, - brand: product.Brand, - category: product.Category, - variant: product.Variant, - item_price: isNumeric(product.Price) ? product.Price : null - }; - - // Apply configured mappings to custom attributes - productAttributeMapping.forEach(function(productMapping) { - if (!isobject(productMapping) || !productMapping.map || !productMapping.value) { - return; - } - - var sourceField = productMapping.map; - var facebookFieldName = productMapping.value; - var value = null; - - // Check for Product level field first - if (product.hasOwnProperty(sourceField)) { - value = product[sourceField]; - } - // then check for Product.Attributes level field - else if (product.Attributes && product.Attributes[sourceField]) { - value = product.Attributes[sourceField]; - } - - if (value !== null && value !== undefined) { - contentItem[facebookFieldName] = value; - } - }); - return contentItem; - }); + else { + enumTypeValues = mParticle.EventType; + enumValue = event.EventCategory; } - // https://developers.facebook.com/docs/marketing-api/conversions-api/deduplicate-pixel-and-server-events#event-deduplication-options - function createEventId(event) { - return { - eventID: event.SourceMessageId || null + if (enumTypeValues && enumValue) { + + for (var category in enumTypeValues) { + if (enumValue == enumTypeValues[category]) { + return category; + } } } - this.init = initForwarder; - this.process = processEvent; - }; - - function getId() { - return moduleId; - } - - function register(config) { - if (!config) { - console.log('You must pass a config object to register the kit ' + name); - return; + return null; } - if (!isobject(config)) { - console.log('\'config\' must be an object. You passed in a ' + typeof config); - return; + /** + * Builds contents array for Facebook Pixel commerce events. + * Creates a nested array of content items with product details. + * + * @param {Array} productList - Array of products from event.ProductAction.ProductList + * @returns {Array} Array of content objects for Facebook Pixel + */ + function buildProductContents(productList) { + if (!productList || productList.length === 0) { + return []; + } + + return productList + .filter(function(product) { + return product && product.Sku; + }) + .map(function(product) { + var contentItem = { + id: product.Sku, + quantity: isNumeric(product.Quantity) ? product.Quantity : 1, + name: product.Name, + brand: product.Brand, + category: product.Category, + variant: product.Variant, + item_price: isNumeric(product.Price) ? product.Price : null + }; + + // Apply configured mappings to custom attributes + productAttributeMapping.forEach(function(productMapping) { + if (!isobject(productMapping) || !productMapping.map || !productMapping.value) { + return; + } + + var sourceField = productMapping.map; + var facebookFieldName = productMapping.value; + var value = null; + + // Check for Product level field first + if (product.hasOwnProperty(sourceField)) { + value = product[sourceField]; + } + // then check for Product.Attributes level field + else if (product.Attributes && product.Attributes[sourceField]) { + value = product.Attributes[sourceField]; + } + + if (value !== null && value !== undefined) { + contentItem[facebookFieldName] = value; + } + }); + return contentItem; + }); } - if (isobject(config.kits)) { - config.kits[name] = { - constructor: constructor - }; - } else { - config.kits = {}; - config.kits[name] = { - constructor: constructor - }; + // https://developers.facebook.com/docs/marketing-api/conversions-api/deduplicate-pixel-and-server-events#event-deduplication-options + function createEventId(event) { + return { + eventID: event.SourceMessageId || null + } } - console.log('Successfully registered ' + name + ' to your mParticle configuration'); + + this.init = initForwarder; + this.process = processEvent; + }; + +function getId() { + return moduleId; +} + +function register(config) { + if (!config) { + console.log('You must pass a config object to register the kit ' + name); + return; } - if (typeof window !== 'undefined') { - if (window && window.mParticle && window.mParticle.addForwarder) { - window.mParticle.addForwarder({ - name: name, - constructor: constructor, - getId: getId - }); - } + if (!isobject(config)) { + console.log('\'config\' must be an object. You passed in a ' + typeof config); + return; } - module.exports = { - register: register - }; + if (isobject(config.kits)) { + config.kits[name] = { + constructor: constructor + }; + } else { + config.kits = {}; + config.kits[name] = { + constructor: constructor + }; + } + console.log('Successfully registered ' + name + ' to your mParticle configuration'); +} + +if (typeof window !== 'undefined') { + if (window && window.mParticle && window.mParticle.addForwarder) { + window.mParticle.addForwarder({ + name: name, + constructor: constructor, + getId: getId + }); + } +} + +module.exports = { + register: register +}; From a414e9e8f70fcb53bcfa67355070160138e5be03 Mon Sep 17 00:00:00 2001 From: Jaissica Date: Tue, 25 Nov 2025 13:56:10 -0500 Subject: [PATCH 02/10] extend product attribute mapping to AddToCart and Checkout event --- src/FacebookEventForwarder.js | 18 ++-- test/tests.js | 153 +++++++++++++++++++++++++++++++++- 2 files changed, 164 insertions(+), 7 deletions(-) diff --git a/src/FacebookEventForwarder.js b/src/FacebookEventForwarder.js index 7a4bd60..a39a7aa 100755 --- a/src/FacebookEventForwarder.js +++ b/src/FacebookEventForwarder.js @@ -216,6 +216,16 @@ var name = 'Facebook', } else if (event.ProductAction.ProductActionType == mParticle.ProductActionType.AddToCart){ eventName = ADD_TO_CART_EVENT_NAME; + if (event.ProductAction.TransactionId) { + params['order_id'] = event.ProductAction.TransactionId; + } + + // Build contents array for Purchase events + var contents = buildProductContents(event.ProductAction.ProductList); + if (contents && contents.length > 0) { + params['contents'] = contents; + } + } else{ eventName = VIEW_CONTENT_EVENT_NAME; @@ -244,11 +254,9 @@ var name = 'Facebook', } // Build contents array for Purchase events - if (event.ProductAction.ProductActionType == mParticle.ProductActionType.Purchase) { - var contents = buildProductContents(event.ProductAction.ProductList); - if (contents && contents.length > 0) { - params['contents'] = contents; - } + var contents = buildProductContents(event.ProductAction.ProductList); + if (contents && contents.length > 0) { + params['contents'] = contents; } } else if (event.ProductAction.ProductActionType == mParticle.ProductActionType.RemoveFromCart) { diff --git a/test/tests.js b/test/tests.js index b0e0763..b130031 100755 --- a/test/tests.js +++ b/test/tests.js @@ -371,6 +371,8 @@ describe('Facebook Forwarder', function () { window.fbqObj.params.should.have.property('eventAttr1', 'value1'); window.fbqObj.params.should.have.property('eventAttr2', 'value2'); window.fbqObj.eventData.should.have.property('eventID', SOURCE_MESSAGE_ID); + window.fbqObj.params.should.have.property('order_id', 123); + window.fbqObj.params.should.have.property('contents'); done(); }); @@ -439,7 +441,8 @@ describe('Facebook Forwarder', function () { window.fbqObj.params.should.have.property('checkout_step', 1); window.fbqObj.params.should.have.property('num_items', 9); window.fbqObj.eventData.should.have.property('eventID', SOURCE_MESSAGE_ID); - + window.fbqObj.params.should.have.property('order_id', 123); + window.fbqObj.params.should.have.property('contents'); done(); }); @@ -481,7 +484,8 @@ describe('Facebook Forwarder', function () { window.fbqObj.params.should.have.property('content_name', 'eCommerce - AddToCart'); window.fbqObj.params.should.have.property('content_ids', ['12345']); window.fbqObj.eventData.should.have.property('eventID', SOURCE_MESSAGE_ID); - window.fbqObj.params.should.not.have.property('contents'); + window.fbqObj.params.should.have.property('contents'); + window.fbqObj.params.should.have.property('order_id', 123); done(); }); @@ -540,6 +544,8 @@ describe('Facebook Forwarder', function () { window.fbqObj.params.should.have.property('content_name', 'eCommerce - AddToCart'); window.fbqObj.params.should.have.property('content_ids', ['12345', '888', '666']); window.fbqObj.eventData.should.have.property('eventID', SOURCE_MESSAGE_ID); + window.fbqObj.params.should.have.property('order_id', 123); + window.fbqObj.params.should.have.property('contents'); done(); }); @@ -581,6 +587,8 @@ describe('Facebook Forwarder', function () { window.fbqObj.params.should.have.property('content_name', 'eCommerce - RemoveFromCart'); window.fbqObj.params.should.have.property('content_ids', ['12345']); window.fbqObj.eventData.should.have.property('eventID', SOURCE_MESSAGE_ID); + window.fbqObj.params.should.not.have.property('order_id'); + window.fbqObj.params.should.not.have.property('contents'); done(); }); @@ -621,6 +629,8 @@ describe('Facebook Forwarder', function () { window.fbqObj.params.should.have.property('content_name', 'eCommerce - RemoveFromCart'); window.fbqObj.params.should.have.property('content_ids', ['12345']); window.fbqObj.eventData.should.have.property('eventID', SOURCE_MESSAGE_ID); + window.fbqObj.params.should.not.have.property('order_id'); + window.fbqObj.params.should.not.have.property('contents'); done(); }); @@ -685,6 +695,8 @@ describe('Facebook Forwarder', function () { window.fbqObj.params.should.have.property('eventAttr1', 'value1'); window.fbqObj.params.should.have.property('eventAttr2', 'value2'); window.fbqObj.eventData.should.have.property('eventID', SOURCE_MESSAGE_ID); + window.fbqObj.params.should.not.have.property('order_id'); + window.fbqObj.params.should.not.have.property('contents'); done(); }); @@ -732,6 +744,8 @@ describe('Facebook Forwarder', function () { window.fbqObj.params.should.have.property('eventAttr1', 'value1'); window.fbqObj.params.should.have.property('eventAttr2', 'value2'); window.fbqObj.eventData.should.have.property('eventID', SOURCE_MESSAGE_ID); + window.fbqObj.params.should.not.have.property('order_id'); + window.fbqObj.params.should.not.have.property('contents'); done(); }); @@ -778,6 +792,8 @@ describe('Facebook Forwarder', function () { window.fbqObj.params.should.have.property('eventAttr1', 'value1'); window.fbqObj.params.should.have.property('eventAttr2', 'value2'); window.fbqObj.eventData.should.have.property('eventID', SOURCE_MESSAGE_ID); + window.fbqObj.params.should.not.have.property('order_id'); + window.fbqObj.params.should.not.have.property('contents'); done(); }); @@ -824,6 +840,8 @@ describe('Facebook Forwarder', function () { window.fbqObj.params.should.have.property('eventAttr1', 'value1'); window.fbqObj.params.should.have.property('eventAttr2', 'value2'); window.fbqObj.eventData.should.have.property('eventID', SOURCE_MESSAGE_ID); + window.fbqObj.params.should.not.have.property('order_id'); + window.fbqObj.params.should.not.have.property('contents'); done(); }); @@ -924,5 +942,136 @@ describe('Facebook Forwarder', function () { done(); }); + + it('should build contents array with mapped attributes for AddToCart events', function (done) { + // Initialize with product attribute mapping + mParticle.forwarder.init({ + pixelCode: 'test-pixel-code', + "productAttributeMapping":"[{"jsmap":"3373707","map":"Name","maptype":"ProductAttributeSelector.Name","value":"custom_name"},{"jsmap":"93997959","map":"Brand","maptype":"ProductAttributeSelector.Name","value":"custom_brand"},{"jsmap":"106934601","map":"Price","maptype":"ProductAttributeSelector.Name","value":"custom_price"},{"jsmap":"50511102","map":"Category","maptype":"ProductAttributeSelector.Name","value":"custom_category"},{"jsmap":"94842723","map":"category","maptype":"ProductAttributeSelector.Name","value":"custom_attribute_category"}]" + }, reportService.cb, true); + + mParticle.forwarder.process({ + EventName: 'eCommerce - AddToCart', + EventDataType: MessageType.Commerce, + ProductAction: { + ProductActionType: ProductActionType.AddToCart, + ProductList: [ + { + Sku: 'sku-12', + Name: 'iPhone', + Brand: 'Apple', + Category: 'electronics', + Variant: 'blue', + Price: 1000.99, + Quantity: 1, + Attributes: { + category: 'phones' + } + }, + { + Sku: 'sku-34', + Name: 'Watch', + Brand: 'Samsung', + Price: 450.99, + Quantity: 2 + } + ], + TransactionId: 123 + }, + CurrencyCode: 'USD', + SourceMessageId: SOURCE_MESSAGE_ID, + }); + + checkBasicProperties('track'); + window.fbqObj.should.have.property('eventName', 'AddToCart'); + window.fbqObj.params.should.have.property('contents'); + window.fbqObj.params.contents.length.should.equal(2); + window.fbqObj.params.should.have.property('order_id', 123); + + var firstProduct = window.fbqObj.params.contents[0]; + // Standard Facebook fields + firstProduct.should.have.property('id', 'sku-12'); + firstProduct.should.have.property('name', 'iPhone'); + firstProduct.should.have.property('brand', 'Apple'); + firstProduct.should.have.property('item_price', 1000.99); + firstProduct.should.have.property('quantity', 1); + + // Mapped standard fields + firstProduct.should.have.property('custom_name', 'iPhone'); + firstProduct.should.have.property('custom_brand', 'Apple'); + firstProduct.should.have.property('custom_price', 1000.99); + firstProduct.should.have.property('custom_category', 'electronics'); + + // Mapped custom attribute + firstProduct.should.have.property('custom_attribute_category', 'phones'); + + done(); + }); + + it('should build contents array with mapped attributes for Checkout events', function (done) { + // Initialize with product attribute mapping + mParticle.forwarder.init({ + pixelCode: 'test-pixel-code', + "productAttributeMapping":"[{"jsmap":"3373707","map":"Name","maptype":"ProductAttributeSelector.Name","value":"custom_name"},{"jsmap":"93997959","map":"Brand","maptype":"ProductAttributeSelector.Name","value":"custom_brand"},{"jsmap":"106934601","map":"Price","maptype":"ProductAttributeSelector.Name","value":"custom_price"},{"jsmap":"50511102","map":"Category","maptype":"ProductAttributeSelector.Name","value":"custom_category"},{"jsmap":"94842723","map":"category","maptype":"ProductAttributeSelector.Name","value":"custom_attribute_category"}]" + }, reportService.cb, true); + + mParticle.forwarder.process({ + EventName: 'eCommerce - Checkout', + EventDataType: MessageType.Commerce, + ProductAction: { + ProductActionType: ProductActionType.Checkout, + ProductList: [ + { + Sku: 'sku-12', + Name: 'iPhone', + Brand: 'Apple', + Category: 'electronics', + Variant: 'blue', + Price: 1000.99, + Quantity: 1, + Attributes: { + category: 'phones' + } + }, + { + Sku: 'sku-34', + Name: 'Watch', + Brand: 'Samsung', + Price: 450.99, + Quantity: 2 + } + ], + TransactionId: 123, + TotalAmount: 1902.97 + }, + CurrencyCode: 'USD', + SourceMessageId: SOURCE_MESSAGE_ID, + }); + + checkBasicProperties('track'); + window.fbqObj.should.have.property('eventName', 'InitiateCheckout'); + window.fbqObj.params.should.have.property('contents'); + window.fbqObj.params.contents.length.should.equal(2); + window.fbqObj.params.should.have.property('order_id', 123); + + var firstProduct = window.fbqObj.params.contents[0]; + // Standard Facebook fields + firstProduct.should.have.property('id', 'sku-12'); + firstProduct.should.have.property('name', 'iPhone'); + firstProduct.should.have.property('brand', 'Apple'); + firstProduct.should.have.property('item_price', 1000.99); + firstProduct.should.have.property('quantity', 1); + + // Mapped standard fields + firstProduct.should.have.property('custom_name', 'iPhone'); + firstProduct.should.have.property('custom_brand', 'Apple'); + firstProduct.should.have.property('custom_price', 1000.99); + firstProduct.should.have.property('custom_category', 'electronics'); + + // Mapped custom attribute + firstProduct.should.have.property('custom_attribute_category', 'phones'); + + done(); + }); }); }); From 76b3c97e32b5e2b88c6a2763d20fe1199152c7e5 Mon Sep 17 00:00:00 2001 From: Jaissica Date: Tue, 25 Nov 2025 16:22:36 -0500 Subject: [PATCH 03/10] update comment for buildProductContents --- src/FacebookEventForwarder.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/FacebookEventForwarder.js b/src/FacebookEventForwarder.js index a39a7aa..6f440b4 100755 --- a/src/FacebookEventForwarder.js +++ b/src/FacebookEventForwarder.js @@ -220,7 +220,7 @@ var name = 'Facebook', params['order_id'] = event.ProductAction.TransactionId; } - // Build contents array for Purchase events + // Build contents array for AddToCart events var contents = buildProductContents(event.ProductAction.ProductList); if (contents && contents.length > 0) { params['contents'] = contents; @@ -253,7 +253,7 @@ var name = 'Facebook', params['order_id'] = event.ProductAction.TransactionId; } - // Build contents array for Purchase events + // Build contents array for Purchase/Checkout events var contents = buildProductContents(event.ProductAction.ProductList); if (contents && contents.length > 0) { params['contents'] = contents; From 0ebd3ac3d98b517db675866c0ef978c99db44864 Mon Sep 17 00:00:00 2001 From: Jaissica Date: Wed, 26 Nov 2025 09:44:02 -0500 Subject: [PATCH 04/10] declare contents variable at function scope to avoid redeclaration --- src/FacebookEventForwarder.js | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/FacebookEventForwarder.js b/src/FacebookEventForwarder.js index 6f440b4..064508b 100755 --- a/src/FacebookEventForwarder.js +++ b/src/FacebookEventForwarder.js @@ -168,6 +168,7 @@ var name = 'Facebook', var eventName, totalValue, + contents, params = cloneEventAttributes(event), eventID = createEventId(event); params['currency'] = event.CurrencyCode || 'USD'; @@ -216,15 +217,15 @@ var name = 'Facebook', } else if (event.ProductAction.ProductActionType == mParticle.ProductActionType.AddToCart){ eventName = ADD_TO_CART_EVENT_NAME; - if (event.ProductAction.TransactionId) { - params['order_id'] = event.ProductAction.TransactionId; - } - - // Build contents array for AddToCart events - var contents = buildProductContents(event.ProductAction.ProductList); - if (contents && contents.length > 0) { - params['contents'] = contents; - } + if (event.ProductAction.TransactionId) { + params['order_id'] = event.ProductAction.TransactionId; + } + + // Build contents array for AddToCart events + contents = buildProductContents(event.ProductAction.ProductList); + if (contents && contents.length > 0) { + params['contents'] = contents; + } } else{ @@ -254,7 +255,7 @@ var name = 'Facebook', } // Build contents array for Purchase/Checkout events - var contents = buildProductContents(event.ProductAction.ProductList); + contents = buildProductContents(event.ProductAction.ProductList); if (contents && contents.length > 0) { params['contents'] = contents; } From cf575986cef6f6f0d15da2c5fbd209e1f7969f10 Mon Sep 17 00:00:00 2001 From: Jaissica Date: Wed, 26 Nov 2025 09:56:25 -0500 Subject: [PATCH 05/10] fix indentation in AddToCart if block --- src/FacebookEventForwarder.js | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/FacebookEventForwarder.js b/src/FacebookEventForwarder.js index 064508b..fde1fc5 100755 --- a/src/FacebookEventForwarder.js +++ b/src/FacebookEventForwarder.js @@ -217,16 +217,15 @@ var name = 'Facebook', } else if (event.ProductAction.ProductActionType == mParticle.ProductActionType.AddToCart){ eventName = ADD_TO_CART_EVENT_NAME; - if (event.ProductAction.TransactionId) { - params['order_id'] = event.ProductAction.TransactionId; - } - - // Build contents array for AddToCart events - contents = buildProductContents(event.ProductAction.ProductList); - if (contents && contents.length > 0) { - params['contents'] = contents; - } + if (event.ProductAction.TransactionId) { + params['order_id'] = event.ProductAction.TransactionId; + } + // Build contents array for AddToCart events + contents = buildProductContents(event.ProductAction.ProductList); + if (contents && contents.length > 0) { + params['contents'] = contents; + } } else{ eventName = VIEW_CONTENT_EVENT_NAME; From 76cd8ddfe10ae1209c00bdf073019e5853647f59 Mon Sep 17 00:00:00 2001 From: Jaissica Date: Tue, 9 Dec 2025 17:58:12 -0500 Subject: [PATCH 06/10] add sendProductNamesAsContents setting to use product names array for content_name --- src/FacebookEventForwarder.js | 65 +++++++- test/tests.js | 294 +++++++++++++++++++++++++++++++++- 2 files changed, 347 insertions(+), 12 deletions(-) diff --git a/src/FacebookEventForwarder.js b/src/FacebookEventForwarder.js index fde1fc5..46ebe40 100755 --- a/src/FacebookEventForwarder.js +++ b/src/FacebookEventForwarder.js @@ -165,12 +165,13 @@ var name = 'Facebook', event.ProductAction.ProductList && event.ProductAction.ProductActionType && SupportedCommerceTypes.indexOf(event.ProductAction.ProductActionType) > -1) { - var eventName, totalValue, contents, params = cloneEventAttributes(event), - eventID = createEventId(event); + eventID = createEventId(event), + sendProductNamesAsContents = settings.sendProductNamesasContents || false; + params['currency'] = event.CurrencyCode || 'USD'; if (event.EventName) { @@ -217,10 +218,14 @@ var name = 'Facebook', } else if (event.ProductAction.ProductActionType == mParticle.ProductActionType.AddToCart){ eventName = ADD_TO_CART_EVENT_NAME; - if (event.ProductAction.TransactionId) { - params['order_id'] = event.ProductAction.TransactionId; + + // Set product names as content_name if enabled + if (sendProductNamesAsContents) { + setProductNamesAsContentName(params, event.ProductAction.ProductList); } + setOrderId(params, event.ProductAction.TransactionId); + // Build contents array for AddToCart events contents = buildProductContents(event.ProductAction.ProductList); if (contents && contents.length > 0) { @@ -248,11 +253,14 @@ var name = 'Facebook', return sum; }, 0); params['num_items'] = num_items; - - if (event.ProductAction.TransactionId) { - params['order_id'] = event.ProductAction.TransactionId; - } + // Set product names as content_name if enabled + if (sendProductNamesAsContents) { + setProductNamesAsContentName(params, event.ProductAction.ProductList); + } + + setOrderId(params, event.ProductAction.TransactionId); + // Build contents array for Purchase/Checkout events contents = buildProductContents(event.ProductAction.ProductList); if (contents && contents.length > 0) { @@ -279,6 +287,19 @@ var name = 'Facebook', params['value'] = totalValue; + // Set product names as content_name if enabled + if (sendProductNamesAsContents) { + setProductNamesAsContentName(params, event.ProductAction.ProductList); + } + + setOrderId(params, event.ProductAction.TransactionId); + + // Build contents array for RemoveFromCart events + contents = buildProductContents(event.ProductAction.ProductList); + if (contents && contents.length > 0) { + params['contents'] = contents; + } + fbq('trackCustom', eventName || 'customEvent', params, eventID); return true; } @@ -415,6 +436,34 @@ var name = 'Facebook', } } + /** + * Sets product names as content_name parameter + * @param {Object} params - The parameters object to modify + * @param {Array} productList - Array of products + */ + function setProductNamesAsContentName(params, productList) { + if (productList) { + params['content_name'] = productList + .filter(function(product) { + return product && product.Name; + }) + .map(function(product) { + return product.Name; + }); + } + } + + /** + * Sets order_id parameter if transaction ID exists + * @param {Object} params - The parameters object to modify + * @param {String} transactionId - The transaction ID + */ + function setOrderId(params, transactionId) { + if (transactionId) { + params['order_id'] = transactionId; + } + } + this.init = initForwarder; this.process = processEvent; }; diff --git a/test/tests.js b/test/tests.js index b130031..719deca 100755 --- a/test/tests.js +++ b/test/tests.js @@ -587,8 +587,8 @@ describe('Facebook Forwarder', function () { window.fbqObj.params.should.have.property('content_name', 'eCommerce - RemoveFromCart'); window.fbqObj.params.should.have.property('content_ids', ['12345']); window.fbqObj.eventData.should.have.property('eventID', SOURCE_MESSAGE_ID); - window.fbqObj.params.should.not.have.property('order_id'); - window.fbqObj.params.should.not.have.property('contents'); + window.fbqObj.params.should.have.property('order_id', 123); + window.fbqObj.params.should.have.property('contents'); done(); }); @@ -629,8 +629,8 @@ describe('Facebook Forwarder', function () { window.fbqObj.params.should.have.property('content_name', 'eCommerce - RemoveFromCart'); window.fbqObj.params.should.have.property('content_ids', ['12345']); window.fbqObj.eventData.should.have.property('eventID', SOURCE_MESSAGE_ID); - window.fbqObj.params.should.not.have.property('order_id'); - window.fbqObj.params.should.not.have.property('contents'); + window.fbqObj.params.should.have.property('order_id', 123); + window.fbqObj.params.should.have.property('contents'); done(); }); @@ -1073,5 +1073,291 @@ describe('Facebook Forwarder', function () { done(); }); + + it('should build contents array with mapped attributes for RemoveFromCart events', function (done) { + // Initialize with product attribute mapping + mParticle.forwarder.init({ + pixelCode: 'test-pixel-code', + "productAttributeMapping":"[{"jsmap":"3373707","map":"Name","maptype":"ProductAttributeSelector.Name","value":"custom_name"},{"jsmap":"93997959","map":"Brand","maptype":"ProductAttributeSelector.Name","value":"custom_brand"},{"jsmap":"106934601","map":"Price","maptype":"ProductAttributeSelector.Name","value":"custom_price"},{"jsmap":"50511102","map":"Category","maptype":"ProductAttributeSelector.Name","value":"custom_category"},{"jsmap":"94842723","map":"category","maptype":"ProductAttributeSelector.Name","value":"custom_attribute_category"}]" + }, reportService.cb, true); + + mParticle.forwarder.process({ + EventName: 'eCommerce - RemoveFromCart', + EventCategory: CommerceEventType.ProductRemoveFromCart, + EventDataType: MessageType.Commerce, + ProductAction: { + ProductActionType: ProductActionType.RemoveFromCart, + ProductList: [ + { + Sku: '12345', + Name: 'iPhone 6', + Category: 'Phones', + Brand: 'iPhone', + Variant: '6', + Price: 200, + CouponCode: null, + Quantity: 1, + TotalAmount: 200, + Attributes: { + category: 'phones' + } + } + ], + TransactionId: 123, + Affiliation: 'my-affiliation', + TaxAmount: 40, + ShippingAmount: 10, + TotalAmount: 205 + }, + CurrencyCode: 'USD', + SourceMessageId: SOURCE_MESSAGE_ID, + }); + + checkBasicProperties('trackCustom'); + window.fbqObj.should.have.property('eventName', 'RemoveFromCart'); + window.fbqObj.params.should.have.property('contents'); + window.fbqObj.params.contents.length.should.equal(1); + window.fbqObj.params.should.have.property('order_id', 123); + + var firstProduct = window.fbqObj.params.contents[0]; + // Standard Facebook fields + firstProduct.should.have.property('id', '12345'); + firstProduct.should.have.property('name', 'iPhone 6'); + firstProduct.should.have.property('brand', 'iPhone'); + firstProduct.should.have.property('item_price', 200); + firstProduct.should.have.property('quantity', 1); + + // Mapped standard fields + firstProduct.should.have.property('custom_name', 'iPhone 6'); + firstProduct.should.have.property('custom_brand', 'iPhone'); + firstProduct.should.have.property('custom_price', 200); + firstProduct.should.have.property('custom_category', 'Phones'); + + // Mapped custom attribute + firstProduct.should.have.property('custom_attribute_category', 'phones'); + + done(); + }); + + it('should log Purchase event with product names as contents', function (done) { + mParticle.forwarder.init({ + pixelCode: 'test-pixel-code', + sendProductNamesasContents: true + }, reportService.cb, true); + + mParticle.forwarder.process({ + EventName: 'eCommerce - Purchase', + EventDataType: MessageType.Commerce, + ProductAction: { + ProductActionType: ProductActionType.Purchase, + ProductList: [ + { + Sku: 'sku-12', + Name: 'iPhone', + Brand: 'Apple', + Category: 'electronics', + Variant: 'blue', + Price: 1000.99, + Quantity: 1, + Attributes: { + category: 'phones' + } + }, + { + Sku: 'sku-no-name', + // Name missing to test filter + Brand: 'Generic', + Price: 99.99, + Quantity: 1 + }, + { + Sku: 'sku-34', + Name: 'Watch', + Brand: 'Samsung', + Price: 450.99, + Quantity: 2 + } + ], + TransactionId: 'txn-1234', + TotalAmount: 1551.97 + }, + CurrencyCode: 'USD', + SourceMessageId: SOURCE_MESSAGE_ID, + }); + + checkBasicProperties('track'); + window.fbqObj.should.have.property('eventName', 'Purchase'); + window.fbqObj.params.should.have.property('contents'); + window.fbqObj.params.contents.length.should.equal(3); + window.fbqObj.params.contents[0].should.have.property('name', 'iPhone'); + window.fbqObj.params.contents[2].should.have.property('name', 'Watch'); + // content_name should only include products with names (filters out product without Name) + window.fbqObj.params.should.have.property('content_name', ['iPhone', 'Watch']); + window.fbqObj.params.should.have.property('order_id', 'txn-1234'); + window.fbqObj.params.should.have.property('value', 1551.97); + window.fbqObj.params.should.have.property('currency', 'USD'); + window.fbqObj.params.should.have.property('num_items', 4); + window.fbqObj.eventData.should.have.property('eventID', SOURCE_MESSAGE_ID); + + done(); + }); + + it('should log AddToCart event with product names as contents', function (done) { + mParticle.forwarder.init({ + pixelCode: 'test-pixel-code', + sendProductNamesasContents: true + }, reportService.cb, true); + + mParticle.forwarder.process({ + EventName: 'eCommerce - AddToCart', + EventCategory: CommerceEventType.ProductAddToCart, + EventDataType: MessageType.Commerce, + ProductAction: { + ProductActionType: ProductActionType.AddToCart, + ProductList: [ + { + Sku: '12345', + Name: 'iPhone 6', + Category: 'Phones', + Brand: 'iPhone', + Variant: '6', + Price: 400, + CouponCode: null, + Quantity: 1 + }, + { + Sku: '1234', + Name: 'iPhone 11', + Category: 'Phones', + Brand: 'iPhone', + Variant: '6', + Price: 500, + CouponCode: null, + Quantity: 2 + } + ], + TransactionId: 123, + Affiliation: 'my-affiliation', + TaxAmount: 40, + ShippingAmount: 10 + }, + CurrencyCode: 'USD', + SourceMessageId: SOURCE_MESSAGE_ID, + + }); + + checkBasicProperties('track'); + window.fbqObj.should.have.property('eventName', 'AddToCart'); + window.fbqObj.params.should.have.property('content_name', ['iPhone 6', 'iPhone 11']); + window.fbqObj.params.should.have.property('content_ids', ['12345', '1234']); + window.fbqObj.eventData.should.have.property('eventID', SOURCE_MESSAGE_ID); + done(); + }); + + it('should log RemoveFromCart event with product names as content_name', function (done) { + mParticle.forwarder.init({ + pixelCode: 'test-pixel-code', + sendProductNamesasContents: true + }, reportService.cb, true); + + mParticle.forwarder.process({ + EventName: 'eCommerce - RemoveFromCart', + EventCategory: CommerceEventType.ProductRemoveFromCart, + EventDataType: MessageType.Commerce, + ProductAction: { + ProductActionType: ProductActionType.RemoveFromCart, + ProductList: [ + { + Sku: '12345', + Name: 'iPhone 6', + Category: 'Phones', + Brand: 'iPhone', + Variant: '6', + Price: 200, + CouponCode: null, + Quantity: 1, + TotalAmount: 200 + } + ], + TransactionId: 123, + Affiliation: 'my-affiliation', + TaxAmount: 40, + ShippingAmount: 10, + TotalAmount: 205 + }, + CurrencyCode: 'USD', + SourceMessageId: SOURCE_MESSAGE_ID, + }); + + checkBasicProperties('trackCustom'); + window.fbqObj.should.have.property('eventName', 'RemoveFromCart'); + window.fbqObj.params.should.have.property('content_name', ['iPhone 6']); + window.fbqObj.params.should.have.property('content_ids', ['12345']); + window.fbqObj.eventData.should.have.property('eventID', SOURCE_MESSAGE_ID); + done(); + }); + + it('should log Checkout event with product names as contents', function (done) { + mParticle.forwarder.init({ + pixelCode: 'test-pixel-code', + sendProductNamesasContents: true + }, reportService.cb, true); + + mParticle.forwarder.process({ + EventName: 'eCommerce - Checkout', + EventCategory: CommerceEventType.ProductCheckout, + EventDataType: MessageType.Commerce, + ProductAction: { + ProductActionType: ProductActionType.Checkout, + ProductList: [ + { + Sku: 'sku-12', + Name: 'iPhone', + Brand: 'Apple', + Category: 'electronics', + Variant: 'blue', + Price: 1000.99, + Quantity: 1 + }, + { + Sku: 'sku-no-name', + // Name missing to test filter + Brand: 'Generic', + Price: 75.50, + Quantity: 1 + }, + { + Sku: 'sku-34', + Name: 'Watch', + Brand: 'Samsung', + Price: 450.99, + Quantity: 2 + } + ], + TransactionId: 'txn-5678', + TotalAmount: 1978.47, + CheckoutStep: 1 + }, + CurrencyCode: 'USD', + SourceMessageId: SOURCE_MESSAGE_ID, + }); + + checkBasicProperties('track'); + window.fbqObj.should.have.property('eventName', 'InitiateCheckout'); + window.fbqObj.params.should.have.property('contents'); + window.fbqObj.params.contents.length.should.equal(3); + window.fbqObj.params.contents[0].should.have.property('name', 'iPhone'); + window.fbqObj.params.contents[2].should.have.property('name', 'Watch'); + // content_name should only include products with names (filters out product without Name) + window.fbqObj.params.should.have.property('content_name', ['iPhone', 'Watch']); + window.fbqObj.params.should.have.property('order_id', 'txn-5678'); + window.fbqObj.params.should.have.property('value', 1978.47); + window.fbqObj.params.should.have.property('currency', 'USD'); + window.fbqObj.params.should.have.property('num_items', 4); + window.fbqObj.params.should.have.property('checkout_step', 1); + window.fbqObj.eventData.should.have.property('eventID', SOURCE_MESSAGE_ID); + + done(); + }); }); }); From 997c2b0aa9afea71e0e811611cc393a4b8c03cd8 Mon Sep 17 00:00:00 2001 From: Jaissica Date: Wed, 10 Dec 2025 16:36:20 -0500 Subject: [PATCH 07/10] removed helper setOrderId and updated return type for setProductNamesAsContentName --- src/FacebookEventForwarder.js | 62 +++++++++++++++++++---------------- 1 file changed, 34 insertions(+), 28 deletions(-) diff --git a/src/FacebookEventForwarder.js b/src/FacebookEventForwarder.js index 46ebe40..f83c3ed 100755 --- a/src/FacebookEventForwarder.js +++ b/src/FacebookEventForwarder.js @@ -221,10 +221,15 @@ var name = 'Facebook', // Set product names as content_name if enabled if (sendProductNamesAsContents) { - setProductNamesAsContentName(params, event.ProductAction.ProductList); + var productNames = buildProductNames(event.ProductAction.ProductList); + if (productNames && productNames.length > 0) { + params['content_name'] = productNames; + } } - setOrderId(params, event.ProductAction.TransactionId); + if (event.ProductAction.TransactionId) { + params['order_id'] = event.ProductAction.TransactionId; + } // Build contents array for AddToCart events contents = buildProductContents(event.ProductAction.ProductList); @@ -256,10 +261,15 @@ var name = 'Facebook', // Set product names as content_name if enabled if (sendProductNamesAsContents) { - setProductNamesAsContentName(params, event.ProductAction.ProductList); + var productNames = buildProductNames(event.ProductAction.ProductList); + if (productNames && productNames.length > 0) { + params['content_name'] = productNames; + } } - setOrderId(params, event.ProductAction.TransactionId); + if (event.ProductAction.TransactionId) { + params['order_id'] = event.ProductAction.TransactionId; + } // Build contents array for Purchase/Checkout events contents = buildProductContents(event.ProductAction.ProductList); @@ -289,10 +299,15 @@ var name = 'Facebook', // Set product names as content_name if enabled if (sendProductNamesAsContents) { - setProductNamesAsContentName(params, event.ProductAction.ProductList); + var productNames = buildProductNames(event.ProductAction.ProductList); + if (productNames && productNames.length > 0) { + params['content_name'] = productNames; + } } - setOrderId(params, event.ProductAction.TransactionId); + if (event.ProductAction.TransactionId) { + params['order_id'] = event.ProductAction.TransactionId; + } // Build contents array for RemoveFromCart events contents = buildProductContents(event.ProductAction.ProductList); @@ -437,31 +452,22 @@ var name = 'Facebook', } /** - * Sets product names as content_name parameter - * @param {Object} params - The parameters object to modify + * Builds array of product names from product list * @param {Array} productList - Array of products + * @returns {Array} Array of product names */ - function setProductNamesAsContentName(params, productList) { - if (productList) { - params['content_name'] = productList - .filter(function(product) { - return product && product.Name; - }) - .map(function(product) { - return product.Name; - }); - } - } - - /** - * Sets order_id parameter if transaction ID exists - * @param {Object} params - The parameters object to modify - * @param {String} transactionId - The transaction ID - */ - function setOrderId(params, transactionId) { - if (transactionId) { - params['order_id'] = transactionId; + function buildProductNames(productList) { + if (!productList || productList.length === 0) { + return []; } + + return productList + .filter(function(product) { + return product && product.Name; + }) + .map(function(product) { + return product.Name; + }); } this.init = initForwarder; From a44e5ae7135203fd907546bb2bcd264cd611b710 Mon Sep 17 00:00:00 2001 From: Jaissica Date: Wed, 10 Dec 2025 16:45:39 -0500 Subject: [PATCH 08/10] declare productNames at function scope in logCommerceEvent --- src/FacebookEventForwarder.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/FacebookEventForwarder.js b/src/FacebookEventForwarder.js index f83c3ed..a3838f8 100755 --- a/src/FacebookEventForwarder.js +++ b/src/FacebookEventForwarder.js @@ -168,6 +168,7 @@ var name = 'Facebook', var eventName, totalValue, contents, + productNames, params = cloneEventAttributes(event), eventID = createEventId(event), sendProductNamesAsContents = settings.sendProductNamesasContents || false; @@ -221,7 +222,7 @@ var name = 'Facebook', // Set product names as content_name if enabled if (sendProductNamesAsContents) { - var productNames = buildProductNames(event.ProductAction.ProductList); + productNames = buildProductNames(event.ProductAction.ProductList); if (productNames && productNames.length > 0) { params['content_name'] = productNames; } @@ -261,7 +262,7 @@ var name = 'Facebook', // Set product names as content_name if enabled if (sendProductNamesAsContents) { - var productNames = buildProductNames(event.ProductAction.ProductList); + productNames = buildProductNames(event.ProductAction.ProductList); if (productNames && productNames.length > 0) { params['content_name'] = productNames; } @@ -299,7 +300,7 @@ var name = 'Facebook', // Set product names as content_name if enabled if (sendProductNamesAsContents) { - var productNames = buildProductNames(event.ProductAction.ProductList); + productNames = buildProductNames(event.ProductAction.ProductList); if (productNames && productNames.length > 0) { params['content_name'] = productNames; } From 0d775d2116a171d126d9395f39086bd6fed8e058 Mon Sep 17 00:00:00 2001 From: Jaissica Date: Thu, 11 Dec 2025 10:06:44 -0500 Subject: [PATCH 09/10] extend order_id and content_name as product_names to all commerce events --- src/FacebookEventForwarder.js | 50 +++++-------------- test/tests.js | 92 +++++++++++++++++++++++++++++++++-- 2 files changed, 101 insertions(+), 41 deletions(-) diff --git a/src/FacebookEventForwarder.js b/src/FacebookEventForwarder.js index a3838f8..a4a2811 100755 --- a/src/FacebookEventForwarder.js +++ b/src/FacebookEventForwarder.js @@ -168,7 +168,6 @@ var name = 'Facebook', var eventName, totalValue, contents, - productNames, params = cloneEventAttributes(event), eventID = createEventId(event), sendProductNamesAsContents = settings.sendProductNamesasContents || false; @@ -190,6 +189,19 @@ var name = 'Facebook', params['content_ids'] = productSkus; } + // Override content_name with product names array if setting enabled + if (sendProductNamesAsContents) { + var productNames = buildProductNames(event.ProductAction.ProductList); + if (productNames && productNames.length > 0) { + params['content_name'] = productNames; + } + } + + // Set order_id if TransactionId exists + if (event.ProductAction.TransactionId) { + params['order_id'] = event.ProductAction.TransactionId; + } + if (event.ProductAction.ProductActionType == mParticle.ProductActionType.AddToWishlist || event.ProductAction.ProductActionType == mParticle.ProductActionType.Checkout) { var eventCategory = getEventCategoryString(event); @@ -219,18 +231,6 @@ var name = 'Facebook', } else if (event.ProductAction.ProductActionType == mParticle.ProductActionType.AddToCart){ eventName = ADD_TO_CART_EVENT_NAME; - - // Set product names as content_name if enabled - if (sendProductNamesAsContents) { - productNames = buildProductNames(event.ProductAction.ProductList); - if (productNames && productNames.length > 0) { - params['content_name'] = productNames; - } - } - - if (event.ProductAction.TransactionId) { - params['order_id'] = event.ProductAction.TransactionId; - } // Build contents array for AddToCart events contents = buildProductContents(event.ProductAction.ProductList); @@ -259,18 +259,6 @@ var name = 'Facebook', return sum; }, 0); params['num_items'] = num_items; - - // Set product names as content_name if enabled - if (sendProductNamesAsContents) { - productNames = buildProductNames(event.ProductAction.ProductList); - if (productNames && productNames.length > 0) { - params['content_name'] = productNames; - } - } - - if (event.ProductAction.TransactionId) { - params['order_id'] = event.ProductAction.TransactionId; - } // Build contents array for Purchase/Checkout events contents = buildProductContents(event.ProductAction.ProductList); @@ -297,18 +285,6 @@ var name = 'Facebook', } params['value'] = totalValue; - - // Set product names as content_name if enabled - if (sendProductNamesAsContents) { - productNames = buildProductNames(event.ProductAction.ProductList); - if (productNames && productNames.length > 0) { - params['content_name'] = productNames; - } - } - - if (event.ProductAction.TransactionId) { - params['order_id'] = event.ProductAction.TransactionId; - } // Build contents array for RemoveFromCart events contents = buildProductContents(event.ProductAction.ProductList); diff --git a/test/tests.js b/test/tests.js index 719deca..43a72d0 100755 --- a/test/tests.js +++ b/test/tests.js @@ -695,7 +695,7 @@ describe('Facebook Forwarder', function () { window.fbqObj.params.should.have.property('eventAttr1', 'value1'); window.fbqObj.params.should.have.property('eventAttr2', 'value2'); window.fbqObj.eventData.should.have.property('eventID', SOURCE_MESSAGE_ID); - window.fbqObj.params.should.not.have.property('order_id'); + window.fbqObj.params.should.have.property('order_id', 123); window.fbqObj.params.should.not.have.property('contents'); done(); @@ -744,7 +744,7 @@ describe('Facebook Forwarder', function () { window.fbqObj.params.should.have.property('eventAttr1', 'value1'); window.fbqObj.params.should.have.property('eventAttr2', 'value2'); window.fbqObj.eventData.should.have.property('eventID', SOURCE_MESSAGE_ID); - window.fbqObj.params.should.not.have.property('order_id'); + window.fbqObj.params.should.have.property('order_id', 123); window.fbqObj.params.should.not.have.property('contents'); done(); @@ -792,7 +792,7 @@ describe('Facebook Forwarder', function () { window.fbqObj.params.should.have.property('eventAttr1', 'value1'); window.fbqObj.params.should.have.property('eventAttr2', 'value2'); window.fbqObj.eventData.should.have.property('eventID', SOURCE_MESSAGE_ID); - window.fbqObj.params.should.not.have.property('order_id'); + window.fbqObj.params.should.have.property('order_id', 123); window.fbqObj.params.should.not.have.property('contents'); done(); @@ -840,7 +840,7 @@ describe('Facebook Forwarder', function () { window.fbqObj.params.should.have.property('eventAttr1', 'value1'); window.fbqObj.params.should.have.property('eventAttr2', 'value2'); window.fbqObj.eventData.should.have.property('eventID', SOURCE_MESSAGE_ID); - window.fbqObj.params.should.not.have.property('order_id'); + window.fbqObj.params.should.have.property('order_id', 123); window.fbqObj.params.should.not.have.property('contents'); done(); @@ -1357,6 +1357,90 @@ describe('Facebook Forwarder', function () { window.fbqObj.params.should.have.property('checkout_step', 1); window.fbqObj.eventData.should.have.property('eventID', SOURCE_MESSAGE_ID); + done(); + }); + + it('should log AddToWishlist event with product names as contents', function (done) { + mParticle.forwarder.init({ + pixelCode: 'test-pixel-code', + sendProductNamesasContents: true + }, reportService.cb, true); + + mParticle.forwarder.process({ + EventName: 'eCommerce - AddToWishlist', + EventCategory: CommerceEventType.ProductAddToWishlist, + EventDataType: MessageType.Commerce, + ProductAction: { + ProductActionType: ProductActionType.AddToWishlist, + ProductList: [ + { + Sku: 'sku-111', + Name: 'Laptop', + Category: 'Electronics', + Brand: 'Dell', + Price: 1200, + Quantity: 1 + }, + { + Sku: 'sku-222', + Name: 'Mouse', + Category: 'Accessories', + Brand: 'Logitech', + Price: 50, + Quantity: 1 + } + ], + TransactionId: 456 + }, + CurrencyCode: 'USD', + SourceMessageId: SOURCE_MESSAGE_ID, + }); + + checkBasicProperties('track'); + window.fbqObj.should.have.property('eventName', 'AddToWishlist'); + window.fbqObj.params.should.have.property('content_name', ['Laptop', 'Mouse']); + window.fbqObj.params.should.have.property('order_id', 456); + window.fbqObj.params.should.have.property('content_ids', ['sku-111', 'sku-222']); + window.fbqObj.params.should.not.have.property('contents'); + window.fbqObj.eventData.should.have.property('eventID', SOURCE_MESSAGE_ID); + done(); + }); + + it('should log ViewDetail event with product names as contents', function (done) { + mParticle.forwarder.init({ + pixelCode: 'test-pixel-code', + sendProductNamesasContents: true + }, reportService.cb, true); + + mParticle.forwarder.process({ + EventName: 'eCommerce - ViewDetail', + EventCategory: CommerceEventType.ProductViewDetail, + EventDataType: MessageType.Commerce, + ProductAction: { + ProductActionType: ProductActionType.ViewDetail, + ProductList: [ + { + Sku: 'sku-999', + Name: 'Tablet', + Category: 'Electronics', + Brand: 'Samsung', + Price: 800, + Quantity: 1 + } + ], + TransactionId: 789 + }, + CurrencyCode: 'USD', + SourceMessageId: SOURCE_MESSAGE_ID, + }); + + checkBasicProperties('track'); + window.fbqObj.should.have.property('eventName', 'ViewContent'); + window.fbqObj.params.should.have.property('content_name', ['Tablet']); + window.fbqObj.params.should.have.property('order_id', 789); + window.fbqObj.params.should.have.property('content_ids', ['sku-999']); + window.fbqObj.params.should.not.have.property('contents'); + window.fbqObj.eventData.should.have.property('eventID', SOURCE_MESSAGE_ID); done(); }); }); From 89525033e7193721bc3da136f796312dcce2a902 Mon Sep 17 00:00:00 2001 From: Jaissica Date: Thu, 11 Dec 2025 15:46:40 -0500 Subject: [PATCH 10/10] extend custom product attribute mapping to all ecommerce events --- src/FacebookEventForwarder.js | 25 ++---- test/tests.js | 154 ++++++++++++++++++++++++++++++++-- 2 files changed, 154 insertions(+), 25 deletions(-) diff --git a/src/FacebookEventForwarder.js b/src/FacebookEventForwarder.js index a4a2811..359d5ef 100755 --- a/src/FacebookEventForwarder.js +++ b/src/FacebookEventForwarder.js @@ -167,7 +167,6 @@ var name = 'Facebook', SupportedCommerceTypes.indexOf(event.ProductAction.ProductActionType) > -1) { var eventName, totalValue, - contents, params = cloneEventAttributes(event), eventID = createEventId(event), sendProductNamesAsContents = settings.sendProductNamesasContents || false; @@ -202,6 +201,12 @@ var name = 'Facebook', params['order_id'] = event.ProductAction.TransactionId; } + // Build contents array + var contents = buildProductContents(event.ProductAction.ProductList); + if (contents && contents.length > 0) { + params['contents'] = contents; + } + if (event.ProductAction.ProductActionType == mParticle.ProductActionType.AddToWishlist || event.ProductAction.ProductActionType == mParticle.ProductActionType.Checkout) { var eventCategory = getEventCategoryString(event); @@ -231,12 +236,6 @@ var name = 'Facebook', } else if (event.ProductAction.ProductActionType == mParticle.ProductActionType.AddToCart){ eventName = ADD_TO_CART_EVENT_NAME; - - // Build contents array for AddToCart events - contents = buildProductContents(event.ProductAction.ProductList); - if (contents && contents.length > 0) { - params['contents'] = contents; - } } else{ eventName = VIEW_CONTENT_EVENT_NAME; @@ -259,12 +258,6 @@ var name = 'Facebook', return sum; }, 0); params['num_items'] = num_items; - - // Build contents array for Purchase/Checkout events - contents = buildProductContents(event.ProductAction.ProductList); - if (contents && contents.length > 0) { - params['contents'] = contents; - } } else if (event.ProductAction.ProductActionType == mParticle.ProductActionType.RemoveFromCart) { eventName = REMOVE_FROM_CART_EVENT_NAME; @@ -285,12 +278,6 @@ var name = 'Facebook', } params['value'] = totalValue; - - // Build contents array for RemoveFromCart events - contents = buildProductContents(event.ProductAction.ProductList); - if (contents && contents.length > 0) { - params['contents'] = contents; - } fbq('trackCustom', eventName || 'customEvent', params, eventID); return true; diff --git a/test/tests.js b/test/tests.js index 43a72d0..a8b9bf5 100755 --- a/test/tests.js +++ b/test/tests.js @@ -696,7 +696,14 @@ describe('Facebook Forwarder', function () { window.fbqObj.params.should.have.property('eventAttr2', 'value2'); window.fbqObj.eventData.should.have.property('eventID', SOURCE_MESSAGE_ID); window.fbqObj.params.should.have.property('order_id', 123); - window.fbqObj.params.should.not.have.property('contents'); + window.fbqObj.params.should.have.property('contents'); + window.fbqObj.params.contents.length.should.equal(3); + window.fbqObj.params.contents[0].should.have.property('id', '12345'); + window.fbqObj.params.contents[0].should.have.property('quantity', 2); + window.fbqObj.params.contents[1].should.have.property('id', '888'); + window.fbqObj.params.contents[1].should.have.property('quantity', 1); + window.fbqObj.params.contents[2].should.have.property('id', '666'); + window.fbqObj.params.contents[2].should.have.property('quantity', 1); done(); }); @@ -745,7 +752,10 @@ describe('Facebook Forwarder', function () { window.fbqObj.params.should.have.property('eventAttr2', 'value2'); window.fbqObj.eventData.should.have.property('eventID', SOURCE_MESSAGE_ID); window.fbqObj.params.should.have.property('order_id', 123); - window.fbqObj.params.should.not.have.property('contents'); + window.fbqObj.params.should.have.property('contents'); + window.fbqObj.params.contents.length.should.equal(1); + window.fbqObj.params.contents[0].should.have.property('id', '12345'); + window.fbqObj.params.contents[0].should.have.property('quantity', 1); done(); }); @@ -793,7 +803,10 @@ describe('Facebook Forwarder', function () { window.fbqObj.params.should.have.property('eventAttr2', 'value2'); window.fbqObj.eventData.should.have.property('eventID', SOURCE_MESSAGE_ID); window.fbqObj.params.should.have.property('order_id', 123); - window.fbqObj.params.should.not.have.property('contents'); + window.fbqObj.params.should.have.property('contents'); + window.fbqObj.params.contents.length.should.equal(1); + window.fbqObj.params.contents[0].should.have.property('id', '145'); + window.fbqObj.params.contents[0].should.have.property('quantity', 1); done(); }); @@ -841,7 +854,10 @@ describe('Facebook Forwarder', function () { window.fbqObj.params.should.have.property('eventAttr2', 'value2'); window.fbqObj.eventData.should.have.property('eventID', SOURCE_MESSAGE_ID); window.fbqObj.params.should.have.property('order_id', 123); - window.fbqObj.params.should.not.have.property('contents'); + window.fbqObj.params.should.have.property('contents'); + window.fbqObj.params.contents.length.should.equal(1); + window.fbqObj.params.contents[0].should.have.property('id', '12345'); + window.fbqObj.params.contents[0].should.have.property('quantity', 1); done(); }); @@ -1139,6 +1155,124 @@ describe('Facebook Forwarder', function () { done(); }); + it('should build contents array with mapped attributes for AddToWishlist events', function (done) { + // Initialize with product attribute mapping + mParticle.forwarder.init({ + pixelCode: 'test-pixel-code', + "productAttributeMapping":"[{"jsmap":"3373707","map":"Name","maptype":"ProductAttributeSelector.Name","value":"custom_name"},{"jsmap":"93997959","map":"Brand","maptype":"ProductAttributeSelector.Name","value":"custom_brand"},{"jsmap":"106934601","map":"Price","maptype":"ProductAttributeSelector.Name","value":"custom_price"},{"jsmap":"50511102","map":"Category","maptype":"ProductAttributeSelector.Name","value":"custom_category"},{"jsmap":"94842723","map":"category","maptype":"ProductAttributeSelector.Name","value":"custom_attribute_category"}]" + }, reportService.cb, true); + + mParticle.forwarder.process({ + EventName: 'eCommerce - AddToWishlist', + EventCategory: CommerceEventType.ProductAddToWishlist, + EventDataType: MessageType.Commerce, + ProductAction: { + ProductActionType: ProductActionType.AddToWishlist, + ProductList: [ + { + Sku: 'wishlist-sku', + Name: 'Designer Bag', + Category: 'Accessories', + Brand: 'Gucci', + Variant: 'Black', + Price: 2500, + Quantity: 1, + Attributes: { + category: 'luxury' + } + } + ], + TransactionId: 789 + }, + CurrencyCode: 'USD', + SourceMessageId: SOURCE_MESSAGE_ID, + }); + + checkBasicProperties('track'); + window.fbqObj.should.have.property('eventName', 'AddToWishlist'); + window.fbqObj.params.should.have.property('contents'); + window.fbqObj.params.contents.length.should.equal(1); + window.fbqObj.params.should.have.property('order_id', 789); + + var firstProduct = window.fbqObj.params.contents[0]; + // Standard Facebook fields + firstProduct.should.have.property('id', 'wishlist-sku'); + firstProduct.should.have.property('name', 'Designer Bag'); + firstProduct.should.have.property('brand', 'Gucci'); + firstProduct.should.have.property('item_price', 2500); + firstProduct.should.have.property('quantity', 1); + + // Mapped standard fields + firstProduct.should.have.property('custom_name', 'Designer Bag'); + firstProduct.should.have.property('custom_brand', 'Gucci'); + firstProduct.should.have.property('custom_price', 2500); + firstProduct.should.have.property('custom_category', 'Accessories'); + + // Mapped custom attribute + firstProduct.should.have.property('custom_attribute_category', 'luxury'); + + done(); + }); + + it('should build contents array with mapped attributes for ViewDetail events', function (done) { + // Initialize with product attribute mapping + mParticle.forwarder.init({ + pixelCode: 'test-pixel-code', + "productAttributeMapping":"[{"jsmap":"3373707","map":"Name","maptype":"ProductAttributeSelector.Name","value":"custom_name"},{"jsmap":"93997959","map":"Brand","maptype":"ProductAttributeSelector.Name","value":"custom_brand"},{"jsmap":"106934601","map":"Price","maptype":"ProductAttributeSelector.Name","value":"custom_price"},{"jsmap":"50511102","map":"Category","maptype":"ProductAttributeSelector.Name","value":"custom_category"},{"jsmap":"94842723","map":"category","maptype":"ProductAttributeSelector.Name","value":"custom_attribute_category"}]" + }, reportService.cb, true); + + mParticle.forwarder.process({ + EventName: 'eCommerce - ViewDetail', + EventCategory: CommerceEventType.ProductViewDetail, + EventDataType: MessageType.Commerce, + ProductAction: { + ProductActionType: ProductActionType.ViewDetail, + ProductList: [ + { + Sku: 'view-sku-999', + Name: 'Laptop Pro', + Category: 'Electronics', + Brand: 'Apple', + Variant: '16-inch', + Price: 3000, + Quantity: 1, + Attributes: { + category: 'computers' + } + } + ], + TransactionId: 999 + }, + CurrencyCode: 'USD', + SourceMessageId: SOURCE_MESSAGE_ID, + }); + + checkBasicProperties('track'); + window.fbqObj.should.have.property('eventName', 'ViewContent'); + window.fbqObj.params.should.have.property('contents'); + window.fbqObj.params.contents.length.should.equal(1); + window.fbqObj.params.should.have.property('order_id', 999); + + var firstProduct = window.fbqObj.params.contents[0]; + // Standard Facebook fields + firstProduct.should.have.property('id', 'view-sku-999'); + firstProduct.should.have.property('name', 'Laptop Pro'); + firstProduct.should.have.property('brand', 'Apple'); + firstProduct.should.have.property('item_price', 3000); + firstProduct.should.have.property('quantity', 1); + + // Mapped standard fields + firstProduct.should.have.property('custom_name', 'Laptop Pro'); + firstProduct.should.have.property('custom_brand', 'Apple'); + firstProduct.should.have.property('custom_price', 3000); + firstProduct.should.have.property('custom_category', 'Electronics'); + + // Mapped custom attribute + firstProduct.should.have.property('custom_attribute_category', 'computers'); + + done(); + }); + it('should log Purchase event with product names as contents', function (done) { mParticle.forwarder.init({ pixelCode: 'test-pixel-code', @@ -1401,7 +1535,12 @@ describe('Facebook Forwarder', function () { window.fbqObj.params.should.have.property('content_name', ['Laptop', 'Mouse']); window.fbqObj.params.should.have.property('order_id', 456); window.fbqObj.params.should.have.property('content_ids', ['sku-111', 'sku-222']); - window.fbqObj.params.should.not.have.property('contents'); + window.fbqObj.params.should.have.property('contents'); + window.fbqObj.params.contents.length.should.equal(2); + window.fbqObj.params.contents[0].should.have.property('id', 'sku-111'); + window.fbqObj.params.contents[0].should.have.property('quantity', 1); + window.fbqObj.params.contents[1].should.have.property('id', 'sku-222'); + window.fbqObj.params.contents[1].should.have.property('quantity', 1); window.fbqObj.eventData.should.have.property('eventID', SOURCE_MESSAGE_ID); done(); }); @@ -1439,7 +1578,10 @@ describe('Facebook Forwarder', function () { window.fbqObj.params.should.have.property('content_name', ['Tablet']); window.fbqObj.params.should.have.property('order_id', 789); window.fbqObj.params.should.have.property('content_ids', ['sku-999']); - window.fbqObj.params.should.not.have.property('contents'); + window.fbqObj.params.should.have.property('contents'); + window.fbqObj.params.contents.length.should.equal(1); + window.fbqObj.params.contents[0].should.have.property('id', 'sku-999'); + window.fbqObj.params.contents[0].should.have.property('quantity', 1); window.fbqObj.eventData.should.have.property('eventID', SOURCE_MESSAGE_ID); done(); });