From c9c08e0b4de2bdc4f398cbe091ca0108abdc5f8e Mon Sep 17 00:00:00 2001 From: Jaissica Date: Thu, 30 Oct 2025 12:54:27 -0400 Subject: [PATCH 1/7] chore: added helper function performSessionEnd --- src/sessionManager.ts | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/sessionManager.ts b/src/sessionManager.ts index fafbde631..9ab819523 100644 --- a/src/sessionManager.ts +++ b/src/sessionManager.ts @@ -134,12 +134,7 @@ export default function SessionManager( ); if (override) { - mpInstance._Events.logEvent({ - messageType: Types.MessageType.SessionEnd, - }); - - mpInstance._Store.nullifySession(); - mpInstance._timeOnSiteTimer?.resetTimer(); + performSessionEnd(); return; } @@ -185,15 +180,9 @@ export default function SessionManager( if (timeSinceLastEventSent < sessionTimeoutInMilliseconds) { self.setSessionTimer(); } else { - mpInstance._Events.logEvent({ - messageType: Types.MessageType.SessionEnd, - }); - - mpInstance._Store.sessionStartDate = null; - mpInstance._Store.nullifySession(); + performSessionEnd(); } } - mpInstance._timeOnSiteTimer?.resetTimer(); }; @@ -234,4 +223,13 @@ export default function SessionManager( } } }; + + function performSessionEnd(): void { + mpInstance._Events.logEvent({ + messageType: Types.MessageType.SessionEnd, + }); + mpInstance._Store.sessionStartDate = null; + mpInstance._Store.nullifySession(); + mpInstance._timeOnSiteTimer?.resetTimer(); + } } From 952f67afb53b68749d9a80ff95c75b7bd8f6dd0e Mon Sep 17 00:00:00 2001 From: Jaissica Date: Mon, 1 Dec 2025 09:25:44 -0500 Subject: [PATCH 2/7] add hasSessionTimedOut helper to unify timeout logic and updated tests --- src/sessionManager.ts | 46 ++++---- test/src/tests-session-manager.ts | 182 ++++++++++++++++++++++++++++++ 2 files changed, 207 insertions(+), 21 deletions(-) diff --git a/src/sessionManager.ts b/src/sessionManager.ts index 9ab819523..b2e3c82f5 100644 --- a/src/sessionManager.ts +++ b/src/sessionManager.ts @@ -33,16 +33,7 @@ export default function SessionManager( this.initialize = function (): void { if (mpInstance._Store.sessionId) { - const sessionTimeoutInMilliseconds: number = - mpInstance._Store.SDKConfig.sessionTimeout * 60000; - - if ( - new Date() > - new Date( - mpInstance._Store.dateLastEventSent.getTime() + - sessionTimeoutInMilliseconds - ) - ) { + if (hasSessionTimedOut(mpInstance._Store.dateLastEventSent.getTime())) { self.endSession(); self.startNewSession(); } else { @@ -151,9 +142,6 @@ export default function SessionManager( return; } - let sessionTimeoutInMilliseconds: number; - let timeSinceLastEventSent: number; - const cookies: IPersistenceMinified = mpInstance._Persistence.getPersistence(); @@ -172,15 +160,10 @@ export default function SessionManager( } if (cookies?.gs?.les) { - sessionTimeoutInMilliseconds = - mpInstance._Store.SDKConfig.sessionTimeout * 60000; - const newDate: number = new Date().getTime(); - timeSinceLastEventSent = newDate - cookies.gs.les; - - if (timeSinceLastEventSent < sessionTimeoutInMilliseconds) { - self.setSessionTimer(); - } else { + if (hasSessionTimedOut(cookies.gs.les)) { performSessionEnd(); + } else { + self.setSessionTimer(); } } mpInstance._timeOnSiteTimer?.resetTimer(); @@ -224,6 +207,27 @@ export default function SessionManager( } }; + /** + * Checks if the session has expired based on the last event timestamp + * @param lastEventTimestamp - Unix timestamp in milliseconds of the last event + * @returns true if the session has expired, false otherwise + */ + function hasSessionTimedOut(lastEventTimestamp: number): boolean { + const sessionTimeoutInMilliseconds: number = + mpInstance._Store.SDKConfig.sessionTimeout * 60000; + const timeSinceLastEvent: number = + new Date().getTime() - lastEventTimestamp; + + return timeSinceLastEvent >= sessionTimeoutInMilliseconds; + } + + /** + * Performs session end operations: + * - Logs a SessionEnd event + * - Clears session start date + * - Nullifies the session ID and related data + * - Resets the time-on-site timer + */ function performSessionEnd(): void { mpInstance._Events.logEvent({ messageType: Types.MessageType.SessionEnd, diff --git a/test/src/tests-session-manager.ts b/test/src/tests-session-manager.ts index 94b97b43c..7a74ec588 100644 --- a/test/src/tests-session-manager.ts +++ b/test/src/tests-session-manager.ts @@ -297,6 +297,188 @@ describe('SessionManager', () => { }); }); + describe('#hasSessionTimedOut', () => { + it('should return true when elapsed time exceeds session timeout', () => { + const timePassed = 35 * (MILLIS_IN_ONE_SEC * 60); // 35 minutes + + mParticle.init(apiKey, window.mParticle.config); + const mpInstance = mParticle.getInstance(); + + mpInstance._Store.sessionId = 'OLD-ID'; + const timeLastEventSent = mpInstance._Store.dateLastEventSent.getTime(); + mpInstance._Store.dateLastEventSent = new Date(timeLastEventSent - timePassed); + + // initialize() uses hasSessionTimedOut internally + mpInstance._SessionManager.initialize(); + + // Should have created a new session because timeout was exceeded + expect(mpInstance._Store.sessionId).to.not.equal('OLD-ID'); + }); + + it('should return false when elapsed time is within session timeout', () => { + const timePassed = 15 * (MILLIS_IN_ONE_SEC * 60); // 15 minutes + + mParticle.init(apiKey, window.mParticle.config); + const mpInstance = mParticle.getInstance(); + + mpInstance._Store.sessionId = 'OLD-ID'; + const timeLastEventSent = mpInstance._Store.dateLastEventSent.getTime(); + mpInstance._Store.dateLastEventSent = new Date(timeLastEventSent - timePassed); + + // initialize() uses hasSessionTimedOut internally + mpInstance._SessionManager.initialize(); + + // Should have kept the old session because timeout was not exceeded + expect(mpInstance._Store.sessionId).to.equal('OLD-ID'); + }); + + it('should work consistently with both in-memory and persisted timestamps', () => { + const now = new Date(); + const thirtyOneMinutesAgo = new Date(); + thirtyOneMinutesAgo.setMinutes(now.getMinutes() - 31); + + mParticle.init(apiKey, window.mParticle.config); + const mpInstance = mParticle.getInstance(); + + // Test with in-memory store (via initialize) + mpInstance._Store.sessionId = 'TEST-ID'; + mpInstance._Store.dateLastEventSent = thirtyOneMinutesAgo; + mpInstance._SessionManager.initialize(); + + // Session should have expired (default timeout is 30 minutes) + expect(mpInstance._Store.sessionId).to.not.equal('TEST-ID'); + + // Test with persistence (via endSession) + const newSessionId = mpInstance._Store.sessionId; + sinon.stub(mpInstance._Persistence, 'getPersistence').returns({ + gs: { + les: thirtyOneMinutesAgo.getTime(), + sid: newSessionId, + }, + }); + + mpInstance._SessionManager.endSession(); + + // Session should have ended (same timeout logic) + expect(mpInstance._Store.sessionId).to.equal(null); + }); + + it('should return true when elapsed time equals session timeout exactly', () => { + const now = new Date(); + const exactlyThirtyMinutesAgo = new Date(); + exactlyThirtyMinutesAgo.setMinutes(now.getMinutes() - 30); + + mParticle.init(apiKey, window.mParticle.config); + const mpInstance = mParticle.getInstance(); + + sinon.stub(mpInstance._Persistence, 'getPersistence').returns({ + gs: { + les: exactlyThirtyMinutesAgo.getTime(), + sid: 'TEST-ID', + }, + }); + + mpInstance._SessionManager.endSession(); + + // At exactly 30 minutes, session should be expired + expect(mpInstance._Store.sessionId).to.equal(null); + }); + }); + + describe('#performSessionEnd', () => { + it('should log a SessionEnd event', () => { + mParticle.init(apiKey, window.mParticle.config); + const mpInstance = mParticle.getInstance(); + const eventSpy = sinon.spy(mpInstance._Events, 'logEvent'); + + mpInstance._SessionManager.endSession(true); + + // Find the SessionEnd event call + const sessionEndCall = eventSpy.getCalls().find(call => + call.args[0]?.messageType === MessageType.SessionEnd + ); + + expect(sessionEndCall).to.not.be.undefined; + expect(sessionEndCall.args[0]).to.eql({ + messageType: MessageType.SessionEnd, + }); + }); + + it('should clear sessionStartDate', () => { + mParticle.init(apiKey, window.mParticle.config); + const mpInstance = mParticle.getInstance(); + + const sessionStartDate = mpInstance._Store.sessionStartDate; + expect(sessionStartDate).to.not.be.null; + + mpInstance._SessionManager.endSession(true); + + expect(mpInstance._Store.sessionStartDate).to.equal(null); + }); + + it('should nullify session ID and session attributes', () => { + mParticle.init(apiKey, window.mParticle.config); + const mpInstance = mParticle.getInstance(); + + // Set up session data + mpInstance._Store.sessionAttributes = { testAttr: 'value' }; + mpInstance._Store.localSessionAttributes = { localAttr: 'value' }; + + expect(mpInstance._Store.sessionId).to.not.be.null; + + mpInstance._SessionManager.endSession(true); + + expect(mpInstance._Store.sessionId).to.equal(null); + expect(mpInstance._Store.sessionAttributes).to.eql({}); + expect(mpInstance._Store.localSessionAttributes).to.eql({}); + }); + + it('should reset timeOnSiteTimer if it exists', () => { + mParticle.init(apiKey, window.mParticle.config); + const mpInstance = mParticle.getInstance(); + + // Timer should exist since workspaceToken is present in config + expect(mpInstance._timeOnSiteTimer).to.exist; + + const resetTimerSpy = sinon.spy(mpInstance._timeOnSiteTimer, 'resetTimer'); + + mpInstance._SessionManager.endSession(true); + + expect(resetTimerSpy.called).to.equal(true); + }); + + it('should handle missing timeOnSiteTimer gracefully', () => { + mParticle.init(apiKey, window.mParticle.config); + const mpInstance = mParticle.getInstance(); + + // Explicitly remove the timer to test the optional chaining behavior + mpInstance._timeOnSiteTimer = undefined; + + expect(() => { + mpInstance._SessionManager.endSession(true); + }).to.not.throw(); + + // Session should still end properly + expect(mpInstance._Store.sessionId).to.equal(null); + }); + + it('should perform all session end operations', () => { + mParticle.init(apiKey, window.mParticle.config); + const mpInstance = mParticle.getInstance(); + + const eventSpy = sinon.spy(mpInstance._Events, 'logEvent'); + const persistenceSpy = sinon.spy(mpInstance._Persistence, 'update'); + + mpInstance._SessionManager.endSession(true); + + // Verify all operations happened + expect(eventSpy.called).to.equal(true); + expect(mpInstance._Store.sessionStartDate).to.equal(null); + expect(mpInstance._Store.sessionId).to.equal(null); + expect(persistenceSpy.called).to.equal(true); + }); + }); + describe('#endSession', () => { it('should end a session', () => { mParticle.init(apiKey, window.mParticle.config); From a42fbf98b54faab1b8ba466e52d16d48a97f63a2 Mon Sep 17 00:00:00 2001 From: Jaissica Date: Wed, 17 Dec 2025 10:38:34 -0500 Subject: [PATCH 3/7] refactor initialize, hasSessionTimedOut, performSessionEnd function --- src/sessionManager.ts | 27 ++++++++++++++------------- src/store.ts | 1 + 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/sessionManager.ts b/src/sessionManager.ts index b2e3c82f5..538a3f236 100644 --- a/src/sessionManager.ts +++ b/src/sessionManager.ts @@ -33,22 +33,24 @@ export default function SessionManager( this.initialize = function (): void { if (mpInstance._Store.sessionId) { - if (hasSessionTimedOut(mpInstance._Store.dateLastEventSent.getTime())) { + const { dateLastEventSent, SDKConfig } = mpInstance._Store; + const { sessionTimeout } = SDKConfig; + + if (hasSessionTimedOut(dateLastEventSent.getTime(), sessionTimeout)) { self.endSession(); self.startNewSession(); } else { // https://go.mparticle.com/work/SQDSDKS-6045 // https://go.mparticle.com/work/SQDSDKS-6323 const currentUser = mpInstance.Identity.getCurrentUser(); - const sdkIdentityRequest = - mpInstance._Store.SDKConfig.identifyRequest; + const sdkIdentityRequest = SDKConfig.identifyRequest; if ( hasIdentityRequestChanged(currentUser, sdkIdentityRequest) ) { mpInstance.Identity.identify( - mpInstance._Store.SDKConfig.identifyRequest, - mpInstance._Store.SDKConfig.identityCallback + sdkIdentityRequest, + SDKConfig.identityCallback ); mpInstance._Store.identifyCalled = true; mpInstance._Store.SDKConfig.identityCallback = null; @@ -160,7 +162,9 @@ export default function SessionManager( } if (cookies?.gs?.les) { - if (hasSessionTimedOut(cookies.gs.les)) { + const sessionTimeout = mpInstance._Store.SDKConfig.sessionTimeout; + + if (hasSessionTimedOut(cookies.gs.les, sessionTimeout)) { performSessionEnd(); } else { self.setSessionTimer(); @@ -210,13 +214,12 @@ export default function SessionManager( /** * Checks if the session has expired based on the last event timestamp * @param lastEventTimestamp - Unix timestamp in milliseconds of the last event + * @param sessionTimeout - Session timeout in minutes * @returns true if the session has expired, false otherwise */ - function hasSessionTimedOut(lastEventTimestamp: number): boolean { - const sessionTimeoutInMilliseconds: number = - mpInstance._Store.SDKConfig.sessionTimeout * 60000; - const timeSinceLastEvent: number = - new Date().getTime() - lastEventTimestamp; + function hasSessionTimedOut(lastEventTimestamp: number, sessionTimeout: number): boolean { + const sessionTimeoutInMilliseconds: number = sessionTimeout * 60000; + const timeSinceLastEvent: number = Date.now() - lastEventTimestamp; return timeSinceLastEvent >= sessionTimeoutInMilliseconds; } @@ -224,7 +227,6 @@ export default function SessionManager( /** * Performs session end operations: * - Logs a SessionEnd event - * - Clears session start date * - Nullifies the session ID and related data * - Resets the time-on-site timer */ @@ -232,7 +234,6 @@ export default function SessionManager( mpInstance._Events.logEvent({ messageType: Types.MessageType.SessionEnd, }); - mpInstance._Store.sessionStartDate = null; mpInstance._Store.nullifySession(); mpInstance._timeOnSiteTimer?.resetTimer(); } diff --git a/src/store.ts b/src/store.ts index 64d560dc8..329f8e24d 100644 --- a/src/store.ts +++ b/src/store.ts @@ -683,6 +683,7 @@ export default function Store( this.nullifySession = (): void => { this.sessionId = null; this.dateLastEventSent = null; + this.sessionStartDate = null; this.sessionAttributes = {}; this.localSessionAttributes = {}; mpInstance._Persistence.update(); From 1741978051deca6edeb2edf39db144e1baed79ed Mon Sep 17 00:00:00 2001 From: Jaissica Date: Fri, 19 Dec 2025 12:00:07 -0500 Subject: [PATCH 4/7] fix stub leaks and timing races in session manager tests --- src/sessionManager.ts | 10 +++--- test/src/tests-session-manager.ts | 56 +++++++++++++++++++++++-------- 2 files changed, 48 insertions(+), 18 deletions(-) diff --git a/src/sessionManager.ts b/src/sessionManager.ts index 538a3f236..984f81495 100644 --- a/src/sessionManager.ts +++ b/src/sessionManager.ts @@ -140,7 +140,6 @@ export default function SessionManager( Messages.InformationMessages.AbandonEndSession ); mpInstance._timeOnSiteTimer?.resetTimer(); - return; } @@ -152,7 +151,6 @@ export default function SessionManager( Messages.InformationMessages.NoSessionToEnd ); mpInstance._timeOnSiteTimer?.resetTimer(); - return; } @@ -168,9 +166,9 @@ export default function SessionManager( performSessionEnd(); } else { self.setSessionTimer(); + mpInstance._timeOnSiteTimer?.resetTimer(); } - } - mpInstance._timeOnSiteTimer?.resetTimer(); + } }; this.setSessionTimer = function (): void { @@ -218,6 +216,10 @@ export default function SessionManager( * @returns true if the session has expired, false otherwise */ function hasSessionTimedOut(lastEventTimestamp: number, sessionTimeout: number): boolean { + if (!lastEventTimestamp || !sessionTimeout || sessionTimeout <= 0) { + return false; + } + const sessionTimeoutInMilliseconds: number = sessionTimeout * 60000; const timeSinceLastEvent: number = Date.now() - lastEventTimestamp; diff --git a/test/src/tests-session-manager.ts b/test/src/tests-session-manager.ts index ed8e91fad..d1579a163 100644 --- a/test/src/tests-session-manager.ts +++ b/test/src/tests-session-manager.ts @@ -294,6 +294,14 @@ describe('SessionManager', () => { }); describe('#hasSessionTimedOut', () => { + beforeEach(() => { + clock = sinon.useFakeTimers(now.getTime()); + }); + + afterEach(() => { + clock.restore(); + }); + it('should return true when elapsed time exceeds session timeout', () => { const timePassed = 35 * (MILLIS_IN_ONE_SEC * 60); // 35 minutes @@ -329,9 +337,7 @@ describe('SessionManager', () => { }); it('should work consistently with both in-memory and persisted timestamps', () => { - const now = new Date(); - const thirtyOneMinutesAgo = new Date(); - thirtyOneMinutesAgo.setMinutes(now.getMinutes() - 31); + const thirtyOneMinutesAgo = new Date(now.getTime() - (31 * MILLIS_IN_ONE_SEC * 60)); mParticle.init(apiKey, window.mParticle.config); const mpInstance = mParticle.getInstance(); @@ -346,7 +352,7 @@ describe('SessionManager', () => { // Test with persistence (via endSession) const newSessionId = mpInstance._Store.sessionId; - sinon.stub(mpInstance._Persistence, 'getPersistence').returns({ + const getPersistenceStub = sinon.stub(mpInstance._Persistence, 'getPersistence').returns({ gs: { les: thirtyOneMinutesAgo.getTime(), sid: newSessionId, @@ -357,17 +363,18 @@ describe('SessionManager', () => { // Session should have ended (same timeout logic) expect(mpInstance._Store.sessionId).to.equal(null); + + // Clean up stub + getPersistenceStub.restore(); }); it('should return true when elapsed time equals session timeout exactly', () => { - const now = new Date(); - const exactlyThirtyMinutesAgo = new Date(); - exactlyThirtyMinutesAgo.setMinutes(now.getMinutes() - 30); + const exactlyThirtyMinutesAgo = new Date(now.getTime() - (30 * MILLIS_IN_ONE_SEC * 60)); mParticle.init(apiKey, window.mParticle.config); const mpInstance = mParticle.getInstance(); - sinon.stub(mpInstance._Persistence, 'getPersistence').returns({ + const getPersistenceStub = sinon.stub(mpInstance._Persistence, 'getPersistence').returns({ gs: { les: exactlyThirtyMinutesAgo.getTime(), sid: 'TEST-ID', @@ -378,6 +385,9 @@ describe('SessionManager', () => { // At exactly 30 minutes, session should be expired expect(mpInstance._Store.sessionId).to.equal(null); + + // Clean up stub + getPersistenceStub.restore(); }); }); @@ -522,7 +532,7 @@ describe('SessionManager', () => { // Session Manager relies on persistence to determine last event sent (LES) time // Also requires sid to verify session exists - sinon.stub(mpInstance._Persistence, 'getPersistence').returns({ + const getPersistenceStub = sinon.stub(mpInstance._Persistence, 'getPersistence').returns({ gs: { les: twentyMinutesAgo, sid: 'fake-session-id', @@ -539,6 +549,9 @@ describe('SessionManager', () => { // When session is not timed out, setSessionTimer is called to keep track // of current session timeout expect(timerSpy.getCalls().length).to.equal(1); + + // Clean up stub + getPersistenceStub.restore(); }); it('should force a session end when override is used', () => { @@ -694,7 +707,7 @@ describe('SessionManager', () => { mParticle.init(apiKey, window.mParticle.config); const mpInstance = mParticle.getInstance(); - sinon.stub(mpInstance._Persistence, 'getPersistence').returns({ + const getPersistenceStub = sinon.stub(mpInstance._Persistence, 'getPersistence').returns({ gs: { sid: 'cookie-session-id', }, @@ -707,6 +720,9 @@ describe('SessionManager', () => { expect(mpInstance._Store.sessionId).to.equal( 'cookie-session-id' ); + + // Clean up stub + getPersistenceStub.restore(); }); it('should end session if the session timeout limit has been reached', () => { @@ -926,7 +942,7 @@ describe('SessionManager', () => { const mpInstance = mParticle.getInstance(); // Session Manager relies on persistence check sid (Session ID) - sinon.stub(mpInstance._Persistence, 'getPersistence').returns({ + const getPersistenceStub = sinon.stub(mpInstance._Persistence, 'getPersistence').returns({ gs: { sid: null, }, @@ -941,6 +957,9 @@ describe('SessionManager', () => { mpInstance._SessionManager.startNewSessionIfNeeded(); expect(startNewSessionSpy.called).to.equal(true); + + // Clean up stub + getPersistenceStub.restore(); }); it('should NOT call startNewSession if sessionId is undefined and Persistence is undefined', () => { @@ -970,7 +989,7 @@ describe('SessionManager', () => { const mpInstance = mParticle.getInstance(); // Session Manager relies on persistence check sid (Session ID) - sinon.stub(mpInstance._Persistence, 'getPersistence').returns({ + const getPersistenceStub = sinon.stub(mpInstance._Persistence, 'getPersistence').returns({ gs: { sid: 'sid-from-persistence', }, @@ -981,6 +1000,9 @@ describe('SessionManager', () => { mpInstance._SessionManager.startNewSessionIfNeeded(); mpInstance._Store.sessionId = 'sid-from-persistence'; + + // Clean up stub + getPersistenceStub.restore(); }); it('should set sessionId from Persistence if Store.sessionId is undefined', () => { @@ -988,7 +1010,7 @@ describe('SessionManager', () => { const mpInstance = mParticle.getInstance(); // Session Manager relies on persistence check sid (Session ID) - sinon.stub(mpInstance._Persistence, 'getPersistence').returns({ + const getPersistenceStub = sinon.stub(mpInstance._Persistence, 'getPersistence').returns({ gs: { sid: 'sid-from-persistence', }, @@ -1006,6 +1028,9 @@ describe('SessionManager', () => { mpInstance._Store.sessionId = 'sid-from-persistence'; expect(startNewSessionSpy.called).to.equal(true); + + // Clean up stub + getPersistenceStub.restore(); }); it('should NOT call startNewSession if Store.sessionId and Persistence are null', () => { @@ -1075,7 +1100,7 @@ describe('SessionManager', () => { // Session Manager relies on persistence to determine last time seen (LES) // Also requires sid to verify session exists - sinon.stub(mpInstance._Persistence, 'getPersistence').returns({ + const getPersistenceStub = sinon.stub(mpInstance._Persistence, 'getPersistence').returns({ gs: { les: now, sid: 'fake-session-id', @@ -1093,6 +1118,9 @@ describe('SessionManager', () => { // Persistence isn't necessary for this feature, but we should test // to see that it is called in case this ever needs to be refactored expect(persistenceSpy.called).to.equal(true); + + // Clean up stub + getPersistenceStub.restore(); }); it('should call identify when SDKConfig.identifyRequest differs from getCurrentUser().userIdentities on page refresh', async () => { From 6039a3b474d87f30e23bced9a7aca27c38eabe71 Mon Sep 17 00:00:00 2001 From: Jaissica Date: Fri, 19 Dec 2025 12:35:17 -0500 Subject: [PATCH 5/7] update test content in session manager tests --- src/sessionManager.ts | 2 +- test/src/tests-session-manager.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sessionManager.ts b/src/sessionManager.ts index 984f81495..4c9c6303b 100644 --- a/src/sessionManager.ts +++ b/src/sessionManager.ts @@ -166,7 +166,7 @@ export default function SessionManager( performSessionEnd(); } else { self.setSessionTimer(); - mpInstance._timeOnSiteTimer?.resetTimer(); + mpInstance._timeOnSiteTimer?.resetTimer(); } } }; diff --git a/test/src/tests-session-manager.ts b/test/src/tests-session-manager.ts index d1579a163..6bb3f25de 100644 --- a/test/src/tests-session-manager.ts +++ b/test/src/tests-session-manager.ts @@ -302,7 +302,7 @@ describe('SessionManager', () => { clock.restore(); }); - it('should return true when elapsed time exceeds session timeout', () => { + it('should end the session when elapsed time exceeds session timeout', () => { const timePassed = 35 * (MILLIS_IN_ONE_SEC * 60); // 35 minutes mParticle.init(apiKey, window.mParticle.config); @@ -319,7 +319,7 @@ describe('SessionManager', () => { expect(mpInstance._Store.sessionId).to.not.equal('OLD-ID'); }); - it('should return false when elapsed time is within session timeout', () => { + it('should preserve the session when elapsed time is within session timeout', () => { const timePassed = 15 * (MILLIS_IN_ONE_SEC * 60); // 15 minutes mParticle.init(apiKey, window.mParticle.config); @@ -368,7 +368,7 @@ describe('SessionManager', () => { getPersistenceStub.restore(); }); - it('should return true when elapsed time equals session timeout exactly', () => { + it('should end the session when elapsed time equals session timeout exactly', () => { const exactlyThirtyMinutesAgo = new Date(now.getTime() - (30 * MILLIS_IN_ONE_SEC * 60)); mParticle.init(apiKey, window.mParticle.config); From 1ad176d6c8b8092c46fb9f3f2a3355af1f08096a Mon Sep 17 00:00:00 2001 From: Jaissica Date: Fri, 19 Dec 2025 12:45:44 -0500 Subject: [PATCH 6/7] Update src/sessionManager.ts remove trailing whitespace Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/sessionManager.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sessionManager.ts b/src/sessionManager.ts index 4c9c6303b..b71f6e8f4 100644 --- a/src/sessionManager.ts +++ b/src/sessionManager.ts @@ -168,7 +168,7 @@ export default function SessionManager( self.setSessionTimer(); mpInstance._timeOnSiteTimer?.resetTimer(); } - } + } }; this.setSessionTimer = function (): void { From 3c4a64485a1126a4513fa1a81c8278ca0d8c1e2c Mon Sep 17 00:00:00 2001 From: Jaissica Date: Mon, 5 Jan 2026 14:21:10 -0500 Subject: [PATCH 7/7] add null check for dateLastEventSent in sessionManager --- src/sessionManager.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sessionManager.ts b/src/sessionManager.ts index b71f6e8f4..9b5fc59fc 100644 --- a/src/sessionManager.ts +++ b/src/sessionManager.ts @@ -36,7 +36,7 @@ export default function SessionManager( const { dateLastEventSent, SDKConfig } = mpInstance._Store; const { sessionTimeout } = SDKConfig; - if (hasSessionTimedOut(dateLastEventSent.getTime(), sessionTimeout)) { + if (hasSessionTimedOut(dateLastEventSent?.getTime(), sessionTimeout)) { self.endSession(); self.startNewSession(); } else {