Skip to content
This repository was archived by the owner on Apr 27, 2024. It is now read-only.

Commit

Permalink
Merge pull request #37 from john-doherty/improve-testing
Browse files Browse the repository at this point in the history
Add properties & improve tests
  • Loading branch information
john-doherty authored Mar 10, 2024
2 parents 96ef2ef + 36c1339 commit 6a01a9f
Show file tree
Hide file tree
Showing 14 changed files with 501 additions and 130 deletions.
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,18 @@ if (mixpanel.muted) {
}
```

### Additional tracking properties

**mixpanel-lite** adds additional properties to each tracking event:

Name | Type | Description
:-----------|:----------|:---------------------------------------------------
`offline` | `boolean` | `true` if event captured when offline
`automated` | `boolean` | `true` if event captured using an automated browser
`dev` | `boolean` | `true` if event captured when running locally
`ad` | `object` | object containing online ad click ids
`utm` | `object` | object containing UTM tracking values

## Contributing

Pull requests are welcomed:
Expand Down
4 changes: 2 additions & 2 deletions dist/mixpanel-lite.min.js

Large diffs are not rendered by default.

7 changes: 6 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
{
"name": "mixpanel-lite",
"version": "1.5.8",
"version": "1.5.9",
"description": "A lightweight alternative to mixpanel-js with offline support for PWAs",
"main": "src/mixpanel-lite.js",
"scripts": {
"start": "node server/dev-server.js",
"test": "node_modules/.bin/jasmine tests/**/*-spec.js --reporter=jasmine-console-reporter",
"test": "NODE_ENV=test node_modules/.bin/jasmine --config=tests/jasmine/config.json",
"build": "npm test && node_modules/gulp/bin/gulp.js build",
"clean": "node_modules/gulp/bin/gulp.js clean"
},
Expand Down Expand Up @@ -155,5 +155,8 @@
"error"
]
}
},
"dependencies": {
"cuid": "^3.0.0"
}
}
130 changes: 97 additions & 33 deletions src/mixpanel-lite.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,21 @@
}
});

// track online status
if (!window.navigator.onLine) {
eventData.properties.offline = true;
}

var isAutomated = isBrowserAutomated();
if (isAutomated) {
eventData.properties.automated = true;
}

var isDevMode = isDevEnviroment();
if (isDevMode) {
eventData.properties.dev = true;
}

// save the event
transactions.add(eventData);

Expand Down Expand Up @@ -514,34 +529,24 @@
}

/**
* Get advertising click IDs from the URL.
*
* Get advertising click IDs from the URL
* @returns {Object} An object containing the advertising click IDs found in the URL. The object can have the following properties:
* - facebookClickId {string}: for tracking interactions with Facebook ads
* - doubleClickId {string}: for tracking ads served by Google's DoubleClick
* - googleClickId {string}: for tracking Google Ads campaigns
* - genericClickId {string}: for tracking clicks on certain advertising platforms
* - linkedInClickId {string}: for tracking interactions with LinkedIn ads
* - microsoftClickId {string}: for tracking interactions with Microsoft Advertising
* - tikTokClickId {string}: for tracking interactions with TikTok ads
* - twitterClickId {string}: for tracking interactions with Twitter ads
* - webBrowserReferrerId {string}: for tracking sources of traffic or conversions.
* Each property is included only if its corresponding param exists
*/
function getAdvertisingClickIDs() {

var urlParams = new URLSearchParams(window.location.search || '');
var clickIDs = {};

if (urlParams.has('dclid')) clickIDs.doubleClickId = urlParams.get('dclid');
if (urlParams.has('fbclid')) clickIDs.facebookClickId = urlParams.get('fbclid');
if (urlParams.has('gclid')) clickIDs.googleClickId = urlParams.get('gclid');
if (urlParams.has('ko_click_id')) clickIDs.genericClickId = urlParams.get('ko_click_id');
if (urlParams.has('li_fat_id')) clickIDs.linkedInClickId = urlParams.get('li_fat_id');
if (urlParams.has('msclkid')) clickIDs.microsoftClickId = urlParams.get('msclkid');
if (urlParams.has('ttclid')) clickIDs.tikTokClickId = urlParams.get('ttclid');
if (urlParams.has('twclid')) clickIDs.twitterClickId = urlParams.get('twclid');
if (urlParams.has('wbraid')) clickIDs.webBrowserReferrerId = urlParams.get('wbraid');
if (urlParams.has('dclid')) clickIDs.dclid = urlParams.get('dclid');
if (urlParams.has('fbclid')) clickIDs.fbclid = urlParams.get('fbclid');
if (urlParams.has('gclid')) clickIDs.gclid = urlParams.get('gclid');
if (urlParams.has('ko_click_id')) clickIDs.ko_click_id = urlParams.get('ko_click_id');
if (urlParams.has('li_fat_id')) clickIDs.li_fat_id = urlParams.get('li_fat_id');
if (urlParams.has('msclkid')) clickIDs.msclkid = urlParams.get('msclkid');
if (urlParams.has('ttclid')) clickIDs.ttclid = urlParams.get('ttclid');
if (urlParams.has('twclid')) clickIDs.twclid = urlParams.get('twclid');
if (urlParams.has('wbraid')) clickIDs.wbraid = urlParams.get('wbraid');

return Object.keys(clickIDs).length > 0 ? clickIDs : null;
}
Expand Down Expand Up @@ -587,6 +592,59 @@
'en'; // Default to English if none is found
}

/**
* Determines if the current browser session is controlled by automation software
* @example
* if (isBrowserAutomated()) {
* console.log('The browser is automated.');
* } else {
* console.log('The browser is not automated.');
* }
* @returns {boolean} true if the browser session is controlled by automation software, otherwise false.
*/
function isBrowserAutomated() {

// Check for PhantomJS
if (window._phantom || window.phantom) {
return true;
}

// Check for Nightmare
if (window.__nightmare) {
return true;
}

// Check for WebDriver (Puppeteer, Selenium)
if (navigator.webdriver) {
return true;
}

// Check for Cypress
if (window.Cypress) {
return true;
}

// Check for headless Chrome
if (/HeadlessChrome/.test(window.navigator.userAgent)) {
return true;
}

// Check for reduced screen size (common in headless environments)
if (screen.width === 0 || screen.height === 0) {
return true;
}

return false;
}

/**
* Checks if the script is running locally
* @returns {boolean} true if running locally, otherwise false
*/
function isDevEnviroment() {
return (/^localhost$|^127(\.[0-9]+){0,2}\.[0-9]+$|^\[::1?\]$/.test(location.hostname) || location.protocol === 'file:');
}

/**
* Gets the referring domain
* @returns {string} domain or empty string
Expand Down Expand Up @@ -641,20 +699,26 @@
*/
function getNewUUID() {

// Time/ticks information
// 1*new Date() is a cross browser version of Date.now()
var T = function () {
var d = 1 * new Date(),
i = 0;

// this while loop figures how many browser ticks go by
// before 1*new Date() returns a new number, ie the amount
// of ticks that go by per millisecond
while (d == 1 * new Date()) {
i++;
// Time-based entropy
var T = function() {
var time = 1 * new Date(); // cross-browser version of Date.now()
var ticks;
if (window.performance && window.performance.now) {
ticks = window.performance.now();
}
else {
// fall back to busy loop
ticks = 0;

// this while loop figures how many browser ticks go by
// before 1*new Date() returns a new number, ie the amount
// of ticks that go by per millisecond
while (time == 1 * new Date()) {
ticks++;
}
}

return d.toString(16) + i.toString(16);
return time.toString(16) + Math.floor(ticks).toString(16);
};

// Math.Random entropy
Expand Down Expand Up @@ -834,7 +898,7 @@

var advertParams = getAdvertisingClickIDs();
if (advertParams) {
_properties.advert = advertParams;
_properties.ad = advertParams;
}

// only track page URLs (not file etc)
Expand Down
12 changes: 12 additions & 0 deletions tests/jasmine/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"spec_dir": "./tests/**",
"spec_files": [
"*-spec.js"
],
"helpers": [
"jasmine/reporter.js"
],
"stopSpecOnExpectationFailure": true,
"random": false,
"failFast": true
}
18 changes: 18 additions & 0 deletions tests/jasmine/reporter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
var JasmineConsoleReporter = require('jasmine-console-reporter');

jasmine.DEFAULT_TIMEOUT_INTERVAL = 20000;

var jasmineEnv = jasmine.getEnv();
var reporter = new JasmineConsoleReporter({
colors: 1, // (0|false)|(1|true)|2
cleanStack: 1, // (0|false)|(1|true)|2|3
verbosity: 4, // (0|false)|1|2|(3|true)|4|Object
listStyle: 'indent', // "flat"|"indent"
timeUnit: 'ms', // "ms"|"ns"|"s"
timeThreshold: { ok: 500, warn: 1000, ouch: 3000 }, // Object|Number
activity: false, // boolean or string ("dots"|"star"|"flip"|"bouncingBar"|...)
emoji: true
});

jasmineEnv.clearReporters();
jasmineEnv.addReporter(reporter);
22 changes: 10 additions & 12 deletions tests/mixpanel-lite-advert-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,11 @@ var puppeteer = require('puppeteer');
var querystring = require('querystring');
var utils = require('./utils');

jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000;

var url = 'file://' + path.join(__dirname, 'environment.html');
var page = null;
var browser = null;

describe('mixpanel-lite UTM', function () {
describe('mixpanel-lite .ad', function () {

// create a new browser instance before each test
beforeEach(async function () {
Expand Down Expand Up @@ -68,14 +66,14 @@ describe('mixpanel-lite UTM', function () {
expect(data.properties.distinct_id).toBeDefined();
expect(data.properties.$browser).toEqual('Chrome');
expect(data.properties.token).toEqual(token);
expect(data.properties.advert.doubleClickId).toEqual('randomDclidValue');
expect(data.properties.advert.facebookClickId).toEqual('randomFbclidValue');
expect(data.properties.advert.genericClickId).toEqual('randomKoClickIdValue');
expect(data.properties.advert.linkedInClickId).toEqual('randomLiFatIdValue');
expect(data.properties.advert.microsoftClickId).toEqual('randomMsclkidValue');
expect(data.properties.advert.tikTokClickId).toEqual('randomTtclidValue');
expect(data.properties.advert.twitterClickId).toEqual('randomTwclidValue');
expect(data.properties.advert.webBrowserReferrerId).toEqual('randomWbraidValue');
expect(data.properties.ad.dclid).toEqual('randomDclidValue');
expect(data.properties.ad.fbclid).toEqual('randomFbclidValue');
expect(data.properties.ad.ko_click_id).toEqual('randomKoClickIdValue');
expect(data.properties.ad.li_fat_id).toEqual('randomLiFatIdValue');
expect(data.properties.ad.msclkid).toEqual('randomMsclkidValue');
expect(data.properties.ad.ttclid).toEqual('randomTtclidValue');
expect(data.properties.ad.twclid).toEqual('randomTwclidValue');
expect(data.properties.ad.wbraid).toEqual('randomWbraidValue');

resolve(); // Resolve the promise after assertions
}
Expand Down Expand Up @@ -127,7 +125,7 @@ describe('mixpanel-lite UTM', function () {
expect(data.properties.$browser).toEqual('Chrome');
expect(data.properties.token).toEqual(token);

expect(data.properties.advert).toBeUndefined();
expect(data.properties.ad).toBeUndefined();

resolve(); // Resolve the promise after assertions
}
Expand Down
Loading

0 comments on commit 6a01a9f

Please sign in to comment.