Skip to content

Commit 3a4e35e

Browse files
authored
FB Pixel: Custom Content Types and Automatic Configuration (segmentio#59)
* Fixes facade call and adds tests * allow disable autoConfig * Fixes tests * Bumps Facebook Pixel version * Updates HISTORY * Updates auto configuration naming * Replaces free form content type with a mapped setting + defaults * Removes ununused block
1 parent 63393d1 commit 3a4e35e

File tree

4 files changed

+162
-19
lines changed

4 files changed

+162
-19
lines changed

integrations/facebook-pixel/HISTORY.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
2.5.1 / 2018-09-10
2+
==================
3+
4+
* Makes content type customizable for Product List Viewed, Product Viewed, Product Added, and
5+
Order Completed events.
6+
* Adds a setting to disable Automatic Configuration.
7+
18
2.5.0 / 2018-06-22
29
==================
310

integrations/facebook-pixel/lib/index.js

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,10 @@ var FacebookPixel = module.exports = integration('Facebook Pixel')
2424
.option('valueIdentifier', 'value')
2525
.option('initWithExistingTraits', false)
2626
.option('traverse', false)
27+
.option('automaticConfiguration', true)
2728
.mapping('standardEvents')
2829
.mapping('legacyEvents')
30+
.mapping('contentTypes')
2931
.tag('<script src="//connect.facebook.net/en_US/fbevents.js">');
3032

3133
/**
@@ -52,6 +54,9 @@ FacebookPixel.prototype.initialize = function() {
5254
window.fbq.version = '2.0';
5355
window.fbq.queue = [];
5456
this.load(this.ready);
57+
if (!this.options.automaticConfiguration) {
58+
window.fbq('set', 'autoConfig', false, this.options.pixelId);
59+
}
5560
if (this.options.initWithExistingTraits) {
5661
var traits = formatTraits(this.analytics);
5762
window.fbq('init', this.options.pixelId, traits);
@@ -183,15 +188,15 @@ FacebookPixel.prototype.productListViewed = function(track) {
183188
// If no products have been defined, fallback on legacy behavior.
184189
// Facebook documents the content_type parameter decision here: https://developers.facebook.com/docs/facebook-pixel/api-reference
185190
if (contentIds.length) {
186-
contentType = 'product';
191+
contentType = ['product'];
187192
} else {
188193
contentIds.push(track.category() || '');
189-
contentType = 'product_group';
194+
contentType = ['product_group'];
190195
}
191196

192197
window.fbq('track', 'ViewContent', {
193198
content_ids: contentIds,
194-
content_type: contentType
199+
content_type: this.mappedContentTypesOrDefault(track.category(), contentType),
195200
});
196201

197202
// fall through for mapped legacy conversions
@@ -213,7 +218,7 @@ FacebookPixel.prototype.productListViewed = function(track) {
213218
FacebookPixel.prototype.productViewed = function(track) {
214219
window.fbq('track', 'ViewContent', {
215220
content_ids: [track.productId() || track.id() || track.sku() || ''],
216-
content_type: 'product',
221+
content_type: this.mappedContentTypesOrDefault(track.category(), ['product']),
217222
content_name: track.name() || '',
218223
content_category: track.category() || '',
219224
currency: track.currency(),
@@ -239,7 +244,7 @@ FacebookPixel.prototype.productViewed = function(track) {
239244
FacebookPixel.prototype.productAdded = function(track) {
240245
window.fbq('track', 'AddToCart', {
241246
content_ids: [track.productId() || track.id() || track.sku() || ''],
242-
content_type: 'product',
247+
content_type: this.mappedContentTypesOrDefault(track.category(), ['product']),
243248
content_name: track.name() || '',
244249
content_category: track.category() || '',
245250
currency: track.currency(),
@@ -263,18 +268,27 @@ FacebookPixel.prototype.productAdded = function(track) {
263268
*/
264269

265270
FacebookPixel.prototype.orderCompleted = function(track) {
271+
var products = track.products() || [];
272+
266273
var content_ids = foldl(function(acc, product) {
267274
var item = new Track({ properties: product });
268275
var key = item.productId() || item.id() || item.sku();
269276
if (key) acc.push(key);
270277
return acc;
271-
}, [], track.products() || []);
278+
}, [], products);
272279

273280
var revenue = formatRevenue(track.revenue());
274281

282+
// Order completed doesn't have a top-level category spec'd.
283+
// Let's default to the category of the first product. - @gabriel
284+
var contentType = ['product_group'];
285+
if (products.length) {
286+
contentType = this.mappedContentTypesOrDefault(products[0].category, contentType);
287+
}
288+
275289
window.fbq('track', 'Purchase', {
276290
content_ids: content_ids,
277-
content_type: 'product',
291+
content_type: contentType,
278292
currency: track.currency(),
279293
value: revenue
280294
});
@@ -288,6 +302,22 @@ FacebookPixel.prototype.orderCompleted = function(track) {
288302
}, this.legacyEvents(track.event()));
289303
};
290304

305+
/**
306+
* mappedContentTypesOrDefault returns an array of mapped content types for
307+
* the category - or returns the defaul value.
308+
* @param {Facade.Track} track
309+
* @param {Array} def
310+
*/
311+
FacebookPixel.prototype.mappedContentTypesOrDefault = function(category, def) {
312+
if (!category) return def
313+
314+
var mapped = this.contentTypes(category);
315+
if (mapped.length) {
316+
return mapped
317+
}
318+
319+
return def
320+
}
291321

292322
/**
293323
* Get Revenue Formatted Correctly for FB.

integrations/facebook-pixel/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@segment/analytics.js-integration-facebook-pixel",
33
"description": "The Facebook Pixel analytics.js integration.",
4-
"version": "2.5.0",
4+
"version": "2.5.1",
55
"keywords": [
66
"analytics.js",
77
"analytics.js-integration",

integrations/facebook-pixel/test/index.test.js

Lines changed: 117 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ describe('Facebook Pixel', function() {
99
var analytics;
1010
var facebookPixel;
1111
var options = {
12+
automaticConfiguration: true,
1213
legacyEvents: {
1314
legacyEvent: 'asdFrkj'
1415
},
@@ -17,6 +18,9 @@ describe('Facebook Pixel', function() {
1718
'booking completed': 'Purchase',
1819
search: 'Search'
1920
},
21+
contentTypes: {
22+
Cars: "vehicle"
23+
},
2024
pixelId: '123123123',
2125
agent: 'test',
2226
initWithExistingTraits: false
@@ -86,6 +90,20 @@ describe('Facebook Pixel', function() {
8690
analytics.assert(window.fbq instanceof Function);
8791
});
8892

93+
before(function() {
94+
options.automaticConfiguration = false;
95+
});
96+
97+
after(function() {
98+
options.automaticConfiguration = true;
99+
});
100+
101+
it('should call set autoConfig if option disableAutoConfig is enabled', function () {
102+
analytics.stub(window, 'fbq');
103+
analytics.initialize();
104+
analytics.called(window.fbq, 'set', 'autoConfig', false, options.pixelId);
105+
});
106+
89107
before(function() {
90108
options.initWithExistingTraits = true;
91109
});
@@ -225,7 +243,6 @@ describe('Facebook Pixel', function() {
225243
property: true
226244
});
227245
});
228-
229246
describe('Dyanmic Ads for Travel date parsing', function() {
230247
it('should correctly pass in iso8601 formatted date objects', function() {
231248
analytics.track('search', {
@@ -260,7 +277,7 @@ describe('Facebook Pixel', function() {
260277
name: 'Monopoly: 3rd Edition',
261278
price: 19,
262279
position: 1,
263-
category: 'Games',
280+
category: 'Cars',
264281
url: 'https://www.example.com/product/path',
265282
image_url: 'https://www.example.com/product/path.jpg'
266283
},
@@ -270,23 +287,52 @@ describe('Facebook Pixel', function() {
270287
name: 'Uno Card Game',
271288
price: 3,
272289
position: 2,
273-
category: 'Games'
290+
category: 'Cars'
274291
}
275292
]
276293
});
277294
analytics.called(window.fbq, 'track', 'ViewContent', {
278295
content_ids: ['507f1f77bcf86cd799439011', '505bd76785ebb509fc183733'],
279-
content_type: 'product'
296+
content_type: ['product']
280297
});
281298
});
282299

283300
it('Should fallback on mapping content_ids to the product category and content_type to "product_group"', function() {
284301
analytics.track('Product List Viewed', { category: 'Games' });
285302
analytics.called(window.fbq, 'track', 'ViewContent', {
286303
content_ids: ['Games'],
287-
content_type: 'product_group'
304+
content_type: ['product_group']
288305
});
289306
});
307+
308+
it('should send the custom content type if mapped', function() {
309+
analytics.track('Product List Viewed', {
310+
category: 'Cars', products: [
311+
{
312+
product_id: '507f1f77bcf86cd799439011',
313+
sku: '45790-32',
314+
name: 'Monopoly: 3rd Edition',
315+
price: 19,
316+
position: 1,
317+
category: 'Games',
318+
url: 'https://www.example.com/product/path',
319+
image_url: 'https://www.example.com/product/path.jpg'
320+
},
321+
{
322+
product_id: '505bd76785ebb509fc183733',
323+
sku: '46493-32',
324+
name: 'Uno Card Game',
325+
price: 3,
326+
position: 2,
327+
category: 'Games'
328+
}
329+
]
330+
});
331+
analytics.called(window.fbq, 'track', 'ViewContent', {
332+
content_ids: ['507f1f77bcf86cd799439011', '505bd76785ebb509fc183733'],
333+
content_type: ['vehicle']
334+
});
335+
})
290336
});
291337

292338
it('Product Viewed', function() {
@@ -302,7 +348,7 @@ describe('Facebook Pixel', function() {
302348
});
303349
analytics.called(window.fbq, 'track', 'ViewContent', {
304350
content_ids: ['507f1f77bcf86cd799439011'],
305-
content_type: 'product',
351+
content_type: ['product'],
306352
content_name: 'my product',
307353
content_category: 'cat 1',
308354
currency: 'USD',
@@ -324,14 +370,35 @@ describe('Facebook Pixel', function() {
324370
});
325371
analytics.called(window.fbq, 'track', 'ViewContent', {
326372
content_ids: ['507f1f77bcf86cd799439011'],
327-
content_type: 'product',
373+
content_type: ['product'],
328374
content_name: 'my product',
329375
content_category: 'cat 1',
330376
currency: 'USD',
331377
value: '44.33'
332378
});
333379
});
334380

381+
it('should send the custom content type if mapped', function() {
382+
analytics.track('Product Viewed', {
383+
product_id: '507f1f77bcf86cd799439011',
384+
currency: 'USD',
385+
quantity: 1,
386+
price: 44.33,
387+
name: 'my product',
388+
category: 'Cars',
389+
sku: 'p-298',
390+
value: 24.75,
391+
});
392+
analytics.called(window.fbq, 'track', 'ViewContent', {
393+
content_ids: ['507f1f77bcf86cd799439011'],
394+
content_type: ['vehicle'],
395+
content_name: 'my product',
396+
content_category: 'Cars',
397+
currency: 'USD',
398+
value: '24.75'
399+
});
400+
})
401+
335402
it('Adding to Cart', function() {
336403
analytics.track('Product Added', {
337404
product_id: '507f1f77bcf86cd799439011',
@@ -345,7 +412,7 @@ describe('Facebook Pixel', function() {
345412
});
346413
analytics.called(window.fbq, 'track', 'AddToCart', {
347414
content_ids: ['507f1f77bcf86cd799439011'],
348-
content_type: 'product',
415+
content_type: ['product'],
349416
content_name: 'my product',
350417
content_category: 'cat 1',
351418
currency: 'USD',
@@ -367,14 +434,36 @@ describe('Facebook Pixel', function() {
367434
});
368435
analytics.called(window.fbq, 'track', 'AddToCart', {
369436
content_ids: ['507f1f77bcf86cd799439011'],
370-
content_type: 'product',
437+
content_type: ['product'],
371438
content_name: 'my product',
372439
content_category: 'cat 1',
373440
currency: 'USD',
374441
value: '44.33'
375442
});
376443
});
377444

445+
it('should send the custom content type if mapped', function() {
446+
analytics.track('Product Added', {
447+
product_id: '507f1f77bcf86cd799439011',
448+
currency: 'USD',
449+
quantity: 1,
450+
price: 44.33,
451+
name: 'my product',
452+
category: 'Cars',
453+
sku: 'p-298',
454+
value: 24.75,
455+
content_type: "stuff"
456+
});
457+
analytics.called(window.fbq, 'track', 'AddToCart', {
458+
content_ids: ['507f1f77bcf86cd799439011'],
459+
content_type: ['vehicle'],
460+
content_name: 'my product',
461+
content_category: 'Cars',
462+
currency: 'USD',
463+
value: '24.75'
464+
});
465+
})
466+
378467
it('Completing an Order', function() {
379468
analytics.track('Order Completed', {
380469
products: [
@@ -386,7 +475,7 @@ describe('Facebook Pixel', function() {
386475
});
387476
analytics.called(window.fbq, 'track', 'Purchase', {
388477
content_ids: ['507f1f77bcf86cd799439011', '505bd76785ebb509fc183733'],
389-
content_type: 'product',
478+
content_type: ['product_group'],
390479
currency: 'USD',
391480
value: '0.50'
392481
});
@@ -404,7 +493,7 @@ describe('Facebook Pixel', function() {
404493
});
405494
analytics.called(window.fbq, 'track', 'Purchase', {
406495
content_ids: ['507f1f77bcf86cd799439011', '505bd76785ebb509fc183733'],
407-
content_type: 'product',
496+
content_type: ['product_group'],
408497
currency: 'USD',
409498
value: '0.50'
410499
});
@@ -413,6 +502,23 @@ describe('Facebook Pixel', function() {
413502
value: '0.50'
414503
});
415504
});
505+
506+
it('should send the custom content type if mapped', function() {
507+
analytics.track('Order Completed', {
508+
products: [
509+
{ product_id: '507f1f77bcf86cd799439011', category: 'Cars' },
510+
{ product_id: '505bd76785ebb509fc183733', category: 'Cars' }
511+
],
512+
currency: 'USD',
513+
total: 0.50,
514+
});
515+
analytics.called(window.fbq, 'track', 'Purchase', {
516+
content_ids: ['507f1f77bcf86cd799439011', '505bd76785ebb509fc183733'],
517+
content_type: ['vehicle'],
518+
currency: 'USD',
519+
value: '0.50'
520+
});
521+
})
416522
});
417523
});
418524
});

0 commit comments

Comments
 (0)