-
Notifications
You must be signed in to change notification settings - Fork 229
Release 4.8.0 #1470
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Release 4.8.0 #1470
Changes from all commits
86a1f47
27cebec
7b5ef25
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| { | ||
| "changes": [ | ||
| { | ||
| "packageName": "@snowplow/browser-plugin-optimizely-x", | ||
| "comment": "Fix optimizely personalization campaign attribution", | ||
| "type": "none" | ||
| } | ||
| ], | ||
| "packageName": "@snowplow/browser-plugin-optimizely-x" | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| { | ||
| "changes": [ | ||
| { | ||
| "comment": "Add opt-in activity metrics tracking with activity_metrics entity", | ||
| "type": "none", | ||
| "packageName": "@snowplow/browser-tracker-core" | ||
| } | ||
| ], | ||
| "packageName": "@snowplow/browser-tracker-core" | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| { | ||
| "changes": [ | ||
| { | ||
| "comment": "Add opt-in activity metrics tracking with activity_metrics entity", | ||
| "type": "none", | ||
| "packageName": "@snowplow/browser-tracker" | ||
| } | ||
| ], | ||
| "packageName": "@snowplow/browser-tracker" | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -35,6 +35,7 @@ import { | |||||||||||||||||||||||||||||||||||
| PageViewEvent, | ||||||||||||||||||||||||||||||||||||
| ActivityCallback, | ||||||||||||||||||||||||||||||||||||
| ActivityCallbackData, | ||||||||||||||||||||||||||||||||||||
| ActivityMetrics, | ||||||||||||||||||||||||||||||||||||
| TrackerConfiguration, | ||||||||||||||||||||||||||||||||||||
| BrowserTracker, | ||||||||||||||||||||||||||||||||||||
| ActivityTrackingConfiguration, | ||||||||||||||||||||||||||||||||||||
|
|
@@ -65,7 +66,13 @@ import { | |||||||||||||||||||||||||||||||||||
| emptyIdCookie, | ||||||||||||||||||||||||||||||||||||
| eventIndexFromIdCookie, | ||||||||||||||||||||||||||||||||||||
| } from './id_cookie'; | ||||||||||||||||||||||||||||||||||||
| import { CLIENT_SESSION_SCHEMA, WEB_PAGE_SCHEMA, BROWSER_CONTEXT_SCHEMA, APPLICATION_CONTEXT_SCHEMA } from './schemata'; | ||||||||||||||||||||||||||||||||||||
| import { | ||||||||||||||||||||||||||||||||||||
| CLIENT_SESSION_SCHEMA, | ||||||||||||||||||||||||||||||||||||
| WEB_PAGE_SCHEMA, | ||||||||||||||||||||||||||||||||||||
| BROWSER_CONTEXT_SCHEMA, | ||||||||||||||||||||||||||||||||||||
| APPLICATION_CONTEXT_SCHEMA, | ||||||||||||||||||||||||||||||||||||
| ACTIVITY_METRICS_SCHEMA, | ||||||||||||||||||||||||||||||||||||
| } from './schemata'; | ||||||||||||||||||||||||||||||||||||
| import { getBrowserProperties } from '../helpers/browser_props'; | ||||||||||||||||||||||||||||||||||||
| import { asyncCookieStorage, syncCookieStorage } from './cookie_storage'; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
|
|
@@ -88,6 +95,8 @@ type ActivityConfig = { | |||||||||||||||||||||||||||||||||||
| configHeartBeatTimer: number; | ||||||||||||||||||||||||||||||||||||
| /** The setInterval identifier */ | ||||||||||||||||||||||||||||||||||||
| activityInterval?: number; | ||||||||||||||||||||||||||||||||||||
| /** Whether activity metrics tracking is enabled for this configuration */ | ||||||||||||||||||||||||||||||||||||
| activityMetrics?: boolean; | ||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| /** The configurations for the two types of Activity Tracking */ | ||||||||||||||||||||||||||||||||||||
|
|
@@ -264,6 +273,14 @@ export function Tracker( | |||||||||||||||||||||||||||||||||||
| maxXOffset: number, | ||||||||||||||||||||||||||||||||||||
| minYOffset: number, | ||||||||||||||||||||||||||||||||||||
| maxYOffset: number, | ||||||||||||||||||||||||||||||||||||
| // Activity metrics tracking state | ||||||||||||||||||||||||||||||||||||
| activityMetricsState = { | ||||||||||||||||||||||||||||||||||||
| metrics: { mouseDistance: 0, scrollDistance: 0, keyPresses: 0, clicks: 0, touches: 0 } as ActivityMetrics, | ||||||||||||||||||||||||||||||||||||
| lastMouseX: undefined as number | undefined, | ||||||||||||||||||||||||||||||||||||
| lastMouseY: undefined as number | undefined, | ||||||||||||||||||||||||||||||||||||
| lastScrollX: undefined as number | undefined, | ||||||||||||||||||||||||||||||||||||
| lastScrollY: undefined as number | undefined, | ||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||
| // Domain hash value | ||||||||||||||||||||||||||||||||||||
| domainHash: string, | ||||||||||||||||||||||||||||||||||||
| // Domain unique user ID | ||||||||||||||||||||||||||||||||||||
|
|
@@ -514,17 +531,66 @@ export function Tracker( | |||||||||||||||||||||||||||||||||||
| * Process all "activity" events. | ||||||||||||||||||||||||||||||||||||
| * For performance, this function must have low overhead. | ||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||
| function activityHandler() { | ||||||||||||||||||||||||||||||||||||
| function activityHandler(event?: Event) { | ||||||||||||||||||||||||||||||||||||
| const now = new Date(); | ||||||||||||||||||||||||||||||||||||
| lastActivityTime = now.getTime(); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| if (isActivityMetricsEnabled() && event) { | ||||||||||||||||||||||||||||||||||||
| switch (event.type) { | ||||||||||||||||||||||||||||||||||||
| case 'mousemove': { | ||||||||||||||||||||||||||||||||||||
| const me = event as MouseEvent; | ||||||||||||||||||||||||||||||||||||
| if (activityMetricsState.lastMouseX !== undefined && activityMetricsState.lastMouseY !== undefined) { | ||||||||||||||||||||||||||||||||||||
| const dx = me.clientX - activityMetricsState.lastMouseX; | ||||||||||||||||||||||||||||||||||||
| const dy = me.clientY - activityMetricsState.lastMouseY; | ||||||||||||||||||||||||||||||||||||
| activityMetricsState.metrics.mouseDistance += Math.sqrt(dx * dx + dy * dy); | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| activityMetricsState.lastMouseX = me.clientX; | ||||||||||||||||||||||||||||||||||||
| activityMetricsState.lastMouseY = me.clientY; | ||||||||||||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| case 'click': | ||||||||||||||||||||||||||||||||||||
| activityMetricsState.metrics.clicks++; | ||||||||||||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||||||||||||
| case 'keydown': | ||||||||||||||||||||||||||||||||||||
| activityMetricsState.metrics.keyPresses++; | ||||||||||||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||||||||||||
| case 'touchstart': | ||||||||||||||||||||||||||||||||||||
| activityMetricsState.metrics.touches++; | ||||||||||||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||||||||||||
| // scroll handled in scrollHandler | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| /* | ||||||||||||||||||||||||||||||||||||
| * Process all "scroll" events. | ||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||
| function scrollHandler() { | ||||||||||||||||||||||||||||||||||||
| updateMaxScrolls(); | ||||||||||||||||||||||||||||||||||||
| activityHandler(); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| const offsets = getPageOffsets(); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| const x = offsets[0]; | ||||||||||||||||||||||||||||||||||||
| if (x < minXOffset) { | ||||||||||||||||||||||||||||||||||||
| minXOffset = x; | ||||||||||||||||||||||||||||||||||||
| } else if (x > maxXOffset) { | ||||||||||||||||||||||||||||||||||||
| maxXOffset = x; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| const y = offsets[1]; | ||||||||||||||||||||||||||||||||||||
| if (y < minYOffset) { | ||||||||||||||||||||||||||||||||||||
| minYOffset = y; | ||||||||||||||||||||||||||||||||||||
| } else if (y > maxYOffset) { | ||||||||||||||||||||||||||||||||||||
| maxYOffset = y; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| if (isActivityMetricsEnabled()) { | ||||||||||||||||||||||||||||||||||||
| if (activityMetricsState.lastScrollX !== undefined && activityMetricsState.lastScrollY !== undefined) { | ||||||||||||||||||||||||||||||||||||
| activityMetricsState.metrics.scrollDistance += Math.abs(x - activityMetricsState.lastScrollX) + Math.abs(y - activityMetricsState.lastScrollY); | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| activityMetricsState.lastScrollX = x; | ||||||||||||||||||||||||||||||||||||
| activityMetricsState.lastScrollY = y; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| /* | ||||||||||||||||||||||||||||||||||||
|
|
@@ -555,24 +621,35 @@ export function Tracker( | |||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| /* | ||||||||||||||||||||||||||||||||||||
| * Check the max scroll levels, updating as necessary | ||||||||||||||||||||||||||||||||||||
| * Whether activity metrics tracking is enabled for any active configuration | ||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||
| function updateMaxScrolls() { | ||||||||||||||||||||||||||||||||||||
| const offsets = getPageOffsets(); | ||||||||||||||||||||||||||||||||||||
| function isActivityMetricsEnabled(): boolean { | ||||||||||||||||||||||||||||||||||||
| return !!( | ||||||||||||||||||||||||||||||||||||
| activityTrackingConfig.configurations.pagePing?.activityMetrics || | ||||||||||||||||||||||||||||||||||||
| activityTrackingConfig.configurations.callback?.activityMetrics | ||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| const x = offsets[0]; | ||||||||||||||||||||||||||||||||||||
| if (x < minXOffset) { | ||||||||||||||||||||||||||||||||||||
| minXOffset = x; | ||||||||||||||||||||||||||||||||||||
| } else if (x > maxXOffset) { | ||||||||||||||||||||||||||||||||||||
| maxXOffset = x; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| /* | ||||||||||||||||||||||||||||||||||||
| * Reset activity metrics accumulators and position trackers | ||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||
| function resetActivityMetricsState() { | ||||||||||||||||||||||||||||||||||||
| activityMetricsState.metrics = { mouseDistance: 0, scrollDistance: 0, keyPresses: 0, clicks: 0, touches: 0 }; | ||||||||||||||||||||||||||||||||||||
| activityMetricsState.lastMouseX = undefined; | ||||||||||||||||||||||||||||||||||||
| activityMetricsState.lastMouseY = undefined; | ||||||||||||||||||||||||||||||||||||
| activityMetricsState.lastScrollX = undefined; | ||||||||||||||||||||||||||||||||||||
| activityMetricsState.lastScrollY = undefined; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| const y = offsets[1]; | ||||||||||||||||||||||||||||||||||||
| if (y < minYOffset) { | ||||||||||||||||||||||||||||||||||||
| minYOffset = y; | ||||||||||||||||||||||||||||||||||||
| } else if (y > maxYOffset) { | ||||||||||||||||||||||||||||||||||||
| maxYOffset = y; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| /* | ||||||||||||||||||||||||||||||||||||
| * Return a snapshot of current activity metrics | ||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||
| function getActivityMetrics(): ActivityMetrics { | ||||||||||||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||||||||||||
| ...activityMetricsState.metrics, | ||||||||||||||||||||||||||||||||||||
| mouseDistance: Math.round(activityMetricsState.metrics.mouseDistance), | ||||||||||||||||||||||||||||||||||||
| scrollDistance: Math.round(activityMetricsState.metrics.scrollDistance), | ||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| /* | ||||||||||||||||||||||||||||||||||||
|
|
@@ -1098,6 +1175,7 @@ export function Tracker( | |||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| // Capture our initial scroll points | ||||||||||||||||||||||||||||||||||||
| resetMaxScrolls(); | ||||||||||||||||||||||||||||||||||||
| resetActivityMetricsState(); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| // Add event handlers; cross-browser compatibility here varies significantly | ||||||||||||||||||||||||||||||||||||
| // @see http://quirksmode.org/dom/events | ||||||||||||||||||||||||||||||||||||
|
|
@@ -1126,6 +1204,7 @@ export function Tracker( | |||||||||||||||||||||||||||||||||||
| if (activityTrackingConfig.enabled && (resetActivityTrackingOnPageView || installingActivityTracking)) { | ||||||||||||||||||||||||||||||||||||
| // Periodic check for activity. | ||||||||||||||||||||||||||||||||||||
| lastActivityTime = now.getTime(); | ||||||||||||||||||||||||||||||||||||
| resetActivityMetricsState(); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| let key: keyof ActivityConfigurations; | ||||||||||||||||||||||||||||||||||||
| for (key in activityTrackingConfig.configurations) { | ||||||||||||||||||||||||||||||||||||
|
|
@@ -1147,8 +1226,16 @@ export function Tracker( | |||||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||||||
| const executePagePing = (cb: ActivityCallback, context: Array<SelfDescribingJson>) => { | ||||||||||||||||||||||||||||||||||||
| refreshUrl(); | ||||||||||||||||||||||||||||||||||||
| cb({ context, pageViewId: getPageViewId(), minXOffset, minYOffset, maxXOffset, maxYOffset }); | ||||||||||||||||||||||||||||||||||||
| let activityMetrics: ActivityMetrics | undefined; | ||||||||||||||||||||||||||||||||||||
| if (isActivityMetricsEnabled()) { | ||||||||||||||||||||||||||||||||||||
| activityMetrics = getActivityMetrics(); | ||||||||||||||||||||||||||||||||||||
| context = context.concat([{ schema: ACTIVITY_METRICS_SCHEMA, data: activityMetrics }]); | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| cb({ context, pageViewId: getPageViewId(), minXOffset, minYOffset, maxXOffset, maxYOffset, activityMetrics }); | ||||||||||||||||||||||||||||||||||||
| resetMaxScrolls(); | ||||||||||||||||||||||||||||||||||||
| if (isActivityMetricsEnabled()) { | ||||||||||||||||||||||||||||||||||||
|
Comment on lines
+1229
to
+1236
|
||||||||||||||||||||||||||||||||||||
| let activityMetrics: ActivityMetrics | undefined; | |
| if (isActivityMetricsEnabled()) { | |
| activityMetrics = getActivityMetrics(); | |
| context = context.concat([{ schema: ACTIVITY_METRICS_SCHEMA, data: activityMetrics }]); | |
| } | |
| cb({ context, pageViewId: getPageViewId(), minXOffset, minYOffset, maxXOffset, maxYOffset, activityMetrics }); | |
| resetMaxScrolls(); | |
| if (isActivityMetricsEnabled()) { | |
| const activityMetricsEnabledForConfig = !!config.activityMetrics; | |
| let activityMetrics: ActivityMetrics | undefined; | |
| if (activityMetricsEnabledForConfig) { | |
| activityMetrics = getActivityMetrics(); | |
| context = context.concat([{ schema: ACTIVITY_METRICS_SCHEMA, data: activityMetrics }]); | |
| } | |
| cb({ context, pageViewId: getPageViewId(), minXOffset, minYOffset, maxXOffset, maxYOffset, activityMetrics }); | |
| resetMaxScrolls(); | |
| if (activityMetricsEnabledForConfig) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
scrollDistanceaccumulation logic is new here, but the added test suite doesn’t currently include a positive assertion thatscrollDistanceincreases when scroll offsets change (only that it remains 0 in some cases). Adding a unit test that mutates scroll offsets and dispatchesscrollwould help prevent regressions in this metric.