Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 121 additions & 0 deletions documentation/prioritized-test-plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# Prioritized Test Plan for `Universal-Federated-Analytics.js`

## Priority 1

These cover the highest-value public behavior with the least setup risk.

1. `gas()` legacy API
- `gas('send','pageview', ...)` emits `page_view`
- `gas('send','pageview', ..., title)` uses explicit title
- `gas('send','event', ...)` emits `dap_event`
- `gas('send','event', ...)` defaults `event_value` to `0` when missing or invalid
- `gas('send','event', ..., nonInteraction=true)` maps `non_interaction`
- invalid `gas()` calls do not emit unexpected events

2. `gas4()` core helper coverage
- `gas4('page_view', {...})` emits `page_view`
- unsupported `gas4()` name falls back to `dap_event`
- supported event with empty params object still emits event
- `gas4()` with malformed args does not emit
- `gas4('view_search_results', ...)` explicit call emits expected event

3. Autotracker link classifications
- internal `mailto:` emits `email_click`
- external `mailto:` emits `email_click`
- formatted telephone link emits `telephone_click`
- invalid or ignored telephone link does not emit
- generic external link emits `click`
- social share link emits `share`

4. Existing config behavior hardening
- agency-only config sets expected defaults
- omitted site topic/platform fall back to `unspecified:<domain>`
- autotracker toggle off suppresses tracked click classes beyond downloads

## Priority 2

These cover important user-visible functionality but need a bit more page interaction logic.

1. `gas4()` event family matrix
- `social_click`
- `share`
- `navigation_click`
- `accordion_click`
- `faq_click`
- `cta_click`
- `content_view`
- `sort`
- `filter`
- `error`
- `was_this_helpful_submit`

2. Parameter-shape resilience
- “incorrect parameter” examples still emit the supported event name
- emitted payload preserves only provided keys
- invalid event name plus valid payload falls back to `dap_event`

3. Search and querystring handling
- page URL with search param emits `view_search_results`
- search term is preserved in event payload
- non-search query params are scrubbed from tracked page location
- allowlisted agency query params remain in tracked page location
- disallowed query params are removed

4. Dynamic DOM tracking
- dynamically inserted links are tracked by autotracker
- dynamic downloadable link emits `file_download`

## Priority 3

These are valuable but slower, more brittle, or likely to need harness work.

1. HTML5 media tracking
- video start
- video pause
- video progress milestone
- video complete
- audio start/pause/progress/complete if applicable on page

2. YouTube tracking
- YouTube enabled loads tracker and emits `video_start`
- emits `video_play`
- emits `video_pause`
- emits `video_progress`
- emits `video_complete`
- handles player error with `video_error`

3. Environment/config toggles
- `youtube=true` enables YouTube tracking
- `htmlvideo=false` suppresses HTML5 media tracking
- `webvitals=true` or homepage conditions inject/report vitals behavior
- dev env switches GA property ID as expected
- production/staging query handling stays correct

4. Parallel/custom-dimension variants
- parallel tracker custom dimension mapping
- alternate dimension names via query params
- script source / protocol / hostname dimensions present when expected

## Priority 4

These are lower ROI or better suited to unit-style tests around pure helpers.

1. URL/PII sanitization internals
- scrub email-like values from URLs
- scrub phone-like values
- nested querystring redaction
- allowed-querystring merging behavior
- object-to-query / query-to-object round trips where relied upon

2. Defensive/error-tolerance paths
- malformed URLs do not break tracking
- missing `dataLayer` initialization path recovers
- bad media state changes do not throw
- missing banner element is harmless

## Recommended Rollout

1. Add `10-15` Priority 1 scenarios first.
2. Add `10-12` Priority 2 scenarios next.
3. Decide whether media/video coverage belongs in Cucumber or a thinner harness.
4. Cover the remaining helper/sanitization logic with lower-level tests if possible.
54 changes: 54 additions & 0 deletions features/autotracker_links.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
Feature: Autotracker reports supported non-download link interactions

Background:
Given I load an empty browser
And DAP is configured for agency "GSA"

Scenario: Autotracker reports external mail links
Given DAP is configured with autotracking enabled
When I load the test site
And I click link 1 with text "mailto:test@domain.com" in the "Email Click" section
Then the dataLayer contains the event "email_click"
| link_url | [REDACTED_EMAIL] |
| link_domain | domain.com |
| link_text | mailto:[REDACTED_EMAIL] |
| outbound | true |
| interaction_type | Mouse Click |

Scenario: Autotracker reports formatted telephone links
Given DAP is configured with autotracking enabled
When I load the test site
And I click link 1 with text "Telephone +1437-925-1855" in the "Telephone Click" section
Then the dataLayer contains the event "telephone_click"
| link_url | [REDACTED_TEL] |
| link_text | Telephone [REDACTED_TEL] |
| interaction_type | Mouse Click |

Scenario: Autotracker reports generic external clicks
Given DAP is configured with autotracking enabled
When I load the test site
And I click link 1 with text "http://www.gsa.gov/travelpolicy" in the "External Links" section
Then the dataLayer contains the event "click"
| link_domain | gsa.gov |
| outbound | true |
| interaction_type | Mouse Click |

Scenario: Autotracker reports addtoany share links
Given DAP is configured with autotracking enabled
When I load the test site
And I add an external share link to the page
And I click the injected link with selector "#dynamicShareLink"
Then the dataLayer contains the event "share"
| method | facebook |
| content_name | Travel Policy |
| shared_via | add to any: facebook |
| content_type | content |
| content_url | https://gsa.gov/travelpolicy |
| outbound | true |
| interaction_type | Mouse Click |

Scenario: Autotracker disabled suppresses generic external clicks
Given DAP is configured with autotracking disabled
When I load the test site
And I click link 1 with text "http://www.gsa.gov/travelpolicy" in the "External Links" section
Then the dataLayer does not contain the event "click"
11 changes: 10 additions & 1 deletion features/configuration.feature
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,13 @@ Feature: A site can load the DAP code with varying levels of customization
Then DAP will set custom dimensions
| agency | GSA |
| site_topic | analytics |
| site_platform | cloud.gov |
| site_platform | cloud.gov |

Scenario: Load a DAP-enabled page with only agency uses the default custom dimensions
Given DAP is configured for agency "GSA"
When I load the test site
Then DAP will set custom dimensions
| agency | GSA |
| subagency | LOCALHOST |
| site_topic | unspecified:localhost |
| site_platform | unspecified:localhost |
61 changes: 61 additions & 0 deletions features/gas4_events.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
Feature: gas4 helper events are sent to DAP

Background:
Given I load an empty browser
And DAP is configured for agency "GSA"

Scenario: Clicking the official USA banner reports the banner event
When I load the test site
And I click on element with selector "#banner-button"
Then the dataLayer contains the event "official_usa_site_banner_click"
| link_text | Here’s how you know |
| section | header |

Scenario: Submitting the form interaction example reports a form_submit event
When I load the test site
And I click the submit button in the "gas4()" example of the "gas4() - Form Interaction" section
Then the dataLayer contains the event "form_submit"
| form_name | <form_name> |
| form_id | <form_id> |
| form_destination | <form_destination> |
| section | <section> |
| form_submit_text | <form_submit_text> |

Scenario: Unsupported gas4 event names fall back to dap_event
When I load the test site
And I click link 1 in the "Incorrect Event Name" example of the "gas4() - Social click" section
Then the dataLayer contains the event "dap_event"
| link_text | <link_text> |
| link_domain | <link_domain> |
| link_url | <link_url> |
| link_id | <link_id> |
| link_classes | <link_classes> |
| social_network | <social_network> |
| content_type | <content_type> |
| section | <section> |

Scenario: gas4 page view reports the provided page title and location
When I load the test site
And I call gas4 "page_view" with parameters
| page_location | /priority-one?page=1 |
| page_title | Priority One |
Then the dataLayer contains the event "page_view"
| page_location | http://localhost/priority-one |
| page_title | Priority One |

Scenario: gas4 view_search_results emits the provided search term
When I load the test site
And I call gas4 "view_search_results" with parameters
| search_term | analytics |
Then the dataLayer contains the event "view_search_results"
| search_term | analytics |

Scenario: gas4 with an empty parameter object does not emit an event
When I load the test site
And I call gas4 "share" with an empty parameter object
Then the dataLayer does not contain the event "share"

Scenario: gas4 with malformed arguments does not emit an event
When I load the test site
And I call gas4 with malformed arguments
Then the dataLayer does not contain the event "share"
43 changes: 43 additions & 0 deletions features/gas_legacy.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
Feature: gas legacy helper calls are reported to DAP

Background:
Given I load an empty browser
And DAP is configured for agency "GSA"

Scenario: gas pageview without title reports the current document title
When I load the test site
And I click link 1 with text "Pageview without title" in the "gas() - Custom Events / Pageviews / Custom Dimensions / Custom Metrics (Federated Only)" section
Then the dataLayer contains the event "page_view"
| page_location | http://localhost/dir/virtual-page?query=term |
| page_title | DAP test site |

Scenario: gas pageview with title reports the provided page title
When I load the test site
And I click link 1 with text "Pageview with title" in the "gas() - Custom Events / Pageviews / Custom Dimensions / Custom Metrics (Federated Only)" section
Then the dataLayer contains the event "page_view"
| page_location | http://localhost/dir/virtual-page |
| page_title | virtual page title |

Scenario: gas event reports a dap_event payload
When I load the test site
And I click link 1 with text "Event" in the "gas() - Custom Events / Pageviews / Custom Dimensions / Custom Metrics (Federated Only)" section
Then the dataLayer contains the event "dap_event"
| event_category | category event |
| event_action | action event |
| event_label | label event |
| event_value | 10 |

Scenario: gas event supports non-interaction events
When I load the test site
And I click link 1 with text "Custom Dimension" in the "gas() - Custom Events / Pageviews / Custom Dimensions / Custom Metrics (Federated Only)" section
Then the dataLayer contains the event "dap_event"
| event_category | custom dimension |
| event_action | slot 9 |
| event_label | dimension value |
| event_value | 0 |
| non_interaction | true |

Scenario: invalid gas calls do not emit a dap_event
When I load the test site
And I call gas with invalid arguments
Then the dataLayer does not contain the event "dap_event"
35 changes: 29 additions & 6 deletions features/support/step_definitions/dataLayer_steps.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,18 @@ import { Then } from "@cucumber/cucumber";
import * as chai from 'chai'
const expect = chai.expect;

async function getDataLayerEvent(page, eventName) {
return page.evaluate((eventName) => {
return [...window.dataLayer]
.reverse()
.find(item => item[0] === 'event' && item[1] === eventName);
}, eventName);
}

Then("DAP will set custom dimensions", async function (table) {
await this.page.waitForFunction(() => {
return Array.isArray(window.dataLayer) && window.dataLayer.some(item => item[0] === 'config');
});
const configCommand = await this.page.evaluate(() => {
return window.dataLayer.find(item => item[0] === 'config');
});
Expand All @@ -12,9 +23,7 @@ Then("DAP will set custom dimensions", async function (table) {
});

Then("the file download is reported to DAP with interaction type {string}", async function (interactionType) {
const event = await this.page.evaluate(() => {
return window.dataLayer.find(item => item[0] === 'event' && item[1] === 'file_download');
});
const event = await getDataLayerEvent(this.page, 'file_download');
expect(event).to.deep.equal(
{
'0': 'event',
Expand All @@ -35,8 +44,22 @@ Then("the file download is reported to DAP with interaction type {string}", asyn
});

Then("the file download is not reported to DAP", async function () {
const event = await this.page.evaluate(() => {
return window.dataLayer.find(item => item[0] === 'event' && item[1] === 'file_download');
});
const event = await getDataLayerEvent(this.page, 'file_download');
expect(event).to.be.undefined;
});

Then("the dataLayer contains the event {string}", async function (eventName, table) {
await this.page.waitForFunction((eventName) => {
return Array.isArray(window.dataLayer) && window.dataLayer.some(item => item[0] === 'event' && item[1] === eventName);
}, {}, eventName);
const event = await getDataLayerEvent(this.page, eventName);
expect(event).to.exist;
expect(event["0"]).to.equal("event");
expect(event["1"]).to.equal(eventName);
expect(event["2"]).to.include(table.rowsHash());
});

Then("the dataLayer does not contain the event {string}", async function (eventName) {
const event = await getDataLayerEvent(this.page, eventName);
expect(event).to.be.undefined;
});
Loading
Loading