From 97b918600c57c98973de482a0d635f49c0eb65c4 Mon Sep 17 00:00:00 2001 From: Alexander Sapountzis Date: Tue, 16 Aug 2022 17:02:21 -0400 Subject: [PATCH 01/11] Add tests for determineLocalStorageAvailability --- src/persistence.js | 6 +-- test/src/tests-persistence.js | 72 +++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 4 deletions(-) diff --git a/src/persistence.js b/src/persistence.js index 53e3623e1..ae8311b15 100644 --- a/src/persistence.js +++ b/src/persistence.js @@ -227,17 +227,15 @@ export default function _Persistence(mpInstance) { }; this.determineLocalStorageAvailability = function(storage) { - var result; - if (window.mParticle && window.mParticle._forceNoLocalStorage) { storage = undefined; } try { storage.setItem('mparticle', 'test'); - result = storage.getItem('mparticle') === 'test'; + storage.getItem('mparticle') === 'test'; storage.removeItem('mparticle'); - return result && storage; + return true; } catch (e) { return false; } diff --git a/test/src/tests-persistence.js b/test/src/tests-persistence.js index 2fffcf51d..ab3f5619c 100644 --- a/test/src/tests-persistence.js +++ b/test/src/tests-persistence.js @@ -11,6 +11,78 @@ var findCookie = Utils.findCookie, getEvent = Utils.getEvent, mockServer; +describe('Persistence', function () { + describe('#useLocalStorage', function () { + it.skip('returns true if Local Storage is available', function () { + mParticle._resetForTests(MPConfig); + // FIXME: Returns an object instead of boolean for some reason + mParticle.getInstance()._Persistence.useLocalStorage().should.eq(true); + }); + }); + describe('#initializeStorage', function () {}); + describe('#update', function () {}); + describe('#storeProductsInMemory', function () {}); + describe('#storeDataInMemory', function () {}); + + describe.only('#determineLocalStorageAvailability', function () { + it('returns true if Local Storage is available', function (){ + mParticle._resetForTests(MPConfig); + mParticle.getInstance()._Persistence.determineLocalStorageAvailability(window.localStorage).should.equal(true); + }); + + it('returns false if Local Storage is not available', function (){ + mParticle._resetForTests(MPConfig); + // FIXME: this method should not take storage as a param + mParticle.getInstance()._Persistence.determineLocalStorageAvailability(null).should.equal(false); + }); + + it('returns false if Local Storage disabled via _forceNoLocalStorage', function (){ + mParticle._resetForTests(MPConfig); + mParticle._forceNoLocalStorage = true; + mParticle.getInstance()._Persistence.determineLocalStorageAvailability(window.localStorage).should.equal(false); + }); + }); + + describe('#getUserProductsFromLS', function () {}); + describe('#getAllUserProductsFromLS', function () {}); + describe('#setLocalStorage', function () {}); + describe('#getLocalStorage', function () {}); + describe('#expireCookies', function () {}); + describe('#getCookie', function () {}); + describe('#setCookie', function () {}); + describe('#reduceAndEncodePersistence', function () {}); + describe('#findPrevCookiesBasedOnUI', function () {}); + describe('#encodePersistence', function () {}); + describe('#decodePersistence', function () {}); + describe('#replaceCommasWithPipes', function () {}); + describe('#replacePipesWithCommas', function () {}); + describe('#replaceApostrophesWithQuotes', function () {}); + describe('#replaceQuotesWithApostrophes', function () {}); + describe('#createCookieString', function () {}); + describe('#revertCookieString', function () {}); + describe('#getCookieDomain', function () {}); + describe('#getDomain', function () {}); + describe('#getUserIdentities', function () {}); + describe('#getAllUserAttributes', function () {}); + describe('#getCartProducts', function () {}); + describe('#setCartProducts', function () {}); + describe('#saveUserIdentitiesToPersistence', function () {}); + describe('#saveUserAttributesToPersistence', function () {}); + describe('#saveUserCookieSyncDatesToPersistence', function () {}); + describe('#saveUserConsentStateToCookies', function () {}); + describe('#savePersistence', function () {}); + describe('#getPersistence', function () {}); + describe('#getConsentState', function () {}); + describe('#getFirstSeenTime', function () {}); + describe('#setFirstSeenTime', function () {}); + describe('#getLastSeenTime', function () {}); + describe('#setLastSeenTime', function () {}); + describe('#getDeviceId', function () {}); + describe('#setDeviceId', function () {}); + describe('#resetPersistence', function () {}); + +}); + describe('migrations and persistence-related', function() { beforeEach(function() { mockServer = sinon.createFakeServer(); From 0d18be4463844fa3f7b00e6f19664f8b1dbe20d4 Mon Sep 17 00:00:00 2001 From: Alexander Sapountzis Date: Tue, 20 Sep 2022 16:50:08 -0400 Subject: [PATCH 02/11] Continue writing unit tests for persistence --- src/persistence.js | 12 +- test/src/tests-persistence.js | 488 ++++++++++++++++++++++++++++------ 2 files changed, 415 insertions(+), 85 deletions(-) diff --git a/src/persistence.js b/src/persistence.js index ae8311b15..6799b27e9 100644 --- a/src/persistence.js +++ b/src/persistence.js @@ -463,11 +463,13 @@ export default function _Persistence(mpInstance) { ); } + // TODO: Is this part of the deprecated cookie conversion flow? if (key && key === name) { result = mpInstance._Helpers.converted(cookie); break; } + // TODO: Which path should we be using? if (!key) { result[name] = mpInstance._Helpers.converted(cookie); } @@ -541,7 +543,7 @@ export default function _Persistence(mpInstance) { mpInstance._Store.SDKConfig.maxCookieSize ); - mpInstance.Logger.verbose(Messages.InformationMessages.CookieSet); + // mpInstance.Logger.verbose(Messages.InformationMessages.CookieSet); window.document.cookie = encodeURIComponent(key) + '=' + encodedCookiesWithExpirationAndPath; @@ -701,6 +703,8 @@ export default function _Persistence(mpInstance) { } }; + // TODO: Describe expected functionality and give examples of a persistence object + // FIXME: this should not mutate persistence this.encodePersistence = function(persistence) { persistence = JSON.parse(persistence); for (var key in persistence.gs) { @@ -761,6 +765,8 @@ export default function _Persistence(mpInstance) { return self.createCookieString(JSON.stringify(persistence)); }; + // TODO: Describe expected functionality and give examples of a persistence object + // FIXME: this should not mutate persistence this.decodePersistence = function(persistence) { try { if (persistence) { @@ -815,18 +821,22 @@ export default function _Persistence(mpInstance) { } }; + // TODO: This should be a helper this.replaceCommasWithPipes = function(string) { return string.replace(/,/g, '|'); }; + // TODO: This should be a helper this.replacePipesWithCommas = function(string) { return string.replace(/\|/g, ','); }; + // TODO: This should be a helper this.replaceApostrophesWithQuotes = function(string) { return string.replace(/\'/g, '"'); }; + // TODO: This should be a helper this.replaceQuotesWithApostrophes = function(string) { return string.replace(/\"/g, "'"); }; diff --git a/test/src/tests-persistence.js b/test/src/tests-persistence.js index ab3f5619c..8eac3dac8 100644 --- a/test/src/tests-persistence.js +++ b/test/src/tests-persistence.js @@ -1,6 +1,15 @@ import Utils from './utils'; import sinon from 'sinon'; -import { urls, apiKey, testMPID, MPConfig,localStorageProductsV4, LocalStorageProductsV4WithWorkSpaceName, workspaceCookieName, v4LSKey } from './config'; +import { + urls, + apiKey, + testMPID, + MPConfig, + localStorageProductsV4, + LocalStorageProductsV4WithWorkSpaceName, + workspaceCookieName, + v4LSKey, +} from './config'; /* eslint-disable quotes*/ var findCookie = Utils.findCookie, @@ -11,76 +20,382 @@ var findCookie = Utils.findCookie, getEvent = Utils.getEvent, mockServer; -describe('Persistence', function () { - describe('#useLocalStorage', function () { - it.skip('returns true if Local Storage is available', function () { +describe('Persistence', function() { + describe('#useLocalStorage', function() { + it('returns true if Local Storage is available', function() { mParticle._resetForTests(MPConfig); - // FIXME: Returns an object instead of boolean for some reason - mParticle.getInstance()._Persistence.useLocalStorage().should.eq(true); + mParticle + .getInstance() + ._Persistence.useLocalStorage() + .should.eql(true); + }); + + it('returns false if Local Storage is not available', function() { + mParticle._resetForTests(MPConfig); + mParticle.getInstance()._Store.isLocalStorageAvailable = false; + mParticle + .getInstance() + ._Persistence.useLocalStorage() + .should.eql(false); + }); + + it('returns false if cookie storage is preferred', function() { + mParticle._resetForTests(MPConfig); + mParticle.getInstance()._Store.SDKConfig.useCookieStorage = true; + mParticle + .getInstance() + ._Persistence.useLocalStorage() + .should.eql(false); + }); + }); + describe('#initializeStorage', function() {}); + + describe('#update', function() { + it('sets local storage', () => { + var bond = sinon.spy( + mParticle.getInstance()._Persistence, + 'setLocalStorage' + ); + + mParticle._resetForTests(MPConfig); + mParticle.getInstance()._Persistence.update(); + + bond.called.should.eql(true); + }); + + it.skip('sets a cookie when useCookieStorage is set', () => { + var bond = sinon.spy( + mParticle.getInstance()._Persistence, + 'setCookie' + ); + + // var setCookieSpy = sinon.spy( + // mParticle.getInstance()._Persistence, + // 'setCookie' + // ); + + // var getCookieSpy = sinon.spy( + // mParticle.getInstance()._Persistence, + // 'getCookie' + // ); + + document.cookie = + "mprtcl-v4_4DD884CD={'gs':{'ie':1|'dt':'9aa8aa0514a802498e8e941d53e2a1d9'|'cgid':'e32ee0cf-83c7-4398-bd50-462a943d16b6'|'das':'99f5ad4d-ed1b-4044-89b6-977d7fac40c5'|'ia':'eyIxMjQiOnsibWlkIjoiNDk3NTQ1MzkyNzgyOTUxNTkxOTA4OTgwNzQ5NzYyOTQwNDQyNzAifX0='|'av':'1.0.0'}|'l':1|'9128337746531357694':{'fst':1663610956871|'ui':'eyIxIjoiMTIzNDU2IiwiNyI6ImVtYWlsQGV4YW1wbGUuY29tIn0='}|'cu':'9128337746531357694'}"; + + mParticle.getInstance().Logger = { + verbose: function(msg) {}, + error: function(msg) {}, + }; + + mParticle._resetForTests(MPConfig); + + mParticle.getInstance()._Persistence.update(); + + bond.called.should.eql(false); + + mParticle._resetForTests( + Object.assign(MPConfig, { + useCookieStorage: true, + workspaceToken: 'cookie-test', + }) + ); + + debugger; + + mParticle.getInstance()._Persistence.update(); + + bond.called.should.eql(true); + }); + }); + + describe('#storeProductsInMemory', function() { + it('stores an array of products in memory', () => { + var products = { + 'test-mpid': { + cp: 'foo', + }, + }; + mParticle._resetForTests(MPConfig); + mParticle + .getInstance() + ._Persistence.storeProductsInMemory(products, 'test-mpid'); + + mParticle.getInstance()._Store.cartProducts.should.equal('foo'); + }); + + it('stores an empty array if products are not groupd by mpid', () => { + var products = {}; + mParticle._resetForTests(MPConfig); + mParticle + .getInstance() + ._Persistence.storeProductsInMemory(products, 'test-mpid'); + mParticle.getInstance()._Store.cartProducts.should.deepEqual([]); + }); + + it.skip('logs an error if something goes wrong', () => { + var bond = sinon.spy(mParticle.getInstance().Logger, 'warning'); + mParticle._resetForTests(MPConfig); + mParticle + .getInstance() + ._Persistence.storeProductsInMemory(products, 'test-mpid'); + mParticle.getInstance()._Store.cartProducts.should.deepEqual([]); + bond.getCalls()[0].args[0].should.eql('Could not parse cookie'); }); }); - describe('#initializeStorage', function () {}); - describe('#update', function () {}); - describe('#storeProductsInMemory', function () {}); - describe('#storeDataInMemory', function () {}); - describe.only('#determineLocalStorageAvailability', function () { - it('returns true if Local Storage is available', function (){ + describe('#storeDataInMemory', function() {}); + + describe('#determineLocalStorageAvailability', function() { + it('returns true if Local Storage is available', function() { mParticle._resetForTests(MPConfig); - mParticle.getInstance()._Persistence.determineLocalStorageAvailability(window.localStorage).should.equal(true); + mParticle + .getInstance() + ._Persistence.determineLocalStorageAvailability( + window.localStorage + ) + .should.equal(true); }); - it('returns false if Local Storage is not available', function (){ + it('returns false if Local Storage is not available', function() { mParticle._resetForTests(MPConfig); // FIXME: this method should not take storage as a param - mParticle.getInstance()._Persistence.determineLocalStorageAvailability(null).should.equal(false); + mParticle + .getInstance() + ._Persistence.determineLocalStorageAvailability(null) + .should.equal(false); }); - it('returns false if Local Storage disabled via _forceNoLocalStorage', function (){ + it('returns false if Local Storage disabled via _forceNoLocalStorage', function() { mParticle._resetForTests(MPConfig); mParticle._forceNoLocalStorage = true; - mParticle.getInstance()._Persistence.determineLocalStorageAvailability(window.localStorage).should.equal(false); + mParticle + .getInstance() + ._Persistence.determineLocalStorageAvailability( + window.localStorage + ) + .should.equal(false); + }); + }); + + describe('#getUserProductsFromLS', function() {}); + describe('#getAllUserProductsFromLS', function() {}); + describe('#setLocalStorage', function() {}); + + describe('#getLocalStorage', function() { + it('returns null if storage name is not set', function() { + mParticle._resetForTests(MPConfig); + + ( + mParticle.getInstance()._Persistence.getLocalStorage() === null + ).should.eql(true); + }); + + it('returns null if local storage data is empty', function() { + window.localStorage.setItem('foo-storage', '{}'); + + mParticle._resetForTests(MPConfig); + + mParticle.getInstance()._Store.isLocalStorageAvailable = true; + mParticle.getInstance()._Store.storageName = 'foo-storage'; + + ( + mParticle.getInstance()._Persistence.getLocalStorage() === null + ).should.eql(true); + }); + + it('returns a local storage object', function() { + window.localStorage.setItem( + 'foo-storage', + '{"foo": "bar", "mpid": "12345"}' + ); + + mParticle._resetForTests(MPConfig); + + mParticle.getInstance()._Store.isLocalStorageAvailable = true; + mParticle.getInstance()._Store.storageName = 'foo-storage'; + + mParticle + .getInstance() + ._Persistence.getLocalStorage() + .should.eql({ + foo: 'bar', + mpid: '12345', + }); + }); + }); + + describe('#expireCookies', function() {}); + + describe.only('#getCookie', function() { + it('returns a cookie', function() { + // TODO: What does our cookie look like? + document.cookie = + // "mprtcl-v4_4DD884CD={'gs':{'ie':1|'dt':'9aa8aa0514a802498e8e941d53e2a1d9'|'cgid':'e32ee0cf-83c7-4398-bd50-462a943d16b6'|'das':'99f5ad4d-ed1b-4044-89b6-977d7fac40c5'|'ia':'eyIxMjQiOnsibWlkIjoiNDk3NTQ1MzkyNzgyOTUxNTkxOTA4OTgwNzQ5NzYyOTQwNDQyNzAifX0='|'av':'1.0.0'}|'l':1|'9128337746531357694':{'fst':1663610956871|'ui':'eyIxIjoiMTIzNDU2IiwiNyI6ImVtYWlsQGV4YW1wbGUuY29tIn0='}|'cu':'9128337746531357694'}"; + "{'gs':{'ie':1|'dt':'9aa8aa0514a802498e8e941d53e2a1d9'|'cgid':'e32ee0cf-83c7-4398-bd50-462a943d16b6'|'das':'99f5ad4d-ed1b-4044-89b6-977d7fac40c5'|'ia':'eyIxMjQiOnsibWlkIjoiNDk3NTQ1MzkyNzgyOTUxNTkxOTA4OTgwNzQ5NzYyOTQwNDQyNzAifX0='|'av':'1.0.0'}|'l':1|'9128337746531357694':{'fst':1663610956871|'ui':'eyIxIjoiMTIzNDU2IiwiNyI6ImVtYWlsQGV4YW1wbGUuY29tIn0='}|'cu':'9128337746531357694'}"; + + mParticle._resetForTests(MPConfig); + + mParticle.getInstance().Logger = { + verbose: function(msg) {}, + error: function(msg) {}, + }; + + // TODO: What storage name should we be passing? + // mParticle.getInstance()._Store.storageName = 'mprtcl-v4'; + // mParticle.getInstance()._Store.storageName = 'mprtcl-v4_4DD884CD'; + mParticle.getInstance()._Store.storageName = undefined; + + mParticle + .getInstance() + ._Persistence.getCookie() + .should.deepEqual({ + gs: { + ie: true, + dt: '9aa8aa0514a802498e8e941d53e2a1d9', + cgid: 'e32ee0cf-83c7-4398-bd50-462a943d16b6', + das: '99f5ad4d-ed1b-4044-89b6-977d7fac40c5', + ia: + 'eyIxMjQiOnsibWlkIjoiNDk3NTQ1MzkyNzgyOTUxNTkxOTA4OTgwNzQ5NzYyOTQwNDQyNzAifX0=', + av: '1.0.0', + }, + l: 1, + '9128337746531357694': { + fst: 1663610956871, + ui: + 'eyIxIjoiMTIzNDU2IiwiNyI6ImVtYWlsQGV4YW1wbGUuY29tIn0=', + }, + cu: '9128337746531357694', + }); + }); + + it.skip('returns a null if cookie is not available', function() { + ( + mParticle.getInstance()._Persistence.getCookie() === null + ).should.eql(true); + }); + }); + + describe('#setCookie', function() { + it('should set a cookie', () => {}); + }); + + describe('#reduceAndEncodePersistence', function() {}); + describe('#findPrevCookiesBasedOnUI', function() {}); + + describe('#encodePersistence', function() { + it('should encode a persistence object', function() { + var persistanceObject = + '{"gs": {"ie": "1", "sid": "This is an id"}, "mpid": "12345"}'; + var encodedPersistenceObject = `{\'gs\':{\'ie\':1|\'sid\':\'This is an id\'}|\'mpid\':\'12345\'}`; + mParticle + .getInstance() + ._Persistence.encodePersistence(persistanceObject) + .should.deepEqual(encodedPersistenceObject); + }); + }); + + describe('#decodePersistence', function() { + it('should decode a persistence object', function() { + var persistanceObject = `{\'gs\':{\'ie\':1|\'sid\':\'This is an id\'}|\'mpid\':\'12345\'}`; + var decodedPersistenceObject = + '{"gs":{"ie":true,"sid":"This is an id"},"mpid":"12345"}'; + mParticle + .getInstance() + ._Persistence.decodePersistence(persistanceObject) + .should.deepEqual(decodedPersistenceObject); + }); + }); + + describe('#replaceCommasWithPipes', function() { + it('replaces commas with pipes', function() { + mParticle + .getInstance() + ._Persistence.replaceCommasWithPipes('veni,vidi,vici') + .should.eql('veni|vidi|vici'); + }); + }); + + describe('#replacePipesWithCommas', function() { + it('replaces pipes with commas', function() { + mParticle + .getInstance() + ._Persistence.replacePipesWithCommas('veni|vidi|vici') + .should.eql('veni,vidi,vici'); + }); + }); + + describe('#replaceApostrophesWithQuotes', function() { + it('replaces apostrophies with quotes', function() { + mParticle + .getInstance() + ._Persistence.replaceApostrophesWithQuotes("'''") + .should.eql('"""'); + }); + }); + + describe('#replaceQuotesWithApostrophes', function() { + it('replaces quotes with apostrophies', function() { + mParticle + .getInstance() + ._Persistence.replaceQuotesWithApostrophes('"""""') + .should.eql("'''''"); }); }); - describe('#getUserProductsFromLS', function () {}); - describe('#getAllUserProductsFromLS', function () {}); - describe('#setLocalStorage', function () {}); - describe('#getLocalStorage', function () {}); - describe('#expireCookies', function () {}); - describe('#getCookie', function () {}); - describe('#setCookie', function () {}); - describe('#reduceAndEncodePersistence', function () {}); - describe('#findPrevCookiesBasedOnUI', function () {}); - describe('#encodePersistence', function () {}); - describe('#decodePersistence', function () {}); - describe('#replaceCommasWithPipes', function () {}); - describe('#replacePipesWithCommas', function () {}); - describe('#replaceApostrophesWithQuotes', function () {}); - describe('#replaceQuotesWithApostrophes', function () {}); - describe('#createCookieString', function () {}); - describe('#revertCookieString', function () {}); - describe('#getCookieDomain', function () {}); - describe('#getDomain', function () {}); - describe('#getUserIdentities', function () {}); - describe('#getAllUserAttributes', function () {}); - describe('#getCartProducts', function () {}); - describe('#setCartProducts', function () {}); - describe('#saveUserIdentitiesToPersistence', function () {}); - describe('#saveUserAttributesToPersistence', function () {}); - describe('#saveUserCookieSyncDatesToPersistence', function () {}); - describe('#saveUserConsentStateToCookies', function () {}); - describe('#savePersistence', function () {}); - describe('#getPersistence', function () {}); - describe('#getConsentState', function () {}); - describe('#getFirstSeenTime', function () {}); - describe('#setFirstSeenTime', function () {}); - describe('#getLastSeenTime', function () {}); - describe('#setLastSeenTime', function () {}); - describe('#getDeviceId', function () {}); - describe('#setDeviceId', function () {}); - describe('#resetPersistence', function () {}); + describe('#createCookieString', function() { + it('should create a cookie string', function() { + mParticle + .getInstance() + ._Persistence.createCookieString( + '"ie":1,"ua":"eyJ0ZXN"0Ijoiwq7igJkifQ=="' + ) + .should.eql(`\'ie\':1|\'ua\':\'eyJ0ZXN\'0Ijoiwq7igJkifQ==\'`); + }); + }); + describe('#revertCookieString', function() { + it('should revert a cookie string', function() { + mParticle + .getInstance() + ._Persistence.revertCookieString( + `\'ie\':1|\'ua\':\'eyJ0ZXN\'0Ijoiwq7igJkifQ==\'` + ) + .should.eql('"ie":1,"ua":"eyJ0ZXN"0Ijoiwq7igJkifQ=="'); + }); + }); + + describe('#getCookieDomain', function() {}); + describe('#getDomain', function() {}); + describe('#getUserIdentities', function() {}); + describe('#getAllUserAttributes', function() {}); + describe('#getCartProducts', function() {}); + describe('#setCartProducts', function() {}); + describe('#saveUserIdentitiesToPersistence', function() {}); + describe('#saveUserAttributesToPersistence', function() {}); + describe('#saveUserCookieSyncDatesToPersistence', function() {}); + describe('#saveUserConsentStateToCookies', function() {}); + describe('#savePersistence', function() {}); + describe('#getPersistence', function() {}); + describe('#getConsentState', function() {}); + describe('#getFirstSeenTime', function() {}); + describe('#setFirstSeenTime', function() {}); + describe('#getLastSeenTime', function() {}); + describe('#setLastSeenTime', function() {}); + + describe('#getDeviceId', function() { + it('returns a device ID', () => { + mParticle._resetForTests( + Object.assign(MPConfig, { deviceId: 'foo-id' }) + ); + mParticle + .getInstance() + ._Persistence.getDeviceId() + .should.equal('foo-id'); + }); + }); + + describe('#setDeviceId', function() {}); + describe('#resetPersistence', function() {}); }); describe('migrations and persistence-related', function() { @@ -91,8 +406,8 @@ describe('migrations and persistence-related', function() { mockServer.respondWith(urls.eventsV2, [ 200, {}, - JSON.stringify({ mpid: testMPID, Store: {}}) - ]) + JSON.stringify({ mpid: testMPID, Store: {} }), + ]); mockServer.respondWith(urls.identify, [ 200, {}, @@ -520,13 +835,13 @@ describe('migrations and persistence-related', function() { mParticle._resetForTests(MPConfig); mParticle.init(apiKey, window.mParticle.config); - + mockServer.respondWith(urls.login, [ 200, {}, JSON.stringify({ mpid: 'mpid1', is_logged_in: false }), ]); - + var user1 = { userIdentities: { customerid: 'customerid1' } }; mParticle.Identity.login(user1); @@ -544,9 +859,7 @@ describe('migrations and persistence-related', function() { mParticle.Identity.logout(); - var user2Result = mParticle - .getInstance() - .Identity.getCurrentUser(); + var user2Result = mParticle.getInstance().Identity.getCurrentUser(); Object.keys( user2Result.getUserIdentities().userIdentities ).length.should.equal(0); @@ -651,11 +964,10 @@ describe('migrations and persistence-related', function() { JSON.stringify({ mpid: 'mpid1', is_logged_in: false }), ]); - mockServer.respondWith('https://identity.mparticle.com/v1/mpid1/modify', [ - 200, - {}, - JSON.stringify({ mpid: 'mpid1', is_logged_in: false }), - ]); + mockServer.respondWith( + 'https://identity.mparticle.com/v1/mpid1/modify', + [200, {}, JSON.stringify({ mpid: 'mpid1', is_logged_in: false })] + ); mParticle.Identity.login(user1); @@ -912,11 +1224,11 @@ describe('migrations and persistence-related', function() { .Identity.getCurrentUser() .setUserAttribute('id', 'id1'); - mockServer.respondWith(urls.login, [ - 200, - {}, - JSON.stringify({ mpid: 'MPID1', is_logged_in: false }), - ]); + mockServer.respondWith(urls.login, [ + 200, + {}, + JSON.stringify({ mpid: 'MPID1', is_logged_in: false }), + ]); mParticle.Identity.login(); @@ -1095,11 +1407,11 @@ describe('migrations and persistence-related', function() { .Identity.getCurrentUser() .setUserAttribute('id', 'id1'); - mockServer.respondWith(urls.login, [ - 200, - {}, - JSON.stringify({ mpid: 'MPID1', is_logged_in: false }), - ]); + mockServer.respondWith(urls.login, [ + 200, + {}, + JSON.stringify({ mpid: 'MPID1', is_logged_in: false }), + ]); mParticle.Identity.login(); @@ -1845,7 +2157,9 @@ describe('migrations and persistence-related', function() { .getInstance() ._Persistence.getLastSeenTime('previous_set') .should.equal(10); - mParticle.getInstance()._Persistence.setLastSeenTime('previous_set', 20); + mParticle + .getInstance() + ._Persistence.setLastSeenTime('previous_set', 20); mParticle .getInstance() ._Persistence.getLastSeenTime('previous_set') @@ -1961,11 +2275,14 @@ describe('migrations and persistence-related', function() { it('should save to persistance a device id set with setDeviceId', function(done) { mParticle._resetForTests(MPConfig); - + mParticle.init(apiKey, window.mParticle.config); mParticle.setDeviceId('foo-guid'); - mParticle.getInstance()._Persistence.getLocalStorage().gs.das.should.equal('foo-guid'); + mParticle + .getInstance() + ._Persistence.getLocalStorage() + .gs.das.should.equal('foo-guid'); done(); }); @@ -1975,7 +2292,10 @@ describe('migrations and persistence-related', function() { window.mParticle.config.deviceId = 'foo-guid'; mParticle.init(apiKey, window.mParticle.config); - mParticle.getInstance()._Persistence.getLocalStorage().gs.das.should.equal('foo-guid'); + mParticle + .getInstance() + ._Persistence.getLocalStorage() + .gs.das.should.equal('foo-guid'); done(); }); @@ -1998,8 +2318,8 @@ describe('migrations and persistence-related', function() { var user = mParticle.Identity.getCurrentUser(); - user.setUserAttribute("ua-1","a") - user.setUserAttributeList("ua-list", ["a\\",""]); + user.setUserAttribute('ua-1', 'a'); + user.setUserAttributeList('ua-list', ['a\\', '']); user.getAllUserAttributes()['ua-list'][0].should.equal('a\\'); user.getAllUserAttributes()['ua-list'][1].should.equal(''); @@ -2014,8 +2334,8 @@ describe('migrations and persistence-related', function() { var user2 = mParticle.Identity.getCurrentUser(); - user2.setUserAttribute("ua-1","a") - user2.setUserAttributeList("ua-list", ["a\\",""]); + user2.setUserAttribute('ua-1', 'a'); + user2.setUserAttributeList('ua-list', ['a\\', '']); user2.getAllUserAttributes()['ua-list'][0].should.equal('a\\'); user2.getAllUserAttributes()['ua-list'][1].should.equal(''); From ab5c78413cfdaccc94f57ec0bb26cd177fba36db Mon Sep 17 00:00:00 2001 From: Alexander Sapountzis Date: Mon, 26 Sep 2022 17:28:16 -0400 Subject: [PATCH 03/11] Continue writing unit tests for persistence --- src/persistence.js | 6 ++ test/src/tests-persistence.js | 134 +++++++++++++++++++++++++++++++--- 2 files changed, 128 insertions(+), 12 deletions(-) diff --git a/src/persistence.js b/src/persistence.js index 6799b27e9..349967d1d 100644 --- a/src/persistence.js +++ b/src/persistence.js @@ -139,6 +139,7 @@ export default function _Persistence(mpInstance) { }; this.storeProductsInMemory = function(products, mpid) { + // TODO: Is it necessary to do a try/catch? When would it throw? if (products) { try { mpInstance._Store.cartProducts = @@ -156,6 +157,8 @@ export default function _Persistence(mpInstance) { this.storeDataInMemory = function(obj, currentMPID) { try { if (!obj) { + // TODO: why do we need to update client id or device id if object + // is empty? mpInstance.Logger.verbose( Messages.InformationMessages.CookieNotFound ); @@ -215,11 +218,14 @@ export default function _Persistence(mpInstance) { mpInstance._Store.sessionStartDate = new Date(); } + // TODO: What is the purpose of overwriting the obj? if (currentMPID) { obj = obj[currentMPID]; } else { + // FIXME: This could make obj === undefined obj = obj[obj.cu]; } + console.log('what is my obj?', obj); } } catch (e) { mpInstance.Logger.error(Messages.ErrorMessages.CookieParseError); diff --git a/test/src/tests-persistence.js b/test/src/tests-persistence.js index 8eac3dac8..65031ccf7 100644 --- a/test/src/tests-persistence.js +++ b/test/src/tests-persistence.js @@ -1,5 +1,6 @@ import Utils from './utils'; import sinon from 'sinon'; +import { expect } from 'chai'; import { urls, apiKey, @@ -131,19 +132,127 @@ describe('Persistence', function() { ._Persistence.storeProductsInMemory(products, 'test-mpid'); mParticle.getInstance()._Store.cartProducts.should.deepEqual([]); }); + }); + + describe('#storeDataInMemory', function() { + it.skip('updates Store with client ID with a unique ID', () => { + // var bond = sinon.spy(mParticle.getInstance().Logger, 'verbose'); - it.skip('logs an error if something goes wrong', () => { - var bond = sinon.spy(mParticle.getInstance().Logger, 'warning'); mParticle._resetForTests(MPConfig); + + // mParticle.getInstance().Logger = { + // verbose: function() {}, + // error: function() {}, + // }; + mParticle .getInstance() - ._Persistence.storeProductsInMemory(products, 'test-mpid'); - mParticle.getInstance()._Store.cartProducts.should.deepEqual([]); - bond.getCalls()[0].args[0].should.eql('Could not parse cookie'); + ._Persistence.storeDataInMemory(null, 'test_mpid'); + mParticle.getInstance()._Store.clientId.should.equal('foo'); + // bond.called.should.eql(true); + // bond.getCalls()[0].args[0].should.eql( + // 'I like turtles' + // ); }); - }); + it.skip('updates Store with device ID with a unique ID', () => {}); + it('stores the persistence object in the Store', () => { + mParticle._resetForTests(MPConfig); + var persistenceObject = { + cu: 'my_cu', + gs: { + sid: 'my_session_id', + ie: true, + sa: { + foo: 'bar', + }, + ss: { + fizz: 'buzz', + }, + dt: 'my_dev_token', + av: '1.2.3.4', + cgid: 'my_client_id', + das: 'my_device_id', + ia: { bizz: 'bazz' }, + c: { + data_plan: { + plan_id: 'my data plan', + plan_version: 2, + }, + }, + csm: ['123456', '555', 'my_test_mpid'], + les: 1661548842, + ssd: 1661552442, + }, + l: false, + }; + mParticle + .getInstance() + ._Persistence.storeDataInMemory(persistenceObject); - describe('#storeDataInMemory', function() {}); + expect(mParticle.getInstance()._Store.mpid, '_Store.mpid').to.equal( + 'my_cu' + ); + expect( + mParticle.getInstance()._Store.sessionId, + '_Store.sessionId' + ).to.equal('my_session_id'); + expect( + mParticle.getInstance()._Store.isEnabled, + '_Store.isEnabled' + ).to.equal(true); + expect( + mParticle.getInstance()._Store.sessionAttributes, + '_Store.sessionAttributes' + ).to.eql({ + foo: 'bar', + }); + expect( + mParticle.getInstance()._Store.serverSettings, + '_Store.serverSettings' + ).to.eql({ + fizz: 'buzz', + }); + expect( + mParticle.getInstance()._Store.devToken, + '._Store.devToken' + ).to.equal('my_dev_token'); + expect( + mParticle.getInstance()._Store.deviceId, + '_Store.deviceId.' + ).to.equal('my_device_id'); + expect( + mParticle.getInstance()._Store.integrationAttributes, + '_Store.integrationAttributes' + ).to.eql({ + bizz: 'bazz', + }); + expect( + mParticle.getInstance()._Store.context, + '_Store.context' + ).to.eql({ + data_plan: { + plan_id: 'my data plan', + plan_version: 2, + }, + }); + expect( + mParticle.getInstance()._Store.currentSessionMPIDs, + '_Store.currentSessionMPIDs' + ).to.eql(['123456', '555', 'my_test_mpid']); + expect( + mParticle.getInstance()._Store.isLoggedIn, + '_Store.isLoggedIn' + ).to.equal(false); + expect( + mParticle.getInstance()._Store.dateLastEventSent, + '_Store.dateLastEventSent' + ).to.equal('111111'); + expect( + mParticle.getInstance()._Store.SDKConfig.appVersion, + '_Store.SDKConfig.appVersion' + ).to.equal('1.2.3.4'); + }); + }); describe('#determineLocalStorageAvailability', function() { it('returns true if Local Storage is available', function() { @@ -153,7 +262,7 @@ describe('Persistence', function() { ._Persistence.determineLocalStorageAvailability( window.localStorage ) - .should.equal(true); + .to.equal(true); }); it('returns false if Local Storage is not available', function() { @@ -226,8 +335,9 @@ describe('Persistence', function() { describe('#expireCookies', function() {}); - describe.only('#getCookie', function() { - it('returns a cookie', function() { + describe('#getCookie', function() { + it.skip('returns a cookie', function() { + // TODO: Ask ob what a cookie should look like // TODO: What does our cookie look like? document.cookie = // "mprtcl-v4_4DD884CD={'gs':{'ie':1|'dt':'9aa8aa0514a802498e8e941d53e2a1d9'|'cgid':'e32ee0cf-83c7-4398-bd50-462a943d16b6'|'das':'99f5ad4d-ed1b-4044-89b6-977d7fac40c5'|'ia':'eyIxMjQiOnsibWlkIjoiNDk3NTQ1MzkyNzgyOTUxNTkxOTA4OTgwNzQ5NzYyOTQwNDQyNzAifX0='|'av':'1.0.0'}|'l':1|'9128337746531357694':{'fst':1663610956871|'ui':'eyIxIjoiMTIzNDU2IiwiNyI6ImVtYWlsQGV4YW1wbGUuY29tIn0='}|'cu':'9128337746531357694'}"; @@ -236,8 +346,8 @@ describe('Persistence', function() { mParticle._resetForTests(MPConfig); mParticle.getInstance().Logger = { - verbose: function(msg) {}, - error: function(msg) {}, + verbose: function() {}, + error: function() {}, }; // TODO: What storage name should we be passing? From c8dd4ecb1bcc3ef9fbd0daff3a18087ee6acc6dc Mon Sep 17 00:00:00 2001 From: Alexander Sapountzis Date: Wed, 28 Sep 2022 14:42:41 -0400 Subject: [PATCH 04/11] Continue writing unit tests for persistence --- src/persistence.js | 2 - test/src/tests-persistence.js | 73 ++++++++++++++++++----------------- 2 files changed, 38 insertions(+), 37 deletions(-) diff --git a/src/persistence.js b/src/persistence.js index 349967d1d..763e7d32d 100644 --- a/src/persistence.js +++ b/src/persistence.js @@ -469,13 +469,11 @@ export default function _Persistence(mpInstance) { ); } - // TODO: Is this part of the deprecated cookie conversion flow? if (key && key === name) { result = mpInstance._Helpers.converted(cookie); break; } - // TODO: Which path should we be using? if (!key) { result[name] = mpInstance._Helpers.converted(cookie); } diff --git a/test/src/tests-persistence.js b/test/src/tests-persistence.js index 65031ccf7..fc53812ed 100644 --- a/test/src/tests-persistence.js +++ b/test/src/tests-persistence.js @@ -336,52 +336,55 @@ describe('Persistence', function() { describe('#expireCookies', function() {}); describe('#getCookie', function() { - it.skip('returns a cookie', function() { - // TODO: Ask ob what a cookie should look like - // TODO: What does our cookie look like? + it('returns a cookie', function() { document.cookie = - // "mprtcl-v4_4DD884CD={'gs':{'ie':1|'dt':'9aa8aa0514a802498e8e941d53e2a1d9'|'cgid':'e32ee0cf-83c7-4398-bd50-462a943d16b6'|'das':'99f5ad4d-ed1b-4044-89b6-977d7fac40c5'|'ia':'eyIxMjQiOnsibWlkIjoiNDk3NTQ1MzkyNzgyOTUxNTkxOTA4OTgwNzQ5NzYyOTQwNDQyNzAifX0='|'av':'1.0.0'}|'l':1|'9128337746531357694':{'fst':1663610956871|'ui':'eyIxIjoiMTIzNDU2IiwiNyI6ImVtYWlsQGV4YW1wbGUuY29tIn0='}|'cu':'9128337746531357694'}"; - "{'gs':{'ie':1|'dt':'9aa8aa0514a802498e8e941d53e2a1d9'|'cgid':'e32ee0cf-83c7-4398-bd50-462a943d16b6'|'das':'99f5ad4d-ed1b-4044-89b6-977d7fac40c5'|'ia':'eyIxMjQiOnsibWlkIjoiNDk3NTQ1MzkyNzgyOTUxNTkxOTA4OTgwNzQ5NzYyOTQwNDQyNzAifX0='|'av':'1.0.0'}|'l':1|'9128337746531357694':{'fst':1663610956871|'ui':'eyIxIjoiMTIzNDU2IiwiNyI6ImVtYWlsQGV4YW1wbGUuY29tIn0='}|'cu':'9128337746531357694'}"; + "mprtcl-v4_defghi={'gs':{'ie':1|'dt':'test_key'|'cgid':'4bb52bdd-e021-4476-bf79-d1060ca2482b'|'das':'13d96730-9332-45ea-aebf-0e3cb10f5f09'|'csm':'WyJ0ZXN0TVBJRCJd'|'sid':'1A3B41A0-42F4-49A1-96D0-178A40A4DDFE'|'les':1664380692122|'ssd':1664380540712}|'l':0|'testMPID':{'fst':1664380540716|'ua':'eyJmb28iOiJiYXIiLCJmaXp6IjoiYmF6eiJ9'|'con':'eyJnZHByIjp7ImxvY2F0aW9uX2NvbGxlY3Rpb24iOnsiYyI6dHJ1ZSwidHMiOjE2NjQzODA2NzQ3MjYsImQiOiJsb2NhdGlvbl9jb2xsZWN0aW9uX2FncmVlbWVudF92NCIsImwiOiIxNyBDaGVycnkgVHJlZSBMYW5lIiwiaCI6IklERkE6YTVkOTM0bjAtMjMyZi00YWZjLTJlOWEtMzgzMmQ5NXpjNzAyIn19fQ=='}|'cu':'testMPID'}"; - mParticle._resetForTests(MPConfig); - - mParticle.getInstance().Logger = { - verbose: function() {}, - error: function() {}, - }; - - // TODO: What storage name should we be passing? - // mParticle.getInstance()._Store.storageName = 'mprtcl-v4'; - // mParticle.getInstance()._Store.storageName = 'mprtcl-v4_4DD884CD'; - mParticle.getInstance()._Store.storageName = undefined; + mParticle.init(apiKey, window.mParticle.config); + mParticle.getInstance()._Store.storageName = 'mprtcl-v4_defghi'; - mParticle + expect(mParticle .getInstance() - ._Persistence.getCookie() - .should.deepEqual({ + ._Persistence.getCookie()) + .to.eql({ + cu: 'testMPID', gs: { + cgid: '4bb52bdd-e021-4476-bf79-d1060ca2482b', + csm: ['testMPID'], + das: '13d96730-9332-45ea-aebf-0e3cb10f5f09', + dt: 'test_key', ie: true, - dt: '9aa8aa0514a802498e8e941d53e2a1d9', - cgid: 'e32ee0cf-83c7-4398-bd50-462a943d16b6', - das: '99f5ad4d-ed1b-4044-89b6-977d7fac40c5', - ia: - 'eyIxMjQiOnsibWlkIjoiNDk3NTQ1MzkyNzgyOTUxNTkxOTA4OTgwNzQ5NzYyOTQwNDQyNzAifX0=', - av: '1.0.0', + les: 1664380692122, + sid: '1A3B41A0-42F4-49A1-96D0-178A40A4DDFE', + ssd: 1664380540712, }, - l: 1, - '9128337746531357694': { - fst: 1663610956871, - ui: - 'eyIxIjoiMTIzNDU2IiwiNyI6ImVtYWlsQGV4YW1wbGUuY29tIn0=', - }, - cu: '9128337746531357694', + l: false, + testMPID: { + con: { + gdpr: { + location_collection: { + c: true, + d: 'location_collection_agreement_v4', + h: 'IDFA:a5d934n0-232f-4afc-2e9a-3832d95zc702', + l: '17 Cherry Tree Lane', + ts: 1664380674726 + } + } + }, + fst: 1664380540716, + ua: { fizz: 'bazz', foo: 'bar' } + } }); }); - it.skip('returns a null if cookie is not available', function() { - ( + it('returns a null if cookie is not available', function() { + document.cookie = 'mprtcl-v4_defghi='; + mParticle.init(apiKey, window.mParticle.config); + mParticle.getInstance()._Store.storageName = 'mprtcl-v4_defghi'; + + expect( mParticle.getInstance()._Persistence.getCookie() === null - ).should.eql(true); + ).to.eql(true); }); }); From 3b47222be8be2ef9e367f813b9dd7d1e1e412904 Mon Sep 17 00:00:00 2001 From: Alexander Sapountzis Date: Wed, 28 Sep 2022 17:38:51 -0400 Subject: [PATCH 05/11] Revert code change --- src/persistence.js | 6 ++++-- test/src/tests-persistence.js | 25 +++++++++++++++---------- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/persistence.js b/src/persistence.js index 763e7d32d..fde675aa2 100644 --- a/src/persistence.js +++ b/src/persistence.js @@ -233,15 +233,17 @@ export default function _Persistence(mpInstance) { }; this.determineLocalStorageAvailability = function(storage) { + var result; if (window.mParticle && window.mParticle._forceNoLocalStorage) { storage = undefined; } try { storage.setItem('mparticle', 'test'); - storage.getItem('mparticle') === 'test'; + result = storage.getItem('mparticle') === 'test'; storage.removeItem('mparticle'); - return true; + // FIXME: This should return a boolean + return result && storage; } catch (e) { return false; } diff --git a/test/src/tests-persistence.js b/test/src/tests-persistence.js index fc53812ed..ca3b4e1e8 100644 --- a/test/src/tests-persistence.js +++ b/test/src/tests-persistence.js @@ -254,35 +254,40 @@ describe('Persistence', function() { }); }); - describe('#determineLocalStorageAvailability', function() { + describe.only('#determineLocalStorageAvailability', function() { it('returns true if Local Storage is available', function() { mParticle._resetForTests(MPConfig); - mParticle + + // TODO: test should really for a boolean but funciton + // currently returns an object if true + expect(mParticle .getInstance() ._Persistence.determineLocalStorageAvailability( window.localStorage - ) - .to.equal(true); + )) + .to.be.ok; }); it('returns false if Local Storage is not available', function() { mParticle._resetForTests(MPConfig); + // FIXME: this method should not take storage as a param - mParticle + expect(mParticle .getInstance() - ._Persistence.determineLocalStorageAvailability(null) - .should.equal(false); + ._Persistence.determineLocalStorageAvailability(null)) + .to.equal(false); }); it('returns false if Local Storage disabled via _forceNoLocalStorage', function() { mParticle._resetForTests(MPConfig); mParticle._forceNoLocalStorage = true; - mParticle + + expect(mParticle .getInstance() ._Persistence.determineLocalStorageAvailability( window.localStorage - ) - .should.equal(false); + )) + .to.equal(false); }); }); From 0064657ce7f6d668d47aa0398b72bc23ba37e5bf Mon Sep 17 00:00:00 2001 From: Alexander Sapountzis Date: Wed, 28 Sep 2022 18:12:12 -0400 Subject: [PATCH 06/11] Continue writing unit tests for persistence --- src/persistence.js | 3 +- test/src/tests-persistence.js | 142 ++++++++++++++++++---------------- 2 files changed, 79 insertions(+), 66 deletions(-) diff --git a/src/persistence.js b/src/persistence.js index fde675aa2..f8270d40d 100644 --- a/src/persistence.js +++ b/src/persistence.js @@ -12,6 +12,7 @@ export default function _Persistence(mpInstance) { this.useLocalStorage = function() { return ( !mpInstance._Store.SDKConfig.useCookieStorage && + // FIXME: Should return boolean but is returning an object mpInstance._Store.isLocalStorageAvailable ); }; @@ -549,7 +550,7 @@ export default function _Persistence(mpInstance) { mpInstance._Store.SDKConfig.maxCookieSize ); - // mpInstance.Logger.verbose(Messages.InformationMessages.CookieSet); + mpInstance.Logger.verbose(Messages.InformationMessages.CookieSet); window.document.cookie = encodeURIComponent(key) + '=' + encodedCookiesWithExpirationAndPath; diff --git a/test/src/tests-persistence.js b/test/src/tests-persistence.js index ca3b4e1e8..6721fb9bb 100644 --- a/test/src/tests-persistence.js +++ b/test/src/tests-persistence.js @@ -25,10 +25,11 @@ describe('Persistence', function() { describe('#useLocalStorage', function() { it('returns true if Local Storage is available', function() { mParticle._resetForTests(MPConfig); - mParticle - .getInstance() - ._Persistence.useLocalStorage() - .should.eql(true); + + // FIXME: Test should check for boolean but function is + // returning an object + expect(mParticle.getInstance()._Persistence.useLocalStorage()).to.be + .ok; }); it('returns false if Local Storage is not available', function() { @@ -49,6 +50,7 @@ describe('Persistence', function() { .should.eql(false); }); }); + describe('#initializeStorage', function() {}); describe('#update', function() { @@ -135,26 +137,36 @@ describe('Persistence', function() { }); describe('#storeDataInMemory', function() { - it.skip('updates Store with client ID with a unique ID', () => { + it('updates Store with unique client and device IDs if persistence object is empty', () => { // var bond = sinon.spy(mParticle.getInstance().Logger, 'verbose'); + var bond = sinon + .stub(mParticle.getInstance()._Helpers, 'generateUniqueId') + .callsFake(function() { + return '12345'; + }); mParticle._resetForTests(MPConfig); - // mParticle.getInstance().Logger = { - // verbose: function() {}, - // error: function() {}, - // }; + mParticle.getInstance().Logger = { + verbose: function() {}, + error: function() {}, + }; mParticle .getInstance() ._Persistence.storeDataInMemory(null, 'test_mpid'); - mParticle.getInstance()._Store.clientId.should.equal('foo'); - // bond.called.should.eql(true); - // bond.getCalls()[0].args[0].should.eql( - // 'I like turtles' - // ); + + expect( + mParticle.getInstance()._Store.clientId, + '_Store.clientId' + ).to.equal('12345'); + + expect( + mParticle.getInstance()._Store.clientId, + '_Store.deviceId' + ).to.equal('12345'); }); - it.skip('updates Store with device ID with a unique ID', () => {}); + it('stores the persistence object in the Store', () => { mParticle._resetForTests(MPConfig); var persistenceObject = { @@ -246,7 +258,7 @@ describe('Persistence', function() { expect( mParticle.getInstance()._Store.dateLastEventSent, '_Store.dateLastEventSent' - ).to.equal('111111'); + ).to.eql(new Date(1661548842)); expect( mParticle.getInstance()._Store.SDKConfig.appVersion, '_Store.SDKConfig.appVersion' @@ -254,40 +266,43 @@ describe('Persistence', function() { }); }); - describe.only('#determineLocalStorageAvailability', function() { + describe('#determineLocalStorageAvailability', function() { it('returns true if Local Storage is available', function() { mParticle._resetForTests(MPConfig); // TODO: test should really for a boolean but funciton // currently returns an object if true - expect(mParticle - .getInstance() - ._Persistence.determineLocalStorageAvailability( - window.localStorage - )) - .to.be.ok; + expect( + mParticle + .getInstance() + ._Persistence.determineLocalStorageAvailability( + window.localStorage + ) + ).to.be.ok; }); it('returns false if Local Storage is not available', function() { mParticle._resetForTests(MPConfig); // FIXME: this method should not take storage as a param - expect(mParticle - .getInstance() - ._Persistence.determineLocalStorageAvailability(null)) - .to.equal(false); + expect( + mParticle + .getInstance() + ._Persistence.determineLocalStorageAvailability(null) + ).to.equal(false); }); it('returns false if Local Storage disabled via _forceNoLocalStorage', function() { mParticle._resetForTests(MPConfig); mParticle._forceNoLocalStorage = true; - expect(mParticle - .getInstance() - ._Persistence.determineLocalStorageAvailability( - window.localStorage - )) - .to.equal(false); + expect( + mParticle + .getInstance() + ._Persistence.determineLocalStorageAvailability( + window.localStorage + ) + ).to.equal(false); }); }); @@ -343,43 +358,40 @@ describe('Persistence', function() { describe('#getCookie', function() { it('returns a cookie', function() { document.cookie = - "mprtcl-v4_defghi={'gs':{'ie':1|'dt':'test_key'|'cgid':'4bb52bdd-e021-4476-bf79-d1060ca2482b'|'das':'13d96730-9332-45ea-aebf-0e3cb10f5f09'|'csm':'WyJ0ZXN0TVBJRCJd'|'sid':'1A3B41A0-42F4-49A1-96D0-178A40A4DDFE'|'les':1664380692122|'ssd':1664380540712}|'l':0|'testMPID':{'fst':1664380540716|'ua':'eyJmb28iOiJiYXIiLCJmaXp6IjoiYmF6eiJ9'|'con':'eyJnZHByIjp7ImxvY2F0aW9uX2NvbGxlY3Rpb24iOnsiYyI6dHJ1ZSwidHMiOjE2NjQzODA2NzQ3MjYsImQiOiJsb2NhdGlvbl9jb2xsZWN0aW9uX2FncmVlbWVudF92NCIsImwiOiIxNyBDaGVycnkgVHJlZSBMYW5lIiwiaCI6IklERkE6YTVkOTM0bjAtMjMyZi00YWZjLTJlOWEtMzgzMmQ5NXpjNzAyIn19fQ=='}|'cu':'testMPID'}"; + "mprtcl-v4_defghi={'gs':{'ie':1|'dt':'test_key'|'cgid':'4bb52bdd-e021-4476-bf79-d1060ca2482b'|'das':'13d96730-9332-45ea-aebf-0e3cb10f5f09'|'csm':'WyJ0ZXN0TVBJRCJd'|'sid':'1A3B41A0-42F4-49A1-96D0-178A40A4DDFE'|'les':1664380692122|'ssd':1664380540712}|'l':0|'testMPID':{'fst':1664380540716|'ua':'eyJmb28iOiJiYXIiLCJmaXp6IjoiYmF6eiJ9'|'con':'eyJnZHByIjp7ImxvY2F0aW9uX2NvbGxlY3Rpb24iOnsiYyI6dHJ1ZSwidHMiOjE2NjQzODA2NzQ3MjYsImQiOiJsb2NhdGlvbl9jb2xsZWN0aW9uX2FncmVlbWVudF92NCIsImwiOiIxNyBDaGVycnkgVHJlZSBMYW5lIiwiaCI6IklERkE6YTVkOTM0bjAtMjMyZi00YWZjLTJlOWEtMzgzMmQ5NXpjNzAyIn19fQ=='}|'cu':'testMPID'}"; mParticle.init(apiKey, window.mParticle.config); mParticle.getInstance()._Store.storageName = 'mprtcl-v4_defghi'; - expect(mParticle - .getInstance() - ._Persistence.getCookie()) - .to.eql({ - cu: 'testMPID', - gs: { - cgid: '4bb52bdd-e021-4476-bf79-d1060ca2482b', - csm: ['testMPID'], - das: '13d96730-9332-45ea-aebf-0e3cb10f5f09', - dt: 'test_key', - ie: true, - les: 1664380692122, - sid: '1A3B41A0-42F4-49A1-96D0-178A40A4DDFE', - ssd: 1664380540712, - }, - l: false, - testMPID: { - con: { - gdpr: { - location_collection: { - c: true, - d: 'location_collection_agreement_v4', - h: 'IDFA:a5d934n0-232f-4afc-2e9a-3832d95zc702', - l: '17 Cherry Tree Lane', - ts: 1664380674726 - } - } + expect(mParticle.getInstance()._Persistence.getCookie()).to.eql({ + cu: 'testMPID', + gs: { + cgid: '4bb52bdd-e021-4476-bf79-d1060ca2482b', + csm: ['testMPID'], + das: '13d96730-9332-45ea-aebf-0e3cb10f5f09', + dt: 'test_key', + ie: true, + les: 1664380692122, + sid: '1A3B41A0-42F4-49A1-96D0-178A40A4DDFE', + ssd: 1664380540712, + }, + l: false, + testMPID: { + con: { + gdpr: { + location_collection: { + c: true, + d: 'location_collection_agreement_v4', + h: 'IDFA:a5d934n0-232f-4afc-2e9a-3832d95zc702', + l: '17 Cherry Tree Lane', + ts: 1664380674726, + }, }, - fst: 1664380540716, - ua: { fizz: 'bazz', foo: 'bar' } - } - }); + }, + fst: 1664380540716, + ua: { fizz: 'bazz', foo: 'bar' }, + }, + }); }); it('returns a null if cookie is not available', function() { From 610c1a480ce60b8f8f1c070e1f1bf2f863bcabea Mon Sep 17 00:00:00 2001 From: Alexander Sapountzis Date: Wed, 28 Sep 2022 18:12:46 -0400 Subject: [PATCH 07/11] Continue writing unit tests for persistence --- src/persistence.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/persistence.js b/src/persistence.js index f8270d40d..635e28276 100644 --- a/src/persistence.js +++ b/src/persistence.js @@ -226,7 +226,6 @@ export default function _Persistence(mpInstance) { // FIXME: This could make obj === undefined obj = obj[obj.cu]; } - console.log('what is my obj?', obj); } } catch (e) { mpInstance.Logger.error(Messages.ErrorMessages.CookieParseError); From 3e440c9a66b3ea2128311fb706b3959d0dc7b0d3 Mon Sep 17 00:00:00 2001 From: Alexander Sapountzis Date: Thu, 29 Sep 2022 10:29:05 -0400 Subject: [PATCH 08/11] Continue writing unit tests for persistence --- src/persistence.js | 7 +++++-- test/src/tests-persistence.js | 4 ++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/persistence.js b/src/persistence.js index 635e28276..de9dfe42b 100644 --- a/src/persistence.js +++ b/src/persistence.js @@ -155,11 +155,14 @@ export default function _Persistence(mpInstance) { } }; + // Stores the state of the Store after being read from Persistence + // If this is a first visit, does a basic assignment of client and + // device IDs this.storeDataInMemory = function(obj, currentMPID) { try { if (!obj) { - // TODO: why do we need to update client id or device id if object - // is empty? + // Sets up the default "Store" if a cookie is not found + // (user's fresh visit) mpInstance.Logger.verbose( Messages.InformationMessages.CookieNotFound ); diff --git a/test/src/tests-persistence.js b/test/src/tests-persistence.js index 6721fb9bb..dfa7bd804 100644 --- a/test/src/tests-persistence.js +++ b/test/src/tests-persistence.js @@ -364,6 +364,7 @@ describe('Persistence', function() { mParticle.getInstance()._Store.storageName = 'mprtcl-v4_defghi'; expect(mParticle.getInstance()._Persistence.getCookie()).to.eql({ + // TODO: Use this to create a Persistence Object Interface cu: 'testMPID', gs: { cgid: '4bb52bdd-e021-4476-bf79-d1060ca2482b', @@ -378,6 +379,7 @@ describe('Persistence', function() { l: false, testMPID: { con: { + // TODO: Should have CCPA gdpr: { location_collection: { c: true, @@ -412,6 +414,7 @@ describe('Persistence', function() { describe('#reduceAndEncodePersistence', function() {}); describe('#findPrevCookiesBasedOnUI', function() {}); + // TODO: Use a full sized persistence object describe('#encodePersistence', function() { it('should encode a persistence object', function() { var persistanceObject = @@ -424,6 +427,7 @@ describe('Persistence', function() { }); }); + // TODO: Use a full sized persistence object describe('#decodePersistence', function() { it('should decode a persistence object', function() { var persistanceObject = `{\'gs\':{\'ie\':1|\'sid\':\'This is an id\'}|\'mpid\':\'12345\'}`; From 926ef5fe0e179cd2dab9ad41015ed04f358b0046 Mon Sep 17 00:00:00 2001 From: Alexander Sapountzis Date: Mon, 5 Dec 2022 13:58:25 -0500 Subject: [PATCH 09/11] Add tests for getUserProductsFromLS --- test/src/tests-persistence.js | 99 +++++++++++++++++++++++++++++++++-- 1 file changed, 95 insertions(+), 4 deletions(-) diff --git a/test/src/tests-persistence.js b/test/src/tests-persistence.js index dfa7bd804..e354b7f0e 100644 --- a/test/src/tests-persistence.js +++ b/test/src/tests-persistence.js @@ -12,6 +12,8 @@ import { v4LSKey, } from './config'; +import Polyfill from '../../src/polyfill'; + /* eslint-disable quotes*/ var findCookie = Utils.findCookie, getLocalStorage = Utils.getLocalStorage, @@ -21,7 +23,9 @@ var findCookie = Utils.findCookie, getEvent = Utils.getEvent, mockServer; -describe('Persistence', function() { +var Base64 = Polyfill.Base64; + +describe.only('Persistence', function() { describe('#useLocalStorage', function() { it('returns true if Local Storage is available', function() { mParticle._resetForTests(MPConfig); @@ -103,8 +107,6 @@ describe('Persistence', function() { }) ); - debugger; - mParticle.getInstance()._Persistence.update(); bond.called.should.eql(true); @@ -306,7 +308,96 @@ describe('Persistence', function() { }); }); - describe('#getUserProductsFromLS', function() {}); + describe('#getUserProductsFromLS', function() { + it('returns an empty array if mpid is missing', () => { + mParticle._resetForTests(MPConfig); + expect( + mParticle.getInstance()._Persistence.getUserProductsFromLS('') + ).to.eql([]); + }); + + it('returns an empty array if no user products exist in local storage', () => { + const lsKey = 'test-getUserProductsFromLS'; + + mParticle._resetForTests(MPConfig); + mParticle.getInstance()._Store.prodStorageName = lsKey; + + window.localStorage.setItem(lsKey, JSON.stringify('')); + + expect( + mParticle + .getInstance() + ._Persistence.getUserProductsFromLS( + 'test-mpid-getUserProductsFromLS' + ) + ).to.eql([]); + }); + + it('returns an array user products if they exist in local storage', () => { + const lsKey = 'test-getUserProductsFromLS'; + const testMPID = 'test-mpid-getUserProductsFromLS'; + + mParticle._resetForTests(MPConfig); + mParticle.getInstance()._Store.prodStorageName = lsKey; + + const expectedProducts = [ + { + Attributes: { + plannedAttr1: 'val1', + plannedAttr2: 'val2', + unplannedAttr1: 'val3', + allowedAttr1: 'val4', + allowedAttr2: 'val5', + }, + Name: 'iPhone', + Category: 'category', + CouponCode: 'coupon', + Position: 1, + Price: 999, + Quantity: 1, + Sku: 'iphoneSKU', + TotalAmount: 999, + Variant: '128', + }, + { + Attributes: { + plannedAttr1: 'val1', + plannedAttr2: 'val2', + unplannedAttr1: 'val3', + allowedAttr1: 'val4', + allowedAttr2: 'val5', + }, + Name: 'S10', + Category: 'category', + CouponCode: 'coupon', + Position: 2, + Price: 500, + Quantity: 1, + Sku: 'galaxySKU', + TotalAmount: 500, + Variant: '256', + }, + ]; + + const actualStorageItem = { + [testMPID]: { + cp: expectedProducts, + }, + }; + + window.localStorage.setItem( + lsKey, + Base64.encode(JSON.stringify(actualStorageItem)) + ); + + expect( + mParticle + .getInstance() + ._Persistence.getUserProductsFromLS(testMPID) + ).to.eql(expectedProducts); + }); + }); + describe('#getAllUserProductsFromLS', function() {}); describe('#setLocalStorage', function() {}); From 65c785cf6fcc1b9c8c82d82dd078573b748de641 Mon Sep 17 00:00:00 2001 From: Alexander Sapountzis Date: Tue, 21 Feb 2023 10:19:07 -0500 Subject: [PATCH 10/11] Save Point --- src/mockBatchCreator.ts | 11 ++- src/persistence.interfaces.ts | 38 ++++++++ src/sdkRuntimeModels.ts | 8 +- src/store.ts | 12 ++- ...ts-persistence.js => tests-persistence.ts} | 93 +++++++++++-------- 5 files changed, 111 insertions(+), 51 deletions(-) create mode 100644 src/persistence.interfaces.ts rename test/src/{tests-persistence.js => tests-persistence.ts} (97%) diff --git a/src/mockBatchCreator.ts b/src/mockBatchCreator.ts index 36116d231..63823bcfd 100644 --- a/src/mockBatchCreator.ts +++ b/src/mockBatchCreator.ts @@ -5,7 +5,7 @@ import { SDKEvent, BaseEvent, MParticleWebSDK } from './sdkRuntimeModels'; import { convertEvents } from './sdkToEventsApiConverter'; import * as EventsApi from '@mparticle/event-models'; -const mockFunction = function () { +const mockFunction = function() { return null; }; export default class _BatchValidator { @@ -13,12 +13,12 @@ export default class _BatchValidator { return { // Certain Helper, Store, and Identity properties need to be mocked to be used in the `returnBatch` method _Helpers: { - sanitizeAttributes: - window.mParticle.getInstance()._Helpers.sanitizeAttributes, - generateHash: function () { + sanitizeAttributes: window.mParticle.getInstance()._Helpers + .sanitizeAttributes, + generateHash: function() { return 'mockHash'; }, - generateUniqueId: function () { + generateUniqueId: function() { return 'mockId'; }, extend: window.mParticle.getInstance()._Helpers.extend, @@ -39,6 +39,7 @@ export default class _BatchValidator { _ServerModel: null, _SessionManager: null, _Store: { + mpid: 'test-mpid', sessionId: 'mockSessionId', devToken: 'test_dev_token', isFirstRun: true, diff --git a/src/persistence.interfaces.ts b/src/persistence.interfaces.ts new file mode 100644 index 000000000..d51e0a68c --- /dev/null +++ b/src/persistence.interfaces.ts @@ -0,0 +1,38 @@ +import { Context } from '@mparticle/event-models'; +import { MPID, Product } from '@mparticle/web-sdk'; +import { + IntegrationAttributes, + ServerSettings, + SessionAttributes, +} from './store'; + +export interface IGlobalStoreV2DTO { + sid: string; // Session ID + ie: boolean; // Is Enabled + // Session Attributes + sa: SessionAttributes; + ss: ServerSettings; + dt: string; // Dev Token + av: string; // App Version + cgid: string; // Client Generated ID + das: string; // Device ID/ Device Application String + ia: IntegrationAttributes; + c: Context; + csm: MPID[]; // Current Session MPIDs + les: number; // Last Event Sent Timestamp + ssd: number; // Session Start Date +} + +export interface IPersistenceV2DTO { + cu: MPID; // Current User MPID + gs: IGlobalStoreV2DTO; + l: false; // IsLoggedIn +} + +export interface IPersistence { + useLocalStorage(): boolean; + initializeStorage(): void; + update(): void; + storeProductsInMemory(products: Product[], mpid: MPID): void; + storeDataInMemory(obj: IPersistenceV2DTO, currentMPID: MPID): void; +} diff --git a/src/sdkRuntimeModels.ts b/src/sdkRuntimeModels.ts index 38b05c0e9..7ba5cc0c0 100644 --- a/src/sdkRuntimeModels.ts +++ b/src/sdkRuntimeModels.ts @@ -126,6 +126,12 @@ export interface SDKProduct { } export interface MParticleWebSDK { + _forceNoLocalStorage?: boolean; + + // TODO: Why are tehse both in the Store SDK Config and in the MP SDK object? + maxCookieSize?: number; + useCookieStorage?: boolean; + addForwarder(mockForwarder: MPForwarder): void; Identity: SDKIdentityApi; Logger: SDKLoggerApi; @@ -141,7 +147,7 @@ export interface MParticleWebSDK { _NativeSdkHelpers: any; // TODO: Set up API _Persistence: any; // TODO: Set up Persistence API _preInit: any; // TODO: Set up API - _resetForTests(MPConfig?: SDKInitConfig): void; + _resetForTests(MPConfig?: SDKInitConfig, keepPersistence?: boolean): void; init(apiKey: string, config: SDKInitConfig, instanceName?: string): void; getAppName(): string; getAppVersion(): string; diff --git a/src/store.ts b/src/store.ts index 61fb50d44..af5c3f892 100644 --- a/src/store.ts +++ b/src/store.ts @@ -101,6 +101,8 @@ function createSDKConfig(config: SDKInitConfig): SDKConfig { export type PixelConfiguration = Dictionary; export type MigrationData = Dictionary; export type ServerSettings = Dictionary; +export type SessionAttributes = Dictionary; +export type IntegrationAttributes = Dictionary>; type WrapperSDKTypes = 'flutter' | 'none'; interface WrapperSDKInfo { @@ -111,8 +113,10 @@ interface WrapperSDKInfo { // Temporary Interface until Store can be refactored as a class export interface IStore { + mpid?: MPID; isEnabled: boolean; sessionAttributes: Dictionary; + // sessionAttributes: SessionAttributes; currentSessionMPIDs: MPID[]; consentState: SDKConsentState | null; sessionId: string | null; @@ -141,6 +145,7 @@ export interface IStore { isLoggedIn: boolean; cookieSyncDates: Dictionary; integrationAttributes: Dictionary>; + // integrationAttributes: IntegrationAttributes; requireDelay: boolean; isLocalStorageAvailable: boolean | null; storageName: string | null; @@ -219,10 +224,9 @@ export default function Store( this.deviceId = config.deviceId; } if (config.hasOwnProperty('isDevelopmentMode')) { - this.SDKConfig.isDevelopmentMode = - mpInstance._Helpers.returnConvertedBoolean( - config.isDevelopmentMode - ); + this.SDKConfig.isDevelopmentMode = mpInstance._Helpers.returnConvertedBoolean( + config.isDevelopmentMode + ); } else { this.SDKConfig.isDevelopmentMode = false; } diff --git a/test/src/tests-persistence.js b/test/src/tests-persistence.ts similarity index 97% rename from test/src/tests-persistence.js rename to test/src/tests-persistence.ts index e354b7f0e..67d674ee1 100644 --- a/test/src/tests-persistence.js +++ b/test/src/tests-persistence.ts @@ -13,18 +13,35 @@ import { } from './config'; import Polyfill from '../../src/polyfill'; +import { MParticleWebSDK } from '../../src/sdkRuntimeModels'; + +declare global { + interface Window { + mParticle: MParticleWebSDK; + fetchMock: any; + } +} /* eslint-disable quotes*/ -var findCookie = Utils.findCookie, - getLocalStorage = Utils.getLocalStorage, - getLocalStorageProducts = Utils.getLocalStorageProducts, - setCookie = Utils.setCookie, - setLocalStorage = Utils.setLocalStorage, - getEvent = Utils.getEvent, - mockServer; +const { + findCookie, + getLocalStorage, + getLocalStorageProducts, + setCookie, + setLocalStorage, + getEvent, +} = Utils; +let mockServer; var Base64 = Polyfill.Base64; +const mParticle = window.mParticle; +const MockLogger = { + warning: () => {}, + verbose: () => {}, + error: () => {}, +}; + describe.only('Persistence', function() { describe('#useLocalStorage', function() { it('returns true if Local Storage is available', function() { @@ -89,10 +106,7 @@ describe.only('Persistence', function() { document.cookie = "mprtcl-v4_4DD884CD={'gs':{'ie':1|'dt':'9aa8aa0514a802498e8e941d53e2a1d9'|'cgid':'e32ee0cf-83c7-4398-bd50-462a943d16b6'|'das':'99f5ad4d-ed1b-4044-89b6-977d7fac40c5'|'ia':'eyIxMjQiOnsibWlkIjoiNDk3NTQ1MzkyNzgyOTUxNTkxOTA4OTgwNzQ5NzYyOTQwNDQyNzAifX0='|'av':'1.0.0'}|'l':1|'9128337746531357694':{'fst':1663610956871|'ui':'eyIxIjoiMTIzNDU2IiwiNyI6ImVtYWlsQGV4YW1wbGUuY29tIn0='}|'cu':'9128337746531357694'}"; - mParticle.getInstance().Logger = { - verbose: function(msg) {}, - error: function(msg) {}, - }; + mParticle.getInstance().Logger = MockLogger; mParticle._resetForTests(MPConfig); @@ -134,7 +148,7 @@ describe.only('Persistence', function() { mParticle .getInstance() ._Persistence.storeProductsInMemory(products, 'test-mpid'); - mParticle.getInstance()._Store.cartProducts.should.deepEqual([]); + expect(mParticle.getInstance()._Store.cartProducts).to.eql([]); }); }); @@ -149,10 +163,7 @@ describe.only('Persistence', function() { mParticle._resetForTests(MPConfig); - mParticle.getInstance().Logger = { - verbose: function() {}, - error: function() {}, - }; + mParticle.getInstance().Logger = MockLogger; mParticle .getInstance() @@ -674,7 +685,7 @@ describe('migrations and persistence-related', function() { beforeInitCookieData[testMPID].ui.should.have.property('1', '123'); localStorageData[testMPID].ua.should.have.property('gender', 'male'); localStorageData[testMPID].ui.should.have.property('1', '123'); - Should(afterInitCookieData).not.be.ok(); + expect(afterInitCookieData).not.be.ok; done(); }); @@ -695,7 +706,7 @@ describe('migrations and persistence-related', function() { var localStorageData = localStorage.getItem('mprtcl-api'); var cookieData = findCookie(); - Should(localStorageData).not.be.ok(); + expect(localStorageData).not.be.ok; cookieData[testMPID].ua.should.have.property('gender', 'male'); cookieData[testMPID].ui.should.have.property( '1', @@ -862,7 +873,7 @@ describe('migrations and persistence-related', function() { cookieData[testMPID].should.not.have.property(prop); }); - Should(localStorageData).not.be.ok(); + expect(localStorageData).not.be.ok; done(); }); @@ -897,7 +908,7 @@ describe('migrations and persistence-related', function() { localStorageData[testMPID].should.not.have.property(prop); }); - Should(cookieData).not.be.ok(); + expect(cookieData).not.be.ok; done(); }); @@ -932,7 +943,7 @@ describe('migrations and persistence-related', function() { cookieData[testMPID].should.not.have.property(prop); }); - Should(localStorageData).not.be.ok(); + expect(localStorageData).not.be.ok; done(); }); @@ -966,7 +977,7 @@ describe('migrations and persistence-related', function() { localStorageData[testMPID].should.not.have.property(prop); }); - Should(cookieData).not.be.ok(); + expect(cookieData).not.be.ok; done(); }); @@ -1345,7 +1356,7 @@ describe('migrations and persistence-related', function() { }; var expires = new Date( new Date().getTime() + 365 * 24 * 60 * 60 * 1000 - ).toGMTString(); + ).toString(); var cookiesWithExpiration = mParticle .getInstance() ._Persistence.reduceAndEncodePersistence( @@ -1363,9 +1374,9 @@ describe('migrations and persistence-related', function() { .getInstance() ._Persistence.decodePersistence(cookiesWithoutExpiration) ); - Should(cookiesResult['mpid1']).not.be.ok(); - Should(cookiesResult['mpid2']).be.ok(); - Should(cookiesResult['mpid3']).be.ok(); + expect(cookiesResult['mpid1']).not.be.ok; + expect(cookiesResult['mpid2']).be.ok; + expect(cookiesResult['mpid3']).be.ok; cookiesResult.gs.csm.length.should.equal(3); cookiesResult.gs.csm[0].should.equal('mpid1'); cookiesResult.gs.csm[1].should.equal('mpid2'); @@ -1515,7 +1526,7 @@ describe('migrations and persistence-related', function() { cookieData = findCookie(); - Should(cookieData['testMPID']).not.be.ok(); + expect(cookieData['testMPID']).not.be.ok; cookieData['MPID1'].ua.should.have.property('id', 'id2'); cookieData['MPID1'].ua.should.have.property('gender', 'male'); cookieData['MPID1'].ua.should.have.property('age', 30); @@ -1574,7 +1585,7 @@ describe('migrations and persistence-related', function() { var expires = new Date( new Date().getTime() + 365 * 24 * 60 * 60 * 1000 - ).toGMTString(); + ).toString(); var cookiesWithExpiration = mParticle .getInstance() @@ -1594,9 +1605,9 @@ describe('migrations and persistence-related', function() { ._Persistence.decodePersistence(cookiesWithoutExpiration) ); - Should(cookiesResult['mpid1']).not.be.ok(); - Should(cookiesResult['mpid2']).not.be.ok(); - Should(cookiesResult['mpid3']).be.ok(); + expect(cookiesResult['mpid1']).not.be.ok; + expect(cookiesResult['mpid2']).not.be.ok; + expect(cookiesResult['mpid3']).be.ok; cookiesResult.gs.csm[0].should.equal('mpid3'); cookiesResult.should.have.property('mpid3'); mParticle.maxCookieSize = 3000; @@ -1698,7 +1709,7 @@ describe('migrations and persistence-related', function() { cookieData = findCookie(); - Should(cookieData['testMPID']).not.be.ok(); + expect(cookieData['testMPID']).not.be.ok; cookieData['MPID1'].ua.should.have.property('id', 'id2'); cookieData['MPID1'].ua.should.have.property('gender', 'male'); cookieData['MPID1'].ua.should.have.property('age', 30); @@ -1803,7 +1814,7 @@ describe('migrations and persistence-related', function() { mParticle.init(apiKey, window.mParticle.config); var cookieData = findCookie(); - Should(cookieData['testMPID']).not.be.ok(); + expect(cookieData['testMPID']).not.be.ok; cookieData['MPID1'].ua.should.have.property('id', 'id2'); cookieData['MPID2'].ua.should.have.property('id'); @@ -2060,7 +2071,7 @@ describe('migrations and persistence-related', function() { .getInstance() .Identity.getCurrentUser() .getConsentState(); - (consentState === null).should.be.ok(); + (consentState === null).should.be.ok; consentState = mParticle.Consent.createConsentState(); consentState.addGDPRConsentState( 'foo purpose', @@ -2076,7 +2087,7 @@ describe('migrations and persistence-related', function() { .getInstance() .Identity.getCurrentUser() .getConsentState(); - storedConsentState.should.be.ok(); + storedConsentState.should.be.ok; storedConsentState .getGDPRConsentState() .should.have.property('foo purpose'); @@ -2105,7 +2116,7 @@ describe('migrations and persistence-related', function() { .getInstance() .Identity.getCurrentUser() .getConsentState(); - (user1StoredConsentState === null).should.be.ok(); + (user1StoredConsentState === null).should.be.ok; var consentState = mParticle.Consent.createConsentState(); consentState.addGDPRConsentState( 'foo purpose', @@ -2132,7 +2143,7 @@ describe('migrations and persistence-related', function() { .getInstance() .Identity.getCurrentUser() .getConsentState(); - (user2StoredConsentState === null).should.be.ok(); + (user2StoredConsentState === null).should.be.ok; consentState.removeGDPRConsentState('foo purpose'); @@ -2243,7 +2254,7 @@ describe('migrations and persistence-related', function() { mParticle.init(apiKey, window.mParticle.config); var productsAfterInit = getLocalStorageProducts().testMPID; - Should(productsAfterInit.length).not.be.ok(); + expect(productsAfterInit.length).not.be.ok; done(); }); @@ -2465,13 +2476,13 @@ describe('migrations and persistence-related', function() { mParticle.init(apiKey, window.mParticle.config); - Should( + expect( mParticle.getInstance()._Persistence.getFirstSeenTime('current') ).equal(null); mParticle.Identity.identify(); - Should( + expect( mParticle.getInstance()._Persistence.getFirstSeenTime('current') ).not.equal(null); @@ -2491,7 +2502,7 @@ describe('migrations and persistence-related', function() { mParticle.useCookieStorage = true; mParticle.init(apiKey, window.mParticle.config); - Should( + expect( mParticle.getInstance()._Persistence.getFirstSeenTime('previous') ).equal(null); From 58e479f21e14701e8d8e2da29db5c5076a2a2998 Mon Sep 17 00:00:00 2001 From: Alexander Sapountzis Date: Tue, 7 Mar 2023 11:48:02 -0500 Subject: [PATCH 11/11] Save Point --- src/persistence.interfaces.ts | 69 ++++++++++++++++++++++++++++++++++- test/src/tests-persistence.ts | 1 + 2 files changed, 68 insertions(+), 2 deletions(-) diff --git a/src/persistence.interfaces.ts b/src/persistence.interfaces.ts index d51e0a68c..338a83ba3 100644 --- a/src/persistence.interfaces.ts +++ b/src/persistence.interfaces.ts @@ -1,15 +1,30 @@ import { Context } from '@mparticle/event-models'; -import { MPID, Product } from '@mparticle/web-sdk'; +import { + AllUserAttributes, + ConsentState, + IdentityApiData, + MPID, + Product, + UserIdentities, +} from '@mparticle/web-sdk'; +import { ForwardingStatsData } from './apiClient'; import { IntegrationAttributes, ServerSettings, SessionAttributes, } from './store'; +import { Dictionary } from './utils'; + +export type CookieSyncDate = Dictionary; +export type UploadsTable = Dictionary; +export interface iForwardingStatsBatches { + uploadsTable: UploadsTable; + forwardingStatsEventQueue: ForwardingStatsData[]; +} export interface IGlobalStoreV2DTO { sid: string; // Session ID ie: boolean; // Is Enabled - // Session Attributes sa: SessionAttributes; ss: ServerSettings; dt: string; // Dev Token @@ -35,4 +50,54 @@ export interface IPersistence { update(): void; storeProductsInMemory(products: Product[], mpid: MPID): void; storeDataInMemory(obj: IPersistenceV2DTO, currentMPID: MPID): void; + determineLocalStorageAvailability(storage: Storage): boolean; + getUserProductsFromLS(mpid: MPID): Product[]; + getAllUserProductsFromLS(): Product[]; + setLocalStorage(): void; + getLocalStorage(): IPersistenceV2DTO | null; + expireCookies(cookieName: string): void; + getCookie(): IPersistenceV2DTO | null; + setCookie(): void; + reduceAndEncodePersistence( + persistence: IPersistenceV2DTO, + expires: string, + domain: string, + maxCookieSize: number + ): void; + findPrevCookiesBasedOnUI(identityApiData: IdentityApiData): void; + encodePersistence(persistance: IPersistenceV2DTO): string; + decodePersistence(persistance: IPersistenceV2DTO): string; + replaceCommasWithPipes(string: string): string; + replacePipesWithCommas(string: string): string; + replaceApostrophesWithQuotes(string: string): string; + replaceQuotesWithApostrophes(string: string): string; + createCookieString(string: string): string; + revertCookieString(string: string): string; + getCookieDomain(): string; + getDomain(doc: string, locationHostname: string): string; + getUserIdentities(mpid: MPID): UserIdentities; + getAllUserAttributes(mpid: MPID): AllUserAttributes; + getCartProducts(mpid: MPID): Product[]; + setCartProducts(allProducts: Product[]): void; + saveUserIdentitiesToPersistence( + mpid: MPID, + userIdentities: UserIdentities + ): void; + saveUserAttributesToPersistence( + mpid: MPID, + userIdentities: UserIdentities + ): void; + saveUserCookieSyncDatesToPersistence(mpid: MPID, csd: CookieSyncDate): void; + saveUserConsentStateToCookies(mpid, consentState: ConsentState): void; + savePersistence(persistance: IPersistenceV2DTO): void; + getPersistence(): IPersistenceV2DTO; + getConsentState(mpid: MPID): ConsentState | null; + getFirstSeenTime(mpid: MPID): string | null; + setFirstSeenTime(mpid: MPID, time: number): void; + getLastSeenTime(mpid: MPID): number | null; + setLastSeenTime(mpid: MPID, time: number): void; + getDeviceId(): string; + setDeviceId(guid: string): void; + resetPersistence(): void; + forwardingStatsBatches: iForwardingStatsBatches; } diff --git a/test/src/tests-persistence.ts b/test/src/tests-persistence.ts index 67d674ee1..6c8092042 100644 --- a/test/src/tests-persistence.ts +++ b/test/src/tests-persistence.ts @@ -350,6 +350,7 @@ describe.only('Persistence', function() { mParticle._resetForTests(MPConfig); mParticle.getInstance()._Store.prodStorageName = lsKey; + mParticle.getInstance()._Store.isLocalStorageAvailable = true; const expectedProducts = [ {