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

SDK update 24.11.0 and fixes #531

Merged
merged 1 commit into from
Nov 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
## 24.11.0
- Mitigated an issue where SDK could try to send old stored offline mode data during init if `clear_stored_id` was true
- Mitigated an issue where the SDK could stayed on offline mode after the first init with `offline_mode` set to true
- Mitigated an issue where old Rating widget stickers were not cleared when a new one was presented

- Improved view tracking logic
- Default request method is now set to "POST"
- Healtchecks won't be sent in offline mode anymore
- Added a new interface 'feedback' which includes convenience methods to show feedback widgets:
- showNPS([String nameIDorTag]) - for displaying the first available NPS widget or one with the given name, Tag or ID value
- showSurvey([String nameIDorTag]) - for displaying the first available Survey widget or one with the given name, Tag or ID value
- showRating([String nameIDorTag]) - for displaying the first available Rating widget or one with the given name, Tag or ID value

## 24.4.1
- Added types for the SDK
- Added a new method `set_id(newDeviceId)` for managing device ID changes according to the device ID Type
Expand Down
2 changes: 1 addition & 1 deletion cypress/e2e/bridged_utils.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ function initMain(name, version) {
}

const SDK_NAME = "javascript_native_web";
const SDK_VERSION = "24.4.1";
const SDK_VERSION = "24.11.0";

// tests
describe("Bridged SDK Utilities Tests", () => {
Expand Down
254 changes: 177 additions & 77 deletions cypress/e2e/device_id_init_scenarios.cy.js

Large diffs are not rendered by default.

34 changes: 24 additions & 10 deletions cypress/e2e/health_check.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,28 +10,42 @@ function initMain() {
});
}

describe("Health Check tests ", () => {
describe("Health Check tests", () => {
it("Check if health check is sent at the beginning", () => {
hp.haltAndClearStorage(() => {
initMain();
cy.intercept("https://your.domain.count.ly/i?*").as("getXhr");
cy.wait("@getXhr").then((xhr) => {
const url = new URL(xhr.request.url);
cy.intercept("POST", "https://your.domain.count.ly/i").as("postXhr");
cy.wait("@postXhr").then((xhr) => {
const body = xhr.request.body;
const params = new URLSearchParams(body);

// Test the 'hc' parameter
const hcParam = url.searchParams.get("hc");
const hcParamObj = JSON.parse(hcParam);
const hcParam = params.get("hc");
const hcParamObj = JSON.parse(decodeURIComponent(hcParam));
expect(hcParamObj).to.eql({ el: 0, wl: 0, sc: -1, em: "" });

// Test the 'metrics' parameter
const metricsParam = url.searchParams.get("metrics");
const metricsParam = params.get("metrics");
expect(metricsParam).to.equal("{\"_app_version\":\"0.0\",\"_ua\":\"abcd\"}");

// check nothing in the request queue
cy.fetch_local_request_queue().then((rq) => {
expect(rq.length).to.equal(0);
});
});
});
});
it("Check no health check is sent in offline mode", () => {
hp.haltAndClearStorage(() => {
Countly.init({
app_key: "YOUR_APP_KEY",
url: "https://your.domain.count.ly",
test_mode: true,
offline_mode: true
});

cy.intercept("POST", "https://your.domain.count.ly/i").as("postXhr");
cy.get('@postXhr').should('not.exist');
cy.fetch_local_request_queue().then((rq) => {
expect(rq.length).to.equal(0);
});
});
});
});
8 changes: 4 additions & 4 deletions cypress/e2e/remaining_requests.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ describe("Remaining requests tests ", () => {
initMain(false);

// We will expect 4 requests: health check, begin_session, end_session, orientation
hp.interceptAndCheckRequests(undefined, undefined, undefined, "?hc=*", "hc", (requestParams) => {
hp.interceptAndCheckRequests("POST", undefined, undefined, "?hc=*", "hc", (requestParams) => {
const params = JSON.parse(requestParams.get("hc"));
assert.isTrue(typeof params.el === "number");
assert.isTrue(typeof params.wl === "number");
Expand All @@ -29,19 +29,19 @@ describe("Remaining requests tests ", () => {
cy.wait(1000).then(() => {
// Create a session
Countly.begin_session();
hp.interceptAndCheckRequests(undefined, undefined, undefined, "?begin_session=*", "begin_session", (requestParams) => {
hp.interceptAndCheckRequests("POST", undefined, undefined, "?begin_session=*", "begin_session", (requestParams) => {
expect(requestParams.get("begin_session")).to.equal("1");
expect(requestParams.get("rr")).to.equal("3");
expect(requestParams.get("av")).to.equal(av);
});
// End the session
Countly.end_session(undefined, true);
hp.interceptAndCheckRequests(undefined, undefined, undefined, "?end_session=*", "end", (requestParams) => {
hp.interceptAndCheckRequests("POST", undefined, undefined, "?end_session=*", "end", (requestParams) => {
expect(requestParams.get("end_session")).to.equal("1");
expect(requestParams.get("rr")).to.equal("2");
expect(requestParams.get("av")).to.equal(av);
});
hp.interceptAndCheckRequests(undefined, undefined, undefined, undefined, "orientation", (requestParams) => {
hp.interceptAndCheckRequests("POST", undefined, undefined, undefined, "orientation", (requestParams) => {
expect(JSON.parse(requestParams.get("events"))[0].key).to.equal("[CLY]_orientation");
expect(requestParams.get("rr")).to.equal("1");
expect(requestParams.get("av")).to.equal(av);
Expand Down
44 changes: 28 additions & 16 deletions cypress/support/helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,31 +68,43 @@ var waitFunction = function(startTime, waitTime, waitIncrement, continueCallback
};

/**
* This intercepts the request the SDK makes and returns the request parameters to the callback function
* @param {String} requestType - GET, POST, PUT, DELETE
* @param {String} requestUrl - request url (https://your.domain.count.ly)
* @param {String} endPoint - endpoint (/i)
* @param {String} requestParams - request parameters (?begin_session=**)
* Intercepts SDK requests and returns request parameters to the callback function.
* @param {String} requestType - GET or POST
* @param {String} requestUrl - base URL (e.g., https://your.domain.count.ly)
* @param {String} endPoint - endpoint (e.g., /i)
* @param {String} aliasParam - parameter to match in requests (e.g., "hc", "begin_session")
* @param {String} alias - alias for the request
* @param {Function} callback - callback function
* @param {Function} callback - callback function for parsed parameters
*/
function interceptAndCheckRequests(requestType, requestUrl, endPoint, requestParams, alias, callback) {
// requestType = requestType || "GET";
requestUrl = requestUrl || "https://your.domain.count.ly"; // TODO: might be needed in the future but not yet
function interceptAndCheckRequests(requestType, requestUrl, endPoint, aliasParam, alias, callback) {
requestType = requestType || "GET";
requestUrl = requestUrl || "https://your.domain.count.ly";
endPoint = endPoint || "/i";
requestParams = requestParams || "?*";
alias = alias || "getXhr";

cy.intercept(requestUrl + endPoint + requestParams, (req) => {
// const { url } = req;
req.reply(200, {result: "Success"}, {
// Intercept requests
cy.intercept(requestType, requestUrl + endPoint + "*", (req) => {
if (requestType === "POST" && req.body) {
// Parse URL-encoded body for POST requests
const params = new URLSearchParams(req.body);
callback(params);
} else {
// Parse URL parameters for GET requests
const url = new URL(req.url);
const params = url.searchParams;
callback(params);
}
req.reply(200, { result: "Success" }, {
"x-countly-rr": "2"
});
}).as(alias);

// Wait for the request alias to be triggered
cy.wait("@" + alias).then((xhr) => {
const url = new URL(xhr.request.url);
const searchParams = url.searchParams;
callback(searchParams);
const params = requestType === "POST" && xhr.request.body
? new URLSearchParams(xhr.request.body)
: new URL(xhr.request.url).searchParams;
callback(params);
});
}

Expand Down
16 changes: 15 additions & 1 deletion examples/examples_feedback_widgets.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,21 @@
});

//=================================================
// Fetching and displaying feedback widgets
// Displaying feedback widgets
//=================================================
Countly.feedback.showNPS();

// OR with a specific ID, name or tag
// const id = 'ID_from_server';
// Countly.feedback.showNPS(id);

// Other feedback widgets
// Countly.feedback.showSurvey();
// Countly.feedback.showRating();


//=================================================
// ADVANCED: Fetching and displaying feedback widgets
//=================================================
function fetchAndDisplayWidget() {
// Fetch user's feedbacks widgets from the server (must have been created at server first)
Expand Down
27 changes: 27 additions & 0 deletions lib/countly.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,33 @@ declare module "countly-sdk-web" {
| null
): void;

/**
* Feedback interface with convenience methods for feedback widgets:
* - showNPS([nameIDorTag]) - shows an NPS widget by name, id, or tag, or a random one if not provided
* - showSurvey([nameIDorTag]) - shows a Survey widget by name, id, or tag, or a random one if not provided
* - showRating([nameIDorTag]) - shows a Rating widget by name, id, or tag, or a random one if not provided
*/
const feedback: Feedback;
interface Feedback {
/**
* Displays the first available NPS widget or the one with the provided name, id, or tag.
* @param nameIDorTag - Optional name, id, or tag of the NPS widget to display.
*/
showNPS(nameIDorTag?: string): void;

/**
* Displays the first available Survey widget or the one with the provided name, id, or tag.
* @param nameIDorTag - Optional name, id, or tag of the Survey widget to display.
*/
showSurvey(nameIDorTag?: string): void;

/**
* Displays the first available Rating widget or the one with the provided name, id, or tag.
* @param nameIDorTag - Optional name, id, or tag of the Rating widget to display.
*/
showRating(nameIDorTag?: string): void;
}

/**
* This function retrieves all associated widget information (IDs, type, name etc in an array/list of objects) of your app
* @param {Function} callback - Callback function with two parameters, 1st for returned list, 2nd for error
Expand Down
Loading
Loading