Skip to content

Commit 0678462

Browse files
committed
Add tests for pii redaction
1 parent 0991b5f commit 0678462

6 files changed

Lines changed: 110 additions & 7 deletions

File tree

features/configuration.feature

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ Feature: A site can load the DAP code with varying levels of customization
77
Given DAP is configured for agency "HHS"
88
And DAP is configured for subagency "CDC"
99
When I load the test site
10-
Then DAP will set custom dimensions
10+
Then DAP will set custom dimensions for the DAP property
1111
| agency | HHS |
1212
| subagency | CDC |
1313

@@ -16,7 +16,7 @@ Feature: A site can load the DAP code with varying levels of customization
1616
And DAP is configured with site topic "Analytics"
1717
And DAP is configured with site platform "Cloud.gov"
1818
When I load the test site
19-
Then DAP will set custom dimensions
19+
Then DAP will set custom dimensions for the DAP property
2020
| agency | GSA |
2121
| site_topic | analytics |
2222
| site_platform | cloud.gov |

features/pii_redaction.feature

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
Feature: DAP redacts PII
2+
3+
Background:
4+
Given I load an empty browser
5+
And DAP is configured for agency "GSA"
6+
7+
Scenario: Email addresses are redacted from event parameters
8+
When I load the test site
9+
And I execute script "window.gas4('error', {type: 'form', url: 'user@example.com'})"
10+
Then a "error" event is sent to DAP with parameters
11+
| type | form |
12+
| url | [REDACTED_EMAIL] |
13+
14+
Scenario: SSNs are redacted from event parameters
15+
When I load the test site
16+
And I execute script "window.gas4('error', {type: 'form', url: '123-45-6789'})"
17+
Then a "error" event is sent to DAP with parameters
18+
| type | form |
19+
| url | [REDACTED_SSN] |
20+
21+
Scenario: Phone numbers are redacted from event parameters
22+
When I load the test site
23+
And I execute script "window.gas4('error', {type: 'form', url: '202-555-1234'})"
24+
Then a "error" event is sent to DAP with parameters
25+
| type | form |
26+
| url | [REDACTED_TEL] |
27+
28+
Scenario: Dates of birth are redacted from event parameters when labeled with dob=
29+
When I load the test site
30+
And I execute script "window.gas4('error', {type: 'form', url: 'dob=1990-01-15'})"
31+
Then a "error" event is sent to DAP with parameters
32+
| type | form |
33+
| url | [REDACTED_DOB] |
34+
35+
Scenario: Passwords are redacted from event parameters when labeled with password=
36+
When I load the test site
37+
And I execute script "window.gas4('error', {type: 'form', url: 'password=mysecret123'})"
38+
Then a "error" event is sent to DAP with parameters
39+
| type | form |
40+
| url | [REDACTED_PASSWORD] |
41+
42+
Scenario: ZIP codes are redacted from event parameters when labeled with zip=
43+
When I load the test site
44+
And I execute script "window.gas4('error', {type: 'form', url: 'zip=20001'})"
45+
Then a "error" event is sent to DAP with parameters
46+
| type | form |
47+
| url | [REDACTED_ZIP] |
48+
49+
Scenario: Non-PII data is not redacted from event parameters
50+
When I load the test site
51+
And I execute script "window.gas4('error', {type: 'validation', url: '/contact/submit'})"
52+
Then a "error" event is sent to DAP with parameters
53+
| type | validation |
54+
| url | /contact/submit |
55+
56+
Scenario: PII redaction applies to parameters in config calls
57+
Given the page URL has query parameter "search" set to "user@example.com"
58+
When I load the test site
59+
Then the config call for the DAP property has "page_location" containing "[REDACTED_EMAIL]"
60+
61+
Scenario: PII redaction applies to automatically collected events
62+
Given the page URL has query parameter "search" set to "user@example.com"
63+
And I set the browser to intercept outbound requests
64+
When I load the test site
65+
And I wait 5 seconds
66+
Then there is a GA4 request reporting event "page_view" with parameter "dl" containing "[REDACTED_EMAIL]"

features/support/step_definitions/browser_steps.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,21 @@ Then("there is a GA4 request", function () {
5757
expect(ga4Request).to.exist;
5858
});
5959

60+
Then("there is a GA4 request reporting event {string} with parameter {string} containing {string}", function (eventName, paramName, value) {
61+
const ga4Request = this.requests.find(request => {
62+
try {
63+
const url = new URL(request.url);
64+
return url.host === "www.google-analytics.com"
65+
&& url.pathname === "/g/collect"
66+
&& url.searchParams.has("en", eventName)
67+
&& url.searchParams.has(paramName) && url.searchParams.get(paramName).includes(value);
68+
} catch (e) {
69+
return false;
70+
}
71+
});
72+
expect(ga4Request).to.exist;
73+
});
74+
6075
Then("there are no unexpected requests", function () {
6176
const requestURLs = this.requests.map((request) => {
6277
return (new URL(request.url)).host;

features/support/step_definitions/dataLayer_steps.js

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,17 @@ import { Then } from "@cucumber/cucumber";
22
import * as chai from 'chai'
33
const expect = chai.expect;
44

5-
Then("DAP will set custom dimensions", async function (table) {
5+
Then("the config call for the DAP property has {string} containing {string}", async function (key, substring) {
66
const configCommand = await this.page.evaluate(() => {
7-
return window.dataLayer.find(item => item[0] === 'config');
7+
return window.dataLayer.find(item => item[0] === 'config' && item[1] === "G-9TNNMGP8WJ");
8+
});
9+
expect(configCommand["2"][key]).to.include(substring);
10+
});
11+
12+
Then("DAP will set custom dimensions for the DAP property", async function (table) {
13+
const configCommand = await this.page.evaluate(() => {
14+
return window.dataLayer.find(item => item[0] === 'config' && item[1] === "G-9TNNMGP8WJ");
815
});
9-
expect(configCommand["0"]).to.equal("config");
10-
expect(configCommand["1"]).to.equal("G-9TNNMGP8WJ");
1116
expect(configCommand["2"]).to.include(table.rowsHash());
1217
});
1318

@@ -40,3 +45,11 @@ Then("the file download is not reported to DAP", async function () {
4045
});
4146
expect(event).to.be.undefined;
4247
});
48+
49+
Then("a {string} event is sent to DAP with parameters", async function (eventName, table) {
50+
const event = await this.page.evaluate((name) => {
51+
return window.dataLayer.find(item => item[0] === 'event' && item[1] === name);
52+
}, eventName);
53+
expect(event).to.exist;
54+
expect(event["2"]).to.include(table.rowsHash());
55+
});

features/support/step_definitions/interaction_steps.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import { When } from "@cucumber/cucumber";
22

3+
When("I execute script {string}", async function (script) {
4+
await this.page.evaluate((s) => eval(s), script);
5+
});
6+
37
When("I click on a file to download it", async function () {
48
await this.page.locator('#internalDownload').click();
59
});

features/support/step_definitions/loading_steps.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ Given("DAP is configured with autotracking disabled", function () {
2626
this.dapConfig.autotracker = false;
2727
});
2828

29+
Given("the page URL has query parameter {string} set to {string}", function (key, value) {
30+
this.pageParams = this.pageParams || {};
31+
this.pageParams[key] = value;
32+
});
33+
2934
When("I load the test site", async function () {
30-
await this.page.goto(`http://localhost:8080?${this.dapConfig.toQueryParams()}`);
35+
await this.page.goto(`http://localhost:8080?${this.dapConfig.toQueryParams()}&${new URLSearchParams(this.pageParams).toString()}`);
3136
});

0 commit comments

Comments
 (0)