Skip to content

Commit 8541a46

Browse files
authored
Worker Support (#450)
* web worker example * worker * more tests and measures
1 parent 15b74f7 commit 8541a46

8 files changed

+423
-55
lines changed

.eslintignore

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
lib/countly.min.js
22
plugin/boomerang/boomerang.min.js
3-
examples/react
4-
examples/symbolication
3+
examples/
54
node_modules
65
.vscode
76
.github
+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
describe("Web Worker Local Queue Tests", () => {
2+
it("Verify queues for all features", () => {
3+
// create a worker
4+
const myWorker = new Worker("../../examples/mpa/worker_for_test.js");
5+
6+
// send an event to worker
7+
myWorker.postMessage({ data: { key: "key" }, type: "event" });
8+
myWorker.postMessage({ data: "begin_session", type: "session" });
9+
myWorker.postMessage({ data: "end_session", type: "session" });
10+
myWorker.postMessage({ data: "home_page", type: "view" });
11+
12+
// ask for local queues
13+
myWorker.postMessage({ data: "queues", type: "get" });
14+
15+
let requestQueue;
16+
let eventQueue;
17+
myWorker.onmessage = function(e) {
18+
requestQueue = e.data.requestQ; // Array of requests
19+
eventQueue = e.data.eventQ; // Array of events
20+
myWorker.terminate(); // terminate worker
21+
22+
// verify event queue
23+
expect(eventQueue.length).to.equal(2);
24+
cy.check_event(eventQueue[0], { key: "key" }, undefined, false);
25+
cy.check_view_event(eventQueue[1], "home_page", undefined, false);
26+
27+
// verify request queue
28+
expect(requestQueue.length).to.equal(2);
29+
cy.check_session(requestQueue[0], undefined, false, false, true);
30+
cy.check_session(requestQueue[1], 0, false, false, false);
31+
};
32+
});
33+
});
+125
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
import { appKey } from "../support/helper";
2+
3+
const myEvent = {
4+
key: "buttonClick",
5+
segmentation: {
6+
id: "id"
7+
}
8+
};
9+
10+
describe("Web Worker Request Intercepting Tests", () => {
11+
it("SDK able to send requests for most basic calls", () => {
12+
// create a worker
13+
const myWorker = new Worker("../../examples/worker.js");
14+
15+
// send an event to worker
16+
myWorker.postMessage({ data: myEvent, type: "event" });
17+
myWorker.postMessage({ data: "begin_session", type: "session" });
18+
myWorker.postMessage({ data: "end_session", type: "session" });
19+
myWorker.postMessage({ data: "home_page", type: "view" });
20+
21+
// intercept requests
22+
cy.intercept("GET", "**/i?**", (req) => {
23+
const { url } = req;
24+
25+
// check url starts with https://try.count.ly/i?
26+
assert.isTrue(url.startsWith("https://try.count.ly/i?"));
27+
28+
// turn query string into object
29+
const paramsObject = turnSearchStringToObject(url.split("?")[1]);
30+
31+
// check common params
32+
check_commons(paramsObject);
33+
34+
// we expect 4 requests: begin_session, end_session, healthcheck, event(event includes view and buttonClick)
35+
let expectedRequests = 4;
36+
if (paramsObject.hc) {
37+
// check hc params types, values can change
38+
assert.isTrue(typeof paramsObject.hc.el === "number");
39+
assert.isTrue(typeof paramsObject.hc.wl === "number");
40+
assert.isTrue(typeof paramsObject.hc.sc === "number");
41+
assert.isTrue(typeof paramsObject.hc.em === "string");
42+
expectedRequests--;
43+
}
44+
else if (paramsObject.events) {
45+
// check event params with accordance to event sent (myEvent above)
46+
for (const eventInRequest of paramsObject.events) {
47+
if (eventInRequest.key === "[CLY]_view") { // view event
48+
expect(eventInRequest.segmentation.name).to.equal("home_page");
49+
expect(eventInRequest.segmentation.visit).to.equal(1);
50+
expect(eventInRequest.segmentation.start).to.equal(1);
51+
expect(eventInRequest.segmentation.view).to.equal("web_worker");
52+
expect(eventInRequest.pvid).to.equal("");
53+
}
54+
else { // buttonClick event
55+
expect(eventInRequest.key).to.equal(myEvent.key);
56+
expect(eventInRequest.segmentation).to.deep.equal(myEvent.segmentation);
57+
assert.isTrue(eventInRequest.cvid === "");
58+
}
59+
assert.isTrue(eventInRequest.count === 1);
60+
expect(eventInRequest.id).to.be.ok;
61+
expect(eventInRequest.id.toString().length).to.equal(21);
62+
expect(eventInRequest.timestamp).to.be.ok;
63+
expect(eventInRequest.timestamp.toString().length).to.equal(13);
64+
expect(eventInRequest.hour).to.be.within(0, 23);
65+
expect(eventInRequest.dow).to.be.within(0, 7);
66+
}
67+
expectedRequests--;
68+
}
69+
else if (paramsObject.begin_session === 1) { // check metrics
70+
expect(paramsObject.metrics._app_version).to.equal("0.0");
71+
expect(paramsObject.metrics._ua).to.equal("abcd");
72+
assert.isTrue(typeof paramsObject.metrics._locale === "string");
73+
expectedRequests--;
74+
}
75+
else if (paramsObject.end_session === 1) { // check metrics and session_duration
76+
expect(paramsObject.metrics._ua).to.equal("abcd");
77+
expect(paramsObject.session_duration).to.be.above(-1);
78+
expectedRequests--;
79+
}
80+
if (expectedRequests === 0) {
81+
myWorker.terminate(); // we checked everything, terminate worker
82+
}
83+
});
84+
});
85+
});
86+
87+
/**
88+
* Check common params for all requests
89+
* @param {Object} paramsObject - object from search string
90+
*/
91+
function check_commons(paramsObject) {
92+
expect(paramsObject.timestamp).to.be.ok;
93+
expect(paramsObject.timestamp.toString().length).to.equal(13);
94+
expect(paramsObject.hour).to.be.within(0, 23);
95+
expect(paramsObject.dow).to.be.within(0, 7);
96+
expect(paramsObject.app_key).to.equal(appKey);
97+
expect(paramsObject.device_id).to.be.ok;
98+
expect(paramsObject.sdk_name).to.equal("javascript_native_web");
99+
expect(paramsObject.sdk_version).to.be.ok;
100+
expect(paramsObject.t).to.be.within(0, 3);
101+
expect(paramsObject.av).to.equal(0); // av is 0 as we parsed parsable things
102+
if (!paramsObject.hc) { // hc is direct request
103+
expect(paramsObject.rr).to.be.above(-1);
104+
}
105+
expect(paramsObject.metrics._ua).to.be.ok;
106+
}
107+
108+
/**
109+
* Turn search string into object with values parsed
110+
* @param {String} searchString - search string
111+
* @returns {object} - object from search string
112+
*/
113+
function turnSearchStringToObject(searchString) {
114+
const searchParams = new URLSearchParams(searchString);
115+
const paramsObject = {};
116+
for (const [key, value] of searchParams.entries()) {
117+
try {
118+
paramsObject[key] = JSON.parse(value); // try to parse value
119+
}
120+
catch (e) {
121+
paramsObject[key] = value;
122+
}
123+
}
124+
return paramsObject;
125+
}

cypress/support/commands.js

+9-5
Original file line numberDiff line numberDiff line change
@@ -68,15 +68,17 @@ Cypress.Commands.add("check_crash", (testObject, appKey) => {
6868
* @param {Number} duration - session extension or end session duration to validate
6969
* @param {Boolean} isSessionEnd - a boolean to mark this check is intended for end_session validation
7070
*/
71-
Cypress.Commands.add("check_session", (queueObject, duration, isSessionEnd, appKey) => {
72-
if (!duration) { // if duration is not given that means its begin session
71+
Cypress.Commands.add("check_session", (queueObject, duration, isSessionEnd, appKey, worker) => {
72+
if (duration === undefined) { // if duration is not given that means its begin session
7373
expect(queueObject.begin_session).to.equal(1);
7474
const metrics = JSON.parse(queueObject.metrics);
7575
expect(metrics._app_version).to.be.ok;
7676
expect(metrics._ua).to.be.ok;
77-
expect(metrics._resolution).to.be.ok;
78-
expect(metrics._density).to.be.ok;
7977
expect(metrics._locale).to.be.ok;
78+
if (!worker) {
79+
expect(metrics._resolution).to.be.ok;
80+
expect(metrics._density).to.be.ok;
81+
}
8082
}
8183
else if (!isSessionEnd) {
8284
expect(queueObject.session_duration).to.be.within(duration, duration + 2);
@@ -152,7 +154,9 @@ Cypress.Commands.add("check_view_event", (queueObject, name, duration, hasPvid)
152154
if (duration === undefined) {
153155
expect(queueObject.segmentation.visit).to.equal(1);
154156
expect(queueObject.segmentation.view).to.be.ok;
155-
expect(queueObject.segmentation.domain).to.be.ok;
157+
if (queueObject.segmentation.view !== "web_worker") {
158+
expect(queueObject.segmentation.domain).to.be.ok;
159+
}
156160
// expect(queue.segmentation.start).to.be.ok; // TODO: this is only for manual tracking?
157161
}
158162
else {

examples/example_web_worker.html

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<html>
2+
3+
<head>
4+
<!-- Page styling here -->
5+
<link rel='stylesheet' type='text/css' href='./style/style.css'>
6+
7+
<!--Countly script-->
8+
<script type='text/javascript'>
9+
const myWorker = new Worker("worker.js");
10+
myWorker.onmessage = (e) => {
11+
console.log(e);
12+
};
13+
</script>
14+
</head>
15+
16+
<body>
17+
<!-- Header, banner etc. Top part of your site -->
18+
<div id="header">
19+
<h1>Sync Countly Implementation</h1>
20+
<img id="logo" src="./images/logo.png">
21+
</div>
22+
23+
<center>
24+
<img src="./images/team_countly.jpg" id="wallpaper" />
25+
<br />
26+
<input type="button" id="Record Event" onclick="clickEvent()" value="Record Event">
27+
<input type="button" id="Record View" onclick="recordView()" value="Record View">
28+
<input type="button" id="Begin Session" onclick="beginSession()" value="Begin Session">
29+
<input type="button" id="End Session" onclick="endSession()" value="End Session">
30+
<br />
31+
</center>
32+
<script type='text/javascript'>
33+
const myEvent = {
34+
key: "buttonClick",
35+
"segmentation": {
36+
"id": "id"
37+
}
38+
}
39+
//send event on button click
40+
function clickEvent() {
41+
myWorker.postMessage({ type: "event", data: myEvent });
42+
}
43+
function recordView() {
44+
myWorker.postMessage({ type: "view", data: "home_page" });
45+
}
46+
function beginSession() {
47+
myWorker.postMessage({ type: "session", data: "begin_session" });
48+
}
49+
function endSession() {
50+
myWorker.postMessage({ type: "session", data: "end_session" });
51+
}
52+
</script>
53+
</body>
54+
55+
</html>

examples/mpa/worker_for_test.js

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
importScripts("../../lib/countly.js");
2+
3+
Countly.init({
4+
app_key: "YOUR_APP_KEY",
5+
url: "https://try.count.ly",
6+
debug: true,
7+
test_mode: true
8+
});
9+
10+
onmessage = function(e) {
11+
console.log(`Worker: Message received from main script:[${JSON.stringify(e.data)}]`);
12+
const data = e.data.data; const type = e.data.type;
13+
if (type === "event") {
14+
Countly.add_event(data);
15+
} else if (type === "view") {
16+
Countly.track_pageview(data);
17+
} else if (type === "session") {
18+
if (data === "begin_session") {
19+
Countly.begin_session();
20+
return;
21+
}
22+
Countly.end_session(null, true);
23+
} else if (type === "get") {
24+
const queues = Countly._internals.getLocalQueues();
25+
postMessage(queues);
26+
}
27+
};

examples/worker.js

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
importScripts("../lib/countly.js");
2+
3+
Countly.init({
4+
app_key: "YOUR_APP_KEY",
5+
url: "https://try.count.ly",
6+
debug: true
7+
});
8+
9+
onmessage = function (e) {
10+
console.log(`Worker: Message received from main script:[${JSON.stringify(e.data)}]`);
11+
const data = e.data.data; const type = e.data.type;
12+
if (type === "event") {
13+
Countly.add_event(data);
14+
} else if (type === "view") {
15+
Countly.track_pageview(data);
16+
} else if (type === "session") {
17+
if (data === "begin_session") {
18+
Countly.begin_session();
19+
return;
20+
}
21+
Countly.end_session(null, true);
22+
}
23+
}

0 commit comments

Comments
 (0)