diff --git a/src/FacebookEventForwarder.js b/src/FacebookEventForwarder.js index 782a8ae..359d5ef 100755 --- a/src/FacebookEventForwarder.js +++ b/src/FacebookEventForwarder.js @@ -12,443 +12,470 @@ // 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); } - catch (error) { - return 'Can\'t send to forwarder: ' + name + ' ' + error; + else if (event.EventDataType == MessageType.PageEvent) { + reportEvent = true; + logPageEvent(event); + } + else if (event.EventDataType == MessageType.Commerce) { + reportEvent = logCommerceEvent(event); } - } - function logCommerceEvent(event) { - if (event.ProductAction && - event.ProductAction.ProductList && - event.ProductAction.ProductActionType && - SupportedCommerceTypes.indexOf(event.ProductAction.ProductActionType) > -1) { + if (reportEvent && reportingService) { + reportingService(self, event); + } - var eventName, - totalValue, - params = cloneEventAttributes(event), - eventID = createEventId(event); - params['currency'] = event.CurrencyCode || 'USD'; + return 'Successfully sent to forwarder ' + name; + } + catch (error) { + return 'Can\'t send to forwarder: ' + name + ' ' + error; + } + } - if (event.EventName) { - params['content_name'] = event.EventName; - } + 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), + sendProductNamesAsContents = settings.sendProductNamesasContents || false; - var productSkus = event.ProductAction.ProductList.reduce(function (arr, curr) { - if (curr.Sku) { - arr.push(curr.Sku); - } - return arr; - }, []); + params['currency'] = event.CurrencyCode || 'USD'; - if (productSkus && productSkus.length > 0) { - params['content_ids'] = productSkus; - } + if (event.EventName) { + params['content_name'] = event.EventName; + } - 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; - } + var productSkus = event.ProductAction.ProductList.reduce(function (arr, curr) { + if (curr.Sku) { + arr.push(curr.Sku); } + return arr; + }, []); - if (event.ProductAction.ProductActionType == mParticle.ProductActionType.AddToCart || - event.ProductAction.ProductActionType == mParticle.ProductActionType.AddToWishlist || - event.ProductAction.ProductActionType == mParticle.ProductActionType.ViewDetail) { + if (productSkus && productSkus.length > 0) { + params['content_ids'] = productSkus; + } - totalValue = event.ProductAction.ProductList.reduce(function(sum, product){ - if (isNumeric(product.Price) && isNumeric(product.Quantity)) { - sum += product.Price * product.Quantity; - } - return sum; - }, 0); + // 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; + } + } - params['value'] = totalValue; + // Set order_id if TransactionId exists + if (event.ProductAction.TransactionId) { + params['order_id'] = event.ProductAction.TransactionId; + } - 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; - } + // 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); + if (eventCategory) { + params['content_category'] = eventCategory; } - else if (event.ProductAction.ProductActionType == mParticle.ProductActionType.Checkout || - event.ProductAction.ProductActionType == mParticle.ProductActionType.Purchase) { + if (event.ProductAction.ProductActionType == mParticle.ProductActionType.Checkout && event.ProductAction.CheckoutStep) { + params['checkout_step'] = event.ProductAction.CheckoutStep; + } + } - eventName = event.ProductAction.ProductActionType == mParticle.ProductActionType.Checkout ? CHECKOUT_EVENT_NAME : PURCHASE_EVENT_NAME; + if (event.ProductAction.ProductActionType == mParticle.ProductActionType.AddToCart || + event.ProductAction.ProductActionType == mParticle.ProductActionType.AddToWishlist || + event.ProductAction.ProductActionType == mParticle.ProductActionType.ViewDetail) { - if (event.ProductAction.TotalAmount) { - params['value'] = event.ProductAction.TotalAmount; + totalValue = event.ProductAction.ProductList.reduce(function(sum, product){ + if (isNumeric(product.Price) && isNumeric(product.Quantity)) { + sum += product.Price * product.Quantity; } + return sum; + }, 0); - 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; - } + params['value'] = totalValue; - // 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; - } - } + 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; } - 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; + } + else if (event.ProductAction.ProductActionType == mParticle.ProductActionType.Checkout || + event.ProductAction.ProductActionType == mParticle.ProductActionType.Purchase) { - fbq('trackCustom', eventName || 'customEvent', params, eventID); - return true; - } + eventName = event.ProductAction.ProductActionType == mParticle.ProductActionType.Checkout ? CHECKOUT_EVENT_NAME : PURCHASE_EVENT_NAME; - if (eventName) { - fbq('track', eventName, params, eventID); + if (event.ProductAction.TotalAmount) { + params['value'] = event.ProductAction.TotalAmount; } - else { - return false; + + 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; + } + 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; } - 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); - - eventName = eventName || event.EventName; - if (event.EventName) { - params['content_name'] = event.EventName; - } + return false; + } - fbq('trackCustom', eventName || 'customEvent', params, eventID); - } + function logPageView(event) { + logPageEvent(event, PAGE_VIEW_EVENT_NAME); + } - function cloneEventAttributes(event) { - var attr = {}; - if (event && event.EventAttributes) { - try { - attr = JSON.parse(JSON.stringify(event.EventAttributes)); - } - catch (e) { - // - } - } - return attr; - } + function logPageEvent(event, eventName) { + var params = cloneEventAttributes(event); + var eventID = createEventId(event); - function isNumeric(n) { - return !isNaN(parseFloat(n)) && isFinite(n); + eventName = eventName || event.EventName; + if (event.EventName) { + params['content_name'] = event.EventName; } - 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; - } + return null; + } - function register(config) { - if (!config) { - console.log('You must pass a config object to register the kit ' + name); - 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)) { - console.log('\'config\' must be an object. You passed in a ' + typeof config); - return; + // 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 (isobject(config.kits)) { - config.kits[name] = { - constructor: constructor - }; - } else { - config.kits = {}; - config.kits[name] = { - constructor: constructor - }; + /** + * Builds array of product names from product list + * @param {Array} productList - Array of products + * @returns {Array} Array of product names + */ + function buildProductNames(productList) { + if (!productList || productList.length === 0) { + return []; + } + + return productList + .filter(function(product) { + return product && product.Name; + }) + .map(function(product) { + return product.Name; + }); } - 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 +}; diff --git a/test/tests.js b/test/tests.js index b0e0763..a8b9bf5 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.have.property('order_id', 123); + window.fbqObj.params.should.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.have.property('order_id', 123); + window.fbqObj.params.should.have.property('contents'); done(); }); @@ -685,6 +695,15 @@ 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'); + 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(); }); @@ -732,6 +751,11 @@ 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'); + 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(); }); @@ -778,6 +802,11 @@ 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'); + 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(); }); @@ -824,6 +853,11 @@ 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'); + 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(); }); @@ -924,5 +958,632 @@ 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(); + }); + + 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 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', + 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(); + }); + + 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.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(); + }); + + 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.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(); + }); }); });