Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

24.4.0 Release #490

Merged
merged 22 commits into from
Apr 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/codeql-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,11 @@ jobs:

steps:
- name: Checkout repository
uses: actions/checkout@v2
uses: actions/checkout@v4

# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
Expand All @@ -54,7 +54,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
uses: github/codeql-action/autobuild@v3

# ℹ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
Expand All @@ -68,4 +68,4 @@ jobs:
# make release

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1
uses: github/codeql-action/analyze@v3
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
## 24.4.0
! Minor breaking change ! For implementations using `salt` the browser compatability is tied to SubtleCrypto's `digest` method support

- Added the `salt` init config flag to add checksums to requests (for secure contexts only)
- Improved Health Check feature stability
- Added support for Feedback Widget terms and conditions

## 23.12.6
- Mitigated an issue where error tracking could prevent SDK initialization in async mode

## 23.12.5
- Mitigated an issue where the SDK was not emptying the async queue explicity when closing a browser

## 23.12.4
- Enhanced userAgentData detection for bot filtering

Expand Down
290 changes: 290 additions & 0 deletions cypress/integration/async_queue.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,290 @@
/* eslint-disable cypress/no-unnecessary-waiting */
/* eslint-disable require-jsdoc */
var Countly = require("../../lib/countly");
// import * as Countly from "../../dist/countly_umd.js";
var hp = require("../support/helper.js");

function initMain(clear) {
Countly.init({
app_key: "YOUR_APP_KEY",
url: "https://your.domain.count.ly",
debug: true,
test_mode: true,
clear_stored_id: clear
});
}

function event(number) {
return {
key: `event_${number}`,
segmentation: {
id: number
}
};
}

// All the tests below checks if the functions are working correctly
// Currently tests for 'beforeunload' and 'unload' events has to be done manually by using the throttling option of the browser
describe("Test Countly.q related methods and processes", () => {
// For this tests we disable the internal heatbeat and use processAsyncQueue and sendEventsForced
// So we are able to test if those functions work as intented:
// processAsyncQueue should send events from .q to event queue
// sendEventsForced should send events from event queue to request queue (it also calls processAsyncQueue)
it("Check processAsyncQueue and sendEventsForced works as expected", () => {
hp.haltAndClearStorage(() => {
// Disable heartbeat and init the SDK
Countly.noHeartBeat = true;
initMain();
cy.wait(1000);

// Check that the .q is empty
expect(Countly.q.length).to.equal(0);

// Add 4 events to the .q
Countly.q.push(["track_errors"]); // adding this as calling it during init used to cause an error (at v23.12.5)
Countly.q.push(["add_event", event(1)]);
Countly.q.push(["add_event", event(2)]);
Countly.q.push(["add_event", event(3)]);
Countly.q.push(["add_event", event(4)]);
// Check that the .q has 4 events
expect(Countly.q.length).to.equal(5);

cy.fetch_local_event_queue().then((rq) => {
// Check that events are still in .q
expect(Countly.q.length).to.equal(5);

// Check that the event queue is empty
expect(rq.length).to.equal(0);

// Process the .q (should send things to the event queue)
Countly._internals.processAsyncQueue();

// Check that the .q is empty
expect(Countly.q.length).to.equal(0);

cy.fetch_local_request_queue().then((rq_2) => {
// Check that nothing sent to request queue
expect(rq_2.length).to.equal(0);
cy.fetch_local_event_queue().then((eq) => {
// Check that events are now in event queue
expect(eq.length).to.equal(4);

// Send events from event queue to request queue
Countly._internals.sendEventsForced();
cy.fetch_local_event_queue().then((eq_2) => {
// Check that event queue is empty
expect(eq_2.length).to.equal(0);
cy.fetch_local_request_queue().then((rq_3) => {
// Check that events are now in request queue
expect(rq_3.length).to.equal(1);
const eventsArray = JSON.parse(rq_3[0].events);
expect(eventsArray[0].key).to.equal("event_1");
expect(eventsArray[1].key).to.equal("event_2");
expect(eventsArray[2].key).to.equal("event_3");
expect(eventsArray[3].key).to.equal("event_4");
});
});
});
});
});
});
});
// This test is same with the ones above but this time we use change_id to trigger processAsyncQueue
it("Check changing device ID without merge empties the .q", () => {
hp.haltAndClearStorage(() => {
// Disable heartbeat and init the SDK
Countly.noHeartBeat = true;
Countly.q = [];
initMain();
cy.wait(1000);

// Check that the .q is empty
expect(Countly.q.length).to.equal(0);

// Add 4 events to the .q
Countly.q.push(["add_event", event(1)]);
Countly.q.push(["add_event", event(2)]);
Countly.q.push(["add_event", event(3)]);
Countly.q.push(["add_event", event(4)]);
// Check that the .q has 4 events
expect(Countly.q.length).to.equal(4);

cy.fetch_local_event_queue().then((rq) => {
// Check that the event queue is empty
expect(rq.length).to.equal(0);

// Check that events are still in .q
expect(Countly.q.length).to.equal(4);

// Trigger processAsyncQueue by changing device ID without merge
Countly.change_id("new_user_id", false);

// Check that the .q is empty
expect(Countly.q.length).to.equal(0);
cy.fetch_local_event_queue().then((eq) => {
// Check that event queue has new device ID's orientation event
expect(eq.length).to.equal(1);
expect(eq[0].key).to.equal("[CLY]_orientation");
cy.fetch_local_request_queue().then((rq_2) => {
// Check that events are now in request queue (second request is begin session for new device ID)
expect(rq_2.length).to.equal(2);
const eventsArray = JSON.parse(rq_2[0].events);
expect(eventsArray[0].key).to.equal("event_1");
expect(eventsArray[1].key).to.equal("event_2");
expect(eventsArray[2].key).to.equal("event_3");
expect(eventsArray[3].key).to.equal("event_4");
// check begin session
expect(rq_2[1].begin_session).to.equal(1);
});
});
});
});
});
// This test checks if calling user_details triggers processAsyncQueue (it sends events from .q to event queue and then to request queue)
it("Check sending user details empties .q", () => {
hp.haltAndClearStorage(() => {
// Disable heartbeat and init the SDK
Countly.noHeartBeat = true;
Countly.q = [];
initMain();
cy.wait(1000);

// Check that the .q is empty
expect(Countly.q.length).to.equal(0);

// Add 4 events to the .q
Countly.q.push(["add_event", event(1)]);
Countly.q.push(["add_event", event(2)]);
Countly.q.push(["add_event", event(3)]);
Countly.q.push(["add_event", event(4)]);
// Check that the .q has 4 events
expect(Countly.q.length).to.equal(4);

cy.fetch_local_event_queue().then((rq) => {
// Check that the event queue is empty
expect(rq.length).to.equal(0);

// Check that events are still in .q
expect(Countly.q.length).to.equal(4);

// Trigger processAsyncQueue by adding user details
Countly.user_details({ name: "test_user" });

// Check that the .q is empty
expect(Countly.q.length).to.equal(0);
cy.fetch_local_event_queue().then((eq) => {
// Check that event queue is empty
expect(eq.length).to.equal(0);
cy.fetch_local_request_queue().then((rq_2) => {
// Check that events are now in request queue (second request is user details)
expect(rq_2.length).to.equal(2);
const eventsArray = JSON.parse(rq_2[0].events);
expect(eventsArray[0].key).to.equal("event_1");
expect(eventsArray[1].key).to.equal("event_2");
expect(eventsArray[2].key).to.equal("event_3");
expect(eventsArray[3].key).to.equal("event_4");
// check user details
const user_details = JSON.parse(rq_2[1].user_details);
expect(user_details.name).to.equal("test_user");
});
});
});
});
});
// This Test checks if calling userData.save triggers processAsyncQueue (it sends events from .q to event queue and then to request queue)
it("Check sending custom user info empties .q", () => {
hp.haltAndClearStorage(() => {
// Disable heartbeat and init the SDK
Countly.noHeartBeat = true;
Countly.q = [];
initMain();
cy.wait(1000);

// Check that the .q is empty
expect(Countly.q.length).to.equal(0);

// Add 4 events to the .q
Countly.q.push(["add_event", event(1)]);
Countly.q.push(["add_event", event(2)]);
Countly.q.push(["add_event", event(3)]);
Countly.q.push(["add_event", event(4)]);
// Check that the .q has 4 events
expect(Countly.q.length).to.equal(4);

cy.fetch_local_event_queue().then((rq) => {
// Check that the event queue is empty
expect(rq.length).to.equal(0);

// Check that events are still in .q
expect(Countly.q.length).to.equal(4);

// Trigger processAsyncQueue by saving UserData
Countly.userData.set("name", "test_user");
Countly.userData.save();

// Check that the .q is empty
expect(Countly.q.length).to.equal(0);
cy.fetch_local_event_queue().then((eq) => {
// Check that event queue is empty
expect(eq.length).to.equal(0);
cy.fetch_local_request_queue().then((rq_2) => {
// Check that events are now in request queue (second request is user details)
expect(rq_2.length).to.equal(2);
const eventsArray = JSON.parse(rq_2[0].events);
expect(eventsArray[0].key).to.equal("event_1");
expect(eventsArray[1].key).to.equal("event_2");
expect(eventsArray[2].key).to.equal("event_3");
expect(eventsArray[3].key).to.equal("event_4");
// check user data
const user_details = JSON.parse(rq_2[1].user_details);
expect(user_details.custom.name).to.equal("test_user");
});
});
});
});
});
// This test check if the heartbeat is processing the .q (executes processAsyncQueue)
it("Check if heatbeat is processing .q", () => {
hp.haltAndClearStorage(() => {
// init the SDK
Countly.q = [];
initMain();

// Check that the .q is empty
expect(Countly.q.length).to.equal(0);
cy.fetch_local_event_queue().then((eq) => {
// Check that the event queue is empty
expect(eq.length).to.equal(0);
cy.fetch_local_request_queue().then((rq) => {
// Check that the request queue is empty
expect(rq.length).to.equal(0);
// Add 4 events to the .q
Countly.q.push(["add_event", event(1)]);
Countly.q.push(["add_event", event(2)]);
Countly.q.push(["add_event", event(3)]);
Countly.q.push(["add_event", event(4)]);
// Check that the .q has 4 events
expect(Countly.q.length).to.equal(4);
// Wait for heartBeat to process the .q
cy.wait(1500).then(() => {
// Check that the .q is empty
expect(Countly.q.length).to.equal(0);
cy.fetch_local_event_queue().then((eq_2) => {
// Check that event queue is empty as all must be in request queue
expect(eq_2.length).to.equal(0);
cy.fetch_local_request_queue().then((rq_2) => {
// Check that events are now in request queue
expect(rq_2.length).to.equal(1);
const eventsArray = JSON.parse(rq_2[0].events);
expect(eventsArray[0].key).to.equal("event_1");
expect(eventsArray[1].key).to.equal("event_2");
expect(eventsArray[2].key).to.equal("event_3");
expect(eventsArray[3].key).to.equal("event_4");
});
});
});
});
});
});
});
});
2 changes: 1 addition & 1 deletion cypress/integration/bridge_utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ function initMain(name, version) {
}

const SDK_NAME = "javascript_native_web";
const SDK_VERSION = "23.12.4";
const SDK_VERSION = "24.4.0";

// tests
describe("Bridged SDK Utilities Tests", () => {
Expand Down
2 changes: 1 addition & 1 deletion cypress/integration/health_check.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ describe("Health Check tests ", () => {
// Test the 'hc' parameter
const hcParam = url.searchParams.get("hc");
const hcParamObj = JSON.parse(hcParam);
expect(hcParamObj).to.eql({ el: 0, wl: 0, sc: -1, em: "\"\"" });
expect(hcParamObj).to.eql({ el: 0, wl: 0, sc: -1, em: "" });

// Test the 'metrics' parameter
const metricsParam = url.searchParams.get("metrics");
Expand Down
Loading
Loading