___TERMS_OF_SERVICE___ By creating or modifying this file you agree to Google Tag Manager's Community Template Gallery Developer Terms of Service available at https://developers.google.com/tag-manager/gallery-tos (or such other URL as Google may provide), as modified from time to time. ___INFO___ { "displayName": "Facebook Pixel by Jabjab", "description": "This is an unofficial Google Tag Manager template for the Facebook Pixel. Originally developed by Simo Ahava.", "securityGroups": [], "categories": [ "ADVERTISING", "ANALYTICS" ], "id": "cvt_temp_public_id", "type": "TAG", "version": 1, "brand": { "displayName": "jabjab-online-marketing", "id": "github.com_jabjabonlinemarketing", "thumbnail": "data:image/png;base64,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" }, "containerContexts": [ "WEB" ] } ___TEMPLATE_PARAMETERS___ [ { "alwaysInSummary": true, "valueValidators": [ { "errorMessage": "You must provide a Pixel ID", "type": "NON_EMPTY" }, { "args": [ "^[0-9,]+$" ], "errorMessage": "Invalid Pixel ID format", "type": "REGEX" } ], "displayName": "Facebook Pixel ID(s)", "simpleValueType": true, "name": "pixelId", "type": "TEXT", "valueHint": "e.g. 12345678910" }, { "type": "CHECKBOX", "name": "enhancedEcommerce", "checkboxText": "Ecommerce dataLayer Integration", "simpleValueType": true, "help": "If you check this, then the Facebook pixel will populate \u003cstrong\u003eEvent Name\u003c/strong\u003e and \u003cstrong\u003eObject Properties\u003c/strong\u003e automatically from the last \u003ca href\u003d\"https://developers.google.com/analytics/devguides/collection/ga4/ecommerce?client_type\u003dgtm\"\u003eecommerce\u003c/a\u003e object pushed into the dataLayer array." }, { "type": "RADIO", "name": "eventName", "displayName": "Event Name", "radioItems": [ { "value": "standard", "displayValue": "Standard", "subParams": [ { "type": "SELECT", "name": "standardEventName", "macrosInSelect": false, "selectItems": [ { "displayValue": "PageView", "value": "PageView" }, { "displayValue": "AddPaymentInfo", "value": "AddPaymentInfo" }, { "displayValue": "AddToCart", "value": "AddToCart" }, { "displayValue": "AddToWishlist", "value": "AddToWishlist" }, { "displayValue": "CompleteRegistration", "value": "CompleteRegistration" }, { "displayValue": "Contact", "value": "Contact" }, { "displayValue": "CustomizeProduct", "value": "CustomizeProduct" }, { "displayValue": "Donate", "value": "Donate" }, { "displayValue": "FindLocation", "value": "FindLocation" }, { "displayValue": "InitiateCheckout", "value": "InitiateCheckout" }, { "displayValue": "Lead", "value": "Lead" }, { "displayValue": "Purchase", "value": "Purchase" }, { "displayValue": "Schedule", "value": "Schedule" }, { "displayValue": "Search", "value": "Search" }, { "displayValue": "StartTrial", "value": "StartTrial" }, { "displayValue": "SubmitApplication", "value": "SubmitApplication" }, { "displayValue": "Subscribe", "value": "Subscribe" }, { "displayValue": "ViewContent", "value": "ViewContent" } ], "simpleValueType": true, "defaultValue": "PageView" } ] }, { "value": "custom", "displayValue": "Custom", "subParams": [ { "type": "TEXT", "name": "customEventName", "displayName": "", "simpleValueType": true } ] }, { "value": "variable", "displayValue": "Variable", "subParams": [ { "type": "SELECT", "name": "variableEventName", "macrosInSelect": true, "selectItems": [], "simpleValueType": true } ] } ], "simpleValueType": true, "enablingConditions": [ { "paramName": "enhancedEcommerce", "paramValue": true, "type": "NOT_EQUALS" } ] }, { "type": "RADIO", "name": "eecEventName", "displayName": "Event Name", "radioItems": [ { "value": "eec", "displayValue": "Set automatically from dataLayer" }, { "value": "gtmvar", "displayValue": "Read from variable", "subParams": [ { "type": "SELECT", "name": "eecEventNameGTMVar", "displayName": "", "macrosInSelect": true, "selectItems": [], "simpleValueType": true } ] } ], "simpleValueType": true, "help": "The Ecommerce integration populates the Event Name automatically depending on what \u003cstrong\u003erecommended ecommerce event name\u003c/strong\u003e was last pushed into dataLayer (\"view_item\" -\u003e \"ViewContent\", \"add_to_cart\" -\u003e \"AddToCart\", \"add_to_wishlist\" -\u003e \"AddToWishlist\", \"begin_checkout\" -\u003e \"InitiateCheckout\", \"add_payment_info\" -\u003e \"AddPaymentInfo\". \"purchase\" -\u003e \"Purchase\").\n\nAlternatively you can select a GTM variable that returns one of the recommended ecommerce event names.", "enablingConditions": [ { "paramName": "enhancedEcommerce", "paramValue": true, "type": "EQUALS" } ] }, { "type": "TEXT", "name": "eecDefaultCurrency", "displayName": "Default currency if not present in the datalayer", "simpleValueType": true, "enablingConditions": [ { "paramName": "enhancedEcommerce", "paramValue": true, "type": "EQUALS" } ], "help": "The Ecommerce integration checks for the presence of ecommerce.currency or ecommerce.items[0].currency. If neither is found, this currency will be used. If this fields if left blank, USD will be used as default.", "textAsList": true }, { "type": "SELECT", "name": "consent", "displayName": "Consent Granted (GDPR)", "macrosInSelect": true, "selectItems": [ { "value": true, "displayValue": "True" }, { "value": false, "displayValue": "False" } ], "simpleValueType": true, "help": "If you set Consent Granted to \u003cstrong\u003efalse\u003c/strong\u003e, the pixel will not send any hits until a tag is fired where Consent Granted is set to \u003cstrong\u003etrue\u003c/strong\u003e. See \u003ca href\u003d\"https://developers.facebook.com/docs/facebook-pixel/implementation/gdpr/\"\u003ethis article\u003c/a\u003e for more information." }, { "simpleValueType": true, "name": "advancedMatching", "checkboxText": "Enable Advanced Matching", "type": "CHECKBOX" }, { "type": "GROUP", "name": "dataProcessingOptionsGroup", "displayName": "Data Processing Options", "groupStyle": "ZIPPY_CLOSED", "subParams": [ { "type": "LABEL", "name": "dpoInfo", "displayName": "Data Processing Options force this Facebook event to comply to regional regulations with regard to the processing and selling of user data. Read \u003ca href\u003d\"https://developers.facebook.com/docs/marketing-apis/data-processing-options\"\u003ethis\u003c/a\u003e for more information about how to configure this section." }, { "type": "CHECKBOX", "name": "dpoLDU", "checkboxText": "Limited Data Use (LDU)", "simpleValueType": true }, { "type": "TEXT", "name": "dpoCountry", "displayName": "Country", "simpleValueType": true, "defaultValue": 0, "enablingConditions": [ { "paramName": "dpoLDU", "paramValue": true, "type": "EQUALS" } ], "valueValidators": [ { "type": "NUMBER" } ] }, { "type": "TEXT", "name": "dpoState", "displayName": "State", "simpleValueType": true, "defaultValue": 0, "enablingConditions": [ { "paramName": "dpoLDU", "paramValue": true, "type": "EQUALS" } ], "valueValidators": [ { "type": "NUMBER" } ] } ] }, { "enablingConditions": [ { "paramName": "advancedMatching", "type": "EQUALS", "paramValue": true } ], "displayName": "Customer Information Data Parameters", "name": "advancedMatchingGroup", "groupStyle": "ZIPPY_CLOSED", "type": "GROUP", "subParams": [ { "displayName": "", "name": "advancedMatchingList", "simpleTableColumns": [ { "selectItems": [ { "displayValue": "City", "value": "ct" }, { "displayValue": "Country", "value": "cn" }, { "displayValue": "Date of Birth", "value": "db" }, { "displayValue": "Email", "value": "em" }, { "displayValue": "External ID", "value": "external_id" }, { "displayValue": "First Name", "value": "fn" }, { "displayValue": "Gender", "value": "ge" }, { "displayValue": "Last Name", "value": "ln" }, { "displayValue": "Phone", "value": "ph" }, { "displayValue": "State", "value": "st" }, { "displayValue": "Zip Code", "value": "zp" } ], "defaultValue": "", "displayName": "Parameter name", "name": "name", "isUnique": true, "type": "SELECT" }, { "defaultValue": "", "displayName": "Parameter value", "name": "value", "type": "TEXT" } ], "type": "SIMPLE_TABLE", "newRowButtonText": "Add parameter", "valueValidators": [ { "type": "NON_EMPTY" } ] } ] }, { "displayName": "Object Properties", "name": "objectPropertiesGroup", "groupStyle": "ZIPPY_CLOSED", "type": "GROUP", "subParams": [ { "type": "LABEL", "name": "enhancedEcommerceObject", "displayName": "\u003cstrong\u003eWarning!\u003c/strong\u003e Object properties are populated automatically based on the most recent \u003cstrong\u003eecommerce\u003c/strong\u003e object pushed into dataLayer. If you add properties here that are already set by the integration (content_type, contents, num_items, value, currency), then the properties you add here will override those set automatically by the integration!", "enablingConditions": [ { "paramName": "enhancedEcommerce", "paramValue": true, "type": "EQUALS" } ] }, { "type": "SELECT", "name": "objectPropertiesFromVariable", "displayName": "Load Properties From Variable", "macrosInSelect": true, "selectItems": [ { "value": false, "displayValue": "False" } ], "simpleValueType": true, "help": "You can use a variable that returns a JavaScript object with the properties you want to use. This object will be merged with any additional properties you add via the table below. Any conflicts will be resolved in favor of the properties you add to the table." }, { "name": "objectPropertyList", "simpleTableColumns": [ { "valueValidators": [], "defaultValue": "", "displayName": "Property Name", "name": "name", "isUnique": true, "type": "TEXT" }, { "defaultValue": "", "displayName": "Property Value", "name": "value", "type": "TEXT" } ], "type": "SIMPLE_TABLE", "newRowButtonText": "Add property" } ] }, { "displayName": "More Settings", "name": "moreSettingsGroup", "groupStyle": "ZIPPY_CLOSED", "type": "GROUP", "subParams": [ { "help": "Facebook collects some metadata (e.g. structured data) and user interactions (e.g. clicks) automatically. Check this box to disable this automatic configuration of the pixel.", "simpleValueType": true, "name": "disableAutoConfig", "checkboxText": "Disable Automatic Configuration", "type": "CHECKBOX" }, { "type": "CHECKBOX", "name": "disablePushState", "checkboxText": "Disable History Event Tracking", "simpleValueType": true, "help": "The Facebook Pixel tracks history events (pushState and replaceState) automatically as PageViews. Check this box to prevent the pixel from tracking such events automatically." }, { "type": "TEXT", "name": "eventId", "displayName": "Event ID", "simpleValueType": true, "help": "Set the Event ID parameter in case you are tracking the same event server-side as well. The Event ID can be used to deduplicate the same event if sent from multiple sources. See more \u003ca href\u003d\"https://developers.facebook.com/docs/marketing-api/conversions-api/deduplicate-pixel-and-server-events/\"\u003ehere\u003c/a\u003e." } ] } ] ___SANDBOXED_JS_FOR_WEB_TEMPLATE___ const createQueue = require('createQueue'); const callInWindow = require('callInWindow'); const aliasInWindow = require('aliasInWindow'); const copyFromWindow = require('copyFromWindow'); const setInWindow = require('setInWindow'); const injectScript = require('injectScript'); const makeTableMap = require('makeTableMap'); const makeNumber = require('makeNumber'); const getType = require('getType'); const copyFromDataLayer = require('copyFromDataLayer'); const math = require('Math'); const log = require('logToConsole'); const initIds = copyFromWindow('_fbq_gtm_ids') || []; const pixelIds = data.pixelId; const standardEventNames = ['AddPaymentInfo', 'AddToCart', 'AddToWishlist', 'CompleteRegistration', 'Contact', 'CustomizeProduct', 'Donate', 'FindLocation', 'InitiateCheckout', 'Lead', 'PageView', 'Purchase', 'Schedule', 'Search', 'StartTrial', 'SubmitApplication', 'Subscribe', 'ViewContent']; const ecommerce = copyFromDataLayer('ecommerce', 1); let lastEventName = copyFromDataLayer('event'); // Helper methods const fail = msg => { log(msg); data.gtmOnFailure(); }; const mergeObj = (obj, obj2) => { for (let key in obj2) { if (obj2.hasOwnProperty(key)) { obj[key] = obj2[key]; } } return obj; }; const parseEecObj = prod => { return { id: prod.item_id, quantity: prod.quantity }; }; // Initialize EEC integration let eventName, action, eecObjectProps; if (data.enhancedEcommerce) { if (!ecommerce) return fail('Facebook Pixel: No valid "ecommerce" object found in dataLayer'); if ("gtmvar" === data.eecEventName) { if (data.eecEventNameGTMVar) { lastEventName = data.eecEventNameGTMVar; } if (!lastEventName) { return fail('Facebook Pixel: No event name was returned by the selected GTM variable'); } } if ("view_item" === lastEventName) { eventName = 'ViewContent'; } else if ("add_to_cart" === lastEventName) { eventName = 'AddToCart'; } else if ("add_to_wishlist" === lastEventName) { eventName = 'AddToWishlist'; } else if ("begin_checkout" === lastEventName || "begin_checkout" === lastEventName) { eventName = 'InitiateCheckout'; } else if ("add_payment_info" === lastEventName) { eventName = 'AddPaymentInfo'; } else if ("purchase" === lastEventName) { eventName = 'Purchase'; } else return fail('Facebook Pixel: Most recently pushed ecommerce event name unsupported: ' + lastEventName); if (!ecommerce.items || getType(ecommerce.items) !== 'array') return fail('Facebook pixel: Most recently pushed "ecommerce" object did not have a valid "items" array.'); if (ecommerce.items.length === 0) return fail('Facebook pixel: Most recently pushed "ecommerce" object does not include product data in the "items" array.'); eecObjectProps = { content_type: 'product', contents: ecommerce.items.map(parseEecObj), value: ecommerce.items.reduce((acc, cur) => { const curVal = math.round(makeNumber(cur.price || 0) * (cur.quantity || 1) * 100) / 100; return acc + curVal; }, 0.0), currency: ecommerce.currency || ecommerce.items[0].currency || data.eecDefaultCurrency || 'USD' }; if (['InitiateCheckout', 'Purchase'].indexOf(eventName) > -1) eecObjectProps.num_items = ecommerce.items.reduce((acc,cur) => { return acc + makeNumber(cur.quantity || 1); }, 0); } // Build the fbq() command arguments const cidParams = data.advancedMatchingList && data.advancedMatchingList.length ? makeTableMap(data.advancedMatchingList, 'name', 'value') : {}; const objectProps = data.objectPropertyList && data.objectPropertyList.length ? makeTableMap(data.objectPropertyList, 'name', 'value') : {}; const objectPropsFromVar = getType(data.objectPropertiesFromVariable) === 'object' ? data.objectPropertiesFromVariable : {}; const mergedObjectProps = mergeObj(objectPropsFromVar, objectProps); const finalObjectProps = mergeObj(eecObjectProps || {}, mergedObjectProps); eventName = eventName || (data.eventName === 'custom' ? data.customEventName : (data.eventName === 'variable' ? data.variableEventName : data.standardEventName)); const command = standardEventNames.indexOf(eventName) === -1 ? 'trackSingleCustom' : 'trackSingle'; const consent = data.consent === false || data.consent === 'false' ? 'revoke' : 'grant'; // Utility function to use either fbq.queue[] // (if the FB SDK hasn't loaded yet), or fbq.callMethod() // if the SDK has loaded. const getFbq = () => { // Return the existing 'fbq' global method if available let fbq = copyFromWindow('fbq'); if (fbq) { return fbq; } // Initialize the 'fbq' global method to either use // fbq.callMethod or fbq.queue) setInWindow('fbq', function() { const callMethod = copyFromWindow('fbq.callMethod.apply'); if (callMethod) { callInWindow('fbq.callMethod.apply', null, arguments); } else { callInWindow('fbq.queue.push', arguments); } }); aliasInWindow('_fbq', 'fbq'); // Create the fbq.queue createQueue('fbq.queue'); // Return the global 'fbq' method, created above return copyFromWindow('fbq'); }; // Get reference to the global method const fbq = getFbq(); fbq('consent', consent); // Set Data Processing Options if (data.dpoLDU) { fbq('dataProcessingOptions', ['LDU'], makeNumber(data.dpoCountry), makeNumber(data.dpoState)); } // Handle multiple, comma-separated pixel IDs, // and initialize each ID if not done already. pixelIds.split(',').forEach(pixelId => { if (initIds.indexOf(pixelId) === -1) { // If the user has chosen to disable automatic configuration if (data.disableAutoConfig) { fbq('set', 'autoConfig', false, pixelId); } // If the user has chosen to disable pushState and replaceState tracking if (data.disablePushState) { setInWindow('fbq.disablePushState', true); } // Initialize pixel and store in global array fbq('init', pixelId, cidParams); initIds.push(pixelId); setInWindow('_fbq_gtm_ids', initIds, true); } // Call the fbq() method with the parameters defined earlier if (data.eventId) { fbq(command, pixelId, eventName, finalObjectProps, {eventID: data.eventId}); } else { fbq(command, pixelId, eventName, finalObjectProps); } }); injectScript('https://connect.facebook.net/en_US/fbevents.js', data.gtmOnSuccess, data.gtmOnFailure, 'fbPixel'); ___WEB_PERMISSIONS___ [ { "instance": { "key": { "publicId": "access_globals", "versionId": "1" }, "param": [ { "key": "keys", "value": { "type": 2, "listItem": [ { "type": 3, "mapKey": [ { "type": 1, "string": "key" }, { "type": 1, "string": "read" }, { "type": 1, "string": "write" }, { "type": 1, "string": "execute" } ], "mapValue": [ { "type": 1, "string": "fbq" }, { "type": 8, "boolean": true }, { "type": 8, "boolean": true }, { "type": 8, "boolean": false } ] }, { "type": 3, "mapKey": [ { "type": 1, "string": "key" }, { "type": 1, "string": "read" }, { "type": 1, "string": "write" }, { "type": 1, "string": "execute" } ], "mapValue": [ { "type": 1, "string": "_fbq_gtm" }, { "type": 8, "boolean": true }, { "type": 8, "boolean": true }, { "type": 8, "boolean": false } ] }, { "type": 3, "mapKey": [ { "type": 1, "string": "key" }, { "type": 1, "string": "read" }, { "type": 1, "string": "write" }, { "type": 1, "string": "execute" } ], "mapValue": [ { "type": 1, "string": "_fbq" }, { "type": 8, "boolean": false }, { "type": 8, "boolean": true }, { "type": 8, "boolean": false } ] }, { "type": 3, "mapKey": [ { "type": 1, "string": "key" }, { "type": 1, "string": "read" }, { "type": 1, "string": "write" }, { "type": 1, "string": "execute" } ], "mapValue": [ { "type": 1, "string": "_fbq_gtm_ids" }, { "type": 8, "boolean": true }, { "type": 8, "boolean": true }, { "type": 8, "boolean": false } ] }, { "type": 3, "mapKey": [ { "type": 1, "string": "key" }, { "type": 1, "string": "read" }, { "type": 1, "string": "write" }, { "type": 1, "string": "execute" } ], "mapValue": [ { "type": 1, "string": "fbq.callMethod.apply" }, { "type": 8, "boolean": true }, { "type": 8, "boolean": false }, { "type": 8, "boolean": true } ] }, { "type": 3, "mapKey": [ { "type": 1, "string": "key" }, { "type": 1, "string": "read" }, { "type": 1, "string": "write" }, { "type": 1, "string": "execute" } ], "mapValue": [ { "type": 1, "string": "fbq.queue.push" }, { "type": 8, "boolean": false }, { "type": 8, "boolean": false }, { "type": 8, "boolean": true } ] }, { "type": 3, "mapKey": [ { "type": 1, "string": "key" }, { "type": 1, "string": "read" }, { "type": 1, "string": "write" }, { "type": 1, "string": "execute" } ], "mapValue": [ { "type": 1, "string": "fbq.queue" }, { "type": 8, "boolean": true }, { "type": 8, "boolean": true }, { "type": 8, "boolean": false } ] }, { "type": 3, "mapKey": [ { "type": 1, "string": "key" }, { "type": 1, "string": "read" }, { "type": 1, "string": "write" }, { "type": 1, "string": "execute" } ], "mapValue": [ { "type": 1, "string": "fbq.disablePushState" }, { "type": 8, "boolean": true }, { "type": 8, "boolean": true }, { "type": 8, "boolean": false } ] } ] } } ] }, "clientAnnotations": { "isEditedByUser": true }, "isRequired": true }, { "instance": { "key": { "publicId": "inject_script", "versionId": "1" }, "param": [ { "key": "urls", "value": { "type": 2, "listItem": [ { "type": 1, "string": "https://connect.facebook.net/en_US/fbevents.js" } ] } } ] }, "clientAnnotations": { "isEditedByUser": true }, "isRequired": true }, { "instance": { "key": { "publicId": "logging", "versionId": "1" }, "param": [ { "key": "environments", "value": { "type": 1, "string": "debug" } } ] }, "clientAnnotations": { "isEditedByUser": true }, "isRequired": true }, { "instance": { "key": { "publicId": "read_data_layer", "versionId": "1" }, "param": [ { "key": "allowedKeys", "value": { "type": 1, "string": "specific" } }, { "key": "keyPatterns", "value": { "type": 2, "listItem": [ { "type": 1, "string": "ecommerce" }, { "type": 1, "string": "event" } ] } } ] }, "clientAnnotations": { "isEditedByUser": true }, "isRequired": true } ] ___TESTS___ scenarios: - name: Library is injected code: |- // Call runCode to run the template's code. runCode(mockData); // Verify that the tag finished successfully. assertApi('injectScript').wasCalledWith(scriptUrl, success, failure, 'fbPixel'); assertApi('gtmOnSuccess').wasCalled(); - name: fbq does not exist - method created code: |- let fbq; mock('copyFromWindow', key => { if (key === 'fbq') return fbq; }); mock('createQueue', key => {}); mock('setInWindow', (key, val) => { if (key === 'fbq') fbq = val; }); // Call runCode to run the template's code. runCode(mockData); // Verify that the tag finished successfully. assertApi('aliasInWindow').wasCalledWith('_fbq', 'fbq'); assertApi('setInWindow').wasCalled(); assertApi('gtmOnSuccess').wasCalled(); - name: fbq exists - method copied code: |- mock('setInWindow', key => { if (key === 'fbq') fail('setInWindow called with fbq even though variable exists'); }); mock('createQueue', key => {}); // Call runCode to run the template's code. runCode(mockData); // Verify that the tag finished successfully. assertApi('gtmOnSuccess').wasCalled(); - name: makeTableMap called code: |- mockData.advancedMatching = true; // Call runCode to run the template's code. runCode(mockData); // Verify that the tag finished successfully. assertApi('makeTableMap').wasCalledWith(mockData.advancedMatchingList, 'name', 'value'); assertApi('makeTableMap').wasCalledWith(mockData.objectPropertyList, 'name', 'value'); assertApi('gtmOnSuccess').wasCalled(); - name: Consent set code: |- mock('copyFromWindow', key => { if (key === 'fbq') return function() { if (arguments[0] === 'consent') { assertThat(arguments[1], 'Consent set incorrectly').isEqualTo('grant'); } }; }); // Call runCode to run the template's code. runCode(mockData); // Verify that the tag finished successfully. assertApi('gtmOnSuccess').wasCalled(); - name: DPO LDU set code: |- mockData.dpoLDU = true; mockData.dpoCountry = '0'; mockData.dpoState = '0'; mock('copyFromWindow', key => { if (key === 'fbq') return function() { if (arguments[0] === 'consent') { assertThat(arguments[1], 'Consent set incorrectly').isEqualTo('grant'); } if (arguments[0] === 'dataProcessingOptions') { assertThat(arguments[1], 'LDU array value not set').isEqualTo(['LDU']); assertThat(arguments[2], 'LDU country not set').isEqualTo(0); assertThat(arguments[3], 'LDU state not set').isEqualTo(0); } }; }); // Call runCode to run the template's code. runCode(mockData); // Verify that the tag finished successfully. assertApi('gtmOnSuccess').wasCalled(); - name: DPO LDU not set code: |- mock('copyFromWindow', key => { if (key === 'fbq') return function() { if (arguments[0] === 'consent') { assertThat(arguments[1], 'Consent set incorrectly').isEqualTo('grant'); } if (arguments[0] === 'dataProcessingOptions') { fail('dataProcessingOptions called even though DPO was not set'); } }; }); // Call runCode to run the template's code. runCode(mockData); // Verify that the tag finished successfully. assertApi('gtmOnSuccess').wasCalled(); - name: Pixel IDs set - do not initialize code: |- mock('copyFromWindow', key => { if (key === '_fbq_gtm_ids') return ['12345', '23456']; if (key === 'fbq') return function() { if (arguments[0] === 'init') fail('init called even though pixel IDs already initialized'); }; }); // Call runCode to run the template's code. runCode(mockData); // Verify that the tag finished successfully. assertApi('gtmOnSuccess').wasCalled(); - name: Pixel IDs not set - run init process code: "let index = 0;\nlet count = 0;\nlet _fbq_gtm_ids;\n\nmockData.advancedMatching\ \ = true;\nmockData.disableAutoConfig = true;\nmockData.disablePushState = true;\n\ \nmock('setInWindow', (key, val) => {\n if (key === 'fbq.disablePushState') count\ \ += 1;\n if (key === '_fbq_gtm_ids') _fbq_gtm_ids = val;\n});\n\nconst initObj\ \ = {\n ct: 'Helsinki',\n cn: 'Finland',\n external_id: 'UserId'\n};\n\nmock('copyFromWindow',\ \ key => {\n if (key === 'fbq') return function() {\n if (arguments[0] ===\ \ 'set' && arguments[1] === 'autoConfig' && arguments[2] === false) {\n assertThat(arguments[3],\ \ 'autoConfig called with incorrect pixelId').isEqualTo(mockData.pixelId.split(',')[index]);\n\ \ }\n if (arguments[0] === 'set' && arguments[1] === 'agent') {\n assertThat(arguments[2],\ \ 'agent set with invalid value').isEqualTo('tmSimo-GTM-WebTemplate');\n \ \ assertThat(arguments[3], 'agent set with invalid pixel ID').isEqualTo(mockData.pixelId.split(',')[index]);\n\ \ index += 1;\n }\n if (arguments[0] === 'init') {\n assertThat(arguments[1],\ \ 'init called with incorrect pixelId').isEqualTo(mockData.pixelId.split(',')[index]);\n\ \ assertThat(arguments[2], 'init called with incorrect initObj').isEqualTo(initObj);\n\ \ } \n };\n});\n\n// Call runCode to run the template's code.\nrunCode(mockData);\n\ \nassertThat(_fbq_gtm_ids, '_fbq_gtm_ids has incorrect contents').isEqualTo(mockData.pixelId.split(','));\n\ assertThat(index, 'init called incorrect number of times: ' + index).isEqualTo(2);\n\ assertThat(count, 'fbq.disablePushState called incorrect number of times: ' +\ \ count).isEqualTo(2);\n\n// Verify that the tag finished successfully.\nassertApi('gtmOnSuccess').wasCalled();" - name: Send standard event code: "const eventParams = {\n prop1: 'val1',\n prop2: 'val2'\n};\n\nlet index\ \ = 0;\nmock('copyFromWindow', key => {\n if (key === 'fbq') return function()\ \ {\n if (arguments[0] === 'trackSingle') {\n assertThat(arguments[1],\ \ 'trackSingle called with incorrect pixel ID').isEqualTo(mockData.pixelId.split(',')[index]);\n\ \ assertThat(arguments[2], 'trackSingle called with incorrect event name').isEqualTo(mockData.standardEventName);\n\ \ assertThat(arguments[3], 'trackSingle called with incorrect event parameters').isEqualTo(eventParams);\n\ \ index += 1;\n }\n };\n});\n \n// Call runCode to run the template's\ \ code.\nrunCode(mockData);\n\n// Verify that the tag finished successfully.\n\ assertThat(index, 'trackSingle called incorrect number of times').isEqualTo(2);\n\ assertApi('gtmOnSuccess').wasCalled();" - name: Send custom event code: "mockData.eventName = 'custom';\n\nconst eventParams = {\n prop1: 'val1',\n\ \ prop2: 'val2'\n};\n\nlet index = 0;\nmock('copyFromWindow', key => {\n if\ \ (key === 'fbq') return function() {\n if (arguments[0] === 'trackSingleCustom')\ \ {\n assertThat(arguments[1], 'trackSingleCustom called with incorrect pixel\ \ ID').isEqualTo(mockData.pixelId.split(',')[index]);\n assertThat(arguments[2],\ \ 'trackSingleCustom called with incorrect event name').isEqualTo(mockData.customEventName);\n\ \ assertThat(arguments[3], 'trackSingleCustom called with incorrect event\ \ parameters').isEqualTo(eventParams);\n index += 1;\n }\n };\n});\n\ \ \n// Call runCode to run the template's code.\nrunCode(mockData);\n\n//\ \ Verify that the tag finished successfully.\nassertThat(index, 'trackSingleCustom\ \ called incorrect number of times').isEqualTo(2);\nassertApi('gtmOnSuccess').wasCalled();" - name: Send variable event with standard name code: "mockData.eventName = 'variable';\nmockData.variableEventName = 'PageView';\n\ \nconst eventParams = {\n prop1: 'val1',\n prop2: 'val2'\n};\n\nlet index =\ \ 0;\nmock('copyFromWindow', key => {\n if (key === 'fbq') return function()\ \ {\n if (arguments[0] === 'trackSingle') {\n assertThat(arguments[1],\ \ 'trackSingle called with incorrect pixel ID').isEqualTo(mockData.pixelId.split(',')[index]);\n\ \ assertThat(arguments[2], 'trackSingle called with incorrect event name').isEqualTo(mockData.variableEventName);\n\ \ assertThat(arguments[3], 'trackSingle called with incorrect event parameters').isEqualTo(eventParams);\n\ \ index += 1;\n }\n };\n});\n \n// Call runCode to run the template's\ \ code.\nrunCode(mockData);\n\n// Verify that the tag finished successfully.\n\ assertThat(index, 'trackSingle called incorrect number of times').isEqualTo(2);\n\ assertApi('gtmOnSuccess').wasCalled();" - name: Send variable event with custom name code: "mockData.eventName = 'variable';\nmockData.variableEventName = 'custom';\n\ \nconst eventParams = {\n prop1: 'val1',\n prop2: 'val2'\n};\n\nlet index =\ \ 0;\nmock('copyFromWindow', key => {\n if (key === 'fbq') return function()\ \ {\n if (arguments[0] === 'trackSingleCustom') {\n assertThat(arguments[1],\ \ 'trackSingleCustom called with incorrect pixel ID').isEqualTo(mockData.pixelId.split(',')[index]);\n\ \ assertThat(arguments[2], 'trackSingleCustom called with incorrect event\ \ name').isEqualTo(mockData.variableEventName);\n assertThat(arguments[3],\ \ 'trackSingleCustom called with incorrect event parameters').isEqualTo(eventParams);\n\ \ index += 1;\n }\n };\n});\n \n// Call runCode to run the template's\ \ code.\nrunCode(mockData);\n\n// Verify that the tag finished successfully.\n\ assertThat(index, 'trackSingleCustom called incorrect number of times').isEqualTo(2);\n\ assertApi('gtmOnSuccess').wasCalled();" - name: Send event parameters from a variable code: "mockData.objectPropertiesFromVariable = {\n prop1: 'val1',\n prop2: 'val2'\n\ };\n\nlet index = 0;\nmock('copyFromWindow', key => {\n if (key === 'fbq') return\ \ function() {\n if (arguments[0] === 'trackSingle') {\n assertThat(arguments[1],\ \ 'trackSingle called with incorrect pixel ID').isEqualTo(mockData.pixelId.split(',')[index]);\n\ \ assertThat(arguments[2], 'trackSingle called with incorrect event name').isEqualTo(mockData.standardEventName);\n\ \ assertThat(arguments[3], 'trackSingle called with incorrect event parameters').isEqualTo(mockData.objectPropertiesFromVariable);\n\ \ index += 1;\n }\n };\n});\n \n// Call runCode to run the template's\ \ code.\nrunCode(mockData);\n\n// Verify that the tag finished successfully.\n\ assertThat(index, 'trackSingle called incorrect number of times').isEqualTo(2);\n\ assertApi('gtmOnSuccess').wasCalled();" - name: Enhanced Ecommerce integration fails with invalid object code: |- mockData.enhancedEcommerce = true; // Call runCode to run the template's code. runCode(mockData); // Verify that the tag finished successfully. assertApi('logToConsole').wasCalledWith('Facebook Pixel: No valid "ecommerce" object found in dataLayer'); assertApi('gtmOnFailure').wasCalled(); assertApi('gtmOnSuccess').wasNotCalled(); - name: Enhanced Ecommerce integration fails with invalid action code: "mockData.enhancedEcommerce = true;\n\nmock('copyFromDataLayer', key => {\n\ \ if (key === 'ecommerce') return {\n invalid: true\n };\n \n if (key ===\ \ \"event\") return \"view_item\";\n});\n\n// Call runCode to run the template's\ \ code.\nrunCode(mockData);\n\n// Verify that the tag finished successfully.\n\ assertApi('logToConsole').wasCalledWith('Facebook pixel: Most recently pushed\ \ \"ecommerce\" object did not have a valid \"items\" array.');\nassertApi('gtmOnFailure').wasCalled();\n\ assertApi('gtmOnSuccess').wasNotCalled();" - name: Enhanced Ecommerce ViewContent works code: "mockData.enhancedEcommerce = true;\nmockData.objectPropertyList = {};\n\n\ mock('copyFromDataLayer', key => {\n if (key === 'ecommerce') return {\n currency:\ \ 'EUR',\n items: mockEec.gtm.products\n };\n\n if (key === 'event') return\ \ \"view_item\";\n});\n\nlet index = 0;\nmock('copyFromWindow', key => {\n if\ \ (key === 'fbq') return function() {\n if (arguments[0] === 'trackSingle')\ \ {\n assertThat(arguments[1], 'trackSingle called with incorrect pixel ID').isEqualTo(mockData.pixelId.split(',')[index]);\n\ \ assertThat(arguments[2], 'trackSingle called with incorrect event name').isEqualTo('ViewContent');\n\ \ assertThat(arguments[3], 'trackSingle called with incorrect event parameters').isEqualTo(mockEec.fb);\n\ \ index += 1;\n }\n };\n});\n \n// Call runCode to run the template's\ \ code.\nrunCode(mockData);\n\n// Verify that the tag finished successfully.\n\ assertThat(index, 'trackSingle called incorrect number of times').isEqualTo(2);\n\ assertApi('gtmOnSuccess').wasCalled();" - name: Enhanced Ecommerce AddToCart works code: "mockData.enhancedEcommerce = true;\nmockData.objectPropertyList = {};\n\n\ mock('copyFromDataLayer', key => {\n if (key === 'ecommerce') return {\n currency:\ \ 'EUR',\n items: mockEec.gtm.products\n };\n\n\n if (key === 'event') return\ \ \"add_to_cart\";\n});\n\nlet index = 0;\nmock('copyFromWindow', key => {\n \ \ if (key === 'fbq') return function() {\n if (arguments[0] === 'trackSingle')\ \ {\n assertThat(arguments[1], 'trackSingle called with incorrect pixel ID').isEqualTo(mockData.pixelId.split(',')[index]);\n\ \ assertThat(arguments[2], 'trackSingle called with incorrect event name').isEqualTo('AddToCart');\n\ \ assertThat(arguments[3], 'trackSingle called with incorrect event parameters').isEqualTo(mockEec.fb);\n\ \ index += 1;\n }\n };\n});\n \n// Call runCode to run the template's\ \ code.\nrunCode(mockData);\n\n// Verify that the tag finished successfully.\n\ assertThat(index, 'trackSingle called incorrect number of times').isEqualTo(2);\n\ assertApi('gtmOnSuccess').wasCalled();" - name: Enhanced Ecommerce InitiateCheckout works code: "mockData.enhancedEcommerce = true;\nmockEec.fb.num_items = 3;\nmockData.objectPropertyList\ \ = {};\n\nmock('copyFromDataLayer', key => {\n if (key === 'ecommerce') return\ \ {\n currency: 'EUR',\n items: mockEec.gtm.products\n };\n\n if (key\ \ === 'event') return \"begin_checkout\";\n});\n\nlet index = 0;\nmock('copyFromWindow',\ \ key => {\n if (key === 'fbq') return function() {\n if (arguments[0] ===\ \ 'trackSingle') {\n assertThat(arguments[1], 'trackSingle called with incorrect\ \ pixel ID').isEqualTo(mockData.pixelId.split(',')[index]);\n assertThat(arguments[2],\ \ 'trackSingle called with incorrect event name').isEqualTo('InitiateCheckout');\n\ \ assertThat(arguments[3], 'trackSingle called with incorrect event parameters').isEqualTo(mockEec.fb);\n\ \ index += 1;\n }\n };\n});\n \n// Call runCode to run the template's\ \ code.\nrunCode(mockData);\n\n// Verify that the tag finished successfully.\n\ assertThat(index, 'trackSingle called incorrect number of times').isEqualTo(2);\n\ assertApi('gtmOnSuccess').wasCalled();" - name: Enhanced Ecommerce Purchase works code: "mockData.enhancedEcommerce = true;\nmockEec.fb.num_items = 3;\nmockData.objectPropertyList\ \ = {};\n\nmock('copyFromDataLayer', key => {\n if (key === 'ecommerce') return\ \ {\n currency: 'EUR',\n items: mockEec.gtm.products\n };\n\n if (key\ \ === 'event') return \"purchase\";\n});\n\nlet index = 0;\nmock('copyFromWindow',\ \ key => {\n if (key === 'fbq') return function() {\n if (arguments[0] ===\ \ 'trackSingle') {\n assertThat(arguments[1], 'trackSingle called with incorrect\ \ pixel ID').isEqualTo(mockData.pixelId.split(',')[index]);\n assertThat(arguments[2],\ \ 'trackSingle called with incorrect event name').isEqualTo('Purchase');\n \ \ assertThat(arguments[3], 'trackSingle called with incorrect event parameters').isEqualTo(mockEec.fb);\n\ \ index += 1;\n }\n };\n});\n \n// Call runCode to run the template's\ \ code.\nrunCode(mockData);\n\n// Verify that the tag finished successfully.\n\ assertThat(index, 'trackSingle called incorrect number of times').isEqualTo(2);\n\ assertApi('gtmOnSuccess').wasCalled();" - name: Object merge with variable and list works code: "mockData.objectPropertiesFromVariable = {\n prop1: 'var1',\n prop2: 'var2',\n\ \ prop3: 'var3'\n};\n\nconst expected = {\n prop1: 'val1',\n prop2: 'val2',\n\ \ prop3: 'var3'\n};\n\nlet index = 0;\nmock('copyFromWindow', key => {\n if\ \ (key === 'fbq') return function() {\n if (arguments[0] === 'trackSingle')\ \ {\n assertThat(arguments[1], 'trackSingle called with incorrect pixel ID').isEqualTo(mockData.pixelId.split(',')[index]);\n\ \ assertThat(arguments[2], 'trackSingle called with incorrect event name').isEqualTo('PageView');\n\ \ assertThat(arguments[3], 'trackSingle called with incorrect event parameters').isEqualTo(expected);\n\ \ index += 1;\n }\n };\n});\n \n// Call runCode to run the template's\ \ code.\nrunCode(mockData);\n\n// Verify that the tag finished successfully.\n\ assertThat(index, 'trackSingle called incorrect number of times').isEqualTo(2);\n\ assertApi('gtmOnSuccess').wasCalled();" - name: Object merge with variable, list and eec works code: "mockData.enhancedEcommerce = true;\nmockData.objectPropertiesFromVariable\ \ = {\n content_type: 'product_group'\n};\nmockData.objectPropertyList = [{\n\ \ name: 'currency',\n value: 'USD'\n}];\nmockEec.fb.num_items = 3;\nmockEec.fb.content_type\ \ = 'product_group';\nmockEec.fb.currency = 'USD';\n\nmock('copyFromDataLayer',\ \ key => {\n if (key === 'ecommerce') return {\n currency: 'EUR',\n items:\ \ mockEec.gtm.products\n };\n\n if (key === 'event') return \"purchase\";\n\ });\n\nlet index = 0;\nmock('copyFromWindow', key => {\n if (key === 'fbq') return\ \ function() {\n if (arguments[0] === 'trackSingle') {\n assertThat(arguments[1],\ \ 'trackSingle called with incorrect pixel ID').isEqualTo(mockData.pixelId.split(',')[index]);\n\ \ assertThat(arguments[2], 'trackSingle called with incorrect event name').isEqualTo('Purchase');\n\ \ assertThat(arguments[3], 'trackSingle called with incorrect event parameters').isEqualTo(mockEec.fb);\n\ \ index += 1;\n }\n };\n});\n \n// Call runCode to run the template's\ \ code.\nrunCode(mockData);\n\n// Verify that the tag finished successfully.\n\ assertThat(index, 'trackSingle called incorrect number of times').isEqualTo(2);\n\ assertApi('gtmOnSuccess').wasCalled();" - name: Send event ID code: "mockData.eventId = 'eventId';\n\nmock('copyFromWindow', key => {\n if (key\ \ === 'fbq') return function() {\n if (arguments[0] === 'trackSingle') {\n\ \ assertThat(arguments[4], 'eventID not included in hit').isEqualTo({eventID:\ \ mockData.eventId});\n }\n };\n});\n \n// Call runCode to run the template's\ \ code.\nrunCode(mockData);\n\n// Verify that the tag finished successfully.\n\ assertApi('gtmOnSuccess').wasCalled();" setup: "const mockData = {\n pixelId: '12345,23456',\n eventName: 'standard',\n\ \ standardEventName: 'PageView',\n customEventName: 'custom',\n variableEventName:\ \ 'standard',\n consent: true,\n advancedMatching: false,\n advancedMatchingList:\ \ [{name: 'ct', value: 'Helsinki'},{name: 'cn', value: 'Finland'},{name: 'external_id',\ \ value: 'UserId'}],\n objectPropertiesFromVariable: false,\n objectPropertyList:\ \ [{name: 'prop1', value: 'val1'},{name: 'prop2', value: 'val2'}],\n disableAutoConfig:\ \ false,\n disablePushState: false,\n enhancedEcommerce: false,\n eventId: ''\n\ };\n\nconst mockEec = {\n gtm: { \n products: [{\n item_id: 'i1',\n \ \ item_name: 'n1',\n item_category: 'c1',\n price: '1.00',\n quantity:\ \ 1\n },{\n item_id: 'i2',\n item_name: 'n2',\n item_category:\ \ 'c2',\n price: '2.00',\n quantity: 2\n }]\n },\n fb: {\n content_type:\ \ 'product',\n contents: [{\n id: 'i1',\n quantity: 1\n },{\n \ \ id: 'i2',\n quantity: 2\n }],\n currency: 'EUR',\n value: 5.00\n\ \ }\n};\n\nconst scriptUrl = 'https://connect.facebook.net/en_US/fbevents.js';\n\ \n// Create injectScript mock\nlet success, failure;\nmock('injectScript', (url,\ \ onsuccess, onfailure) => {\n success = onsuccess;\n failure = onfailure;\n \ \ onsuccess();\n});\n\nmock('copyFromWindow', key => {\n if (key === 'fbq') return\ \ () => {};\n});" ___NOTES___ Created on 18/05/2019, 21:57:16