diff --git a/common/changes/@snowplow/javascript-tracker/performance-navigation-timing-config_2024-11-11-14-14.json b/common/changes/@snowplow/javascript-tracker/performance-navigation-timing-config_2024-11-11-14-14.json new file mode 100644 index 000000000..c2e234556 --- /dev/null +++ b/common/changes/@snowplow/javascript-tracker/performance-navigation-timing-config_2024-11-11-14-14.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/javascript-tracker", + "comment": "Add `performanceNavigationTiming` option to JS contexts config", + "type": "none" + } + ], + "packageName": "@snowplow/javascript-tracker" +} \ No newline at end of file diff --git a/trackers/javascript-tracker/src/configuration.ts b/trackers/javascript-tracker/src/configuration.ts index 6a9648516..b1fde26a3 100644 --- a/trackers/javascript-tracker/src/configuration.ts +++ b/trackers/javascript-tracker/src/configuration.ts @@ -39,5 +39,6 @@ export interface JavaScriptTrackerConfiguration extends TrackerConfiguration { geolocation: boolean; clientHints: boolean | { includeHighEntropy: boolean }; webVitals: boolean | { loadWebVitalsScript?: boolean; webVitalsSource?: string }; + performanceNavigationTiming: boolean; }; } diff --git a/trackers/javascript-tracker/src/features.ts b/trackers/javascript-tracker/src/features.ts index 34965637a..a667a772e 100644 --- a/trackers/javascript-tracker/src/features.ts +++ b/trackers/javascript-tracker/src/features.ts @@ -30,13 +30,8 @@ import * as WebVitals from '@snowplow/browser-plugin-web-vitals'; * @param configuration - The tracker configuration object */ export function Plugins(configuration: JavaScriptTrackerConfiguration) { - const { - performanceTiming, - gaCookies, - geolocation, - clientHints, - webVitals - } = configuration?.contexts ?? {}; + const { performanceTiming, gaCookies, geolocation, clientHints, webVitals, performanceNavigationTiming } = + configuration?.contexts ?? {}; const activatedPlugins: Array<[BrowserPlugin, {} | Record]> = []; if (plugins.performanceTiming && performanceTiming) { @@ -148,7 +143,7 @@ export function Plugins(configuration: JavaScriptTrackerConfiguration) { activatedPlugins.push([EventSpecificationsPlugin(), apiMethods]); } - if (plugins.performanceNavigationTiming) { + if (plugins.performanceNavigationTiming && performanceNavigationTiming) { const { PerformanceNavigationTimingPlugin, ...apiMethods } = PerformanceNavigationTiming; activatedPlugins.push([PerformanceNavigationTimingPlugin(), apiMethods]); } diff --git a/trackers/javascript-tracker/test/unit/plugin_features.test.ts b/trackers/javascript-tracker/test/unit/plugin_features.test.ts new file mode 100644 index 000000000..ce75881b9 --- /dev/null +++ b/trackers/javascript-tracker/test/unit/plugin_features.test.ts @@ -0,0 +1,61 @@ +import { SelfDescribingJson } from '@snowplow/tracker-core'; +import { Plugins } from '../../src/features'; + +describe('Performance Navigation Timing', () => { + let windowSpy: any; + + const otherContexts = { + webPage: false, + session: false, + performanceTiming: false, + gaCookies: false, + geolocation: false, + clientHints: false, + webVitals: false, + }; + + const hasPerformanceNavigationTimingContext = (plugins: ReturnType): boolean => { + const pluginContexts = plugins.map((plugin) => plugin[0]?.contexts?.()); + const hasPerformanceContext = pluginContexts.some((contexts?: SelfDescribingJson[]) => + contexts?.some( + (context: { schema?: string }) => context.schema === 'iglu:org.w3/PerformanceNavigationTiming/jsonschema/1-0-0' + ) + ); + return hasPerformanceContext; + }; + + beforeEach(() => { + windowSpy = jest.spyOn(global, 'window', 'get'); + + // The PerformanceNavigationTiming context will only be added if the plugin can: + // - Access the `performance` object on the window + // - See that a value is returned from `getEntriesByType` + windowSpy.mockImplementation(() => ({ + performance: { + getEntriesByType: () => [{}], + }, + })); + }); + + it('Is enabled if contexts.performanceNavigationTiming is true', () => { + const plugins = Plugins({ + contexts: { + performanceNavigationTiming: true, + ...otherContexts, + }, + }); + + expect(hasPerformanceNavigationTimingContext(plugins)).toBe(true); + }); + + it('Is disabled if contexts.performanceNavigationTiming is false', () => { + const plugins = Plugins({ + contexts: { + performanceNavigationTiming: false, + ...otherContexts, + }, + }); + + expect(hasPerformanceNavigationTimingContext(plugins)).toBe(false); + }); +});