diff --git a/package.json b/package.json index ca221f7ae..fa3c523c9 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "sha1": "git://github.com/pvorb/node-sha1.git#v1.0.0" }, "devDependencies": { + "js-base64": "2.1.9", "JSON": "1.0.0", "grunt": "0.4.5", "grunt-browserify": "5.0.0", diff --git a/src/js/snowplow.js b/src/js/snowplow.js index b8c57d24d..7a396b82f 100644 --- a/src/js/snowplow.js +++ b/src/js/snowplow.js @@ -92,6 +92,7 @@ /* Tracker identifier with version */ version = 'js-' + '<%= pkg.version %>', // Update banner.js too + // Initial ID. Can be regenerated by trackPageView pageViewId = uuid.v4(), /* Contains four variables that are shared with tracker.js and must be passed by reference */ @@ -127,7 +128,7 @@ // Flush all POST queues lodash.forEach(mutSnowplowState.bufferFlushers, function (flusher) { flusher(); - }) + }); /* * Delay/pause (blocks UI) diff --git a/src/js/tracker.js b/src/js/tracker.js index 234f273ee..f450670cc 100755 --- a/src/js/tracker.js +++ b/src/js/tracker.js @@ -56,7 +56,8 @@ * @param functionName global function name * @param namespace The namespace of the tracker object * @param version The current version of the JavaScript Tracker - * @param pageViewId ID for the current page view, to be attached to all events in the web_page context + * @param pageViewId Initial (can be modified) ID for the current page view, to be attached to + * all events in the web_page context. * @param mutSnowplowState An object containing hasLoaded, registeredOnLoadHandlers, and expireDateTime * Passed in by reference in case they are altered by snowplow.js * @param argmap Optional dictionary of configuration options. Supported fields and their default values: @@ -264,16 +265,15 @@ commonContexts = [], // Enhanced Ecommerce Contexts to be added on every `trackEnhancedEcommerceAction` call - enhancedEcommerceContexts = []; + enhancedEcommerceContexts = [], + + // Whether pageViewId should be regenerated after each trackPageView. Affect web_page context + preservePageViewId = false; if (argmap.hasOwnProperty('discoverRootDomain') && argmap.discoverRootDomain) { configCookieDomain = helpers.findRootDomain(); } - if (autoContexts.webPage) { - commonContexts.push(getWebPageContext()); - } - if (autoContexts.gaCookies) { commonContexts.push(getGaCookiesContext()); } @@ -738,6 +738,16 @@ return ('https:' === documentAlias.location.protocol ? 'https' : 'http') + '://' + rawUrl; } + /** + * Reset initial `pageViewId` if necessary after successful + * `trackPageView` + */ + function resetPageViewState() { + if (!preservePageViewId) { + pageViewId = uuid.v4(); + } + } + /** * Add common contexts to every event * TODO: move this functionality into the core @@ -748,6 +758,10 @@ function addCommonContexts(userContexts) { var combinedContexts = commonContexts.concat(userContexts || []); + if (autoContexts.webPage) { + combinedContexts.push(getWebPageContext()); + } + // Add PerformanceTiming Context if (autoContexts.performanceTiming) { var performanceTimingContext = getPerformanceTimingContext(); @@ -1162,6 +1176,8 @@ purify(customReferrer || configReferrerUrl), addCommonContexts(finalizeContexts(context, contextCallback)), tstamp); + + resetPageViewState(); // Send ping (to log that user has stayed on page) var now = new Date(); @@ -2202,6 +2218,13 @@ trackError: function (message, filename, lineno, colno, error, contexts) { var enrichedContexts = addCommonContexts(contexts); errorManager.trackError(message, filename, lineno, colno, error, enrichedContexts); + }, + + /** + * Stop regenerating `pageViewId` (available from `web_page` context) + */ + preservePageViewId: function () { + preservePageViewId = true } }; }; diff --git a/tests/integration/integration.js b/tests/integration/integration.js index 7bcff15a1..8a58ff2d7 100755 --- a/tests/integration/integration.js +++ b/tests/integration/integration.js @@ -37,8 +37,10 @@ define([ 'intern/chai!assert', 'intern/dojo/node!lodash', 'intern/dojo/node!http', - 'intern/dojo/node!url' -], function(registerSuite, assert, lodash, http, url) { + 'intern/dojo/node!url', + "intern/dojo/node!js-base64" +], function(registerSuite, assert, lodash, http, url, jsBase64) { + var decodeBase64 = jsBase64.Base64.fromBase64; /** * Expected amount of request for each browser @@ -198,6 +200,11 @@ define([ (event.data.message != null) } })) + }, + + 'Check pageViewId is regenerated for each trackPageView': function () { + assert.isTrue(pageViewsHaveDifferentIds()); } + }) }); diff --git a/tests/pages/integration-template.html b/tests/pages/integration-template.html index 1f33d23c4..08af82b5e 100644 --- a/tests/pages/integration-template.html +++ b/tests/pages/integration-template.html @@ -24,7 +24,10 @@ window.snowplow('newTracker', 'cf', subdomain + '.ngrok.io', { encodeBase64: true, appId: 'CFe23a', - platform: 'mob' + platform: 'mob', + contexts: { + webPage: true + } }); window.snowplow('setUserId', 'Malcolm'); @@ -37,6 +40,9 @@ } ]); + // This should have different pageViewId in web_page context + window.snowplow('trackPageView'); + window.snowplow('trackStructEvent', 'Mixes', 'Play', 'MRC/fabric-0503-mix', '', '0.0'); window.snowplow('trackUnstructEvent', { schema: 'iglu:com.acme_company/viewed_product/jsonschema/5-0-0',