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

ajy-UID2-1667-Local-storage-for-v3-js-sdk #21

Merged
merged 21 commits into from
Oct 9, 2023
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
1 change: 1 addition & 0 deletions src/Uid2Options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export type Uid2Options = BaseUid2Options &
type BaseUid2Options = {
refreshRetryPeriod?: number;
identity?: Uid2Identity;
useCookie?: boolean;
};

export function isUID2OptionsOrThrow(
Expand Down
340 changes: 186 additions & 154 deletions src/integrationTests/async.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,201 +23,233 @@ beforeEach(() => {
xhrMock = new mocks.XhrMock(sdkWindow);
_cryptoMock = new mocks.CryptoMock(sdkWindow);
mocks.setCookieMock(sdkWindow.document);
removeUid2Cookie();
removeUid2LocalStorage();
});

afterEach(() => {
mocks.resetFakeTime();
});

const removeUid2Cookie = mocks.removeUid2Cookie;
const removeUid2LocalStorage = mocks.removeUid2LocalStorage;

const makeIdentity = mocks.makeIdentityV2;

describe("when getAdvertisingTokenAsync is called before init", () => {
describe("when initialising with a valid identity", () => {
const identity = makeIdentity();
test("it should resolve promise after invoking the callback", () => {
const p = uid2.getAdvertisingTokenAsync().then((token: any) => {
expect(callback).toHaveBeenCalled();
return token;
});
uid2.init({ callback: callback, identity: identity });
jest.runAllTimers();
return expect(p).resolves.toBe(identity.advertising_token);
let useCookie: boolean | undefined = undefined;

const testCookieAndLocalStorage = (test: () => void, only = false) => {
const describeFn = only ? describe.only : describe;
describeFn('Using default: ', () => {
beforeEach(() => {
useCookie = undefined;
});
test();
});

describe("when initialising with an invalid identity", () => {
test("it should reject promise after invoking the callback", () => {
const p = uid2.getAdvertisingTokenAsync().catch((e: any) => {
expect(callback).toHaveBeenCalled();
throw e;
});
uid2.init({ callback: callback });
return expect(p).rejects.toBeInstanceOf(Error);
describeFn('Using cookies ', () => {
beforeEach(() => {
useCookie = true;
});
test();
});

describe("when initalising with a non-expired identity which requires a refresh", () => {
test("it should resolve updated advertising", () => {
const originalIdentity = makeIdentity({
refresh_from: Date.now() - 100000,
});
const updatedIdentity = makeIdentity({
advertising_token: "updated_advertising_token",
});
const p = uid2.getAdvertisingTokenAsync();
uid2.init({ identity: originalIdentity });
xhrMock.responseText = btoa(
JSON.stringify({ status: "success", body: updatedIdentity })
);
xhrMock.onreadystatechange(new Event(""));
return expect(p).resolves.toBe(updatedIdentity.advertising_token);
describeFn('Using local storage ', () => {
beforeEach(() => {
useCookie = false;
});
test();
});
};

testCookieAndLocalStorage(() => {
describe("when getAdvertisingTokenAsync is called before init", () => {
describe("when initialising with a valid identity", () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we cover test cases for when initialising without identity? (which calls the loadIdentityWithFallback?)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have any test that testing if the refreshed token has been stored to the correct storage?

const identity = makeIdentity();
test("it should resolve promise after invoking the callback", () => {
const p = uid2.getAdvertisingTokenAsync().then((token: any) => {
expect(callback).toHaveBeenCalled();
return token;
});
uid2.init({ callback: callback, identity: identity, useCookie: useCookie });
jest.runAllTimers();
return expect(p).resolves.toBe(identity.advertising_token);
});
});

describe("when auto refresh fails, but identity still valid", () => {
test("it should resolve original advertising token", () => {
const originalIdentity = makeIdentity({
refresh_from: Date.now() - 100000,
describe("when initialising with an invalid identity", () => {
test("it should reject promise after invoking the callback", () => {
const p = uid2.getAdvertisingTokenAsync().catch((e: any) => {
expect(callback).toHaveBeenCalled();
throw e;
});
uid2.init({ callback: callback, useCookie: useCookie });
return expect(p).rejects.toBeInstanceOf(Error);
});
const p = uid2.getAdvertisingTokenAsync().then((token: any) => {
expect(callback).toHaveBeenCalled();
return token;
});

describe("when initialising with a non-expired identity which requires a refresh", () => {
test("it should resolve updated advertising", () => {
const originalIdentity = makeIdentity({
refresh_from: Date.now() - 100000,
});
const updatedIdentity = makeIdentity({
advertising_token: "updated_advertising_token",
});
const p = uid2.getAdvertisingTokenAsync();
uid2.init({ identity: originalIdentity, useCookie: useCookie });
xhrMock.responseText = btoa(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: maybe we could wrap xhrMock.responseText and xhrMock.onreadystatechange into a mockResponse(response) function?

JSON.stringify({ status: "success", body: updatedIdentity })
);
xhrMock.onreadystatechange(new Event(""));
return expect(p).resolves.toBe(updatedIdentity.advertising_token);
});
uid2.init({ callback: callback, identity: originalIdentity });
xhrMock.responseText = JSON.stringify({ status: "error" });
xhrMock.onreadystatechange(new Event(""));
return expect(p).resolves.toBe(originalIdentity.advertising_token);
});
});

describe("when auto refresh fails, but identity already expired", () => {
test("it should reject promise after invoking the callback", () => {
const originalIdentity = makeIdentity({
refresh_from: Date.now() - 100000,
identity_expires: Date.now() - 1,
describe("when auto refresh fails, but identity still valid", () => {
test("it should resolve original advertising token", () => {
const originalIdentity = makeIdentity({
refresh_from: Date.now() - 100000,
});
const p = uid2.getAdvertisingTokenAsync().then((token: any) => {
expect(callback).toHaveBeenCalled();
return token;
});
uid2.init({ callback: callback, identity: originalIdentity, useCookie: useCookie });
xhrMock.responseText = JSON.stringify({ status: "error" });
xhrMock.onreadystatechange(new Event(""));
return expect(p).resolves.toBe(originalIdentity.advertising_token);
});
const p = uid2.getAdvertisingTokenAsync().catch((e: any) => {
expect(callback).toHaveBeenCalled();
throw e;
});

describe("when auto refresh fails, but identity already expired", () => {
test("it should reject promise after invoking the callback", () => {
const originalIdentity = makeIdentity({
refresh_from: Date.now() - 100000,
identity_expires: Date.now() - 1,
});
const p = uid2.getAdvertisingTokenAsync().catch((e: any) => {
expect(callback).toHaveBeenCalled();
throw e;
});
uid2.init({ callback: callback, identity: originalIdentity, useCookie: useCookie });
xhrMock.responseText = JSON.stringify({ status: "error" });
xhrMock.onreadystatechange(new Event(""));
return expect(p).rejects.toBeInstanceOf(Error);
});
uid2.init({ callback: callback, identity: originalIdentity });
xhrMock.responseText = JSON.stringify({ status: "error" });
xhrMock.onreadystatechange(new Event(""));
return expect(p).rejects.toBeInstanceOf(Error);
});
});

describe("when giving multiple promises", () => {
const identity = makeIdentity();
test("it should resolve all promises", () => {
const p1 = uid2.getAdvertisingTokenAsync();
const p2 = uid2.getAdvertisingTokenAsync();
const p3 = uid2.getAdvertisingTokenAsync();
uid2.init({ identity: identity });
return expect(Promise.all([p1, p2, p3])).resolves.toStrictEqual(
Array(3).fill(identity.advertising_token)
);
describe("when giving multiple promises", () => {
const identity = makeIdentity();
test("it should resolve all promises", () => {
const p1 = uid2.getAdvertisingTokenAsync();
const p2 = uid2.getAdvertisingTokenAsync();
const p3 = uid2.getAdvertisingTokenAsync();
uid2.init({ identity: identity, useCookie: useCookie });
return expect(Promise.all([p1, p2, p3])).resolves.toStrictEqual(
Array(3).fill(identity.advertising_token)
);
});
});
});
});

describe("when getAdvertisingTokenAsync is called after init completed", () => {
describe("when initialised with a valid identity", () => {
const identity = makeIdentity();
test("it should resolve promise", () => {
uid2.init({ identity: identity });
return expect(uid2.getAdvertisingTokenAsync()).resolves.toBe(
identity.advertising_token
);
describe("when getAdvertisingTokenAsync is called after init completed", () => {
describe("when initialised with a valid identity", () => {
const identity = makeIdentity();
test("it should resolve promise", () => {
uid2.init({ identity: identity, useCookie: useCookie });
return expect(uid2.getAdvertisingTokenAsync()).resolves.toBe(
identity.advertising_token
);
});
});
});

describe("when initialisation failed", () => {
test("it should reject promise", () => {
uid2.init({});
return expect(uid2.getAdvertisingTokenAsync()).rejects.toBeInstanceOf(
Error
);
describe("when initialisation failed", () => {
test("it should reject promise", () => {
uid2.init({});
return expect(uid2.getAdvertisingTokenAsync()).rejects.toBeInstanceOf(
Error
);
});
});
});

describe("when identity is temporarily not available", () => {
test("it should reject promise", () => {
const originalIdentity = makeIdentity({
refresh_from: Date.now() - 100000,
identity_expires: Date.now() - 1,
describe("when identity is temporarily not available", () => {
test("it should reject promise", () => {
const originalIdentity = makeIdentity({
refresh_from: Date.now() - 100000,
identity_expires: Date.now() - 1,
});
uid2.init({ identity: originalIdentity, useCookie: useCookie });
xhrMock.responseText = JSON.stringify({ status: "error" });
xhrMock.onreadystatechange(new Event(""));
return expect(uid2.getAdvertisingTokenAsync()).rejects.toBeInstanceOf(
Error
);
});
uid2.init({ identity: originalIdentity });
xhrMock.responseText = JSON.stringify({ status: "error" });
xhrMock.onreadystatechange(new Event(""));
return expect(uid2.getAdvertisingTokenAsync()).rejects.toBeInstanceOf(
Error
);
});
});

describe("when disconnect() has been called", () => {
test("it should reject promise", () => {
uid2.init({ identity: makeIdentity() });
uid2.disconnect();
return expect(uid2.getAdvertisingTokenAsync()).rejects.toBeInstanceOf(
Error
);
describe("when disconnect() has been called", () => {
test("it should reject promise", () => {
uid2.init({ identity: makeIdentity(), useCookie: useCookie });
uid2.disconnect();
return expect(uid2.getAdvertisingTokenAsync()).rejects.toBeInstanceOf(
Error
);
});
});
});
});

describe("when getAdvertisingTokenAsync is called before refresh on init completes", () => {
const originalIdentity = makeIdentity({
refresh_from: Date.now() - 100000,
});
beforeEach(() => {
uid2.init({ identity: originalIdentity });
});
describe("when getAdvertisingTokenAsync is called before refresh on init completes", () => {
const originalIdentity = makeIdentity({
refresh_from: Date.now() - 100000,
});
beforeEach(() => {
uid2.init({ identity: originalIdentity, useCookie: useCookie });
});

describe("when promise obtained after disconnect", () => {
test("it should reject promise", () => {
uid2.disconnect();
return expect(uid2.getAdvertisingTokenAsync()).rejects.toBeInstanceOf(
Error
);
describe("when promise obtained after disconnect", () => {
test("it should reject promise", () => {
uid2.disconnect();
return expect(uid2.getAdvertisingTokenAsync()).rejects.toBeInstanceOf(
Error
);
});
});
});
});

describe("when window.__uid2.init is called on SdkLoaded from a callback", () => {
const identity = makeIdentity();
// Reset window UID2 instance
const callback = jest.fn((eventType: EventType) => {
if (eventType === UID2.EventType.SdkLoaded) {
console.log("Trying");
try {
(sdkWindow.__uid2 as UID2).init({ identity });
} catch (ex) {
console.log(ex);
throw ex;
describe("when window.__uid2.init is called on SdkLoaded from a callback", () => {
const identity = makeIdentity();
// Reset window UID2 instance
const callback = jest.fn((eventType: EventType) => {
if (eventType === UID2.EventType.SdkLoaded) {
console.log("Trying");
try {
(sdkWindow.__uid2 as UID2).init({ identity, useCookie: useCookie });
} catch (ex) {
console.log(ex);
throw ex;
}
console.log("Succeeded");
}
console.log("Succeeded");
}
});
test("the SDK should be initialized correctly", () => {
sdkWindow.__uid2 = { callbacks: [] };
sdkWindow.__uid2.callbacks!.push(callback);
expect(callback).toHaveBeenCalledTimes(0);
__uid2InternalHandleScriptLoad();
jest.runOnlyPendingTimers();
if (!(sdkWindow.__uid2 instanceof UID2))
throw Error(
"UID2 should be ready to use by the time SdkLoaded is triggered."
});
test("the SDK should be initialized correctly", () => {
sdkWindow.__uid2 = { callbacks: [] };
sdkWindow.__uid2.callbacks!.push(callback);
expect(callback).toHaveBeenCalledTimes(0);
__uid2InternalHandleScriptLoad();
jest.runOnlyPendingTimers();
if (!(sdkWindow.__uid2 instanceof UID2))
throw Error(
"UID2 should be ready to use by the time SdkLoaded is triggered."
);
expect(callback).toHaveBeenNthCalledWith(
1,
UID2.EventType.SdkLoaded,
expect.anything()
);
expect(callback).toHaveBeenNthCalledWith(
1,
UID2.EventType.SdkLoaded,
expect.anything()
);
console.log(identity.advertising_token);
expect(sdkWindow.__uid2.getAdvertisingToken()).toBe(
identity.advertising_token
);
console.log(sdkWindow.__uid2.getAdvertisingToken());
console.log(identity.advertising_token);
expect(sdkWindow.__uid2.getAdvertisingToken()).toBe(
identity.advertising_token
);
});
});
});
Loading