Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 6 additions & 7 deletions src/auto-events.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
(function saAutomatedEvents(window) {
// Skip server side rendered pages
if (typeof window === "undefined") return;
// Skip server side rendered pages. Without a window the call at the bottom
// passes false: typeof is then "undefined", which sorts after "u".
if (!window) return;

var log = function (message, type) {
var logger = type === "warn" ? console.warn : console.info;
Expand Down Expand Up @@ -75,7 +76,7 @@
if (typeof optionsLink === "undefined")
log("options object not found, please specify", "warn");

var saAutomatedLink = function saAutomatedLink(element, type) {
function saAutomatedLink(element, type) {
try {
if (!element) return log("no element found");

Expand Down Expand Up @@ -139,9 +140,7 @@
} catch (error) {
log(error.message, "warn");
}
};

window.saAutomatedLink = saAutomatedLink;
}

function collectLink(link) {
if (link.hasAttribute("data-simple-event")) return;
Expand Down Expand Up @@ -208,4 +207,4 @@
if (event.target.readyState === "complete") onDOMContentLoaded();
});
}
})(window);
})(typeof window < "u" && window);
3 changes: 2 additions & 1 deletion src/default.js
Original file line number Diff line number Diff line change
Expand Up @@ -1299,7 +1299,8 @@
/** endif **/
}
})(
typeof window != "undefined" && window,
// typeof is "undefined" without a window, which sorts after "u"
typeof window < "u" && window,
"{{baseUrl}}",
"{{apiUrlPrefix}}",
"{{scriptName}}",
Expand Down
161 changes: 64 additions & 97 deletions test/unit/auto-events.test.js
Original file line number Diff line number Diff line change
@@ -1,57 +1,25 @@
const { expect } = require("chai");
const { createDOM } = require("./helpers/dom-auto-events");
const { createDOM: createTrackerDOM } = require("./helpers/dom");
const { readFileSync } = require("fs");
const { runInContext, runInNewContext } = require("vm");

function setupDOM() {
const events = [];
const dom = createDOM({
beforeRun(vm) {
vm.sa_event = function (name, metadata, cb) {
events.push({ name, metadata });
if (typeof cb === "function") cb();
};
vm.sa_event_loaded = true;
},
});
dom.events = events;
return dom;
}

describe("auto-events", function () {
it("keeps phone metadata when the title names the event", function () {
const dom = setupDOM();
const link = dom.window.document.createElement("a");
link.href = "tel:+31201234567?extension=42";
link.title = "Call sales";

dom.window.saAutomatedLink(link, "phone");

expect(dom.events).to.deep.equal([
{
name: "phone_Call_sales",
metadata: { title: "Call sales", phone: "+31201234567" },
},
]);
dom.window.close();
});

// Runs auto-events.js on a single link and returns what the click sends
function clickLink(href, hostname, event) {
function clickLink(href, { title, event = {} } = {}) {
const calls = [];
const navigations = [];
const attributes = [];
const listeners = [];
const { hostname, pathname } = new URL(href);
const link = {
href,
hostname,
pathname: "/",
pathname,
getAttribute(name) {
return name === "href" ? this.href : null;
return { href, title }[name] ?? null;
},
hasAttribute() {
return false;
hasAttribute(name) {
return this.getAttribute(name) !== null;
},
setAttribute(name) {
attributes.push(name);
Expand Down Expand Up @@ -85,9 +53,47 @@ describe("auto-events", function () {
return { calls, navigations, attributes, listeners };
}

it("keeps phone metadata when the title names the event", function () {
const { calls } = clickLink("tel:+31201234567?extension=42", {
title: "Call sales",
});
expect(calls.map(([name, metadata]) => ({ name, metadata }))).to.deep.equal(
[
{
name: "phone_Call_sales",
metadata: { title: "Call sales", phone: "+31201234567" },
},
]
);
});

for (const [href, name, metadata] of [
[
"https://example.org/path",
"outbound_example_org",
{ url: "https://example.org/path" },
],
[
"https://example.com/file.pdf",
"download_file_pdf",
{ url: "https://example.com/file.pdf" },
],
[
"mailto:test@example.com",
"email_test_example_com",
{ email: "test@example.com" },
],
]) {
it(`tracks clicks on ${href}`, function () {
const { calls } = clickLink(href);
expect(calls[0][0]).to.equal(name);
expect(calls[0][1]).to.include(metadata);
});
}

it("collects phone clicks by default without overriding cancelled navigation", function () {
const { calls, navigations } = clickLink("tel:+31201234567", "", {
defaultPrevented: true,
const { calls, navigations } = clickLink("tel:+31201234567", {
event: { defaultPrevented: true },
});
expect(calls.map(([name, metadata]) => ({ name, metadata }))).to.deep.equal(
[
Expand All @@ -104,10 +110,11 @@ describe("auto-events", function () {
let prevented = false;
const { calls, navigations, attributes, listeners } = clickLink(
"https://example.org/",
"example.org",
{
preventDefault() {
prevented = true;
event: {
preventDefault() {
prevented = true;
},
},
}
);
Expand All @@ -124,22 +131,30 @@ describe("auto-events", function () {
[{ namespace: "ba" }, "ba_event"],
[{ saGlobal: "my_event" }, "my_event"],
]) {
it(`sends events through the ${saGlobal} tracker function`, function () {
it(`sends events through the ${saGlobal} tracker function`, async function () {
const dom = createTrackerDOM({
settings: { autoCollect: false, ...settings },
});
const script = dom.window.document.createElement("script");
const { document } = dom.window;
const script = document.createElement("script");
script.src = "auto-events.js";
script.setAttribute("data-sa-global", saGlobal);
dom.window.document.head.appendChild(script);
document.head.appendChild(script);
const link = document.createElement("a");
link.href = "https://example.org/path";
document.body.appendChild(link);
runInContext(
readFileSync("dist/latest/auto-events.js", "utf8"),
dom.getInternalVMContext()
);
const link = dom.window.document.createElement("a");
link.href = "https://example.org/path";
// auto-events.js binds links once the document is complete
await new Promise((resolve) => setTimeout(resolve));
// jsdom doesn't implement navigation
document.addEventListener("click", (event) => event.preventDefault());

link.click();

dom.window.saAutomatedLink(link, "outbound");
expect(dom.window.saAutomatedLink).to.equal(undefined);

expect(
dom.sent.map((request) =>
Expand All @@ -149,52 +164,4 @@ describe("auto-events", function () {
dom.window.close();
});
}

it("tracks outbound link clicks", function (done) {
const dom = setupDOM();
const link = dom.window.document.createElement("a");
link.href = "https://example.org/path";
link.target = "_blank";
dom.window.document.body.appendChild(link);

dom.window.saAutomatedLink(link, "outbound");

setTimeout(() => {
expect(dom.events[0]).to.deep.include({ name: "outbound_example_org" });
expect(dom.events[0].metadata).to.include({ url: link.href });
done();
}, 0);
});

it("tracks download link clicks", function (done) {
const dom = setupDOM();
const link = dom.window.document.createElement("a");
link.href = "https://example.com/file.pdf";
link.target = "_blank";
dom.window.document.body.appendChild(link);

dom.window.saAutomatedLink(link, "download");

setTimeout(() => {
expect(dom.events[0]).to.deep.include({ name: "download_file_pdf" });
expect(dom.events[0].metadata).to.include({ url: link.href });
done();
}, 0);
});

it("tracks email link clicks", function (done) {
const dom = setupDOM();
const link = dom.window.document.createElement("a");
link.href = "mailto:test@example.com";
link.target = "_blank";
dom.window.document.body.appendChild(link);

dom.window.saAutomatedLink(link, "email");

setTimeout(() => {
expect(dom.events[0]).to.deep.include({ name: "email_test_example_com" });
expect(dom.events[0].metadata).to.include({ email: "test@example.com" });
done();
}, 0);
});
});
78 changes: 0 additions & 78 deletions test/unit/helpers/dom-auto-events.js

This file was deleted.

17 changes: 17 additions & 0 deletions test/unit/no-window.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
const { expect } = require("chai");
const { readFileSync } = require("fs");
const { runInNewContext } = require("vm");

describe("without a window", function () {
for (const file of [
"dist/latest/latest.js",
"dist/latest/light.js",
"dist/latest/auto-events.js",
]) {
it(`loads ${file} without errors`, function () {
expect(() =>
runInNewContext(readFileSync(file, "utf8"), { console })
).not.to.throw();
});
}
});
Loading