Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@financial-times/o-tracking

Package Overview
Dependencies
Maintainers
18
Versions
53
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@financial-times/o-tracking - npm Package Compare versions

Comparing version 3.0.4 to 3.1.0

test.src/o-tracking-test.js

58

dist-esm/javascript/events/click.js
import Delegate from "ftdomdelegate";
import { Queue } from "../core/queue.js";
import core from "../core.js";
import { sanitise, assignIfUndefined, merge, onPage } from "../utils.js";
import { sanitise, assignIfUndefined, merge } from "../utils.js";
import { get as getSetting } from "../core/settings.js";
import { getTrace } from "../../libs/get-trace.js";
var internalQueue;
var delegate; // Trigger the event tracking
var track = eventData => {
var firstDomPathToken = eventData.context.domPathTokens[0];
var href = firstDomPathToken.href || null;
var oTrackingSkipQueueAttr = firstDomPathToken['data-o-tracking-skip-queue']; // TODO: Decide if we should also allow the attribute without a value to mean the same as having a value of `'true'`.
// TODO: Decide is all anchor elements with a download attribute should also skip the queue.
var skipQueue = oTrackingSkipQueueAttr && oTrackingSkipQueueAttr.toLowerCase() === 'true' || false;
var isInternal = href && href.indexOf(window.document.location.hostname) > -1;
if (isInternal && !skipQueue) {
eventData.context.source_id = core.getRootID(); // Queue the event and send it on the next page load
internalQueue.add(eventData).save();
} else {
// Send now, before leaving this page
core.track(eventData);
}
};
var delegate;
var eventPropertiesToCollect = ["ctrlKey", "altKey", "shiftKey", "metaKey"]; // Get properties for the event (as opposed to properties of the clicked element)

@@ -67,28 +45,5 @@ // Available properties include mouse x- and y co-ordinates, for example.

var config = merge(getSetting('config'), eventData);
track(config);
core.track(config);
};
/**
* If there are any requests queued, attempts to send the next one
* Otherwise, does nothing
*
* @returns {void}
*/
/*eslint-disable no-unused-vars*/
var runQueue = _ => {
var next = _ => {
runQueue();
};
var nextLink = internalQueue.shift();
if (nextLink) {
core.track(nextLink, next);
}
};
/*eslint-enable no-unused-vars*/
/**
* Listen for click events.

@@ -113,8 +68,3 @@ *

delegate = delegate || new Delegate(document.body);
delegate.on('click', elementsToTrack, handleClickEvent(eventData), true); // Track any queued events
internalQueue = internalQueue || new Queue('clicks');
runQueue(); // Listen for page requests. If this is a single page app, we can send link requests now.
onPage(runQueue);
delegate.on('click', elementsToTrack, handleClickEvent(eventData), true);
};

@@ -121,0 +71,0 @@

@@ -10,4 +10,2 @@ "use strict";

var _queue = require("../core/queue.js");
var _core = _interopRequireDefault(require("../core.js"));

@@ -23,24 +21,3 @@

var internalQueue;
var delegate; // Trigger the event tracking
var track = eventData => {
var firstDomPathToken = eventData.context.domPathTokens[0];
var href = firstDomPathToken.href || null;
var oTrackingSkipQueueAttr = firstDomPathToken['data-o-tracking-skip-queue']; // TODO: Decide if we should also allow the attribute without a value to mean the same as having a value of `'true'`.
// TODO: Decide is all anchor elements with a download attribute should also skip the queue.
var skipQueue = oTrackingSkipQueueAttr && oTrackingSkipQueueAttr.toLowerCase() === 'true' || false;
var isInternal = href && href.indexOf(window.document.location.hostname) > -1;
if (isInternal && !skipQueue) {
eventData.context.source_id = _core.default.getRootID(); // Queue the event and send it on the next page load
internalQueue.add(eventData).save();
} else {
// Send now, before leaving this page
_core.default.track(eventData);
}
};
var delegate;
var eventPropertiesToCollect = ["ctrlKey", "altKey", "shiftKey", "metaKey"]; // Get properties for the event (as opposed to properties of the clicked element)

@@ -84,27 +61,5 @@ // Available properties include mouse x- and y co-ordinates, for example.

var config = (0, _utils.merge)((0, _settings.get)('config'), eventData);
track(config);
};
/**
* If there are any requests queued, attempts to send the next one
* Otherwise, does nothing
*
* @returns {void}
*/
/*eslint-disable no-unused-vars*/
var runQueue = _ => {
var next = _ => {
runQueue();
};
var nextLink = internalQueue.shift();
if (nextLink) {
_core.default.track(nextLink, next);
}
_core.default.track(config);
};
/*eslint-enable no-unused-vars*/
/**

@@ -130,8 +85,3 @@ * Listen for click events.

delegate = delegate || new _ftdomdelegate.default(document.body);
delegate.on('click', elementsToTrack, handleClickEvent(eventData), true); // Track any queued events
internalQueue = internalQueue || new _queue.Queue('clicks');
runQueue(); // Listen for page requests. If this is a single page app, we can send link requests now.
(0, _utils.onPage)(runQueue);
delegate.on('click', elementsToTrack, handleClickEvent(eventData), true);
};

@@ -138,0 +88,0 @@

@@ -16,3 +16,3 @@ {

"name": "@financial-times/o-tracking",
"version": "3.0.4",
"version": "3.1.0",
"description": "Origami module for FT tracking.",

@@ -19,0 +19,0 @@ "dependencies": {

@@ -138,5 +138,3 @@ # o-tracking

- If the element being clicked is a link which goes to a page on the same domain as the current page, o-tracking will put the tracking data into a queue to send to Spoor at a later time.
- Add the attribute `data-o-tracking-skip-queue` to the element to send the data to Spoor immediately.
- O-tracking click events will also track the path from the root of the DOM tree to the element which was clicked. This is recorded in a property called `domPathTokens`.
- o-tracking click events will also track the path from the root of the DOM tree to the element which was clicked. This is recorded in a property called `domPathTokens`.
- If the clicked element has the `data-trackable` attribute set, sibling elements will also be included within the `domPathTokens` property.

@@ -151,2 +149,4 @@

*Note: The attribute `data-o-tracking-skip-queue` is no longer used, it is now the default behaviour.*
##### oTracking.view.init

@@ -153,0 +153,0 @@

import Delegate from 'ftdomdelegate';
import {Queue} from '../core/queue.js';
import core from '../core.js';
import {sanitise, assignIfUndefined, merge, onPage} from '../utils.js';
import {sanitise, assignIfUndefined, merge } from '../utils.js';
import {get as getSetting} from '../core/settings.js';
import {getTrace} from '../../libs/get-trace.js';
let internalQueue;
let delegate;
// Trigger the event tracking
const track = eventData => {
const firstDomPathToken = eventData.context.domPathTokens[0];
const href = firstDomPathToken.href || null;
const oTrackingSkipQueueAttr = firstDomPathToken['data-o-tracking-skip-queue'];
// TODO: Decide if we should also allow the attribute without a value to mean the same as having a value of `'true'`.
// TODO: Decide is all anchor elements with a download attribute should also skip the queue.
const skipQueue = oTrackingSkipQueueAttr && oTrackingSkipQueueAttr.toLowerCase() === 'true' || false;
const isInternal = href && href.indexOf(window.document.location.hostname) > -1;
if (isInternal && !skipQueue) {
eventData.context.source_id = core.getRootID();
// Queue the event and send it on the next page load
internalQueue.add(eventData).save();
}
else {
// Send now, before leaving this page
core.track(eventData);
}
};
const eventPropertiesToCollect = [

@@ -73,22 +49,6 @@ "ctrlKey",

const config = merge(getSetting('config'), eventData);
track(config);
core.track(config);
};
/**
* If there are any requests queued, attempts to send the next one
* Otherwise, does nothing
*
* @returns {void}
*/
/*eslint-disable no-unused-vars*/
const runQueue = _ => {
const next = _ => { runQueue(); };
const nextLink = internalQueue.shift();
if (nextLink) {
core.track(nextLink, next);
}
};
/*eslint-enable no-unused-vars*/
/**
* Listen for click events.

@@ -113,9 +73,2 @@ *

delegate.on('click', elementsToTrack, handleClickEvent(eventData), true);
// Track any queued events
internalQueue = internalQueue || new Queue('clicks');
runQueue();
// Listen for page requests. If this is a single page app, we can send link requests now.
onPage(runQueue);
};

@@ -122,0 +75,0 @@

@@ -154,85 +154,2 @@ /* eslint-env mocha */

});
it('should not track straight away when the link points to the same domain we are currently on', function (done) {
sinon.spy(core, 'track');
click.init("blah", '#anchorD');
core.track.resetHistory(); // click.init() makes a call to track() so clearing the history here to avoid false positives
const aLinkToPageOnSameDomain = document.createElement('a');
const currentHost = window.document.location.hostname;
aLinkToPageOnSameDomain.href = "https://" + currentHost + "/a-page-on-the-same-domain";
aLinkToPageOnSameDomain.text = "A link to another page on the same domain";
aLinkToPageOnSameDomain.id = "anchorD";
aLinkToPageOnSameDomain.addEventListener('click', function(e){
e.preventDefault();
}); //we don't want the browser to follow click in test
const event = new MouseEvent('click', {
'view': window,
'bubbles': true,
'cancelable': true
});
document.body.appendChild(aLinkToPageOnSameDomain);
aLinkToPageOnSameDomain.dispatchEvent(event, true);
setTimeout(() => {
try {
proclaim.equal(core.track.notCalled, true, "click event not tracked");
core.track.restore();
done();
} catch (error) {
done(error);
}
}, 10);
});
it('should skip the queue when data-o-tracking-skip-queue is "true" on the link', function (done) {
sinon.spy(core, 'track');
click.init("blah", '#anchorE');
core.track.resetHistory(); // click.init() makes a call to track() so clearing the history here to avoid false positives
const aLinkToPageOnSameDomain = document.createElement('a');
const currentHost = window.document.location.hostname;
aLinkToPageOnSameDomain.href = "https://" + currentHost + "/a-page-on-the-same-domain";
aLinkToPageOnSameDomain.text = "A link to another page on the same domain";
aLinkToPageOnSameDomain.id = "anchorE";
aLinkToPageOnSameDomain.setAttribute("data-o-tracking-skip-queue", "true");
aLinkToPageOnSameDomain.addEventListener('click', function(e){
e.preventDefault();
}); //we don't want the browser to follow click in test
const event = new MouseEvent('click', {
'view': window,
'bubbles': true,
'cancelable': true
});
document.body.appendChild(aLinkToPageOnSameDomain);
aLinkToPageOnSameDomain.dispatchEvent(event, true);
setTimeout(() => {
try {
proclaim.equal(core.track.calledOnce, true, "click event not tracked");
core.track.restore();
done();
} catch (error) {
done(error);
}
}, 10);
});
});

@@ -5,7 +5,14 @@ /* eslint-env mocha */

import './setup.js';
import {destroy, get} from '../src/javascript/core/settings.js';
import {Queue} from '../src/javascript/core/queue.js';
import { Queue } from '../src/javascript/core/queue.js';
import oTracking from '../main.js';
import { sendSpy } from './setup.js';
const clickEvent = new MouseEvent('click', {
'view': window,
'bubbles': true,
'cancelable': true
});
describe('main', function () {

@@ -19,3 +26,3 @@ let root_id;

it('should only allow a single tracking instance to exist', function() {
it('should only allow a single tracking instance to exist per context', function() {
oTracking.destroy();

@@ -44,2 +51,85 @@ const confEl = document.createElement('script');

describe('a second instance of o-tracking exists in a separate browser context (e.g. new tab)', function () {
let iframe;
let iframeSrc;
let parentLink;
const config = {
context: {
product: 'desktop'
},
user: {
user_id: '023ur9jfokwenvcklwnfiwhfoi324'
}
};
beforeEach(function () {
// Create iframe (new context to mimic a new tab)
// and a local link click to track.
document.documentElement.innerHTML = `
<a href="#mock">A local link to click.</a>
<iframe></iframe>
`;
iframe = document.querySelector('iframe');
parentLink = document.querySelector('a');
// Initialise o-tracking on the parent context.
const parentTracking = oTracking.init(config);
parentTracking.click.init();
// Generate iframe content.
const karmaFile = '/base/test/o-tracking-test.js';
const bundleUrl =
window.__karma__ &&
window.__karma__.files &&
window.__karma__.files[karmaFile] ? karmaFile : null;
proclaim.ok(
bundleUrl,
'Could not find the o-tracking bundle served from a url, to ' +
'test instances across different browser contexts. Unfortunately ' +
'this depends on the Karma test runner. Has the test setup changed?'
);
const iframeContent = new Blob([`
<html>
<head>
<!-- load o-tracking fixture -->
<script src="${window.location.origin}${bundleUrl}"></script>
</head>
<body>
</body>
</html>
`], { type: 'text/html' });
iframeSrc = URL.createObjectURL(iframeContent);
});
it('should not send the same click event multiple times from the parent page', function(done) {
// First click, e.g. like a cmd-click on macOS which opens a new tab
// (the iframe is used to mimic this)
parentLink.dispatchEvent(clickEvent, true);
iframe.onload = () => {
// init second instance of o-tracking in the iframe
// fire a page event, as if navigated to a new page
// in a new tab e.g. with cmd-click on macOS
iframe.contentWindow.oTracking.init(config);
iframe.contentWindow.oTracking.click.init();
iframe.contentWindow.oTracking.page(config);
// another click in the parent context, after a second instance
// of o-tracking has loaded in the iframe (e.g. a new tab)
sendSpy.resetHistory();
parentLink.dispatchEvent(clickEvent, true);
try {
proclaim.equal(sendSpy.callCount, 1, 'Expected 1 event to be sent, the latest click event, no more.');
} catch (error) {
done(error);
}
done();
};
iframe.src = iframeSrc;
});
});
it('should quit without any config to init with', function() {

@@ -46,0 +136,0 @@ oTracking.destroy();

@@ -7,6 +7,8 @@ /* global sinon*/

const sendSpy = sinon.spy();
export function mockTransport() {
mock.transport = function () {
return {
send: sinon.spy(),
send: sendSpy,
complete: function (callback) {

@@ -24,2 +26,4 @@ if (willError) {

export { sendSpy };
export function unmockTransport() {

@@ -26,0 +30,0 @@ delete mock.transport;

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc