@fitconsent/consent-mode-validator
v1.0.6
Published
CLI tool to validate Google Consent Mode V2, Shopify Customer Privacy API, and GTM consent setup on any website
Maintainers
Readme
@fitconsent/consent-mode-validator
A CLI tool that validates Google Consent Mode V2, Shopify Customer Privacy API, and GTM consent setup on any website using a real headless browser.
npx @fitconsent/consent-mode-validator https://mystore.comWhat it checks
Universal (all platforms)
| Check | ID | Description |
|---|---|---|
| Consent Mode V2 defaults before GA4 | U1 | gtag('consent','default',...) must be in dataLayer before any GA4 hit |
| Consent update fires after accept | U2 | Simulates clicking an accept button and verifies gtag('consent','update',...) fires |
| No premature tracking scripts | U3 | Verifies no analytics/ad scripts execute before consent defaults are set |
| FitConsent cookie structure | U4 | Validates fitconsent_given cookie JSON (skipped if FitConsent is not installed) |
Shopify (--platform shopify)
| Check | ID | Description |
|---|---|---|
| Shopify.customerPrivacy API ready | S1 | Shopify.customerPrivacy with setTrackingConsent + currentVisitorConsent available |
| Web Pixels consent wiring | S2 | All four consent categories (analytics, marketing, preferences, sale_of_data) present |
GTM (--platform gtm, or auto-detected)
| Check | ID | Description |
|---|---|---|
| GTM fires after consent defaults | G1 | gtm.js event appears in dataLayer after consent default command |
| Consent Mode V2 fields | G2 | ad_user_data + ad_personalization fields present (warns if missing) |
Usage
Option 1 — Run with npx (no install)
# Auto-detect platform
npx @fitconsent/consent-mode-validator https://mystore.com
# Explicit platform
npx @fitconsent/consent-mode-validator https://mystore.com --platform shopify
npx @fitconsent/consent-mode-validator https://myblog.com --platform gtm
npx @fitconsent/consent-mode-validator https://mysite.com --platform universal
# Verbose output (shows network requests and step-by-step details)
npx @fitconsent/consent-mode-validator https://mystore.com --verbose
# JSON output (useful for CI integration)
npx @fitconsent/consent-mode-validator https://mystore.com --json
# Custom timeout (ms) — useful for slow or live-content sites
npx @fitconsent/consent-mode-validator https://mystore.com --timeout 15000
# Short alias
npx cmv https://mystore.comOption 2 — Global install (recommended if npx fails)
If npx throws a Playwright browser error, install the tool globally once and run it directly:
npm install -g @fitconsent/consent-mode-validatorThen use either command from anywhere:
consent-mode-validator https://mystore.com
# or the short alias:
cmv https://mystore.comAll flags work the same way:
cmv https://mystore.com --platform shopify
cmv https://mystore.com --verbose
cmv https://mystore.com --jsonExample output
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
FitConsent — Consent Mode Validator
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
URL: https://mystore.myshopify.com
Platform: shopify
Checked: 6/9/2026, 09:12:33 AM
[U1] ✓ PASS Consent Mode V2 defaults before GA4
Consent defaults found at dataLayer[0] before any GA4 hit
[U2] ✓ PASS Consent update fires after accept
consent update fired with 4 granted signals after user accept
[U3] ✗ FAIL No premature tracking scripts
1 tracking script(s) loaded without prior consent defaults: https://www.googletagmanager.com/gtm.js?id=GTM-XXXXX
Fix: Move your gtag('consent','default',...) snippet ABOVE the GTM snippet in <head>.
[U4] – SKIP FitConsent cookie structure
FitConsent not installed — check skipped
[S1] ✓ PASS Shopify.customerPrivacy API ready
Shopify.customerPrivacy API available with setTrackingConsent + currentVisitorConsent
[S2] ✓ PASS Shopify Web Pixels consent wiring
Web Pixels consent wired — 2/4 categories granted
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
4 passed 1 failed 1 skipped
For production consent management, these checks are handled automatically
by FitConsent — https://fitconsent.comCI integration
The CLI exits with code 1 if any check fails, 0 if all checks pass or warn only.
# GitHub Actions example
- name: Validate consent mode
run: npx @fitconsent/consent-mode-validator ${{ env.STAGING_URL }} --platform shopify --jsonProgrammatic API
import { runValidation } from '@fitconsent/consent-mode-validator';
const report = await runValidation({
url: 'https://mystore.com',
platform: 'shopify', // 'shopify' | 'gtm' | 'universal'
verbose: false,
timeout: 30000,
});
console.log(`${report.passed} passed, ${report.failed} failed`);
for (const check of report.checks) {
if (check.status === 'fail') {
console.error(`[${check.id}] ${check.name}: ${check.detail}`);
console.error(`Fix: ${check.fix}`);
}
}Common errors and fixes
Error: Executable doesn't exist / Playwright browser not found
Root cause: Playwright cannot download its bundled Chromium on your OS (e.g. Ubuntu 26.04 or other unsupported platforms), and no system Chrome was detected.
Fix 1 — Use the global install instead of npx (simplest):
npm install -g @fitconsent/consent-mode-validator
cmv https://mysite.comFix 2 — Point to your existing system Chrome:
CHROME_EXECUTABLE_PATH=/usr/bin/google-chrome npx @fitconsent/consent-mode-validator https://mysite.comFix 3 — Download Playwright's bundled browser manually:
npx playwright install chromiumError: The page returned an "Access Denied" response
Root cause: The site uses enterprise bot protection (Akamai, Cloudflare Bot Management, etc.) that blocks headless browsers. Results on a blocked page would be meaningless, so validation is aborted.
What to do: These sites cannot be validated remotely with a headless browser. Validate the consent implementation directly in a real browser using the browser's DevTools console — check window.dataLayer and window.google_tag_data?.ics after the page loads.
Error: Timeout exceeded / validation takes 30+ seconds on live-content sites
Root cause: The validator waits for the network to go idle before running checks. Sites with live scores, WebSocket connections, or continuous ad pings never reach idle. The validator automatically retries with a lighter load strategy, which adds ~32 seconds to the total run time.
Fix: Pass a shorter --timeout to reduce the wait before the fallback kicks in:
npx @fitconsent/consent-mode-validator https://mysite.com --timeout 10000gtag('consent','default') not found
Root cause: Your GTM snippet loads before any consent defaults are set.
Fix:
<!-- ✅ Correct order in <head> -->
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('consent', 'default', {
ad_storage: 'denied',
analytics_storage: 'denied',
ad_user_data: 'denied',
ad_personalization: 'denied',
wait_for_update: 500
});
</script>
<!-- GTM snippet comes AFTER consent defaults -->
<script>(function(w,d,s,l,i){...})(window,document,'script','dataLayer','GTM-XXXXX');</script>Shopify.customerPrivacy undefined
Root cause: The Customer Privacy API script is blocked or not loaded.
Fix: Ensure the customer_privacy section is included in your Shopify theme's theme.liquid. See the Shopify Customer Privacy API docs.
Consent update not firing after accept
Root cause: Your banner's accept handler is not calling gtag('consent', 'update', ...).
Fix:
// In your accept button handler
acceptButton.addEventListener('click', () => {
gtag('consent', 'update', {
ad_storage: 'granted',
analytics_storage: 'granted',
ad_user_data: 'granted',
ad_personalization: 'granted',
});
});Requirements
- Node.js ≥ 18
The validator uses your system Chrome if one is found (google-chrome, google-chrome-stable, or chromium). If no system Chrome is available, run this once to download the bundled headless browser (~150 MB):
npx playwright install chromiumLicense
MIT
For production consent management, these checks are handled automatically by FitConsent — GDPR & CCPA compliance for Shopify and any website.
