dlest
Version:
Jest for your data layer - test runner for analytics tracking implementations
136 lines (113 loc) • 4.03 kB
JavaScript
/**
* Data Layer Spy
*
* Injected into browser to intercept and capture dataLayer events
* Maintains compatibility with existing dataLayer implementations
*/
function createDataLayerSpy(variableName = 'dataLayer') {
// Store original dataLayer if it exists
const originalDataLayer = window[variableName] || [];
window.__dlest_original_dataLayer = originalDataLayer;
// Initialize events storage
window.__dlest_events = [];
// Store original push method
const originalPush = originalDataLayer.push || Array.prototype.push;
// Create intercepting push function
function interceptPush(...args) {
// Store events for DLest
args.forEach(event => {
if (event && typeof event === 'object') {
window.__dlest_events.push({
...event,
__dlest_timestamp: Date.now(),
__dlest_index: window.__dlest_events.length
});
}
});
// Call original implementation to maintain functionality
return originalPush.apply(window[variableName], args);
}
// Replace dataLayer with spy version
if (!window[variableName]) {
window[variableName] = [];
}
// Override the push method
window[variableName].push = interceptPush;
// Process any existing events in the dataLayer
if (originalDataLayer.length > 0) {
originalDataLayer.forEach((event, index) => {
if (event && typeof event === 'object') {
window.__dlest_events.push({
...event,
__dlest_timestamp: Date.now(),
__dlest_index: index,
__dlest_existing: true
});
}
});
}
// Helper functions for DLest
window.__dlest_helpers = {
// Get all captured events
getEvents: () => window.__dlest_events,
// Get events by name
getEventsByName: (eventName) =>
window.__dlest_events.filter(event =>
event.event === eventName ||
event.eventName === eventName ||
event.name === eventName
),
// Get events count
getEventCount: (eventName) => {
if (!eventName) return window.__dlest_events.length;
return window.__dlest_helpers.getEventsByName(eventName).length;
},
// Clear captured events (for testing)
clearEvents: () => {
window.__dlest_events = [];
},
// Check if event exists
hasEvent: (eventName, eventData) => {
const events = window.__dlest_helpers.getEventsByName(eventName);
if (events.length === 0) return false;
if (!eventData) return true;
return events.some(event => {
return Object.keys(eventData).every(key => {
if (typeof eventData[key] === 'object' && eventData[key] !== null) {
// Deep comparison for objects
return JSON.stringify(event[key]) === JSON.stringify(eventData[key]);
}
return event[key] === eventData[key];
});
});
},
// Wait for event (returns Promise)
waitForEvent: (eventName, timeout = 5000) => {
return new Promise((resolve, reject) => {
const startTime = Date.now();
const checkEvent = () => {
const events = window.__dlest_helpers.getEventsByName(eventName);
if (events.length > 0) {
resolve(events[events.length - 1]); // Return latest event
return;
}
if (Date.now() - startTime > timeout) {
reject(new Error(`Event '${eventName}' not found within ${timeout}ms`));
return;
}
setTimeout(checkEvent, 50); // Check every 50ms
};
checkEvent();
});
}
};
return window.__dlest_events;
}
// Export for Node.js usage (test runner will stringify and inject this)
if (typeof module !== 'undefined' && module.exports) {
module.exports = { createDataLayerSpy };
}
// Also make available globally when injected
if (typeof window !== 'undefined') {
window.__dlest_createDataLayerSpy = createDataLayerSpy;
}