Skip to content

Commit fb102da

Browse files
authored
Merge pull request #490 from Countly/staging
24.4.0 Release
2 parents 8c4b464 + d428110 commit fb102da

35 files changed

+1022
-332
lines changed

.github/workflows/codeql-analysis.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,11 @@ jobs:
3939

4040
steps:
4141
- name: Checkout repository
42-
uses: actions/checkout@v2
42+
uses: actions/checkout@v4
4343

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

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

7070
- name: Perform CodeQL Analysis
71-
uses: github/codeql-action/analyze@v1
71+
uses: github/codeql-action/analyze@v3

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,16 @@
1+
## 24.4.0
2+
! Minor breaking change ! For implementations using `salt` the browser compatability is tied to SubtleCrypto's `digest` method support
3+
4+
- Added the `salt` init config flag to add checksums to requests (for secure contexts only)
5+
- Improved Health Check feature stability
6+
- Added support for Feedback Widget terms and conditions
7+
8+
## 23.12.6
9+
- Mitigated an issue where error tracking could prevent SDK initialization in async mode
10+
11+
## 23.12.5
12+
- Mitigated an issue where the SDK was not emptying the async queue explicity when closing a browser
13+
114
## 23.12.4
215
- Enhanced userAgentData detection for bot filtering
316

cypress/integration/async_queue.js

Lines changed: 290 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,290 @@
1+
/* eslint-disable cypress/no-unnecessary-waiting */
2+
/* eslint-disable require-jsdoc */
3+
var Countly = require("../../lib/countly");
4+
// import * as Countly from "../../dist/countly_umd.js";
5+
var hp = require("../support/helper.js");
6+
7+
function initMain(clear) {
8+
Countly.init({
9+
app_key: "YOUR_APP_KEY",
10+
url: "https://your.domain.count.ly",
11+
debug: true,
12+
test_mode: true,
13+
clear_stored_id: clear
14+
});
15+
}
16+
17+
function event(number) {
18+
return {
19+
key: `event_${number}`,
20+
segmentation: {
21+
id: number
22+
}
23+
};
24+
}
25+
26+
// All the tests below checks if the functions are working correctly
27+
// Currently tests for 'beforeunload' and 'unload' events has to be done manually by using the throttling option of the browser
28+
describe("Test Countly.q related methods and processes", () => {
29+
// For this tests we disable the internal heatbeat and use processAsyncQueue and sendEventsForced
30+
// So we are able to test if those functions work as intented:
31+
// processAsyncQueue should send events from .q to event queue
32+
// sendEventsForced should send events from event queue to request queue (it also calls processAsyncQueue)
33+
it("Check processAsyncQueue and sendEventsForced works as expected", () => {
34+
hp.haltAndClearStorage(() => {
35+
// Disable heartbeat and init the SDK
36+
Countly.noHeartBeat = true;
37+
initMain();
38+
cy.wait(1000);
39+
40+
// Check that the .q is empty
41+
expect(Countly.q.length).to.equal(0);
42+
43+
// Add 4 events to the .q
44+
Countly.q.push(["track_errors"]); // adding this as calling it during init used to cause an error (at v23.12.5)
45+
Countly.q.push(["add_event", event(1)]);
46+
Countly.q.push(["add_event", event(2)]);
47+
Countly.q.push(["add_event", event(3)]);
48+
Countly.q.push(["add_event", event(4)]);
49+
// Check that the .q has 4 events
50+
expect(Countly.q.length).to.equal(5);
51+
52+
cy.fetch_local_event_queue().then((rq) => {
53+
// Check that events are still in .q
54+
expect(Countly.q.length).to.equal(5);
55+
56+
// Check that the event queue is empty
57+
expect(rq.length).to.equal(0);
58+
59+
// Process the .q (should send things to the event queue)
60+
Countly._internals.processAsyncQueue();
61+
62+
// Check that the .q is empty
63+
expect(Countly.q.length).to.equal(0);
64+
65+
cy.fetch_local_request_queue().then((rq_2) => {
66+
// Check that nothing sent to request queue
67+
expect(rq_2.length).to.equal(0);
68+
cy.fetch_local_event_queue().then((eq) => {
69+
// Check that events are now in event queue
70+
expect(eq.length).to.equal(4);
71+
72+
// Send events from event queue to request queue
73+
Countly._internals.sendEventsForced();
74+
cy.fetch_local_event_queue().then((eq_2) => {
75+
// Check that event queue is empty
76+
expect(eq_2.length).to.equal(0);
77+
cy.fetch_local_request_queue().then((rq_3) => {
78+
// Check that events are now in request queue
79+
expect(rq_3.length).to.equal(1);
80+
const eventsArray = JSON.parse(rq_3[0].events);
81+
expect(eventsArray[0].key).to.equal("event_1");
82+
expect(eventsArray[1].key).to.equal("event_2");
83+
expect(eventsArray[2].key).to.equal("event_3");
84+
expect(eventsArray[3].key).to.equal("event_4");
85+
});
86+
});
87+
});
88+
});
89+
});
90+
});
91+
});
92+
// This test is same with the ones above but this time we use change_id to trigger processAsyncQueue
93+
it("Check changing device ID without merge empties the .q", () => {
94+
hp.haltAndClearStorage(() => {
95+
// Disable heartbeat and init the SDK
96+
Countly.noHeartBeat = true;
97+
Countly.q = [];
98+
initMain();
99+
cy.wait(1000);
100+
101+
// Check that the .q is empty
102+
expect(Countly.q.length).to.equal(0);
103+
104+
// Add 4 events to the .q
105+
Countly.q.push(["add_event", event(1)]);
106+
Countly.q.push(["add_event", event(2)]);
107+
Countly.q.push(["add_event", event(3)]);
108+
Countly.q.push(["add_event", event(4)]);
109+
// Check that the .q has 4 events
110+
expect(Countly.q.length).to.equal(4);
111+
112+
cy.fetch_local_event_queue().then((rq) => {
113+
// Check that the event queue is empty
114+
expect(rq.length).to.equal(0);
115+
116+
// Check that events are still in .q
117+
expect(Countly.q.length).to.equal(4);
118+
119+
// Trigger processAsyncQueue by changing device ID without merge
120+
Countly.change_id("new_user_id", false);
121+
122+
// Check that the .q is empty
123+
expect(Countly.q.length).to.equal(0);
124+
cy.fetch_local_event_queue().then((eq) => {
125+
// Check that event queue has new device ID's orientation event
126+
expect(eq.length).to.equal(1);
127+
expect(eq[0].key).to.equal("[CLY]_orientation");
128+
cy.fetch_local_request_queue().then((rq_2) => {
129+
// Check that events are now in request queue (second request is begin session for new device ID)
130+
expect(rq_2.length).to.equal(2);
131+
const eventsArray = JSON.parse(rq_2[0].events);
132+
expect(eventsArray[0].key).to.equal("event_1");
133+
expect(eventsArray[1].key).to.equal("event_2");
134+
expect(eventsArray[2].key).to.equal("event_3");
135+
expect(eventsArray[3].key).to.equal("event_4");
136+
// check begin session
137+
expect(rq_2[1].begin_session).to.equal(1);
138+
});
139+
});
140+
});
141+
});
142+
});
143+
// This test checks if calling user_details triggers processAsyncQueue (it sends events from .q to event queue and then to request queue)
144+
it("Check sending user details empties .q", () => {
145+
hp.haltAndClearStorage(() => {
146+
// Disable heartbeat and init the SDK
147+
Countly.noHeartBeat = true;
148+
Countly.q = [];
149+
initMain();
150+
cy.wait(1000);
151+
152+
// Check that the .q is empty
153+
expect(Countly.q.length).to.equal(0);
154+
155+
// Add 4 events to the .q
156+
Countly.q.push(["add_event", event(1)]);
157+
Countly.q.push(["add_event", event(2)]);
158+
Countly.q.push(["add_event", event(3)]);
159+
Countly.q.push(["add_event", event(4)]);
160+
// Check that the .q has 4 events
161+
expect(Countly.q.length).to.equal(4);
162+
163+
cy.fetch_local_event_queue().then((rq) => {
164+
// Check that the event queue is empty
165+
expect(rq.length).to.equal(0);
166+
167+
// Check that events are still in .q
168+
expect(Countly.q.length).to.equal(4);
169+
170+
// Trigger processAsyncQueue by adding user details
171+
Countly.user_details({ name: "test_user" });
172+
173+
// Check that the .q is empty
174+
expect(Countly.q.length).to.equal(0);
175+
cy.fetch_local_event_queue().then((eq) => {
176+
// Check that event queue is empty
177+
expect(eq.length).to.equal(0);
178+
cy.fetch_local_request_queue().then((rq_2) => {
179+
// Check that events are now in request queue (second request is user details)
180+
expect(rq_2.length).to.equal(2);
181+
const eventsArray = JSON.parse(rq_2[0].events);
182+
expect(eventsArray[0].key).to.equal("event_1");
183+
expect(eventsArray[1].key).to.equal("event_2");
184+
expect(eventsArray[2].key).to.equal("event_3");
185+
expect(eventsArray[3].key).to.equal("event_4");
186+
// check user details
187+
const user_details = JSON.parse(rq_2[1].user_details);
188+
expect(user_details.name).to.equal("test_user");
189+
});
190+
});
191+
});
192+
});
193+
});
194+
// This Test checks if calling userData.save triggers processAsyncQueue (it sends events from .q to event queue and then to request queue)
195+
it("Check sending custom user info empties .q", () => {
196+
hp.haltAndClearStorage(() => {
197+
// Disable heartbeat and init the SDK
198+
Countly.noHeartBeat = true;
199+
Countly.q = [];
200+
initMain();
201+
cy.wait(1000);
202+
203+
// Check that the .q is empty
204+
expect(Countly.q.length).to.equal(0);
205+
206+
// Add 4 events to the .q
207+
Countly.q.push(["add_event", event(1)]);
208+
Countly.q.push(["add_event", event(2)]);
209+
Countly.q.push(["add_event", event(3)]);
210+
Countly.q.push(["add_event", event(4)]);
211+
// Check that the .q has 4 events
212+
expect(Countly.q.length).to.equal(4);
213+
214+
cy.fetch_local_event_queue().then((rq) => {
215+
// Check that the event queue is empty
216+
expect(rq.length).to.equal(0);
217+
218+
// Check that events are still in .q
219+
expect(Countly.q.length).to.equal(4);
220+
221+
// Trigger processAsyncQueue by saving UserData
222+
Countly.userData.set("name", "test_user");
223+
Countly.userData.save();
224+
225+
// Check that the .q is empty
226+
expect(Countly.q.length).to.equal(0);
227+
cy.fetch_local_event_queue().then((eq) => {
228+
// Check that event queue is empty
229+
expect(eq.length).to.equal(0);
230+
cy.fetch_local_request_queue().then((rq_2) => {
231+
// Check that events are now in request queue (second request is user details)
232+
expect(rq_2.length).to.equal(2);
233+
const eventsArray = JSON.parse(rq_2[0].events);
234+
expect(eventsArray[0].key).to.equal("event_1");
235+
expect(eventsArray[1].key).to.equal("event_2");
236+
expect(eventsArray[2].key).to.equal("event_3");
237+
expect(eventsArray[3].key).to.equal("event_4");
238+
// check user data
239+
const user_details = JSON.parse(rq_2[1].user_details);
240+
expect(user_details.custom.name).to.equal("test_user");
241+
});
242+
});
243+
});
244+
});
245+
});
246+
// This test check if the heartbeat is processing the .q (executes processAsyncQueue)
247+
it("Check if heatbeat is processing .q", () => {
248+
hp.haltAndClearStorage(() => {
249+
// init the SDK
250+
Countly.q = [];
251+
initMain();
252+
253+
// Check that the .q is empty
254+
expect(Countly.q.length).to.equal(0);
255+
cy.fetch_local_event_queue().then((eq) => {
256+
// Check that the event queue is empty
257+
expect(eq.length).to.equal(0);
258+
cy.fetch_local_request_queue().then((rq) => {
259+
// Check that the request queue is empty
260+
expect(rq.length).to.equal(0);
261+
// Add 4 events to the .q
262+
Countly.q.push(["add_event", event(1)]);
263+
Countly.q.push(["add_event", event(2)]);
264+
Countly.q.push(["add_event", event(3)]);
265+
Countly.q.push(["add_event", event(4)]);
266+
// Check that the .q has 4 events
267+
expect(Countly.q.length).to.equal(4);
268+
// Wait for heartBeat to process the .q
269+
cy.wait(1500).then(() => {
270+
// Check that the .q is empty
271+
expect(Countly.q.length).to.equal(0);
272+
cy.fetch_local_event_queue().then((eq_2) => {
273+
// Check that event queue is empty as all must be in request queue
274+
expect(eq_2.length).to.equal(0);
275+
cy.fetch_local_request_queue().then((rq_2) => {
276+
// Check that events are now in request queue
277+
expect(rq_2.length).to.equal(1);
278+
const eventsArray = JSON.parse(rq_2[0].events);
279+
expect(eventsArray[0].key).to.equal("event_1");
280+
expect(eventsArray[1].key).to.equal("event_2");
281+
expect(eventsArray[2].key).to.equal("event_3");
282+
expect(eventsArray[3].key).to.equal("event_4");
283+
});
284+
});
285+
});
286+
});
287+
});
288+
});
289+
});
290+
});

cypress/integration/bridge_utils.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ function initMain(name, version) {
1616
}
1717

1818
const SDK_NAME = "javascript_native_web";
19-
const SDK_VERSION = "23.12.4";
19+
const SDK_VERSION = "24.4.0";
2020

2121
// tests
2222
describe("Bridged SDK Utilities Tests", () => {

cypress/integration/health_check.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ describe("Health Check tests ", () => {
2121
// Test the 'hc' parameter
2222
const hcParam = url.searchParams.get("hc");
2323
const hcParamObj = JSON.parse(hcParam);
24-
expect(hcParamObj).to.eql({ el: 0, wl: 0, sc: -1, em: "\"\"" });
24+
expect(hcParamObj).to.eql({ el: 0, wl: 0, sc: -1, em: "" });
2525

2626
// Test the 'metrics' parameter
2727
const metricsParam = url.searchParams.get("metrics");

0 commit comments

Comments
 (0)