|
| 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