Skip to main content

How to Test GDPR Export, Delete, and Consent Flows

Short answer

GDPR and privacy flows fail on incomplete exports, partial delete cascades, stale consent after policy updates, and missing audit trails—not on whether a settings page has a delete button. Seed users with data across every entity type, probe export archives and post-delete absence everywhere PII can hide, and map scenarios to compliance matrices with requirement traceability.

Part of Testing Guides by UI patterns.

Who this is for

Teams shipping EU-facing SaaS, healthcare adjacency, HR platforms, or any product with data subject requests (DSAR), marketing consent banners, cookie preferences, and account deletion—especially multi-service architectures with warehouses and third-party processors.

Why testing GDPR privacy matters

Privacy bugs are regulatory incidents, not UX nitpicks:

  • Incomplete export (Art. 15) — ZIP missing analytics warehouse or support tickets; supervisory authority complaint.
  • Incomplete erasure (Art. 17) — Auth user deleted but S3 objects, search index, and backup snapshots retain PII.
  • Consent not provable (Art. 7) — Marketing emails without logged consent version and timestamp.
  • Withdrawal ignored — User opts out; CDP still syncs segments.
  • Retention violations — Scheduled delete job skips cold storage; GDPR retention schedules fail audit.

E2E must probe every storage class your ROPA documents—UI "account deleted" toast is insufficient.

Complexity map

ScenarioEdge caseWhy tests breakApproach
Export completenessMissing microservicePartial ZIPEntity checklist probe
Delete cascadeFK restrictOrphan PIIProbe all tables + blob
Async delete job202 acceptedPremature assertpoll job probe
Consent version bumpOld users grandfatheredWrong emailProbe consent_version
Marketing opt-outHubSpot sync lagStill emailedProbe integration queue
Cookie categoriesAnalytics loads before consentIllegal trackNetwork assert blocked
Child accountsFamily planParent vs childProbe scope per uid
Legal holdDelete blockedSilent failProbe hold flag + UI
Re-signup same emailGhost dataResidual PIIProbe clean slate
Processor subprocessorsThird-party APIRemote copyMock processor probe
Export formatJSON vs PDFMissing machine-readableParse archive manifest
Audit trailNo DSAR logCompliance failProbe audit_event

Seed user with cross-service footprint

await request.post('/api/test/seed-privacy-user', {
data: {
runId,
entities: ['profile', 'orders', 'uploads', 'support_tickets', 'analytics_events'],
},
});

Maintain entity manifest in repo matching Records of Processing Activities—tests drift when new service stores PII without manifest update.

Export completeness Assert

await page.goto('/settings/privacy');
await page.getByRole('button', { name: 'Download my data' }).click();
await expect(page.getByText(/preparing|email when ready/i)).toBeVisible();

await expect.poll(async () => {
const job = await request.get(`/api/test/dsar-export?runId=${runId}`).then(r => r.json());
return job.status;
}, { timeout: 120_000 }).toBe('completed');

expect(job.includedEntities).toEqual(expect.arrayContaining([
'profile', 'orders', 'uploads', 'support_tickets', 'analytics_events',
]));
expect(job.missingEntities).toHaveLength(0);

// Optional: fetch signed URL in test env and parse ZIP manifest
const manifest = await parseExportManifest(job.downloadUrl);
expect(manifest.files.map(f => f.entity)).toEqual(expect.arrayContaining(job.includedEntities));

Export completeness is the primary GDPR E2E bar—document every entity in manifest tests.

Delete cascade Assert

await page.getByRole('button', { name: 'Delete account' }).click();
await page.getByLabel('Type DELETE to confirm').fill('DELETE');
await page.getByRole('button', { name: 'Permanently delete' }).click();

await expect.poll(async () =>
(await request.get(`/api/test/privacy-erasure?runId=${runId}`)).json()
).toMatchObject({ status: 'completed', piiRemaining: [] });

// Spot probes
await expect(request.get(`/api/test/user-profile?runId=${runId}`)).resolves.toMatchObject({ status: 404 });
await expect(request.get(`/api/test/blob-exists?runId=${runId}`)).resolves.toMatchObject({ exists: false });
await expect(request.get(`/api/test/search-index?runId=${runId}`)).resolves.toMatchObject({ hits: 0 });
await request.post('/api/test/set-consent-policy', { data: { version: '2024-06-01' } });
await page.goto('/'); // banner
await page.getByRole('button', { name: 'Accept all' }).click();
const consent = await request.get(`/api/test/consent?runId=${runId}`).then(r => r.json());
expect(consent.version).toBe('2024-06-01');
expect(consent.marketing).toBe(true);

// Policy update requires re-consent
await request.post('/api/test/set-consent-policy', { data: { version: '2024-07-01' } });
await page.reload();
await expect(page.getByRole('dialog')).toContainText(/updated policy/i);

Link scenarios to compliance controls via requirement traceability and // @Scenario: in SmartTests for SOC2/GDPR matrices.

Requirement slices to cover

  • consent_version — policy id accepted
  • data_request_type — export, delete, restrict, object

When TrueCoverage shows prod delete requests rising but tests only cover export, run /testchimp evolve.

CI checklist

  1. Entity manifest reviewed quarterly with eng + legal
  2. Export job polled to completion—never assert button click only
  3. Delete probes all known PII stores (DB, blob, search, cache)
  4. Consent version bump spec after policy change
  5. No real user PII in fixtures—synthetic runId users only
  6. Audit log probe for DSAR events (audit guide)

Anti-patterns

Anti-patternWhy it failsBetter approach
Delete Auth user onlyFirestore/S3 orphanCascade checklist
Export UI click onlyEmpty ZIP untestedPoll job + manifest
Skip marketing integrationsStill syncedQueue probe
Hard-delete in prod CIIllegalIsolated test project
Assert email sent onlyAttachment incompleteParse archive
No audit entryAudit failProbe dsar_requested

Example scenario

Situation: EU user requests full data export then account deletion after download.

Expected outcome: Export ZIP contains all manifest entities; post-delete zero PII probes; DSAR audit complete.

Why UI-only automation breaks: Export email sent but ZIP missing support tickets; delete removes profile but S3 avatars remain.

  1. Arrange: Seed privacy-user across five entity types for runId.
  2. Act: Request export, wait for job, download; then delete account with confirmation.
  3. Assert: Export manifest complete; erasure probe piiRemaining=[]; audit has export+delete events.

TestChimp workflow: Track data_request_type in prod; evolve delete cascade when new storage service ships.

Same Arrange/Act/Assert pattern as expired-coupon checkout.

Connect scenarios to your QA workflow

Capture business rules in markdown test plans and enforce them with seed routes and probe Assert. Link SmartTests with // @Scenario: for requirement traceability. Use /testchimp test on PRs; /testchimp explore on SmartTest paths for non-functional gaps (ExploreChimp).

External references

Frequently asked questions

Are all data_request_types covered by scenarios?

Map export, delete, restrict, and object to SmartTests with // @Scenario: ids in markdown compliance matrices. Use TrueCoverage data_request_type prod vs test to find gaps—run /testchimp evolve when delete rises in prod.

How do I prove export completeness?

Maintain entity manifest matching ROPA; poll DSAR job; assert includedEntities and parse ZIP manifest—never trust email sent alone.

What does delete cascade must include?

Every store with PII: primary DB, blobs, search indexes, caches, analytics identities, third-party processor test doubles—probe each via test routes.

How do consent version updates work in E2E?

Bump policy version in Arrange, assert banner reappears, accept, probe stored consent_version and marketing flags match policy.

Can I run GDPR tests against production?

Never destructive deletes in prod. Use isolated test tenant with synthetic users and processor sandboxes only.

How do audit logs relate to DSAR?

Probe audit_event_type dsar_export_requested and account_deleted with actor self-service—required for enterprise compliance vertical.

HIPAA vs GDPR in tests?

GDPR focuses export/delete completeness; HIPAA adds minimum necessary and PHI bans in logs—see healthcare guide for clinical data fixtures.

Apply these patterns in your repo

Run `/testchimp init` to connect TestChimp to your repo, then `/testchimp test` on PRs to turn these patterns into maintained SmartTests. Use `/testchimp evolve` when you want to expand coverage as your app grows.

Start free on TestChimp · Book a demo