@jitsu/js 1.9.15 → 1.9.18-canary.1269.20250403142744
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/jitsu.cjs.js +6 -2
- package/dist/jitsu.d.ts +1 -12
- package/dist/jitsu.es.js +6 -2
- package/dist/web/p.js.txt +6 -2
- package/package.json +9 -3
- package/.turbo/turbo-build.log +0 -67
- package/.turbo/turbo-clean.log +0 -5
- package/.turbo/turbo-test.log +0 -4671
- package/__tests__/node/method-queue.test.ts +0 -66
- package/__tests__/node/nodejs.test.ts +0 -306
- package/__tests__/playwright/cases/anonymous-id-bug.html +0 -26
- package/__tests__/playwright/cases/basic.html +0 -32
- package/__tests__/playwright/cases/callbacks.html +0 -21
- package/__tests__/playwright/cases/cookie-names.html +0 -22
- package/__tests__/playwright/cases/disable-user-ids.html +0 -23
- package/__tests__/playwright/cases/dont-send.html +0 -22
- package/__tests__/playwright/cases/ip-policy.html +0 -22
- package/__tests__/playwright/cases/reset.html +0 -32
- package/__tests__/playwright/cases/segment-reference.html +0 -64
- package/__tests__/playwright/cases/url-bug.html +0 -20
- package/__tests__/playwright/integration.test.ts +0 -640
- package/__tests__/simple-syrup.ts +0 -136
- package/jest.config.js +0 -13
- package/playwrite.config.ts +0 -91
- package/rollup.config.js +0 -32
- package/src/analytics-plugin.ts +0 -988
- package/src/browser.ts +0 -163
- package/src/destination-plugins/ga4.ts +0 -138
- package/src/destination-plugins/gtm.ts +0 -142
- package/src/destination-plugins/index.ts +0 -61
- package/src/destination-plugins/logrocket.ts +0 -85
- package/src/destination-plugins/tag.ts +0 -85
- package/src/index.ts +0 -255
- package/src/method-queue.ts +0 -70
- package/src/script-loader.ts +0 -76
- package/src/tlds.ts +0 -27
- package/src/version.ts +0 -6
- package/tsconfig.json +0 -23
- package/tsconfig.test.json +0 -15
|
@@ -1,640 +0,0 @@
|
|
|
1
|
-
import { BrowserContext, expect, Page, test } from "@playwright/test";
|
|
2
|
-
import { createServer, SimpleSyrup } from "../simple-syrup";
|
|
3
|
-
import * as fs from "fs";
|
|
4
|
-
import * as path from "path";
|
|
5
|
-
import ejs from "ejs";
|
|
6
|
-
// import chalk from "chalk";
|
|
7
|
-
import * as process from "process";
|
|
8
|
-
import { AnalyticsClientEvent, AnalyticsInterface } from "@jitsu/protocols/analytics.d";
|
|
9
|
-
|
|
10
|
-
test.use({
|
|
11
|
-
ignoreHTTPSErrors: true,
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
const chalk = {
|
|
15
|
-
cyan: (str: string) => str,
|
|
16
|
-
bold: (str: string) => str,
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
const express = require("express");
|
|
20
|
-
const cookieParser = require("cookie-parser");
|
|
21
|
-
const app = express();
|
|
22
|
-
//const forge = require("node-forge");
|
|
23
|
-
|
|
24
|
-
let server: SimpleSyrup;
|
|
25
|
-
|
|
26
|
-
let requestLog: { type: string; body: AnalyticsClientEvent; headers: any }[] = [];
|
|
27
|
-
|
|
28
|
-
test.beforeAll(async () => {
|
|
29
|
-
const testCasesHandlers = fs.readdirSync(path.join(__dirname, "cases")).reduce((res, file) => {
|
|
30
|
-
console.log("Processing file", file);
|
|
31
|
-
return {
|
|
32
|
-
...res,
|
|
33
|
-
[`/${file}`]: (req, res) => {
|
|
34
|
-
res.setHeader("Content-Type", "text/html");
|
|
35
|
-
res.send(
|
|
36
|
-
ejs.compile(
|
|
37
|
-
fs.readFileSync(path.join(__dirname, "cases", file)).toString(),
|
|
38
|
-
{}
|
|
39
|
-
)({
|
|
40
|
-
trackingBase: server.baseUrl,
|
|
41
|
-
})
|
|
42
|
-
);
|
|
43
|
-
},
|
|
44
|
-
};
|
|
45
|
-
}, {});
|
|
46
|
-
server = await createServer({
|
|
47
|
-
port: 3088,
|
|
48
|
-
https: process.env.DISABLE_HTTPS !== "1" && process.env.DISABLE_HTTPS !== "true",
|
|
49
|
-
handlers: {
|
|
50
|
-
"/p.js": (req, res) => {
|
|
51
|
-
res.setHeader("Content-Type", "text/javascript");
|
|
52
|
-
res.send(fs.readFileSync(path.join(__dirname, "../../dist/web/p.js.txt")).toString());
|
|
53
|
-
},
|
|
54
|
-
"/api/s/:type": async (req, res) => {
|
|
55
|
-
//sleep for 30ms to simulate network latency. It helps catch bugs with async processing
|
|
56
|
-
await new Promise(resolve => setTimeout(resolve, 50));
|
|
57
|
-
|
|
58
|
-
res.setHeader("Content-Type", "text/javascript");
|
|
59
|
-
res.send({ ok: true });
|
|
60
|
-
requestLog.push({
|
|
61
|
-
type: req.params.type,
|
|
62
|
-
headers: req.headers,
|
|
63
|
-
body: req.body,
|
|
64
|
-
});
|
|
65
|
-
},
|
|
66
|
-
...testCasesHandlers,
|
|
67
|
-
},
|
|
68
|
-
});
|
|
69
|
-
console.log("Running on " + server.baseUrl);
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
test.afterAll(async () => {
|
|
73
|
-
await server?.close();
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
function sortKeysRecursively(obj: any): any {
|
|
77
|
-
if (typeof obj === "object" && obj !== null && !Array.isArray(obj)) {
|
|
78
|
-
return Object.keys(obj)
|
|
79
|
-
.sort()
|
|
80
|
-
.reduce((res, key) => {
|
|
81
|
-
res[key] = sortKeysRecursively(obj[key]);
|
|
82
|
-
return res;
|
|
83
|
-
}, {});
|
|
84
|
-
}
|
|
85
|
-
return obj;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function shouldKeepBrowserOpen() {
|
|
89
|
-
return process.env.KEEP_BROWSER_OPEN === "true" || process.env.KEEP_BROWSER_OPEN === "1";
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
async function createLoggingPage(browserContext: BrowserContext): Promise<{ page: Page; uncaughtErrors: Error[] }> {
|
|
93
|
-
const page = await browserContext.newPage();
|
|
94
|
-
const errors: Error[] = [];
|
|
95
|
-
|
|
96
|
-
page.on("pageerror", error => {
|
|
97
|
-
errors.push(error);
|
|
98
|
-
const border = chalk.cyan("│");
|
|
99
|
-
console.log();
|
|
100
|
-
console.log(`${border} ${chalk.cyan(`Browser Console UNCAUGHT ERROR:`)}`);
|
|
101
|
-
console.log(`${border} ` + error.stack.split("\n").join(`\n${border} `));
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
page.on("console", msg => {
|
|
105
|
-
const border = chalk.cyan("│");
|
|
106
|
-
console.log();
|
|
107
|
-
console.log(`${border} ${chalk.cyan(`Browser Console ${msg.type().toUpperCase()}`)}`);
|
|
108
|
-
console.log(`${border} ` + msg.text().split("\n").join(`\n${border} `));
|
|
109
|
-
});
|
|
110
|
-
return { page, uncaughtErrors: errors };
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
const generateTestEvents = async () => {
|
|
114
|
-
const implName = `${window["analytics"] ? "segment" : "jitsu"}`;
|
|
115
|
-
const analytics = (window["analytics"] || window["jitsu"]) as AnalyticsInterface;
|
|
116
|
-
console.log(`Generating test events. Implementation ${implName}: ${Object.keys(analytics)}`);
|
|
117
|
-
await analytics.identify("userId2", { email: "john.doe2@gmail.com", caseName: "basic-identify" });
|
|
118
|
-
await analytics.page("test-page-right-after-identify", { caseName: "test-page-right-after-identify" });
|
|
119
|
-
// jitsu must extract traits even from 'id' object
|
|
120
|
-
await analytics.identify({ email: "john.doe3@gmail.com", caseName: "identify-without-user-id" });
|
|
121
|
-
await analytics.group("group1", { name: "Group 1", caseName: "basic-group" });
|
|
122
|
-
await analytics.page({ caseName: "page-without-name", context: { page: { title: "Synthetic Title" } } });
|
|
123
|
-
await analytics.page("test-page", { caseName: "page-with-name" });
|
|
124
|
-
await analytics.track("testEvent", { caseName: "track-with-name" });
|
|
125
|
-
await analytics.identify(9292649175 as any, { caseName: "identify-with-numeric-id-1" });
|
|
126
|
-
console.log(`Test events for ${implName} has been generated`);
|
|
127
|
-
};
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* This test isn't really testing anything. It generates reference segment events
|
|
131
|
-
*/
|
|
132
|
-
test("segment-reference", async ({ browser }) => {
|
|
133
|
-
if (!process.env.SEGMENT_WRITE_KEY) {
|
|
134
|
-
console.log("Skipping segment reference generation, no SEGMENT_WRITE_KEY provided");
|
|
135
|
-
return;
|
|
136
|
-
}
|
|
137
|
-
// Using the browser fixture, you can get access to the BrowserContext
|
|
138
|
-
const browserContext = await browser.newContext();
|
|
139
|
-
const { page } = await createLoggingPage(browserContext);
|
|
140
|
-
const requests: Record<string, { payload: any }[]> = {};
|
|
141
|
-
page.on("response", async response => {
|
|
142
|
-
const request = response.request();
|
|
143
|
-
const apiPrefix = "https://api.segment.io/v1/";
|
|
144
|
-
if (request.url().startsWith(apiPrefix) && request.method() === "POST") {
|
|
145
|
-
const type = request.url().substring(apiPrefix.length);
|
|
146
|
-
requests[type] = requests[type] || [];
|
|
147
|
-
requests[type].push({
|
|
148
|
-
payload: await request.postDataJSON(),
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
|
-
console.log(`Request ${request.method()} ${request.url()} → ${response.status()}`);
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
await page.goto(`${server.baseUrl}/segment-reference.html?utm_source=source&utm_medium=medium&utm_campaign=campaign`);
|
|
155
|
-
|
|
156
|
-
await page.waitForFunction(() => window["__analyticsReady"] === true, undefined, {
|
|
157
|
-
timeout: 5000,
|
|
158
|
-
polling: 100,
|
|
159
|
-
});
|
|
160
|
-
console.log("Segment has been page loaded. Sending events");
|
|
161
|
-
await page.evaluate(generateTestEvents);
|
|
162
|
-
const cookies = (await browserContext.cookies()).reduce(
|
|
163
|
-
(res, cookie) => ({
|
|
164
|
-
...res,
|
|
165
|
-
[cookie.name]: cookie.value,
|
|
166
|
-
}),
|
|
167
|
-
{}
|
|
168
|
-
);
|
|
169
|
-
console.log("🍪 Segment Cookies", cookies);
|
|
170
|
-
let counter = 1;
|
|
171
|
-
for (const type of Object.keys(requests)) {
|
|
172
|
-
for (const { payload } of requests[type]) {
|
|
173
|
-
const dir = path.join(__dirname, "artifacts", "segment-reference");
|
|
174
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
175
|
-
const file = path.join(
|
|
176
|
-
dir,
|
|
177
|
-
`${counter++} - ${payload.traits?.caseName || payload.properties?.caseName || payload.context?.caseName}.json`
|
|
178
|
-
);
|
|
179
|
-
fs.writeFileSync(file, JSON.stringify(sortKeysRecursively(payload), null, 2));
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
function describeEvent(type: string, body: any) {
|
|
185
|
-
const params = [
|
|
186
|
-
body.userId ? "userId=" + body.userId : undefined,
|
|
187
|
-
body.anonymousId ? "anonId=" + body.anonymousId : undefined,
|
|
188
|
-
body.traits ? ["traits=" + JSON.stringify(body.traits)] : [],
|
|
189
|
-
]
|
|
190
|
-
.filter(x => !!x)
|
|
191
|
-
.join(", ");
|
|
192
|
-
return `${type}${type === "track" ? `(${body.event})` : ""}[${params}]`;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
function clearRequestLog() {
|
|
196
|
-
requestLog.length = 0;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
test("jitsu-queue-callbacks", async ({ browser }) => {
|
|
200
|
-
clearRequestLog();
|
|
201
|
-
const browserContext = await browser.newContext();
|
|
202
|
-
const { page, uncaughtErrors } = await createLoggingPage(browserContext);
|
|
203
|
-
const [pageResult] = await Promise.all([page.goto(`${server.baseUrl}/callbacks.html`)]);
|
|
204
|
-
await page.waitForFunction(() => window["jitsu"] !== undefined, undefined, {
|
|
205
|
-
timeout: 1000,
|
|
206
|
-
polling: 100,
|
|
207
|
-
});
|
|
208
|
-
//wait for some time since the server has an artificial latency of 30ms
|
|
209
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
210
|
-
console.log(
|
|
211
|
-
`📝 Request log size of ${requestLog.length}`,
|
|
212
|
-
requestLog.map(x => describeEvent(x.type, x.body))
|
|
213
|
-
);
|
|
214
|
-
expect(requestLog.length).toBe(3);
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
// Skip this test because jitsu-js no longer relies on canonical URL
|
|
218
|
-
test.skip("url-bug", async ({ browser, context }) => {
|
|
219
|
-
//tests a bug in getanalytics.io where the url without slash provided by
|
|
220
|
-
//<link rel="canonical" ../> causes incorrect page path
|
|
221
|
-
const browserContext = await browser.newContext();
|
|
222
|
-
const { page, uncaughtErrors } = await createLoggingPage(browserContext);
|
|
223
|
-
const [pageResult] = await Promise.all([page.goto(`${server.baseUrl}/url-bug.html`)]);
|
|
224
|
-
|
|
225
|
-
await page.waitForFunction(() => window["jitsu"] !== undefined, undefined, {
|
|
226
|
-
timeout: 1000,
|
|
227
|
-
polling: 100,
|
|
228
|
-
});
|
|
229
|
-
expect(pageResult.status()).toBe(200);
|
|
230
|
-
//wait for some time since the server has an artificial latency of 30ms
|
|
231
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
232
|
-
expect(uncaughtErrors.length).toEqual(0);
|
|
233
|
-
expect(requestLog.length).toBe(2);
|
|
234
|
-
console.log(
|
|
235
|
-
`📝 Request log size of ${requestLog.length}`,
|
|
236
|
-
requestLog.map(x => describeEvent(x.type, x.body))
|
|
237
|
-
);
|
|
238
|
-
//a track contains a valid URL, probably because analytics can't grab the canonical URL yet
|
|
239
|
-
const trackEvent = requestLog.find(x => x.type === "page");
|
|
240
|
-
expect(trackEvent).toBeDefined();
|
|
241
|
-
const pagePath = trackEvent.body.context.page.path;
|
|
242
|
-
expect(pagePath).toBeDefined();
|
|
243
|
-
//it's "//localhost:3088" when the bug is present
|
|
244
|
-
expect(pagePath).toEqual("/");
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
test("reset", async ({ browser }) => {
|
|
248
|
-
clearRequestLog();
|
|
249
|
-
const browserContext = await browser.newContext();
|
|
250
|
-
const { page, uncaughtErrors } = await createLoggingPage(browserContext);
|
|
251
|
-
const [pageResult] = await Promise.all([page.goto(`${server.baseUrl}/reset.html`)]);
|
|
252
|
-
await page.waitForFunction(() => window["jitsu"] !== undefined, undefined, {
|
|
253
|
-
timeout: 1000,
|
|
254
|
-
polling: 100,
|
|
255
|
-
});
|
|
256
|
-
expect(pageResult.status()).toBe(200);
|
|
257
|
-
//wait for some time since the server has an artificial latency of 30ms
|
|
258
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
259
|
-
expect(uncaughtErrors.length).toEqual(0);
|
|
260
|
-
expect(requestLog.length).toBe(3);
|
|
261
|
-
console.log(
|
|
262
|
-
`📝 Request log size of ${requestLog.length}`,
|
|
263
|
-
requestLog.map(x => describeEvent(x.type, x.body))
|
|
264
|
-
);
|
|
265
|
-
const [identifyEvent, firstTrack, secondTrack] = requestLog;
|
|
266
|
-
expect(firstTrack.body.anonymousId).toEqual("john-doe-id-1");
|
|
267
|
-
|
|
268
|
-
const cookies = await browserContext.cookies();
|
|
269
|
-
// all cookies should be cleared by .reset()
|
|
270
|
-
// but new cookie for new anonymousId should be set
|
|
271
|
-
expect(cookies.length).toBe(1);
|
|
272
|
-
expect(cookies[0].name).toEqual("__eventn_id");
|
|
273
|
-
const newAnonymousId = cookies[0].value;
|
|
274
|
-
console.log(`🍪Cookies`, cookies);
|
|
275
|
-
|
|
276
|
-
//second identify call should not reach the server, but it should change the traits
|
|
277
|
-
expect(firstTrack.body.context.traits?.email).toEqual("john2@example.com");
|
|
278
|
-
|
|
279
|
-
expect(secondTrack.body.anonymousId).not.toBeNull();
|
|
280
|
-
expect(secondTrack.body.anonymousId).toBeDefined();
|
|
281
|
-
expect(secondTrack.body.anonymousId).toEqual(newAnonymousId);
|
|
282
|
-
expect(secondTrack.body.anonymousId).not.toEqual("john-doe-id-1");
|
|
283
|
-
});
|
|
284
|
-
|
|
285
|
-
const generateEventsForConsentTests = async () => {
|
|
286
|
-
const analytics = window["jitsu"] as AnalyticsInterface;
|
|
287
|
-
await analytics.identify("myUserId", { email: "myUserId@example.com" });
|
|
288
|
-
await analytics.group("myGroupId", { name: "myGroupId" });
|
|
289
|
-
await analytics.page("myPage");
|
|
290
|
-
};
|
|
291
|
-
|
|
292
|
-
test("ip-policy", async ({ browser }) => {
|
|
293
|
-
clearRequestLog();
|
|
294
|
-
const browserContext = await browser.newContext();
|
|
295
|
-
const { page, uncaughtErrors } = await createLoggingPage(browserContext);
|
|
296
|
-
const [pageResult] = await Promise.all([page.goto(`${server.baseUrl}/ip-policy.html`)]);
|
|
297
|
-
await page.waitForFunction(() => window["jitsu"] !== undefined, undefined, {
|
|
298
|
-
timeout: 1000,
|
|
299
|
-
polling: 100,
|
|
300
|
-
});
|
|
301
|
-
expect(pageResult?.status()).toBe(200);
|
|
302
|
-
//wait for some time since the server has an artificial latency of 30ms
|
|
303
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
304
|
-
expect(uncaughtErrors.length).toEqual(0);
|
|
305
|
-
expect(requestLog.length).toBe(1);
|
|
306
|
-
const p = requestLog[0];
|
|
307
|
-
expect(p.headers?.["x-ip-policy"]).toEqual("stripLastOctet");
|
|
308
|
-
});
|
|
309
|
-
|
|
310
|
-
test("dont-send", async ({ browser }) => {
|
|
311
|
-
clearRequestLog();
|
|
312
|
-
const browserContext = await browser.newContext();
|
|
313
|
-
const { page, uncaughtErrors } = await createLoggingPage(browserContext);
|
|
314
|
-
const [pageResult] = await Promise.all([page.goto(`${server.baseUrl}/dont-send.html`)]);
|
|
315
|
-
await page.waitForFunction(() => window["jitsu"] !== undefined, undefined, {
|
|
316
|
-
timeout: 1000,
|
|
317
|
-
polling: 100,
|
|
318
|
-
});
|
|
319
|
-
expect(pageResult?.status()).toBe(200);
|
|
320
|
-
//wait for some time since the server has an artificial latency of 30ms
|
|
321
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
322
|
-
expect(uncaughtErrors.length).toEqual(0);
|
|
323
|
-
expect(requestLog.length).toBe(0);
|
|
324
|
-
await page.evaluate(generateEventsForConsentTests);
|
|
325
|
-
|
|
326
|
-
const cookies = await browserContext.cookies();
|
|
327
|
-
|
|
328
|
-
expect(uncaughtErrors.length).toEqual(0);
|
|
329
|
-
expect(requestLog.length).toBe(0);
|
|
330
|
-
expect(cookies.length).toBe(0);
|
|
331
|
-
});
|
|
332
|
-
|
|
333
|
-
test("dont-send-then-consent", async ({ browser }) => {
|
|
334
|
-
clearRequestLog();
|
|
335
|
-
const browserContext = await browser.newContext();
|
|
336
|
-
const { page, uncaughtErrors } = await createLoggingPage(browserContext);
|
|
337
|
-
const [pageResult] = await Promise.all([page.goto(`${server.baseUrl}/dont-send.html`)]);
|
|
338
|
-
await page.waitForFunction(() => window["jitsu"] !== undefined, undefined, {
|
|
339
|
-
timeout: 1000,
|
|
340
|
-
polling: 100,
|
|
341
|
-
});
|
|
342
|
-
expect(pageResult?.status()).toBe(200);
|
|
343
|
-
//wait for some time since the server has an artificial latency of 30ms
|
|
344
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
345
|
-
expect(uncaughtErrors.length).toEqual(0);
|
|
346
|
-
expect(requestLog.length).toBe(0);
|
|
347
|
-
|
|
348
|
-
await page.evaluate(async () => {
|
|
349
|
-
const analytics = window["jitsu"] as AnalyticsInterface;
|
|
350
|
-
analytics.configure({
|
|
351
|
-
privacy: {
|
|
352
|
-
dontSend: false,
|
|
353
|
-
consentCategories: {
|
|
354
|
-
analytics: true,
|
|
355
|
-
},
|
|
356
|
-
},
|
|
357
|
-
});
|
|
358
|
-
});
|
|
359
|
-
await page.evaluate(generateEventsForConsentTests);
|
|
360
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
361
|
-
|
|
362
|
-
const cookies = await browserContext.cookies();
|
|
363
|
-
expect(uncaughtErrors.length).toEqual(0);
|
|
364
|
-
expect(requestLog.length).toBe(3);
|
|
365
|
-
expect(cookies.length).toBe(5);
|
|
366
|
-
const p = requestLog[2];
|
|
367
|
-
expect(p.type).toEqual("page");
|
|
368
|
-
expect(p.body.userId).toEqual("myUserId");
|
|
369
|
-
expect(p.body.groupId).toEqual("myGroupId");
|
|
370
|
-
expect(p.body.context?.traits?.email).toEqual("myUserId@example.com");
|
|
371
|
-
expect(p.body.context?.consent?.categoryPreferences).toEqual({ analytics: true });
|
|
372
|
-
expect((p.body.anonymousId ?? "").length).toBeGreaterThan(0);
|
|
373
|
-
});
|
|
374
|
-
|
|
375
|
-
test("disable-user-ids", async ({ browser }) => {
|
|
376
|
-
clearRequestLog();
|
|
377
|
-
const browserContext = await browser.newContext();
|
|
378
|
-
const { page, uncaughtErrors } = await createLoggingPage(browserContext);
|
|
379
|
-
const [pageResult] = await Promise.all([page.goto(`${server.baseUrl}/disable-user-ids.html`)]);
|
|
380
|
-
await page.waitForFunction(() => window["jitsu"] !== undefined, undefined, {
|
|
381
|
-
timeout: 1000,
|
|
382
|
-
polling: 100,
|
|
383
|
-
});
|
|
384
|
-
expect(pageResult?.status()).toBe(200);
|
|
385
|
-
//wait for some time since the server has an artificial latency of 30ms
|
|
386
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
387
|
-
expect(uncaughtErrors.length).toEqual(0);
|
|
388
|
-
|
|
389
|
-
expect(requestLog.length).toBe(0);
|
|
390
|
-
await page.evaluate(generateEventsForConsentTests);
|
|
391
|
-
|
|
392
|
-
const cookies = await browserContext.cookies();
|
|
393
|
-
|
|
394
|
-
expect(uncaughtErrors.length).toEqual(0);
|
|
395
|
-
expect(cookies.length).toBe(0);
|
|
396
|
-
expect(requestLog.length).toBe(1);
|
|
397
|
-
const p = requestLog[0];
|
|
398
|
-
expect(p.type).toEqual("page");
|
|
399
|
-
expect(p.body.userId).toBeUndefined();
|
|
400
|
-
expect(p.body.groupId).toBeUndefined();
|
|
401
|
-
expect(p.body.context?.traits?.email).toBeUndefined();
|
|
402
|
-
expect(p.body.anonymousId).toBeUndefined();
|
|
403
|
-
expect(p.body.properties?.path).toBe("/disable-user-ids.html");
|
|
404
|
-
});
|
|
405
|
-
|
|
406
|
-
test("disable-user-ids-then-consent", async ({ browser }) => {
|
|
407
|
-
clearRequestLog();
|
|
408
|
-
const browserContext = await browser.newContext();
|
|
409
|
-
const { page, uncaughtErrors } = await createLoggingPage(browserContext);
|
|
410
|
-
const [pageResult] = await Promise.all([page.goto(`${server.baseUrl}/disable-user-ids.html`)]);
|
|
411
|
-
await page.waitForFunction(() => window["jitsu"] !== undefined, undefined, {
|
|
412
|
-
timeout: 1000,
|
|
413
|
-
polling: 100,
|
|
414
|
-
});
|
|
415
|
-
expect(pageResult?.status()).toBe(200);
|
|
416
|
-
//wait for some time since the server has an artificial latency of 30ms
|
|
417
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
418
|
-
expect(uncaughtErrors.length).toEqual(0);
|
|
419
|
-
expect(requestLog.length).toBe(0);
|
|
420
|
-
await page.evaluate(generateEventsForConsentTests);
|
|
421
|
-
let cookies = await browserContext.cookies();
|
|
422
|
-
expect(uncaughtErrors.length).toEqual(0);
|
|
423
|
-
expect(cookies.length).toBe(0);
|
|
424
|
-
expect(requestLog.length).toBe(1);
|
|
425
|
-
|
|
426
|
-
await page.evaluate(async () => {
|
|
427
|
-
const analytics = window["jitsu"] as AnalyticsInterface;
|
|
428
|
-
analytics.configure({
|
|
429
|
-
privacy: {
|
|
430
|
-
disableUserIds: false,
|
|
431
|
-
consentCategories: {
|
|
432
|
-
analytics: true,
|
|
433
|
-
},
|
|
434
|
-
},
|
|
435
|
-
});
|
|
436
|
-
});
|
|
437
|
-
await page.evaluate(generateEventsForConsentTests);
|
|
438
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
439
|
-
|
|
440
|
-
cookies = await browserContext.cookies();
|
|
441
|
-
expect(uncaughtErrors.length).toEqual(0);
|
|
442
|
-
expect(requestLog.length).toBe(4);
|
|
443
|
-
expect(cookies.length).toBe(5);
|
|
444
|
-
const p = requestLog[3];
|
|
445
|
-
expect(p.type).toEqual("page");
|
|
446
|
-
expect(p.body.userId).toEqual("myUserId");
|
|
447
|
-
expect(p.body.groupId).toEqual("myGroupId");
|
|
448
|
-
expect(p.body.context?.traits?.email).toEqual("myUserId@example.com");
|
|
449
|
-
expect(p.body.context?.consent?.categoryPreferences).toEqual({ analytics: true });
|
|
450
|
-
expect((p.body.anonymousId ?? "").length).toBeGreaterThan(0);
|
|
451
|
-
});
|
|
452
|
-
|
|
453
|
-
test("anonymous-id-bug", async ({ browser }) => {
|
|
454
|
-
clearRequestLog();
|
|
455
|
-
const anonymousId = "1724633695283.638279";
|
|
456
|
-
const browserContext = await browser.newContext();
|
|
457
|
-
await browserContext.addCookies([{ name: "__eventn_id", value: anonymousId, url: server.baseUrl }]);
|
|
458
|
-
const { page, uncaughtErrors } = await createLoggingPage(browserContext);
|
|
459
|
-
const [pageResult] = await Promise.all([page.goto(`${server.baseUrl}/anonymous-id-bug.html`)]);
|
|
460
|
-
await page.waitForFunction(() => window["jitsu"] !== undefined, undefined, {
|
|
461
|
-
timeout: 1000,
|
|
462
|
-
polling: 100,
|
|
463
|
-
});
|
|
464
|
-
expect(pageResult.status()).toBe(200);
|
|
465
|
-
const cookies = (await browserContext.cookies()).reduce(
|
|
466
|
-
(res, cookie) => ({
|
|
467
|
-
...res,
|
|
468
|
-
[cookie.name]: cookie.value,
|
|
469
|
-
}),
|
|
470
|
-
{}
|
|
471
|
-
);
|
|
472
|
-
console.log("🍪 Jitsu Cookies", cookies);
|
|
473
|
-
//wait for some time since the server has an artificial latency of 30ms
|
|
474
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
475
|
-
expect(uncaughtErrors.length).toEqual(0);
|
|
476
|
-
console.log(
|
|
477
|
-
`📝 Request log size of ${requestLog.length}`,
|
|
478
|
-
requestLog.map(x => describeEvent(x.type, x.body))
|
|
479
|
-
);
|
|
480
|
-
const p = requestLog[0];
|
|
481
|
-
console.log(chalk.bold("📝 Checking page event"), JSON.stringify(p, null, 3));
|
|
482
|
-
expect(p.body.anonymousId).toEqual(anonymousId);
|
|
483
|
-
});
|
|
484
|
-
|
|
485
|
-
test("cookie-names", async ({ browser }) => {
|
|
486
|
-
clearRequestLog();
|
|
487
|
-
const browserContext = await browser.newContext();
|
|
488
|
-
const { page, uncaughtErrors } = await createLoggingPage(browserContext);
|
|
489
|
-
const pageResult = await page.goto(`${server.baseUrl}/cookie-names.html`);
|
|
490
|
-
await page.waitForFunction(() => window["jitsu"] !== undefined, undefined, {
|
|
491
|
-
timeout: 1000,
|
|
492
|
-
polling: 100,
|
|
493
|
-
});
|
|
494
|
-
expect(pageResult.status()).toBe(200);
|
|
495
|
-
const cookies = (await browserContext.cookies()).reduce(
|
|
496
|
-
(res, cookie) => ({
|
|
497
|
-
...res,
|
|
498
|
-
[cookie.name]: cookie.value,
|
|
499
|
-
}),
|
|
500
|
-
{}
|
|
501
|
-
);
|
|
502
|
-
console.log("🍪 Jitsu Cookies", cookies);
|
|
503
|
-
expect(cookies).toHaveProperty("my_anon_ck");
|
|
504
|
-
const anonymousId = cookies["my_anon_ck"];
|
|
505
|
-
|
|
506
|
-
//wait for some time since the server has an artificial latency of 30ms
|
|
507
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
508
|
-
expect(uncaughtErrors.length).toEqual(0);
|
|
509
|
-
console.log(
|
|
510
|
-
`📝 Request log size of ${requestLog.length}`,
|
|
511
|
-
requestLog.map(x => describeEvent(x.type, x.body))
|
|
512
|
-
);
|
|
513
|
-
expect(requestLog[0].body.anonymousId).toEqual(anonymousId);
|
|
514
|
-
|
|
515
|
-
const { page: secondPage } = await createLoggingPage(browserContext);
|
|
516
|
-
const pageResult2 = await secondPage.goto(
|
|
517
|
-
`${server.baseUrl}/cookie-names.html?utm_source=source&utm_medium=medium&utm_campaign=campaign`
|
|
518
|
-
);
|
|
519
|
-
await secondPage.waitForFunction(() => window["jitsu"] !== undefined, undefined, {
|
|
520
|
-
timeout: 1000,
|
|
521
|
-
polling: 100,
|
|
522
|
-
});
|
|
523
|
-
expect(pageResult2.status()).toBe(200);
|
|
524
|
-
const cookies2 = (await browserContext.cookies()).reduce(
|
|
525
|
-
(res, cookie) => ({
|
|
526
|
-
...res,
|
|
527
|
-
[cookie.name]: cookie.value,
|
|
528
|
-
}),
|
|
529
|
-
{}
|
|
530
|
-
);
|
|
531
|
-
console.log("🍪 Jitsu Cookies", cookies2);
|
|
532
|
-
expect(cookies2).toHaveProperty("my_anon_ck");
|
|
533
|
-
expect(cookies["my_anon_ck"]).toEqual(anonymousId);
|
|
534
|
-
|
|
535
|
-
//wait for some time since the server has an artificial latency of 30ms
|
|
536
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
537
|
-
expect(uncaughtErrors.length).toEqual(0);
|
|
538
|
-
console.log(
|
|
539
|
-
`📝 Request log size of ${requestLog.length}`,
|
|
540
|
-
requestLog.map(x => describeEvent(x.type, x.body))
|
|
541
|
-
);
|
|
542
|
-
expect(requestLog[1].body.anonymousId).toEqual(anonymousId);
|
|
543
|
-
});
|
|
544
|
-
|
|
545
|
-
test("basic", async ({ browser }) => {
|
|
546
|
-
clearRequestLog();
|
|
547
|
-
const browserContext = await browser.newContext();
|
|
548
|
-
await browserContext.addCookies([
|
|
549
|
-
{ name: "_fbc", value: "fbc-id", url: server.baseUrl },
|
|
550
|
-
{ name: "_fbp", value: "fbp-id", url: server.baseUrl },
|
|
551
|
-
{ name: "_ttp", value: "ttp-id", url: server.baseUrl },
|
|
552
|
-
]);
|
|
553
|
-
|
|
554
|
-
const { page: firstPage, uncaughtErrors: firstPageErrors } = await createLoggingPage(browserContext);
|
|
555
|
-
const [pageResult] = await Promise.all([
|
|
556
|
-
firstPage.goto(`${server.baseUrl}/basic.html?utm_source=source&utm_medium=medium&utm_campaign=campaign`),
|
|
557
|
-
]);
|
|
558
|
-
|
|
559
|
-
await firstPage.waitForFunction(() => window["jitsu"] !== undefined, undefined, {
|
|
560
|
-
timeout: 1000,
|
|
561
|
-
polling: 100,
|
|
562
|
-
});
|
|
563
|
-
expect(pageResult.status()).toBe(200);
|
|
564
|
-
const cookies = (await browserContext.cookies()).reduce(
|
|
565
|
-
(res, cookie) => ({
|
|
566
|
-
...res,
|
|
567
|
-
[cookie.name]: cookie.value,
|
|
568
|
-
}),
|
|
569
|
-
{}
|
|
570
|
-
);
|
|
571
|
-
console.log("🍪 Jitsu Cookies", cookies);
|
|
572
|
-
//wait for some time since the server has an artificial latency of 30ms
|
|
573
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
574
|
-
expect(firstPageErrors.length).toEqual(0);
|
|
575
|
-
const anonymousId = cookies["__eventn_id"];
|
|
576
|
-
expect(anonymousId).toBeDefined();
|
|
577
|
-
expect(cookies["__eventn_uid"]).toBe("john-doe-id-1");
|
|
578
|
-
expect(cookies["__eventn_id_usr"]).toBeDefined();
|
|
579
|
-
expect(JSON.parse(decodeURIComponent(cookies["__eventn_id_usr"])).email).toEqual("john.doe@gmail.com");
|
|
580
|
-
console.log(
|
|
581
|
-
`📝 Request log size of ${requestLog.length}`,
|
|
582
|
-
requestLog.map(x => describeEvent(x.type, x.body))
|
|
583
|
-
);
|
|
584
|
-
let identifies = requestLog.filter(x => x.type === "identify");
|
|
585
|
-
let pages = requestLog.filter(x => x.type === "page");
|
|
586
|
-
let tracks = requestLog.filter(x => x.type === "track");
|
|
587
|
-
expect(identifies.length).toBe(1);
|
|
588
|
-
expect(pages.length).toBe(1);
|
|
589
|
-
expect(tracks.length).toBe(1);
|
|
590
|
-
|
|
591
|
-
const track = tracks[0].body as AnalyticsClientEvent;
|
|
592
|
-
const page = pages[0].body as AnalyticsClientEvent;
|
|
593
|
-
const identify = identifies[0].body as AnalyticsClientEvent;
|
|
594
|
-
|
|
595
|
-
console.log(chalk.bold("📝 Checking track event"), JSON.stringify(track, null, 3));
|
|
596
|
-
expect(track.properties.trackParam).toEqual("trackValue");
|
|
597
|
-
expect(track.type).toEqual("track");
|
|
598
|
-
expect(track.context.clientIds).toHaveProperty("fbc", "fbc-id");
|
|
599
|
-
expect(track.context.clientIds).toHaveProperty("fbp", "fbp-id");
|
|
600
|
-
expect(track.context.clientIds).toHaveProperty("ttp", "ttp-id");
|
|
601
|
-
expect(track.context.traits.email).toEqual("john.doe@gmail.com");
|
|
602
|
-
expect(track.userId).toEqual("john-doe-id-1");
|
|
603
|
-
expect(track.event).toEqual("pageLoaded");
|
|
604
|
-
|
|
605
|
-
console.log(chalk.bold("📝 Checking identify event"), JSON.stringify(identify, null, 3));
|
|
606
|
-
expect(identify.traits.email).toEqual("john.doe@gmail.com");
|
|
607
|
-
expect(identify.userId).toEqual("john-doe-id-1");
|
|
608
|
-
expect(identify.anonymousId).toEqual(anonymousId);
|
|
609
|
-
|
|
610
|
-
console.log(chalk.bold("📝 Checking page event"), JSON.stringify(page, null, 3));
|
|
611
|
-
expect(page.anonymousId).toEqual(anonymousId);
|
|
612
|
-
expect(page.context.clientIds).toHaveProperty("fbc", "fbc-id");
|
|
613
|
-
expect(page.context.clientIds).toHaveProperty("fbp", "fbp-id");
|
|
614
|
-
expect(page.context.clientIds).toHaveProperty("ttp", "ttp-id");
|
|
615
|
-
expect(page.context.traits.email).toEqual("john.doe@gmail.com");
|
|
616
|
-
expect(page.userId).toEqual("john-doe-id-1");
|
|
617
|
-
|
|
618
|
-
expect(page.context.campaign.source).toEqual("source");
|
|
619
|
-
|
|
620
|
-
const { page: secondPage, uncaughtErrors: secondPageErrors } = await createLoggingPage(browserContext);
|
|
621
|
-
await secondPage.goto(`${server.baseUrl}/basic.html?utm_source=source&utm_medium=medium&utm_campaign=campaign`);
|
|
622
|
-
await secondPage.waitForFunction(() => window["jitsu"] !== undefined, undefined, {
|
|
623
|
-
timeout: 1000,
|
|
624
|
-
polling: 100,
|
|
625
|
-
});
|
|
626
|
-
clearRequestLog();
|
|
627
|
-
|
|
628
|
-
await secondPage.evaluate(generateTestEvents);
|
|
629
|
-
expect(secondPageErrors.length).toBe(0);
|
|
630
|
-
let counter = 1;
|
|
631
|
-
requestLog.forEach(({ body: payload }) => {
|
|
632
|
-
const dir = path.join(__dirname, "artifacts", "requests");
|
|
633
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
634
|
-
const file = path.join(
|
|
635
|
-
dir,
|
|
636
|
-
`${counter++} - ${payload.traits?.caseName || payload.properties?.caseName || payload.context?.caseName}.json`
|
|
637
|
-
);
|
|
638
|
-
fs.writeFileSync(file, JSON.stringify(sortKeysRecursively(payload), null, 2));
|
|
639
|
-
});
|
|
640
|
-
});
|