Skip to content

Commit

Permalink
Worker Support (#450)
Browse files Browse the repository at this point in the history
* web worker example

* worker

* more tests and measures
  • Loading branch information
turtledreams committed Nov 17, 2023
1 parent 15b74f7 commit 8541a46
Show file tree
Hide file tree
Showing 8 changed files with 423 additions and 55 deletions.
3 changes: 1 addition & 2 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
lib/countly.min.js
plugin/boomerang/boomerang.min.js
examples/react
examples/symbolication
examples/
node_modules
.vscode
.github
Expand Down
33 changes: 33 additions & 0 deletions cypress/integration/web_worker_queues.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
describe("Web Worker Local Queue Tests", () => {
it("Verify queues for all features", () => {
// create a worker
const myWorker = new Worker("../../examples/mpa/worker_for_test.js");

// send an event to worker
myWorker.postMessage({ data: { key: "key" }, type: "event" });
myWorker.postMessage({ data: "begin_session", type: "session" });
myWorker.postMessage({ data: "end_session", type: "session" });
myWorker.postMessage({ data: "home_page", type: "view" });

// ask for local queues
myWorker.postMessage({ data: "queues", type: "get" });

let requestQueue;
let eventQueue;
myWorker.onmessage = function(e) {
requestQueue = e.data.requestQ; // Array of requests
eventQueue = e.data.eventQ; // Array of events
myWorker.terminate(); // terminate worker

// verify event queue
expect(eventQueue.length).to.equal(2);
cy.check_event(eventQueue[0], { key: "key" }, undefined, false);
cy.check_view_event(eventQueue[1], "home_page", undefined, false);

// verify request queue
expect(requestQueue.length).to.equal(2);
cy.check_session(requestQueue[0], undefined, false, false, true);
cy.check_session(requestQueue[1], 0, false, false, false);
};
});
});
125 changes: 125 additions & 0 deletions cypress/integration/web_worker_requests.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import { appKey } from "../support/helper";

const myEvent = {
key: "buttonClick",
segmentation: {
id: "id"
}
};

describe("Web Worker Request Intercepting Tests", () => {
it("SDK able to send requests for most basic calls", () => {
// create a worker
const myWorker = new Worker("../../examples/worker.js");

// send an event to worker
myWorker.postMessage({ data: myEvent, type: "event" });
myWorker.postMessage({ data: "begin_session", type: "session" });
myWorker.postMessage({ data: "end_session", type: "session" });
myWorker.postMessage({ data: "home_page", type: "view" });

// intercept requests
cy.intercept("GET", "**/i?**", (req) => {
const { url } = req;

// check url starts with https://try.count.ly/i?
assert.isTrue(url.startsWith("https://try.count.ly/i?"));

// turn query string into object
const paramsObject = turnSearchStringToObject(url.split("?")[1]);

// check common params
check_commons(paramsObject);

// we expect 4 requests: begin_session, end_session, healthcheck, event(event includes view and buttonClick)
let expectedRequests = 4;
if (paramsObject.hc) {
// check hc params types, values can change
assert.isTrue(typeof paramsObject.hc.el === "number");
assert.isTrue(typeof paramsObject.hc.wl === "number");
assert.isTrue(typeof paramsObject.hc.sc === "number");
assert.isTrue(typeof paramsObject.hc.em === "string");
expectedRequests--;
}
else if (paramsObject.events) {
// check event params with accordance to event sent (myEvent above)
for (const eventInRequest of paramsObject.events) {
if (eventInRequest.key === "[CLY]_view") { // view event
expect(eventInRequest.segmentation.name).to.equal("home_page");
expect(eventInRequest.segmentation.visit).to.equal(1);
expect(eventInRequest.segmentation.start).to.equal(1);
expect(eventInRequest.segmentation.view).to.equal("web_worker");
expect(eventInRequest.pvid).to.equal("");
}
else { // buttonClick event
expect(eventInRequest.key).to.equal(myEvent.key);
expect(eventInRequest.segmentation).to.deep.equal(myEvent.segmentation);
assert.isTrue(eventInRequest.cvid === "");
}
assert.isTrue(eventInRequest.count === 1);
expect(eventInRequest.id).to.be.ok;
expect(eventInRequest.id.toString().length).to.equal(21);
expect(eventInRequest.timestamp).to.be.ok;
expect(eventInRequest.timestamp.toString().length).to.equal(13);
expect(eventInRequest.hour).to.be.within(0, 23);
expect(eventInRequest.dow).to.be.within(0, 7);
}
expectedRequests--;
}
else if (paramsObject.begin_session === 1) { // check metrics
expect(paramsObject.metrics._app_version).to.equal("0.0");
expect(paramsObject.metrics._ua).to.equal("abcd");
assert.isTrue(typeof paramsObject.metrics._locale === "string");
expectedRequests--;
}
else if (paramsObject.end_session === 1) { // check metrics and session_duration
expect(paramsObject.metrics._ua).to.equal("abcd");
expect(paramsObject.session_duration).to.be.above(-1);
expectedRequests--;
}
if (expectedRequests === 0) {
myWorker.terminate(); // we checked everything, terminate worker
}
});
});
});

/**
* Check common params for all requests
* @param {Object} paramsObject - object from search string
*/
function check_commons(paramsObject) {
expect(paramsObject.timestamp).to.be.ok;
expect(paramsObject.timestamp.toString().length).to.equal(13);
expect(paramsObject.hour).to.be.within(0, 23);
expect(paramsObject.dow).to.be.within(0, 7);
expect(paramsObject.app_key).to.equal(appKey);
expect(paramsObject.device_id).to.be.ok;
expect(paramsObject.sdk_name).to.equal("javascript_native_web");
expect(paramsObject.sdk_version).to.be.ok;
expect(paramsObject.t).to.be.within(0, 3);
expect(paramsObject.av).to.equal(0); // av is 0 as we parsed parsable things
if (!paramsObject.hc) { // hc is direct request
expect(paramsObject.rr).to.be.above(-1);
}
expect(paramsObject.metrics._ua).to.be.ok;
}

/**
* Turn search string into object with values parsed
* @param {String} searchString - search string
* @returns {object} - object from search string
*/
function turnSearchStringToObject(searchString) {
const searchParams = new URLSearchParams(searchString);
const paramsObject = {};
for (const [key, value] of searchParams.entries()) {
try {
paramsObject[key] = JSON.parse(value); // try to parse value
}
catch (e) {
paramsObject[key] = value;
}
}
return paramsObject;
}
14 changes: 9 additions & 5 deletions cypress/support/commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,15 +68,17 @@ Cypress.Commands.add("check_crash", (testObject, appKey) => {
* @param {Number} duration - session extension or end session duration to validate
* @param {Boolean} isSessionEnd - a boolean to mark this check is intended for end_session validation
*/
Cypress.Commands.add("check_session", (queueObject, duration, isSessionEnd, appKey) => {
if (!duration) { // if duration is not given that means its begin session
Cypress.Commands.add("check_session", (queueObject, duration, isSessionEnd, appKey, worker) => {
if (duration === undefined) { // if duration is not given that means its begin session
expect(queueObject.begin_session).to.equal(1);
const metrics = JSON.parse(queueObject.metrics);
expect(metrics._app_version).to.be.ok;
expect(metrics._ua).to.be.ok;
expect(metrics._resolution).to.be.ok;
expect(metrics._density).to.be.ok;
expect(metrics._locale).to.be.ok;
if (!worker) {
expect(metrics._resolution).to.be.ok;
expect(metrics._density).to.be.ok;
}
}
else if (!isSessionEnd) {
expect(queueObject.session_duration).to.be.within(duration, duration + 2);
Expand Down Expand Up @@ -152,7 +154,9 @@ Cypress.Commands.add("check_view_event", (queueObject, name, duration, hasPvid)
if (duration === undefined) {
expect(queueObject.segmentation.visit).to.equal(1);
expect(queueObject.segmentation.view).to.be.ok;
expect(queueObject.segmentation.domain).to.be.ok;
if (queueObject.segmentation.view !== "web_worker") {
expect(queueObject.segmentation.domain).to.be.ok;
}
// expect(queue.segmentation.start).to.be.ok; // TODO: this is only for manual tracking?
}
else {
Expand Down
55 changes: 55 additions & 0 deletions examples/example_web_worker.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<html>

<head>
<!-- Page styling here -->
<link rel='stylesheet' type='text/css' href='./style/style.css'>

<!--Countly script-->
<script type='text/javascript'>
const myWorker = new Worker("worker.js");
myWorker.onmessage = (e) => {
console.log(e);
};
</script>
</head>

<body>
<!-- Header, banner etc. Top part of your site -->
<div id="header">
<h1>Sync Countly Implementation</h1>
<img id="logo" src="./images/logo.png">
</div>

<center>
<img src="./images/team_countly.jpg" id="wallpaper" />
<br />
<input type="button" id="Record Event" onclick="clickEvent()" value="Record Event">
<input type="button" id="Record View" onclick="recordView()" value="Record View">
<input type="button" id="Begin Session" onclick="beginSession()" value="Begin Session">
<input type="button" id="End Session" onclick="endSession()" value="End Session">
<br />
</center>
<script type='text/javascript'>
const myEvent = {
key: "buttonClick",
"segmentation": {
"id": "id"
}
}
//send event on button click
function clickEvent() {
myWorker.postMessage({ type: "event", data: myEvent });
}
function recordView() {
myWorker.postMessage({ type: "view", data: "home_page" });
}
function beginSession() {
myWorker.postMessage({ type: "session", data: "begin_session" });
}
function endSession() {
myWorker.postMessage({ type: "session", data: "end_session" });
}
</script>
</body>

</html>
27 changes: 27 additions & 0 deletions examples/mpa/worker_for_test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
importScripts("../../lib/countly.js");

Countly.init({
app_key: "YOUR_APP_KEY",
url: "https://try.count.ly",
debug: true,
test_mode: true
});

onmessage = function(e) {
console.log(`Worker: Message received from main script:[${JSON.stringify(e.data)}]`);
const data = e.data.data; const type = e.data.type;
if (type === "event") {
Countly.add_event(data);
} else if (type === "view") {
Countly.track_pageview(data);
} else if (type === "session") {
if (data === "begin_session") {
Countly.begin_session();
return;
}
Countly.end_session(null, true);
} else if (type === "get") {
const queues = Countly._internals.getLocalQueues();
postMessage(queues);
}
};
23 changes: 23 additions & 0 deletions examples/worker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
importScripts("../lib/countly.js");

Countly.init({
app_key: "YOUR_APP_KEY",
url: "https://try.count.ly",
debug: true
});

onmessage = function (e) {
console.log(`Worker: Message received from main script:[${JSON.stringify(e.data)}]`);
const data = e.data.data; const type = e.data.type;
if (type === "event") {
Countly.add_event(data);
} else if (type === "view") {
Countly.track_pageview(data);
} else if (type === "session") {
if (data === "begin_session") {
Countly.begin_session();
return;
}
Countly.end_session(null, true);
}
}
Loading

0 comments on commit 8541a46

Please sign in to comment.