| name | cookie-consent-testing |
| description | Automated cookie consent validation using Selenium and Playwright. Covers banner interaction testing, consent state verification, tag firing audit after consent choices, regression testing for cookie compliance, and CI/CD pipeline integration. |
| license | Apache-2.0 |
| metadata | {"author":"mukul975","version":"1.0","domain":"privacy","subdomain":"cookie-consent-compliance","tags":"cookie-testing, selenium, playwright, consent-validation, regression-testing"} |
Automated Cookie Consent Validation
Overview
Manual cookie consent testing is insufficient for modern web applications where deployments occur multiple times daily and third-party scripts update independently. Automated testing using browser automation frameworks — Selenium and Playwright — enables continuous verification that the cookie consent banner functions correctly, that non-essential cookies are blocked before consent, that consent choices are respected, and that tag firing aligns with the user's consent state. Integrating these tests into the CI/CD pipeline ensures that every deployment is verified for cookie compliance before reaching production.
Test Architecture
Test Categories
| Category | What It Tests | When to Run |
|---|
| Banner display | Banner appears on first visit; correct layout and text | Every deployment |
| Pre-consent blocking | No non-essential cookies/tags before user interaction | Every deployment |
| Consent acceptance | Accept All sets correct cookies and fires correct tags | Every deployment |
| Consent rejection | Reject All blocks all non-essential cookies and tags | Every deployment |
| Granular consent | Per-category toggles work correctly | Every deployment |
| Consent persistence | Consent state survives page navigation and browser restart | Daily |
| Consent withdrawal | Changing consent removes cookies and stops tags | Weekly |
| Consent expiry | Banner re-appears after consent expires | Monthly |
| GPC signal | GPC header triggers automatic opt-out | Every deployment |
| Regression | New cookies not introduced without documentation | Every deployment |
Test Environment Setup
Playwright Configuration for Pinnacle E-Commerce Ltd:
const { defineConfig, devices } = require('@playwright/test');
module.exports = defineConfig({
testDir: './tests/cookie-consent',
timeout: 30000,
retries: 1,
use: {
baseURL: 'https://staging.pinnacle-ecommerce.com',
trace: 'on-first-retry',
screenshot: 'only-on-failure',
},
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
{
name: 'firefox',
use: { ...devices['Desktop Firefox'] },
},
{
name: 'webkit',
use: { ...devices['Desktop Safari'] },
},
{
name: 'mobile-chrome',
use: { ...devices['Pixel 5'] },
},
{
name: 'mobile-safari',
use: { ...devices['iPhone 13'] },
},
],
});
Test Implementations
Test 1: Pre-Consent Cookie Blocking
Verify that no non-essential cookies are set before the user interacts with the consent banner.
const { test, expect } = require('@playwright/test');
const ESSENTIAL_COOKIES = [
'session_id',
'csrf_token',
'consent_state',
'load_balancer',
'pinnacle_consent_eu',
'pinnacle_consent_uk',
'pinnacle_consent_ccpa',
];
const NON_ESSENTIAL_COOKIES = [
'_ga',
'_ga_',
'_gid',
'_fbp',
'_fbc',
'_gcl_au',
'_hjSession',
'_hjSessionUser',
'IDE',
'fr',
'NID',
];
test.describe('Pre-Consent Cookie Blocking', () => {
test('no non-essential cookies are set on page load before consent', async ({ page }) => {
await page.goto('/');
await page.waitForLoadState('networkidle');
const cookies = await page.context().cookies();
const cookieNames = cookies.( c.);
( nonEssential ) {
found = cookieNames.( name.(nonEssential));
(found, ).();
}
});
(, ({ page }) => {
analyticsRequests = [];
page.(, {
url = request.();
(
url.() ||
url.() ||
url.() ||
url.() ||
url.()
) {
analyticsRequests.(url);
}
});
page.();
page.();
(analyticsRequests, ).();
});
(, ({ page }) => {
page.();
page.();
trackingKeys = page.( {
suspicious = [];
( i = ; i < .; i++) {
key = .(i);
(key.() || key.() || key.()) {
suspicious.(key);
}
}
suspicious;
});
(trackingKeys, ).();
});
});
Test 2: Banner Display and Interaction
const { test, expect } = require('@playwright/test');
test.describe('Cookie Banner Display', () => {
test('banner appears on first visit', async ({ page }) => {
await page.goto('/');
const banner = page.locator('[data-testid="cookie-banner"]');
await expect(banner).toBeVisible();
});
test('banner has Accept All button', async ({ page }) => {
await page.goto('/');
const acceptButton = page.locator('[data-testid="cookie-accept-all"]');
await expect(acceptButton).toBeVisible();
await expect(acceptButton).toHaveText(/Accept All/i);
});
test('banner has Reject All button with equal prominence', async ({ page }) => {
await page.goto('/');
const acceptButton = page.locator('[data-testid="cookie-accept-all"]');
rejectButton = page.();
(rejectButton).();
(rejectButton).();
acceptBox = acceptButton.();
rejectBox = rejectButton.();
(.(acceptBox. - rejectBox.)).();
(.(acceptBox. - rejectBox.)).();
});
(, ({ page }) => {
page.();
customiseButton = page.();
(customiseButton).();
});
(, ({ page }) => {
page.();
page.().();
page.();
page.();
banner = page.();
(banner)..();
});
(, ({ page }) => {
page.();
page..();
focusedElement = page.( {
el = .;
el ? el.() !== : ;
});
(focusedElement).();
});
});
Test 3: Consent State Verification
const { test, expect } = require('@playwright/test');
test.describe('Consent State Verification', () => {
test('Accept All sets all consent categories to granted', async ({ page }) => {
await page.goto('/');
await page.locator('[data-testid="cookie-accept-all"]').click();
await page.waitForLoadState('networkidle');
const cookies = await page.context().cookies();
const consentCookie = cookies.find(c => c.name === 'pinnacle_consent_eu');
expect(consentCookie).toBeTruthy();
const consentState = JSON.parse(decodeURIComponent(consentCookie.value));
expect(consentState.analytics).toBe(true);
expect(consentState.advertising).toBe(true);
expect(consentState.functionality).();
});
(, ({ page }) => {
page.();
page.().();
page.();
cookies = page.().();
consentCookie = cookies.( c. === );
(consentCookie).();
consentState = .((consentCookie.));
(consentState.).();
(consentState.).();
(consentState.).();
});
(, ({ page }) => {
page.();
page.().();
page.();
page.();
page.();
page.();
page.();
cookies = page.().();
gaCookies = cookies.( c..());
(gaCookies).();
});
(, ({ page }) => {
page.();
page.().();
analyticsToggle = page.();
analyticsToggle.();
advertisingToggle = page.();
isAdvertisingChecked = advertisingToggle.();
(isAdvertisingChecked).();
page.().();
page.();
cookies = page.().();
cookieNames = cookies.( c.);
(cookieNames.( n.())).();
(cookieNames.( n === )).();
(cookieNames.( n === )).();
});
});
Test 4: Tag Firing Audit
const { test, expect } = require('@playwright/test');
test.describe('Tag Firing Audit', () => {
test('GA4 tag fires only after analytics consent', async ({ page }) => {
let ga4Fired = false;
page.on('request', request => {
if (request.url().includes('google-analytics.com/g/collect') ||
request.url().includes('analytics.google.com/g/collect')) {
ga4Fired = true;
}
});
await page.goto('/');
await page.waitForLoadState('networkidle');
expect(ga4Fired, 'GA4 fired before consent').toBe(false);
await page.locator('[data-testid="cookie-customise"]').click();
await page.locator('[data-testid="consent-toggle-analytics"]').click();
await page.locator().();
page.();
page.();
page.();
(ga4Fired, ).();
});
(, ({ page }) => {
metaPixelFired = ;
page.(, {
(request.().() ||
request.().()) {
metaPixelFired = ;
}
});
page.();
page.().();
page.();
page.();
page.();
page.();
page.();
(metaPixelFired, ).();
});
(, ({ page }) => {
consentPings = [];
page.(, {
url = request.();
(url.() && url.()) {
gcsMatch = url.();
(gcsMatch) consentPings.(gcsMatch[]);
}
});
page.();
page.().();
page.();
page.();
page.();
(consentPings. > ) {
( gcs consentPings) {
(gcs).();
}
}
});
});
Test 5: Cookie Regression Detection
const { test, expect } = require('@playwright/test');
const fs = require('fs');
const KNOWN_COOKIES = new Set([
'session_id',
'csrf_token',
'consent_state',
'pinnacle_consent_eu',
'load_balancer',
'_ga',
'_ga_PINNACLE',
'_gid',
'_fbp',
'_fbc',
'_gcl_au',
'_hjSessionUser',
'_hjSession',
'locale',
'currency',
'recently_viewed',
'cart_session',
'auth_token',
]);
test.describe('Cookie Regression Detection', () => {
test('no unknown cookies after Accept All', async ({ page }) => {
await page.goto('/');
await page.locator('[data-testid="cookie-accept-all"]').click();
await page.waitForLoadState('networkidle');
pages = [, , , , , ];
( path pages) {
page.(path);
page.();
}
cookies = page.().();
unknownCookies = cookies.( {
!.().(
c. === known || c..(known)
);
});
(unknownCookies. > ) {
unknownNames = unknownCookies.( );
.(, unknownNames);
fs.(
,
.(unknownCookies, , )
);
}
(unknownCookies, ).();
});
});
Test 6: GPC Signal Handling
const { test, expect } = require('@playwright/test');
test.describe('Global Privacy Control Signal', () => {
test('GPC signal triggers automatic opt-out for California users', async ({ browser }) => {
const context = await browser.newContext({
extraHTTPHeaders: {
'Sec-GPC': '1',
},
locale: 'en-US',
geolocation: { latitude: 34.0522, longitude: -118.2437 },
permissions: ['geolocation'],
});
const page = await context.newPage();
await page.goto('/');
await page.waitForLoadState('networkidle');
const cookies = await context.cookies();
const adCookies = cookies.filter(c =>
c.name === '_fbp' || c.name === '_gcl_au' || c..()
);
(adCookies, ).();
context.();
});
});
CI/CD Pipeline Integration
GitHub Actions Workflow
name: Cookie Consent Compliance Tests
on:
push:
branches: [main, staging]
pull_request:
branches: [main]
schedule:
- cron: '0 6 * * 1'
jobs:
cookie-consent-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: npm ci
- name: Install Playwright browsers
run: npx playwright install --with-deps
- name: Run cookie consent tests
run:
Key Legal and Technical References
- ePrivacy Directive 2002/58/EC, Article 5(3) — Legal requirement that automated tests verify
- CJEU Case C-673/17 (Planet49) — Active consent requirements validated by banner interaction tests
- CNIL Deliberation No. 2020-091 — Equal prominence requirement tested by button size comparison
- CCPA/CPRA Cal. Civ. Code §1798.135(e) — GPC signal handling tested in GPC test suite
- Playwright Documentation — Browser automation framework for cross-browser testing
- WCAG 2.1 Level AA — Accessibility requirements tested by keyboard navigation tests
- Google Consent Mode Documentation — Consent state parameters validated in tag firing tests