| 1 | import assert from "node:assert/strict"; |
| 2 | import { test } from "node:test"; |
| 3 | |
| 4 | import { isPublicPath, scrubEvent, scrubPath, scrubUrl } from "./analytics-scrub.ts"; |
| 5 | |
| 6 | const ORIGIN = "https://g1t.sh"; |
| 7 | |
| 8 | test("the front door is sent as it is", () => { |
| 9 | for (const path of ["/", "/pricing", "/explore", "/register", "/policies/privacy", "/pricing/"]) { |
| 10 | assert.equal(isPublicPath(path), true, path); |
| 11 | assert.equal(scrubPath(path), path); |
| 12 | } |
| 13 | }); |
| 14 | |
| 15 | test("names in other addresses are replaced, and g1t's own words kept", () => { |
| 16 | assert.equal(scrubPath("/acme/web/pull/12/files"), "/:name/:name/pull/:n/files"); |
| 17 | assert.equal(scrubPath("/acme/-/chat/secret-launch"), "/:name/-/chat/:name"); |
| 18 | assert.equal(scrubPath("/u/sam"), "/u/:name"); |
| 19 | assert.equal(scrubPath("/invite/abc123"), "/invite/:name"); |
| 20 | assert.equal(scrubPath("/acme/web/blob/main/src/keys.ts"), "/:name/:name/blob/:name/:name/:name"); |
| 21 | }); |
| 22 | |
| 23 | test("a URL on the site keeps only campaign parameters; another site's is left alone", () => { |
| 24 | assert.equal(scrubUrl("https://g1t.sh/reset?token=s3cret&utm_source=x", ORIGIN), "https://g1t.sh/reset?utm_source=x"); |
| 25 | assert.equal(scrubUrl("https://g1t.sh/search?q=private", ORIGIN), "https://g1t.sh/search"); |
| 26 | assert.equal(scrubUrl("https://g1t.sh/acme/web#L4", ORIGIN), "https://g1t.sh/:name/:name"); |
| 27 | assert.equal(scrubUrl("https://news.example/acme?x=1", ORIGIN), "https://news.example/acme?x=1"); |
| 28 | }); |
| 29 | |
| 30 | test("a click inside the app goes without its text, link, attributes or title", () => { |
| 31 | const event = scrubEvent( |
| 32 | { |
| 33 | event: "$autocapture", |
| 34 | properties: { |
| 35 | $current_url: "https://g1t.sh/acme/secret/issues/3", |
| 36 | $pathname: "/acme/secret/issues/3", |
| 37 | $title: "Rotate the prod key · acme/secret · g1t", |
| 38 | $el_text: "Rotate the prod key", |
| 39 | $elements_chain: 'a.link:text="Rotate the prod key"href="/acme/secret/issues/3"nth-child="1"attr__class="link"', |
| 40 | $elements: [{ tag_name: "a", $el_text: "Rotate", href: "/acme/secret", attr__title: "x", nth_child: 1 }], |
| 41 | $set_once: { $initial_current_url: "https://g1t.sh/acme/secret" }, |
| 42 | }, |
| 43 | }, |
| 44 | "/acme/secret/issues/3", |
| 45 | ORIGIN, |
| 46 | ); |
| 47 | assert.deepEqual(event?.properties, { |
| 48 | $current_url: "https://g1t.sh/:name/:name/issues/:n", |
| 49 | $pathname: "/:name/:name/issues/:n", |
| 50 | $title: "g1t", |
| 51 | $elements_chain: 'a.link:nth-child="1"', |
| 52 | $elements: [{ tag_name: "a", nth_child: 1 }], |
| 53 | $set_once: { $initial_current_url: "https://g1t.sh/:name/:name" }, |
| 54 | }); |
| 55 | }); |
| 56 | |
| 57 | test("on the front door a click keeps its text", () => { |
| 58 | const event = scrubEvent( |
| 59 | { event: "$autocapture", properties: { $el_text: "Start for free", $title: "g1t · Your team", $pathname: "/" } }, |
| 60 | "/", |
| 61 | ORIGIN, |
| 62 | ); |
| 63 | assert.deepEqual(event?.properties, { $el_text: "Start for free", $title: "g1t · Your team", $pathname: "/" }); |
| 64 | }); |
| 65 | |
| 66 | test("recordings and error reports are never sent, and heatmaps only from the front door", () => { |
| 67 | assert.equal(scrubEvent({ event: "$snapshot", properties: {} }, "/", ORIGIN), null); |
| 68 | assert.equal(scrubEvent({ event: "$exception", properties: {} }, "/", ORIGIN), null); |
| 69 | assert.equal(scrubEvent({ event: "$$heatmap", properties: {} }, "/acme/web", ORIGIN), null); |
| 70 | const heatmap = scrubEvent({ event: "$$heatmap", properties: { $heatmap_data: { "https://g1t.sh/pricing?ref=mail": [1] } } }, "/pricing", ORIGIN); |
| 71 | assert.deepEqual(heatmap?.properties, { $heatmap_data: { "https://g1t.sh/pricing": [1] } }); |
| 72 | }); |
| 73 | |
| 74 | test("pages after signing in keep their address but not their text, which can show an email address", () => { |
| 75 | assert.equal(scrubPath("/confirm-email"), "/confirm-email"); |
| 76 | assert.equal(scrubPath("/login/two-factor"), "/login/two-factor"); |
| 77 | const event = scrubEvent({ event: "$autocapture", properties: { $el_text: "Resend to sam@example.com" } }, "/confirm-email", ORIGIN); |
| 78 | assert.deepEqual(event?.properties, {}); |
| 79 | }); |