@jitsu/js 1.7.2 → 1.8.1
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/.turbo/turbo-build.log +85 -105
- package/.turbo/turbo-clean.log +5 -5
- package/.turbo/turbo-test.log +1955 -1369
- package/__tests__/node/nodejs.test.ts +12 -8
- package/__tests__/playwright/cases/basic.html +1 -1
- package/__tests__/playwright/cases/url-bug.html +20 -0
- package/__tests__/playwright/integration.test.ts +60 -8
- package/dist/analytics-plugin.d.ts +6 -1
- package/dist/jitsu.cjs.js +204 -112
- package/dist/jitsu.d.ts +6 -0
- package/dist/jitsu.es.js +204 -112
- package/dist/version.d.ts +2 -1
- package/dist/web/p.js.txt +206 -114
- package/package.json +3 -3
- package/src/analytics-plugin.ts +138 -85
- package/src/browser.ts +3 -2
- package/src/index.ts +87 -17
- package/src/jitsu.ts +7 -0
- package/src/version.ts +4 -1
|
@@ -12,18 +12,22 @@ describe("Test Jitsu NodeJS client", () => {
|
|
|
12
12
|
|
|
13
13
|
const startServer = async () => {
|
|
14
14
|
requestLog = [];
|
|
15
|
+
let handler = (req, res) => {
|
|
16
|
+
res.setHeader("Content-Type", "text/javascript");
|
|
17
|
+
res.send({ ok: true });
|
|
18
|
+
requestLog.push({
|
|
19
|
+
type: req.params.type,
|
|
20
|
+
body: req.body,
|
|
21
|
+
});
|
|
22
|
+
};
|
|
15
23
|
server = await createServer({
|
|
16
24
|
port: 3088,
|
|
17
25
|
https: false,
|
|
18
26
|
handlers: {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
type: req.params.type,
|
|
24
|
-
body: req.body,
|
|
25
|
-
});
|
|
26
|
-
},
|
|
27
|
+
//we're using same handler for s2s and browser events since
|
|
28
|
+
//we don't check types anyway
|
|
29
|
+
"/api/s/:type": handler,
|
|
30
|
+
"/api/s/s2s/:type": handler,
|
|
27
31
|
},
|
|
28
32
|
});
|
|
29
33
|
console.log("Running on " + server.baseUrl);
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
<title>Tracking page</title>
|
|
9
9
|
<script>
|
|
10
10
|
window.testOnload = async j => {
|
|
11
|
-
j.identify("
|
|
11
|
+
j.identify("john-doe-id-1", { email: "john.doe@gmail.com" });
|
|
12
12
|
j.track("pageLoaded", { trackParam: "trackValue" });
|
|
13
13
|
};
|
|
14
14
|
</script>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
|
|
3
|
+
<html lang="en">
|
|
4
|
+
<head>
|
|
5
|
+
<meta charset="utf-8" />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
7
|
+
<link rel="canonical" href="<%=trackingBase%>" />
|
|
8
|
+
<title>Tracking page</title>
|
|
9
|
+
<script type="text/javascript" src="<%=trackingBase%>/p.js" data-debug="true" defer></script>
|
|
10
|
+
<script>
|
|
11
|
+
(window.jitsuQ = window.jitsuQ || []).push(function (jitsu) {
|
|
12
|
+
jitsu.track("test-event");
|
|
13
|
+
});
|
|
14
|
+
</script>
|
|
15
|
+
</head>
|
|
16
|
+
|
|
17
|
+
<body>
|
|
18
|
+
<h1>Test</h1>
|
|
19
|
+
</body>
|
|
20
|
+
</html>
|
|
@@ -52,7 +52,10 @@ test.beforeAll(async () => {
|
|
|
52
52
|
res.setHeader("Content-Type", "text/javascript");
|
|
53
53
|
res.send(fs.readFileSync(path.join(__dirname, "../../dist/web/p.js.txt")).toString());
|
|
54
54
|
},
|
|
55
|
-
"/api/s/:type": (req, res) => {
|
|
55
|
+
"/api/s/:type": async (req, res) => {
|
|
56
|
+
//sleep for 30ms to simulate network latency. It helps catch bugs with async processing
|
|
57
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
58
|
+
|
|
56
59
|
res.setHeader("Content-Type", "text/javascript");
|
|
57
60
|
res.send({ ok: true });
|
|
58
61
|
requestLog.push({
|
|
@@ -112,12 +115,14 @@ const generateTestEvents = async () => {
|
|
|
112
115
|
const analytics = (window["analytics"] || window["jitsu"]) as AnalyticsInterface;
|
|
113
116
|
console.log(`Generating test events. Implementation ${implName}: ${Object.keys(analytics)}`);
|
|
114
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" });
|
|
115
119
|
// jitsu must extract traits even from 'id' object
|
|
116
120
|
await analytics.identify({ email: "john.doe3@gmail.com", caseName: "identify-without-user-id" });
|
|
117
121
|
await analytics.group("group1", { name: "Group 1", caseName: "basic-group" });
|
|
118
122
|
await analytics.page({ caseName: "page-without-name", context: { page: { title: "Synthetic Title" } } });
|
|
119
123
|
await analytics.page("test-page", { caseName: "page-with-name" });
|
|
120
124
|
await analytics.track("testEvent", { caseName: "track-with-name" });
|
|
125
|
+
await analytics.identify(9292649175 as any, { caseName: "identify-with-numeric-id-1" });
|
|
121
126
|
console.log(`Test events for ${implName} has been generated`);
|
|
122
127
|
};
|
|
123
128
|
|
|
@@ -162,21 +167,61 @@ test("segment-reference", async ({ browser }) => {
|
|
|
162
167
|
{}
|
|
163
168
|
);
|
|
164
169
|
console.log("🍪 Segment Cookies", cookies);
|
|
165
|
-
|
|
170
|
+
let counter = 1;
|
|
166
171
|
for (const type of Object.keys(requests)) {
|
|
167
172
|
for (const { payload } of requests[type]) {
|
|
168
173
|
const dir = path.join(__dirname, "artifacts", "segment-reference");
|
|
169
174
|
fs.mkdirSync(dir, { recursive: true });
|
|
170
175
|
const file = path.join(
|
|
171
176
|
dir,
|
|
172
|
-
`${payload.traits?.caseName || payload.properties?.caseName || payload.context?.caseName}.json`
|
|
177
|
+
`${counter++} - ${payload.traits?.caseName || payload.properties?.caseName || payload.context?.caseName}.json`
|
|
173
178
|
);
|
|
174
179
|
fs.writeFileSync(file, JSON.stringify(sortKeysRecursively(payload), null, 2));
|
|
175
180
|
}
|
|
176
181
|
}
|
|
177
182
|
});
|
|
178
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
|
+
test("url-bug", async ({ browser }) => {
|
|
195
|
+
//tests a bug in getanalytics.io where the url without slash provided by
|
|
196
|
+
//<link rel="canonical" ../> causes incorrect page path
|
|
197
|
+
const browserContext = await browser.newContext();
|
|
198
|
+
const { page, uncaughtErrors } = await createLoggingPage(browserContext);
|
|
199
|
+
const [pageResult] = await Promise.all([page.goto(`${server.baseUrl}/url-bug.html`)]);
|
|
200
|
+
|
|
201
|
+
await page.waitForFunction(() => window["jitsu"] !== undefined, undefined, {
|
|
202
|
+
timeout: 1000,
|
|
203
|
+
polling: 100,
|
|
204
|
+
});
|
|
205
|
+
expect(pageResult.status()).toBe(200);
|
|
206
|
+
//wait for some time since the server has an artificial latency of 30ms
|
|
207
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
208
|
+
expect(uncaughtErrors.length).toEqual(0);
|
|
209
|
+
expect(requestLog.length).toBe(2);
|
|
210
|
+
console.log(
|
|
211
|
+
`📝 Request log size of ${requestLog.length}`,
|
|
212
|
+
requestLog.map(x => describeEvent(x.type, x.body))
|
|
213
|
+
);
|
|
214
|
+
//a track contains a valid URL, probably because analytics can't grab the canonical URL yet
|
|
215
|
+
const trackEvent = requestLog.find(x => x.type === "page");
|
|
216
|
+
expect(trackEvent).toBeDefined();
|
|
217
|
+
const pagePath = trackEvent.body.context.page.path;
|
|
218
|
+
expect(pagePath).toBeDefined();
|
|
219
|
+
//it's "//localhost:3088" when the bug is present
|
|
220
|
+
expect(pagePath).toEqual("/");
|
|
221
|
+
});
|
|
222
|
+
|
|
179
223
|
test("basic", async ({ browser }) => {
|
|
224
|
+
requestLog.length = 0;
|
|
180
225
|
const browserContext = await browser.newContext();
|
|
181
226
|
|
|
182
227
|
const { page: firstPage, uncaughtErrors: firstPageErrors } = await createLoggingPage(browserContext);
|
|
@@ -197,12 +242,18 @@ test("basic", async ({ browser }) => {
|
|
|
197
242
|
{}
|
|
198
243
|
);
|
|
199
244
|
console.log("🍪 Jitsu Cookies", cookies);
|
|
245
|
+
//wait for some time since the server has an artificial latency of 30ms
|
|
246
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
200
247
|
expect(firstPageErrors.length).toEqual(0);
|
|
201
248
|
const anonymousId = cookies["__eventn_id"];
|
|
202
249
|
expect(anonymousId).toBeDefined();
|
|
203
|
-
expect(cookies["__eventn_uid"]).toBe("
|
|
250
|
+
expect(cookies["__eventn_uid"]).toBe("john-doe-id-1");
|
|
204
251
|
expect(cookies["__eventn_id_usr"]).toBeDefined();
|
|
205
252
|
expect(JSON.parse(decodeURIComponent(cookies["__eventn_id_usr"])).email).toEqual("john.doe@gmail.com");
|
|
253
|
+
console.log(
|
|
254
|
+
`📝 Request log size of ${requestLog.length}`,
|
|
255
|
+
requestLog.map(x => describeEvent(x.type, x.body))
|
|
256
|
+
);
|
|
206
257
|
let identifies = requestLog.filter(x => x.type === "identify");
|
|
207
258
|
let pages = requestLog.filter(x => x.type === "page");
|
|
208
259
|
let tracks = requestLog.filter(x => x.type === "track");
|
|
@@ -218,18 +269,18 @@ test("basic", async ({ browser }) => {
|
|
|
218
269
|
expect(track.properties.trackParam).toEqual("trackValue");
|
|
219
270
|
expect(track.type).toEqual("track");
|
|
220
271
|
expect(track.context.traits.email).toEqual("john.doe@gmail.com");
|
|
221
|
-
expect(track.userId).toEqual("
|
|
272
|
+
expect(track.userId).toEqual("john-doe-id-1");
|
|
222
273
|
expect(track.event).toEqual("pageLoaded");
|
|
223
274
|
|
|
224
275
|
console.log(chalk.bold("📝 Checking identify event"), JSON.stringify(identify, null, 3));
|
|
225
276
|
expect(identify.traits.email).toEqual("john.doe@gmail.com");
|
|
226
|
-
expect(identify.userId).toEqual("
|
|
277
|
+
expect(identify.userId).toEqual("john-doe-id-1");
|
|
227
278
|
expect(identify.anonymousId).toEqual(anonymousId);
|
|
228
279
|
|
|
229
280
|
console.log(chalk.bold("📝 Checking page event"), JSON.stringify(page, null, 3));
|
|
230
281
|
expect(page.anonymousId).toEqual(anonymousId);
|
|
231
282
|
expect(page.context.traits.email).toEqual("john.doe@gmail.com");
|
|
232
|
-
expect(page.userId).toEqual("
|
|
283
|
+
expect(page.userId).toEqual("john-doe-id-1");
|
|
233
284
|
|
|
234
285
|
expect(page.context.campaign.source).toEqual("source");
|
|
235
286
|
|
|
@@ -243,12 +294,13 @@ test("basic", async ({ browser }) => {
|
|
|
243
294
|
|
|
244
295
|
await secondPage.evaluate(generateTestEvents);
|
|
245
296
|
expect(secondPageErrors.length).toBe(0);
|
|
297
|
+
let counter = 1;
|
|
246
298
|
requestLog.forEach(({ body: payload }) => {
|
|
247
299
|
const dir = path.join(__dirname, "artifacts", "requests");
|
|
248
300
|
fs.mkdirSync(dir, { recursive: true });
|
|
249
301
|
const file = path.join(
|
|
250
302
|
dir,
|
|
251
|
-
`${payload.traits?.caseName || payload.properties?.caseName || payload.context?.caseName}.json`
|
|
303
|
+
`${counter++} - ${payload.traits?.caseName || payload.properties?.caseName || payload.context?.caseName}.json`
|
|
252
304
|
);
|
|
253
305
|
fs.writeFileSync(file, JSON.stringify(sortKeysRecursively(payload), null, 2));
|
|
254
306
|
});
|
|
@@ -23,6 +23,11 @@ export type InternalPluginDescriptor = {
|
|
|
23
23
|
name: string;
|
|
24
24
|
};
|
|
25
25
|
export type DeviceOptions = AnalyticsPluginDescriptor | InternalPluginDescriptor;
|
|
26
|
-
|
|
26
|
+
export type JitsuPluginConfig = JitsuOptions & {
|
|
27
|
+
storageWrapper?: (persistentStorage: PersistentStorage) => PersistentStorage & {
|
|
28
|
+
reset: () => void;
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
declare const jitsuAnalyticsPlugin: (pluginConfig?: JitsuPluginConfig) => AnalyticsPlugin;
|
|
27
32
|
export declare function randomId(hashString?: string | undefined): string;
|
|
28
33
|
export default jitsuAnalyticsPlugin;
|