Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,74 @@ describe('FacebookPixel init tests', () => {
});
});

describe('FacebookPixel multiple destinations bootstrap guard', () => {
const mockAnalytics = {};
let previousFbq;
let previousFbqShim;

beforeEach(() => {
// Capture and clear the globals so this suite exercises a fresh bootstrap,
// then restore them in afterEach so sibling suites are not affected.
previousFbq = window.fbq;
previousFbqShim = window._fbq;
delete window.fbq;
delete window._fbq;
});

afterEach(() => {
window.fbq = previousFbq;
window._fbq = previousFbqShim;
});

test('does not re-bootstrap the global fbq shim when a second destination initializes', () => {
// First destination bootstraps the global fbq shim.
const firstPixel = new FacebookPixel({ pixelId: '111111111' }, mockAnalytics, destinationInfo);
firstPixel.init();

const shimAfterFirstInit = window.fbq;
expect(typeof shimAfterFirstInit).toBe('function');
expect(window.fbq.version).toBe('2.0');

// Simulate the loaded SDK taking over: mark the shim and enqueue an event.
window.fbq.version = 'preserved-by-loaded-sdk';
window.fbq.queue.push('event-from-first-destination');

// Second destination initializes. Its init() must NOT reset the global shim.
const secondPixel = new FacebookPixel({ pixelId: '222222222' }, mockAnalytics, destinationInfo);
secondPixel.init();

// The guard preserves the already established shim: version and queue are untouched.
expect(window.fbq).toBe(shimAfterFirstInit);
expect(window.fbq.version).toBe('preserved-by-loaded-sdk');
expect(window.fbq.queue).toContain('event-from-first-destination');

// The second pixel still registers itself via its own init call.
expect(window.fbq.queue).toContainEqual(
expect.objectContaining({ 0: 'init', 1: '222222222' }),
);
});

test('applies pushState flags on every init even when fbq is pre-existing', () => {
// Simulate the host page (or an earlier destination) having already bootstrapped
// Meta Pixel with automatic pageview tracking enabled.
window.fbq = jest.fn();
window.fbq.queue = [];
window.fbq.version = 'pre-existing';
window.fbq.disablePushState = false;
window.fbq.allowDuplicatePageViews = false;

const pixel = new FacebookPixel({ pixelId: '333333333' }, mockAnalytics, destinationInfo);
pixel.init();

// The one-time shim state is left untouched (no version conflict)...
expect(window.fbq.version).toBe('pre-existing');
// ...but the pageview flags are (re)applied on this init so we never fall back
// to Meta's automatic pageview/pushState tracking.
expect(window.fbq.disablePushState).toBe(true);
expect(window.fbq.allowDuplicatePageViews).toBe(true);
});
});

describe('FacebookPixel page', () => {
let facebookPixel;
const mockAnalytics = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,21 +53,31 @@ class FacebookPixel {
}

init() {
window._fbq = function () {
if (window.fbq.callMethod) {
window.fbq.callMethod.apply(window.fbq, arguments);
} else {
window.fbq.queue.push(arguments);
}
};
// Bootstrap the global fbq shim only once per page. When multiple Meta Pixel
// destinations are configured, each one calls init(), and re-running this block
// mutates the already loaded fbevents.js SDK (resetting fbq.version, fbq.queue, etc.),
// which makes Meta log "Multiple pixels with conflicting versions were detected"
// and prevents the second pixel from initializing. Guard it like Meta's own snippet.
if (!window.fbq) {
window._fbq = function () {
if (window.fbq.callMethod) {
window.fbq.callMethod.apply(window.fbq, arguments);
} else {
window.fbq.queue.push(arguments);
}
};

window.fbq = window.fbq || window._fbq;
window.fbq.push = window.fbq;
window.fbq.loaded = true;
window.fbq = window.fbq || window._fbq;
window.fbq.push = window.fbq;
window.fbq.loaded = true;
window.fbq.version = '2.0';
window.fbq.queue = [];
Comment on lines +61 to +74

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. I moved disablePushState and allowDuplicatePageViews out of the one-time guard so they run on every init(), including when window.fbq was already bootstrapped by the host page or an earlier destination. Only the shim creation and version/queue setup (the parts that cause the version conflict) stay guarded. Added a test covering the pre-existing fbq case.

}
// Apply these flags on every init() (even when fbq was bootstrapped by the host page
// or an earlier destination), so we never fall back to Meta's automatic pageview
// tracking. They don't cause the version conflict, so they stay outside the guard.
window.fbq.disablePushState = true; // disables automatic pageview tracking
window.fbq.allowDuplicatePageViews = true; // enables fb
window.fbq.version = '2.0';
window.fbq.queue = [];
window.fbq('set', 'autoConfig', this.autoConfig, this.pixelId); // toggle autoConfig : sends button click and page metadata
if (this.advancedMapping) {
if (this.useUpdatedMapping) {
Expand Down