Install
$ agentstack add skill-mukul975-privacy-data-protection-skills-cookie-consent-testing ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ● Filesystem access Used
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
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:
// playwright.config.js
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.
// tests/cookie-consent/pre-consent-blocking.spec.js
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 }) => {
// Navigate to homepage without any prior consent
await page.goto('/');
// Wait for page to fully load including all third-party scripts
await page.waitForLoadState('networkidle');
// Get all cookies
const cookies = await page.context().cookies();
const cookieNames = cookies.map(c => c.name);
// Verify no non-essential cookies exist
for (const nonEssential of NON_ESSENTIAL_COOKIES) {
const found = cookieNames.filter(name => name.startsWith(nonEssential));
expect(found, `Non-essential cookie ${nonEssential} found before consent`).toHaveLength(0);
}
});
test('no analytics network requests before consent', async ({ page }) => {
const analyticsRequests = [];
// Monitor network requests
page.on('request', request => {
const url = request.url();
if (
url.includes('google-analytics.com') ||
url.includes('analytics.google.com') ||
url.includes('facebook.com/tr') ||
url.includes('connect.facebook.net') ||
url.includes('hotjar.com')
) {
analyticsRequests.push(url);
}
});
await page.goto('/');
await page.waitForLoadState('networkidle');
expect(analyticsRequests, 'Analytics requests fired before consent').toHaveLength(0);
});
test('no localStorage tracking entries before consent', async ({ page }) => {
await page.goto('/');
await page.waitForLoadState('networkidle');
const trackingKeys = await page.evaluate(() => {
const suspicious = [];
for (let i = 0; i {
test('banner appears on first visit', async ({ page }) => {
await page.goto('/');
// Banner should be visible
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"]');
const rejectButton = page.locator('[data-testid="cookie-reject-all"]');
await expect(rejectButton).toBeVisible();
await expect(rejectButton).toHaveText(/Reject All/i);
// Verify equal visual prominence (same size)
const acceptBox = await acceptButton.boundingBox();
const rejectBox = await rejectButton.boundingBox();
expect(Math.abs(acceptBox.width - rejectBox.width)).toBeLessThan(10);
expect(Math.abs(acceptBox.height - rejectBox.height)).toBeLessThan(5);
});
test('banner has Customise/Manage Preferences option', async ({ page }) => {
await page.goto('/');
const customiseButton = page.locator('[data-testid="cookie-customise"]');
await expect(customiseButton).toBeVisible();
});
test('banner does not reappear after making a choice', async ({ page }) => {
await page.goto('/');
await page.locator('[data-testid="cookie-accept-all"]').click();
// Navigate to another page
await page.goto('/products');
await page.waitForLoadState('networkidle');
const banner = page.locator('[data-testid="cookie-banner"]');
await expect(banner).not.toBeVisible();
});
test('banner is keyboard accessible', async ({ page }) => {
await page.goto('/');
await page.keyboard.press('Tab');
// Focus should be within the banner
const focusedElement = await page.evaluate(() => {
const el = document.activeElement;
return el ? el.closest('[data-testid="cookie-banner"]') !== null : false;
});
expect(focusedElement).toBe(true);
});
});
Test 3: Consent State Verification
// tests/cookie-consent/consent-state.spec.js
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).toBe(true);
});
test('Reject All sets all consent categories to denied', async ({ page }) => {
await page.goto('/');
await page.locator('[data-testid="cookie-reject-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(false);
expect(consentState.advertising).toBe(false);
expect(consentState.functionality).toBe(false);
});
test('Reject All blocks GA4 cookies', async ({ page }) => {
await page.goto('/');
await page.locator('[data-testid="cookie-reject-all"]').click();
await page.waitForLoadState('networkidle');
// Navigate to multiple pages to give GA4 time to attempt cookie setting
await page.goto('/products');
await page.waitForLoadState('networkidle');
await page.goto('/about');
await page.waitForLoadState('networkidle');
const cookies = await page.context().cookies();
const gaCookies = cookies.filter(c => c.name.startsWith('_ga'));
expect(gaCookies).toHaveLength(0);
});
test('Granular consent: analytics only sets only analytics cookies', async ({ page }) => {
await page.goto('/');
// Open customisation layer
await page.locator('[data-testid="cookie-customise"]').click();
// Enable only analytics
const analyticsToggle = page.locator('[data-testid="consent-toggle-analytics"]');
await analyticsToggle.click();
// Ensure advertising is off
const advertisingToggle = page.locator('[data-testid="consent-toggle-advertising"]');
const isAdvertisingChecked = await advertisingToggle.isChecked();
expect(isAdvertisingChecked).toBe(false);
// Confirm choices
await page.locator('[data-testid="cookie-confirm-choices"]').click();
await page.waitForLoadState('networkidle');
const cookies = await page.context().cookies();
const cookieNames = cookies.map(c => c.name);
// GA cookies should be present
expect(cookieNames.some(n => n.startsWith('_ga'))).toBe(true);
// Advertising cookies should NOT be present
expect(cookieNames.some(n => n === '_fbp')).toBe(false);
expect(cookieNames.some(n => n === '_gcl_au')).toBe(false);
});
});
Test 4: Tag Firing Audit
// tests/cookie-consent/tag-firing.spec.js
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;
}
});
// Load page — GA4 should not fire
await page.goto('/');
await page.waitForLoadState('networkidle');
expect(ga4Fired, 'GA4 fired before consent').toBe(false);
// Accept analytics
await page.locator('[data-testid="cookie-customise"]').click();
await page.locator('[data-testid="consent-toggle-analytics"]').click();
await page.locator('[data-testid="cookie-confirm-choices"]').click();
await page.waitForLoadState('networkidle');
// Navigate to trigger a pageview
await page.goto('/products');
await page.waitForLoadState('networkidle');
expect(ga4Fired, 'GA4 did not fire after analytics consent').toBe(true);
});
test('Meta Pixel does not fire when advertising is rejected', async ({ page }) => {
let metaPixelFired = false;
page.on('request', request => {
if (request.url().includes('facebook.com/tr') ||
request.url().includes('connect.facebook.net')) {
metaPixelFired = true;
}
});
await page.goto('/');
await page.locator('[data-testid="cookie-reject-all"]').click();
await page.waitForLoadState('networkidle');
await page.goto('/products');
await page.waitForLoadState('networkidle');
await page.goto('/products/widget-pro');
await page.waitForLoadState('networkidle');
expect(metaPixelFired, 'Meta Pixel fired after reject all').toBe(false);
});
test('Google Consent Mode sends correct consent state', async ({ page }) => {
const consentPings = [];
page.on('request', request => {
const url = request.url();
if (url.includes('google-analytics.com') && url.includes('gcs=')) {
const gcsMatch = url.match(/gcs=([^&]+)/);
if (gcsMatch) consentPings.push(gcsMatch[1]);
}
});
await page.goto('/');
await page.locator('[data-testid="cookie-reject-all"]').click();
await page.waitForLoadState('networkidle');
// Navigate to trigger consent mode ping
await page.goto('/products');
await page.waitForLoadState('networkidle');
// If consent mode pings are sent, verify denied state
if (consentPings.length > 0) {
for (const gcs of consentPings) {
// G100 = all denied, G111 = all granted
expect(gcs).toContain('100');
}
}
});
});
Test 5: Cookie Regression Detection
// tests/cookie-consent/regression.spec.js
const { test, expect } = require('@playwright/test');
const fs = require('fs');
// Baseline of known cookies — update when new cookies are intentionally added
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');
// Visit several pages to trigger all tag scenarios
const pages = ['/', '/products', '/products/widget-pro', '/cart', '/about', '/blog'];
for (const path of pages) {
await page.goto(path);
await page.waitForLoadState('networkidle');
}
const cookies = await page.context().cookies();
const unknownCookies = cookies.filter(c => {
// Check if cookie name matches any known prefix
return !Array.from(KNOWN_COOKIES).some(known =>
c.name === known || c.name.startsWith(known)
);
});
if (unknownCookies.length > 0) {
const unknownNames = unknownCookies.map(c => `${c.name} (domain: ${c.domain}, expires: ${c.expires})`);
console.error('Unknown cookies detected:', unknownNames);
// Write to report file for review
fs.writeFileSync(
'test-results/unknown-cookies.json',
JSON.stringify(unknownCookies, null, 2)
);
}
expect(unknownCookies, `Unknown cookies found: ${unknownCookies.map(c => c.name).join(', ')}`).toHaveLength(0);
});
});
Test 6: GPC Signal Handling
// tests/cookie-consent/gpc-signal.spec.js
const { test, expect } = require('@playwright/test');
test.describe('Global Privacy Control Signal', () => {
test('GPC signal triggers automatic opt-out for California users', async ({ browser }) => {
// Create context with GPC header
const context = await browser.newContext({
extraHTTPHeaders: {
'Sec-GPC': '1',
},
locale: 'en-US',
geolocation: { latitude: 34.0522, longitude: -118.2437 }, // Los Angeles
permissions: ['geolocation'],
});
const page = await context.newPage();
await page.goto('/');
await page.waitForLoadState('networkidle');
// Verify advertising cookies are not set
const cookies = await context.cookies();
const adCookies = cookies.filter(c =>
c.name === '_fbp' || c.name === '_gcl_au' || c.name.startsWith('IDE')
);
expect(adCookies, 'Advertising coo
…
## Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- **Author:** [mukul975](https://github.com/mukul975)
- **Source:** [mukul975/Privacy-Data-Protection-Skills](https://github.com/mukul975/Privacy-Data-Protection-Skills)
- **License:** Apache-2.0
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.