Loading...
Loading...
Comprehensive GTM tracking testing and validation including automated Playwright headless testing, browser console testing, GTM Preview mode validation, and GA4 DebugView verification. Use when users need to "test GTM tracking", "validate dataLayer events", "debug GTM", "check if tracking works", "automated tracking tests", "run tracking tests without opening browser", or troubleshoot tracking issues. Prioritises automated testing over manual when possible.
npx skill4agent add aimonk2025/google-tag-manager-automation gtm-testing# Check if Playwright is installed
npx playwright --version
# If not installed as a project dependency:
npm install --save-dev playwright
npx playwright install chromium
# Check if dev server is running
curl -s -o /dev/null -w "%{http_code}" http://localhost:3000
# If not running, start it:
# npm run devdataLayer.push()async function captureDataLayerEvents(page, action) {
await page.evaluate(() => {
window.__testEvents = [];
const original = window.dataLayer.push.bind(window.dataLayer);
window.dataLayer.push = function (...args) {
window.__testEvents.push(args[0]);
return original(...args);
};
});
await action();
await page.waitForTimeout(300);
return await page.evaluate(() => window.__testEvents || []);
}page.click()dispatchEventawait page.evaluate(() => {
const btn = document.querySelector('a.js-module_nav');
if (btn) btn.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }));
});
await page.waitForTimeout(400);await page.route('**github.com**', route => route.abort());
// Then use dispatchEvent as aboveawait page.locator('#element-id').click({ force: true });scripts/test-tracking.jsconst { chromium } = require('playwright');
const BASE_URL = 'http://localhost:3000';
const results = { passed: [], failed: [], warnings: [] };
function pass(test, detail = '') {
results.passed.push({ test, detail });
console.log(` PASS ${test}${detail ? ' - ' + detail : ''}`);
}
function fail(test, detail = '') {
results.failed.push({ test, detail });
console.log(` FAIL ${test}${detail ? ' - ' + detail : ''}`);
}
function warn(test, detail = '') {
results.warnings.push({ test, detail });
console.log(` WARN ${test}${detail ? ' - ' + detail : ''}`);
}
async function captureDataLayerEvents(page, action) {
await page.evaluate(() => {
window.__testEvents = [];
const original = window.dataLayer.push.bind(window.dataLayer);
window.dataLayer.push = function (...args) {
window.__testEvents.push(args[0]);
return original(...args);
};
});
await action();
await page.waitForTimeout(300);
return await page.evaluate(() => window.__testEvents || []);
}
async function runTests() {
const browser = await chromium.launch({ headless: true });
const context = await browser.newContext();
console.log('\n=== GTM dataLayer Event Tests ===\n');
// TEST: dataLayer initialisation
console.log('Test: dataLayer initialisation');
{
const page = await context.newPage();
await page.goto(BASE_URL, { waitUntil: 'networkidle' });
const hasDataLayer = await page.evaluate(() => Array.isArray(window.dataLayer));
hasDataLayer ? pass('dataLayer initialised') : fail('dataLayer not found');
const gtmEvent = await page.evaluate(() => window.dataLayer.find(e => e['gtm.start']));
gtmEvent ? pass('GTM bootstrap event present') : fail('GTM bootstrap event missing - container may not be installed');
await page.close();
}
// TEST: cta_click event
// Adjust selector to match your actual element ID
console.log('\nTest: cta_click event');
{
const page = await context.newPage();
await page.goto(BASE_URL, { waitUntil: 'networkidle' });
const events = await captureDataLayerEvents(page, async () => {
const el = await page.$('#your-cta-id');
if (el) await page.evaluate(el => el.dispatchEvent(new MouseEvent('click', { bubbles: true })), el);
});
const e = events.find(e => e.event === 'cta_click');
e ? pass('cta_click fired', `location=${e.cta_location}`) : fail('cta_click did not fire');
await page.close();
}
// Add more tests following the same pattern...
// RESULTS
await browser.close();
console.log('\n=== RESULTS ===');
console.log(` Passed: ${results.passed.length}`);
console.log(` Failed: ${results.failed.length}`);
console.log(` Warnings: ${results.warnings.length}`);
if (results.failed.length > 0) {
console.log('\nFailed:');
results.failed.forEach(f => console.log(` - ${f.test}: ${f.detail}`));
}
process.exit(results.failed.length > 0 ? 1 : 0);
}
runTests().catch(err => { console.error('Fatal:', err.message); process.exit(1); });// Add to test script or run as a separate debug script
const page = await context.newPage();
await page.goto(`${BASE_URL}/your-page`, { waitUntil: 'networkidle' });
const tracked = await page.evaluate(() => {
return Array.from(document.querySelectorAll('[id], .js-track, [data-track]')).map(el => ({
id: el.id,
tag: el.tagName,
class: el.className,
text: el.textContent.trim().substring(0, 40),
href: el.getAttribute('href'),
}));
});
console.log(JSON.stringify(tracked, null, 2));// scripts/audit-gtm-coverage.js
// Uses googleapis + gtm-credentials.json + gtm-token.json + gtm-config.json
// Critical checks:
// 1. Base GA4 config tag (googtag) - must fire on Page View trigger
// If firingTriggerId references a non-existent trigger, NO pages are tracked
// 2. GA4 event tags (gaawe) - must have measurementIdOverride set
// Parameter key is "measurementIdOverride" NOT "measurementId"
// 3. All triggers must be used by at least one tag
// 4. All tags must have at least one firing triggergaawemeasurementIdOverrideeventSettingsTablemeasurementIdeventParameters// CORRECT structure for gaawe tags via API
{
type: 'gaawe',
parameter: [
{ type: 'boolean', key: 'sendEcommerceData', value: 'false' },
{ type: 'template', key: 'eventName', value: 'your_event_name' },
{ type: 'template', key: 'measurementIdOverride', value: 'G-XXXXXXXXXX' },
{
type: 'list',
key: 'eventSettingsTable',
list: [
{
type: 'map',
map: [
{ type: 'template', key: 'parameter', value: 'param_name' },
{ type: 'template', key: 'parameterValue', value: '{{DL - Variable Name}}' },
],
},
],
},
],
}node scripts/test-tracking.js01PASSFAILWARN| Failure | Cause | Fix |
|---|---|---|
| GTM snippet missing from | Add GTM container snippet to layout |
| Event didn't fire | Element uses wrong selector in test | Use discovery script to find real ID/class |
| Event fired but page unloaded | Clicking a Next.js | Use |
| Outbound click not captured | Page navigated away before capture | Use |
| No events on page load | GTM base tag has orphaned trigger reference | Fix via GTM API or UI - re-assign to Page View trigger |
window.dataLayer
// Expected: [...] array
// If undefined: GTM not installedconst _push = window.dataLayer.push.bind(window.dataLayer);
window.dataLayer.push = function(...args) {
console.log('%c dataLayer.push', 'background:#222;color:#0f0;padding:2px 6px', args[0]);
return _push(...args);
};
// Now every push is logged in greenExpected for cta_click:
{
event: "cta_click",
cta_location: "hero",
cta_type: "primary",
cta_text: "Start Course",
cta_destination: "/claude-code"
}window.dataLayergtm.starteventEvent fired: "cta_click"
Triggers: CE - CTA Click [FIRED]
Tags: GA4 - CTA Click [FIRED]measurementIdOverride{{DL - ...}}?debug_mode=truecta_clickpage_viewpage_view| Issue | Tier Found | Cause | Fix |
|---|---|---|---|
| 1 | GTM snippet missing | Add GTM snippet to |
| Event fires, trigger doesn't | 2 | Event name mismatch (case-sensitive) | Confirm trigger uses |
| Trigger fires, tag doesn't | 2 | Orphaned trigger or misconfigured tag | Check tag has valid firing trigger assigned |
| Page View tag never fires | 2 | Orphaned trigger reference on base tag | Re-assign base tag to a valid Page View trigger |
Parameters show as | 2 | Data Layer Variable not created | Create DLV with correct dataLayer key name |
| Events in Preview, not DebugView | 3 | Wrong GA4 property or debug mode off | Verify Measurement ID matches, enable debug mode |
| Parameters missing in GA4 | 3 | Not mapped in tag's eventSettingsTable | Add parameter mapping in GTM tag config |
1. Run Tier 0 (automated)
PASS → proceed to publish GTM
FAIL → fix code or GTM config, re-run
2. Run Tier 1 (console) — optional manual spot-check
PASS → proceed
FAIL → fix before Tier 2
3. Run Tier 2 (GTM Preview)
PASS → proceed
FAIL → fix GTM tags/triggers/variables
4. Run Tier 3 (GA4 DebugView)
PASS → publish GTM container
FAIL → fix GA4 config or parameter mappings
5. Publish GTM → GTM UI > Submit > Publish version
6. Disable debug mode in production
7. Monitor GA4 Reports > Engagement > Eventsreferences/debugging-guide.mdreferences/test-checklist.mdexamples/sample.mdgtm-implementationgtm-analytics-auditgtm-reporting