Skip to content

Commit aa513da

Browse files
Merge pull request #77 from simpleanalytics/codex/add-unit-tests-for-auto-events [no ci]
Add auto-events unit tests
2 parents a216bda + 67edab1 commit aa513da

File tree

2 files changed

+145
-0
lines changed

2 files changed

+145
-0
lines changed

test/unit/auto-events.test.js

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
const { expect } = require("chai");
2+
const { createDOM } = require("./helpers/dom-auto-events");
3+
4+
function setupDOM() {
5+
const events = [];
6+
const dom = createDOM({
7+
beforeRun(vm) {
8+
vm.sa_event = function (name, metadata, cb) {
9+
events.push({ name, metadata });
10+
if (typeof cb === "function") cb();
11+
};
12+
vm.sa_event_loaded = true;
13+
},
14+
});
15+
dom.events = events;
16+
return dom;
17+
}
18+
19+
describe("auto-events", function () {
20+
it("tracks outbound link clicks", function (done) {
21+
const dom = setupDOM();
22+
const link = dom.window.document.createElement("a");
23+
link.href = "https://example.org/path";
24+
link.target = "_blank";
25+
dom.window.document.body.appendChild(link);
26+
27+
dom.window.saAutomatedLink(link, "outbound");
28+
29+
setTimeout(() => {
30+
expect(dom.events[0]).to.deep.include({ name: "outbound_example_org" });
31+
expect(dom.events[0].metadata).to.include({ url: link.href });
32+
done();
33+
}, 0);
34+
});
35+
36+
it("tracks download link clicks", function (done) {
37+
const dom = setupDOM();
38+
const link = dom.window.document.createElement("a");
39+
link.href = "https://example.com/file.pdf";
40+
link.target = "_blank";
41+
dom.window.document.body.appendChild(link);
42+
43+
dom.window.saAutomatedLink(link, "download");
44+
45+
setTimeout(() => {
46+
expect(dom.events[0]).to.deep.include({ name: "download_file_pdf" });
47+
expect(dom.events[0].metadata).to.include({ url: link.href });
48+
done();
49+
}, 0);
50+
});
51+
52+
it("tracks email link clicks", function (done) {
53+
const dom = setupDOM();
54+
const link = dom.window.document.createElement("a");
55+
link.href = "mailto:[email protected]";
56+
link.target = "_blank";
57+
dom.window.document.body.appendChild(link);
58+
59+
dom.window.saAutomatedLink(link, "email");
60+
61+
setTimeout(() => {
62+
expect(dom.events[0]).to.deep.include({ name: "email_test_example_com" });
63+
expect(dom.events[0].metadata).to.include({ email: "[email protected]" });
64+
done();
65+
}, 0);
66+
});
67+
});
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
const { JSDOM } = require("jsdom");
2+
const { readFileSync } = require("fs");
3+
const vm = require("vm");
4+
5+
const SCRIPT_PATH = "dist/latest/auto-events.js";
6+
7+
/**
8+
* @typedef {"navigate" | "reload" | "back_forward" | "prerender"} NavigationType
9+
*/
10+
11+
/** @type {Record<NavigationType, {name: NavigationType, code: number}>} */
12+
const NAVIGATION_TYPES = {
13+
navigate: { name: "navigate", code: 0 },
14+
reload: { name: "reload", code: 1 },
15+
back_forward: { name: "back_forward", code: 2 },
16+
prerender: { name: "prerender", code: 255 },
17+
};
18+
19+
function createDOM(options = {}) {
20+
const {
21+
url = "https://example.com/",
22+
navigationType = "navigate",
23+
settings,
24+
beforeRun,
25+
} = options;
26+
const dom = new JSDOM("<!doctype html><html><body></body></html>", {
27+
url,
28+
runScripts: "outside-only",
29+
pretendToBeVisual: true,
30+
});
31+
32+
if (settings) {
33+
vm.runInContext(
34+
`window.sa_settings = ${JSON.stringify(settings)}`,
35+
dom.getInternalVMContext()
36+
);
37+
}
38+
39+
if (typeof beforeRun === "function") beforeRun(dom.getInternalVMContext());
40+
41+
const sent = [];
42+
dom.window.Image = function () {
43+
return {
44+
set src(value) {
45+
sent.push({ type: "image", url: value });
46+
},
47+
};
48+
};
49+
dom.window.navigator.sendBeacon = function (url, data) {
50+
sent.push({ type: "beacon", url, data });
51+
return true;
52+
};
53+
54+
Object.defineProperty(dom.window, "performance", {
55+
writable: true,
56+
value: {
57+
getEntriesByType: function (type) {
58+
if (type === "navigation") {
59+
return [{ type: NAVIGATION_TYPES[navigationType].name }];
60+
}
61+
return [];
62+
},
63+
navigation: { type: NAVIGATION_TYPES[navigationType].code },
64+
},
65+
});
66+
67+
const script = readFileSync(SCRIPT_PATH, "utf8");
68+
vm.runInContext(script, dom.getInternalVMContext());
69+
70+
dom.sent = sent;
71+
return dom;
72+
}
73+
74+
module.exports = {
75+
createDOM,
76+
SCRIPT_PATH,
77+
NAVIGATION_TYPES,
78+
};

0 commit comments

Comments
 (0)