diff --git a/packages/browser-integration-tests/suites/replay/sessionExpiry/init.js b/packages/browser-integration-tests/suites/replay/sessionExpiry/init.js index 3e685021e1fe..46af904118a6 100644 --- a/packages/browser-integration-tests/suites/replay/sessionExpiry/init.js +++ b/packages/browser-integration-tests/suites/replay/sessionExpiry/init.js @@ -17,6 +17,7 @@ Sentry.init({ }); window.Replay._replay.timeouts = { - sessionIdle: 2000, // this is usually 5min, but we want to test this with shorter times + sessionIdlePause: 1000, // this is usually 5min, but we want to test this with shorter times + sessionIdleExpire: 2000, // this is usually 15min, but we want to test this with shorter times maxSessionLife: 3600000, // default: 60min }; diff --git a/packages/browser-integration-tests/suites/replay/sessionInactive/init.js b/packages/browser-integration-tests/suites/replay/sessionInactive/init.js index a3da64ec3bae..4c641d160d79 100644 --- a/packages/browser-integration-tests/suites/replay/sessionInactive/init.js +++ b/packages/browser-integration-tests/suites/replay/sessionInactive/init.js @@ -17,6 +17,7 @@ Sentry.init({ }); window.Replay._replay.timeouts = { - sessionIdle: 1000, // default: 5min - maxSessionLife: 2000, // this is usually 60min, but we want to test this with shorter times + sessionIdlePause: 1000, // this is usually 5min, but we want to test this with shorter times + sessionIdleExpire: 900000, // defayult: 15min + maxSessionLife: 3600000, // default: 60min }; diff --git a/packages/browser-integration-tests/suites/replay/sessionInactive/test.ts b/packages/browser-integration-tests/suites/replay/sessionInactive/test.ts index ed53f155feea..ef2b841cbea3 100644 --- a/packages/browser-integration-tests/suites/replay/sessionInactive/test.ts +++ b/packages/browser-integration-tests/suites/replay/sessionInactive/test.ts @@ -11,8 +11,8 @@ import { waitForReplayRequest, } from '../../../utils/replayHelpers'; -// Session should expire after 2s - keep in sync with init.js -const SESSION_TIMEOUT = 2000; +// Session should be paused after 2s - keep in sync with init.js +const SESSION_PAUSED = 2000; sentryTest('handles an inactive session', async ({ getLocalTestPath, page }) => { if (shouldSkipReplayTest()) { @@ -44,11 +44,8 @@ sentryTest('handles an inactive session', async ({ getLocalTestPath, page }) => await page.click('#button1'); - // We wait for another segment 0 - const reqPromise1 = waitForReplayRequest(page, 0); - // Now we wait for the session timeout, nothing should be sent in the meanwhile - await new Promise(resolve => setTimeout(resolve, SESSION_TIMEOUT)); + await new Promise(resolve => setTimeout(resolve, SESSION_PAUSED)); // nothing happened because no activity/inactivity was detected const replay = await getReplaySnapshot(page); @@ -64,7 +61,10 @@ sentryTest('handles an inactive session', async ({ getLocalTestPath, page }) => expect(replay2._isEnabled).toEqual(true); expect(replay2._isPaused).toEqual(true); - // Trigger an action, should re-start the recording + // We wait for next segment to be sent once we resume the session + const reqPromise1 = waitForReplayRequest(page); + + // Trigger an action, should resume the recording await page.click('#button2'); const req1 = await reqPromise1; @@ -72,9 +72,6 @@ sentryTest('handles an inactive session', async ({ getLocalTestPath, page }) => expect(replay3._isEnabled).toEqual(true); expect(replay3._isPaused).toEqual(false); - const replayEvent1 = getReplayEvent(req1); - expect(replayEvent1).toEqual(getExpectedReplayEvent({})); - const fullSnapshots1 = getFullRecordingSnapshots(req1); expect(fullSnapshots1.length).toEqual(1); const stringifiedSnapshot1 = normalize(fullSnapshots1[0]); diff --git a/packages/browser-integration-tests/suites/replay/sessionMaxAge/init.js b/packages/browser-integration-tests/suites/replay/sessionMaxAge/init.js index cf98205a5576..0c16dc6ca3a1 100644 --- a/packages/browser-integration-tests/suites/replay/sessionMaxAge/init.js +++ b/packages/browser-integration-tests/suites/replay/sessionMaxAge/init.js @@ -17,6 +17,7 @@ Sentry.init({ }); window.Replay._replay.timeouts = { - sessionIdle: 300000, // default: 5min + sessionIdlePause: 300000, // default: 5min + sessionIdleExpire: 900000, // default: 15min maxSessionLife: 4000, // this is usually 60min, but we want to test this with shorter times }; diff --git a/packages/replay/src/constants.ts b/packages/replay/src/constants.ts index e7b5fe2f1b52..f1f8627c120c 100644 --- a/packages/replay/src/constants.ts +++ b/packages/replay/src/constants.ts @@ -11,11 +11,14 @@ export const REPLAY_EVENT_NAME = 'replay_event'; export const RECORDING_EVENT_NAME = 'replay_recording'; export const UNABLE_TO_SEND_REPLAY = 'Unable to send Replay'; -// The idle limit for a session -export const SESSION_IDLE_DURATION = 300_000; // 5 minutes in ms +// The idle limit for a session after which recording is paused. +export const SESSION_IDLE_PAUSE_DURATION = 300_000; // 5 minutes in ms + +// The idle limit for a session after which the session expires. +export const SESSION_IDLE_EXPIRE_DURATION = 900_000; // 15 minutes in ms // The maximum length of a session -export const MAX_SESSION_LIFE = 3_600_000; // 60 minutes +export const MAX_SESSION_LIFE = 3_600_000; // 60 minutes in ms /** Default flush delays */ export const DEFAULT_FLUSH_MIN_DELAY = 5_000; diff --git a/packages/replay/src/replay.ts b/packages/replay/src/replay.ts index 990ca825266e..ff0124018bfb 100644 --- a/packages/replay/src/replay.ts +++ b/packages/replay/src/replay.ts @@ -4,7 +4,13 @@ import { captureException, getCurrentHub } from '@sentry/core'; import type { Breadcrumb, ReplayRecordingMode } from '@sentry/types'; import { logger } from '@sentry/utils'; -import { ERROR_CHECKOUT_TIME, MAX_SESSION_LIFE, SESSION_IDLE_DURATION, WINDOW } from './constants'; +import { + ERROR_CHECKOUT_TIME, + MAX_SESSION_LIFE, + SESSION_IDLE_EXPIRE_DURATION, + SESSION_IDLE_PAUSE_DURATION, + WINDOW, +} from './constants'; import { setupPerformanceObserver } from './coreHandlers/performanceObserver'; import { createEventBuffer } from './eventBuffer'; import { getSession } from './session/getSession'; @@ -61,7 +67,8 @@ export class ReplayContainer implements ReplayContainerInterface { * @hidden */ public readonly timeouts: Timeouts = { - sessionIdle: SESSION_IDLE_DURATION, + sessionIdlePause: SESSION_IDLE_PAUSE_DURATION, + sessionIdleExpire: SESSION_IDLE_EXPIRE_DURATION, maxSessionLife: MAX_SESSION_LIFE, } as const; @@ -423,12 +430,12 @@ export class ReplayContainer implements ReplayContainerInterface { const oldSessionId = this.getSessionId(); // Prevent starting a new session if the last user activity is older than - // SESSION_IDLE_DURATION. Otherwise non-user activity can trigger a new + // SESSION_IDLE_PAUSE_DURATION. Otherwise non-user activity can trigger a new // session+recording. This creates noisy replays that do not have much // content in them. if ( this._lastActivity && - isExpired(this._lastActivity, this.timeouts.sessionIdle) && + isExpired(this._lastActivity, this.timeouts.sessionIdlePause) && this.session && this.session.sampled === 'session' ) { @@ -638,7 +645,7 @@ export class ReplayContainer implements ReplayContainerInterface { const isSessionActive = this.checkAndHandleExpiredSession(); if (!isSessionActive) { - // If the user has come back to the page within SESSION_IDLE_DURATION + // If the user has come back to the page within SESSION_IDLE_PAUSE_DURATION // ms, we will re-use the existing session, otherwise create a new // session __DEBUG_BUILD__ && logger.log('[Replay] Document has become active, but session has expired'); diff --git a/packages/replay/src/types.ts b/packages/replay/src/types.ts index 684e5442ed9c..65dbdc072115 100644 --- a/packages/replay/src/types.ts +++ b/packages/replay/src/types.ts @@ -25,7 +25,8 @@ export interface SendReplayData { } export interface Timeouts { - sessionIdle: number; + sessionIdlePause: number; + sessionIdleExpire: number; maxSessionLife: number; } @@ -455,10 +456,7 @@ export interface ReplayContainer { performanceEvents: AllPerformanceEntry[]; session: Session | undefined; recordingMode: ReplayRecordingMode; - timeouts: { - sessionIdle: number; - maxSessionLife: number; - }; + timeouts: Timeouts; isEnabled(): boolean; isPaused(): boolean; getContext(): InternalEventContext; diff --git a/packages/replay/src/util/addEvent.ts b/packages/replay/src/util/addEvent.ts index b32050665519..4dd2055bb0e7 100644 --- a/packages/replay/src/util/addEvent.ts +++ b/packages/replay/src/util/addEvent.ts @@ -31,7 +31,7 @@ export async function addEvent( // page has been left open and idle for a long period of time and user // comes back to trigger a new session. The performance entries rely on // `performance.timeOrigin`, which is when the page first opened. - if (timestampInMs + replay.timeouts.sessionIdle < Date.now()) { + if (timestampInMs + replay.timeouts.sessionIdlePause < Date.now()) { return null; } diff --git a/packages/replay/src/util/isSessionExpired.ts b/packages/replay/src/util/isSessionExpired.ts index b7025a19cbf6..a51104fd5a47 100644 --- a/packages/replay/src/util/isSessionExpired.ts +++ b/packages/replay/src/util/isSessionExpired.ts @@ -9,7 +9,7 @@ export function isSessionExpired(session: Session, timeouts: Timeouts, targetTim // First, check that maximum session length has not been exceeded isExpired(session.started, timeouts.maxSessionLife, targetTime) || // check that the idle timeout has not been exceeded (i.e. user has - // performed an action within the last `idleTimeout` ms) - isExpired(session.lastActivity, timeouts.sessionIdle, targetTime) + // performed an action within the last `sessionIdleExpire` ms) + isExpired(session.lastActivity, timeouts.sessionIdleExpire, targetTime) ); } diff --git a/packages/replay/test/integration/errorSampleRate.test.ts b/packages/replay/test/integration/errorSampleRate.test.ts index 0b5baa8e2512..03cb35ae0d75 100644 --- a/packages/replay/test/integration/errorSampleRate.test.ts +++ b/packages/replay/test/integration/errorSampleRate.test.ts @@ -5,7 +5,7 @@ import { ERROR_CHECKOUT_TIME, MAX_SESSION_LIFE, REPLAY_SESSION_KEY, - SESSION_IDLE_DURATION, + SESSION_IDLE_EXPIRE_DURATION, WINDOW, } from '../../src/constants'; import type { ReplayContainer } from '../../src/replay'; @@ -252,7 +252,7 @@ describe('Integration | errorSampleRate', () => { }); }); - it('does not send a replay when triggering a full dom snapshot when document becomes visible after [SESSION_IDLE_DURATION]ms', async () => { + it('does not send a replay when triggering a full dom snapshot when document becomes visible after [SESSION_IDLE_EXPIRE_DURATION]ms', async () => { Object.defineProperty(document, 'visibilityState', { configurable: true, get: function () { @@ -260,7 +260,7 @@ describe('Integration | errorSampleRate', () => { }, }); - jest.advanceTimersByTime(SESSION_IDLE_DURATION + 1); + jest.advanceTimersByTime(SESSION_IDLE_EXPIRE_DURATION + 1); document.dispatchEvent(new Event('visibilitychange')); @@ -284,8 +284,8 @@ describe('Integration | errorSampleRate', () => { expect(replay).not.toHaveLastSentReplay(); - // User comes back before `SESSION_IDLE_DURATION` elapses - jest.advanceTimersByTime(SESSION_IDLE_DURATION - 100); + // User comes back before `SESSION_IDLE_EXPIRE_DURATION` elapses + jest.advanceTimersByTime(SESSION_IDLE_EXPIRE_DURATION - 100); Object.defineProperty(document, 'visibilityState', { configurable: true, get: function () { @@ -403,9 +403,9 @@ describe('Integration | errorSampleRate', () => { }); // Should behave the same as above test - it('stops replay if user has been idle for more than SESSION_IDLE_DURATION and does not start a new session thereafter', async () => { + it('stops replay if user has been idle for more than SESSION_IDLE_EXPIRE_DURATION and does not start a new session thereafter', async () => { // Idle for 15 minutes - jest.advanceTimersByTime(SESSION_IDLE_DURATION + 1); + jest.advanceTimersByTime(SESSION_IDLE_EXPIRE_DURATION + 1); const TEST_EVENT = { data: { name: 'lost event' }, @@ -418,7 +418,7 @@ describe('Integration | errorSampleRate', () => { jest.runAllTimers(); await new Promise(process.nextTick); - // We stop recording after SESSION_IDLE_DURATION of inactivity in error mode + // We stop recording after SESSION_IDLE_EXPIRE_DURATION of inactivity in error mode expect(replay).not.toHaveLastSentReplay(); expect(replay.isEnabled()).toBe(false); expect(mockRecord.takeFullSnapshot).not.toHaveBeenCalled(); @@ -544,7 +544,7 @@ describe('Integration | errorSampleRate', () => { expect(replay).not.toHaveLastSentReplay(); // Go idle - jest.advanceTimersByTime(SESSION_IDLE_DURATION + 1); + jest.advanceTimersByTime(SESSION_IDLE_EXPIRE_DURATION + 1); await new Promise(process.nextTick); mockRecord._emitter(TEST_EVENT); diff --git a/packages/replay/test/integration/session.test.ts b/packages/replay/test/integration/session.test.ts index 4720e7b65bc6..b88aa6bd67cc 100644 --- a/packages/replay/test/integration/session.test.ts +++ b/packages/replay/test/integration/session.test.ts @@ -5,7 +5,8 @@ import { DEFAULT_FLUSH_MIN_DELAY, MAX_SESSION_LIFE, REPLAY_SESSION_KEY, - SESSION_IDLE_DURATION, + SESSION_IDLE_EXPIRE_DURATION, + SESSION_IDLE_PAUSE_DURATION, WINDOW, } from '../../src/constants'; import type { ReplayContainer } from '../../src/replay'; @@ -58,7 +59,7 @@ describe('Integration | session', () => { // Require a "user interaction" to start a new session, visibility is not enough. This can be noisy // (e.g. rapidly switching tabs/window focus) and leads to empty sessions. - it('does not create a new session when document becomes visible after [SESSION_IDLE_DURATION]ms', () => { + it('does not create a new session when document becomes visible after [SESSION_IDLE_EXPIRE_DURATION]ms', () => { Object.defineProperty(document, 'visibilityState', { configurable: true, get: function () { @@ -68,7 +69,7 @@ describe('Integration | session', () => { const initialSession = { ...replay.session } as Session; - jest.advanceTimersByTime(SESSION_IDLE_DURATION + 1); + jest.advanceTimersByTime(SESSION_IDLE_EXPIRE_DURATION + 1); document.dispatchEvent(new Event('visibilitychange')); @@ -76,10 +77,10 @@ describe('Integration | session', () => { expect(replay).toHaveSameSession(initialSession); }); - it('does not create a new session when document becomes focused after [SESSION_IDLE_DURATION]ms', () => { + it('does not create a new session when document becomes focused after [SESSION_IDLE_EXPIRE_DURATION]ms', () => { const initialSession = { ...replay.session } as Session; - jest.advanceTimersByTime(SESSION_IDLE_DURATION + 1); + jest.advanceTimersByTime(SESSION_IDLE_EXPIRE_DURATION + 1); WINDOW.dispatchEvent(new Event('focus')); @@ -87,7 +88,7 @@ describe('Integration | session', () => { expect(replay).toHaveSameSession(initialSession); }); - it('does not create a new session if user hides the tab and comes back within [SESSION_IDLE_DURATION] seconds', () => { + it('does not create a new session if user hides the tab and comes back within [SESSION_IDLE_EXPIRE_DURATION] seconds', () => { const initialSession = { ...replay.session } as Session; Object.defineProperty(document, 'visibilityState', { @@ -100,8 +101,8 @@ describe('Integration | session', () => { expect(mockRecord.takeFullSnapshot).not.toHaveBeenCalled(); expect(replay).toHaveSameSession(initialSession); - // User comes back before `SESSION_IDLE_DURATION` elapses - jest.advanceTimersByTime(SESSION_IDLE_DURATION - 1); + // User comes back before `SESSION_IDLE_EXPIRE_DURATION` elapses + jest.advanceTimersByTime(SESSION_IDLE_EXPIRE_DURATION - 1); Object.defineProperty(document, 'visibilityState', { configurable: true, get: function () { @@ -115,7 +116,7 @@ describe('Integration | session', () => { expect(replay).toHaveSameSession(initialSession); }); - it('creates a new session if user has been idle for more than SESSION_IDLE_DURATION and comes back to click their mouse', async () => { + it('creates a new session if user has been idle for more than SESSION_IDLE_EXPIRE_DURATION and comes back to click their mouse', async () => { const initialSession = { ...replay.session } as Session; expect(initialSession?.id).toBeDefined(); @@ -131,7 +132,7 @@ describe('Integration | session', () => { value: new URL(url), }); - const ELAPSED = SESSION_IDLE_DURATION + 1; + const ELAPSED = SESSION_IDLE_EXPIRE_DURATION + 1; jest.advanceTimersByTime(ELAPSED); // Session has become in an idle state @@ -230,6 +231,86 @@ describe('Integration | session', () => { }); }); + it('pauses and resumes a session if user has been idle for more than SESSION_IDLE_PASUE_DURATION and comes back to click their mouse', async () => { + const initialSession = { ...replay.session } as Session; + + expect(initialSession?.id).toBeDefined(); + expect(replay.getContext()).toEqual( + expect.objectContaining({ + initialUrl: 'http://localhost/', + initialTimestamp: BASE_TIMESTAMP, + }), + ); + + const url = 'http://dummy/'; + Object.defineProperty(WINDOW, 'location', { + value: new URL(url), + }); + + const ELAPSED = SESSION_IDLE_PAUSE_DURATION + 1; + jest.advanceTimersByTime(ELAPSED); + + // Session has become in an idle state + // + // This event will put the Replay SDK into a paused state + const TEST_EVENT = { + data: { name: 'lost event' }, + timestamp: BASE_TIMESTAMP, + type: 3, + }; + mockRecord._emitter(TEST_EVENT); + + // performance events can still be collected while recording is stopped + // TODO: we may want to prevent `addEvent` from adding to buffer when user is inactive + replay.addUpdate(() => { + createPerformanceSpans(replay, [ + { + type: 'navigation.navigate' as const, + name: 'foo', + start: BASE_TIMESTAMP + ELAPSED, + end: BASE_TIMESTAMP + ELAPSED + 100, + data: { + decodedBodySize: 1, + encodedBodySize: 2, + duration: 0, + domInteractive: 0, + domContentLoadedEventEnd: 0, + domContentLoadedEventStart: 0, + loadEventStart: 0, + loadEventEnd: 0, + domComplete: 0, + redirectCount: 0, + size: 0, + }, + }, + ]); + return true; + }); + + await new Promise(process.nextTick); + + expect(replay).not.toHaveLastSentReplay(); + expect(replay.isPaused()).toBe(true); + expect(mockRecord.takeFullSnapshot).not.toHaveBeenCalled(); + expect(replay).toHaveSameSession(initialSession); + expect(mockRecord).toHaveBeenCalledTimes(1); + + // Now do a click which will create a new session and start recording again + domHandler({ + name: 'click', + }); + + // Should be same session + expect(replay).toHaveSameSession(initialSession); + + // Replay does not send immediately + expect(replay).not.toHaveLastSentReplay(); + + await advanceTimers(DEFAULT_FLUSH_MIN_DELAY); + + expect(replay).toHaveLastSentReplay(); + }); + it('should have a session after setup', () => { expect(replay.session).toMatchObject({ lastActivity: BASE_TIMESTAMP, diff --git a/packages/replay/test/unit/session/getSession.test.ts b/packages/replay/test/unit/session/getSession.test.ts index 2e7d3ec969b7..be01f0602e51 100644 --- a/packages/replay/test/unit/session/getSession.test.ts +++ b/packages/replay/test/unit/session/getSession.test.ts @@ -1,4 +1,9 @@ -import { MAX_SESSION_LIFE, SESSION_IDLE_DURATION, WINDOW } from '../../../src/constants'; +import { + MAX_SESSION_LIFE, + SESSION_IDLE_EXPIRE_DURATION, + SESSION_IDLE_PAUSE_DURATION, + WINDOW, +} from '../../../src/constants'; import * as CreateSession from '../../../src/session/createSession'; import * as FetchSession from '../../../src/session/fetchSession'; import { getSession } from '../../../src/session/getSession'; @@ -43,7 +48,8 @@ describe('Unit | session | getSession', () => { it('creates a non-sticky session when one does not exist', function () { const { session } = getSession({ timeouts: { - sessionIdle: SESSION_IDLE_DURATION, + sessionIdlePause: SESSION_IDLE_PAUSE_DURATION, + sessionIdleExpire: SESSION_IDLE_EXPIRE_DURATION, maxSessionLife: MAX_SESSION_LIFE, }, stickySession: false, @@ -70,7 +76,8 @@ describe('Unit | session | getSession', () => { const { session } = getSession({ timeouts: { - sessionIdle: 1000, + sessionIdlePause: SESSION_IDLE_PAUSE_DURATION, + sessionIdleExpire: 1000, maxSessionLife: MAX_SESSION_LIFE, }, stickySession: false, @@ -86,7 +93,8 @@ describe('Unit | session | getSession', () => { it('creates a non-sticky session, when one is expired', function () { const { session } = getSession({ timeouts: { - sessionIdle: 1000, + sessionIdlePause: SESSION_IDLE_PAUSE_DURATION, + sessionIdleExpire: 1000, maxSessionLife: MAX_SESSION_LIFE, }, stickySession: false, @@ -112,7 +120,8 @@ describe('Unit | session | getSession', () => { const { session } = getSession({ timeouts: { - sessionIdle: SESSION_IDLE_DURATION, + sessionIdlePause: SESSION_IDLE_PAUSE_DURATION, + sessionIdleExpire: SESSION_IDLE_EXPIRE_DURATION, maxSessionLife: MAX_SESSION_LIFE, }, stickySession: true, @@ -147,7 +156,8 @@ describe('Unit | session | getSession', () => { const { session } = getSession({ timeouts: { - sessionIdle: 1000, + sessionIdlePause: SESSION_IDLE_PAUSE_DURATION, + sessionIdleExpire: 1000, maxSessionLife: MAX_SESSION_LIFE, }, stickySession: true, @@ -173,7 +183,8 @@ describe('Unit | session | getSession', () => { const { session } = getSession({ timeouts: { - sessionIdle: 1000, + sessionIdlePause: SESSION_IDLE_PAUSE_DURATION, + sessionIdleExpire: 1000, maxSessionLife: MAX_SESSION_LIFE, }, stickySession: true, @@ -192,7 +203,8 @@ describe('Unit | session | getSession', () => { it('fetches a non-expired non-sticky session', function () { const { session } = getSession({ timeouts: { - sessionIdle: 1000, + sessionIdlePause: SESSION_IDLE_PAUSE_DURATION, + sessionIdleExpire: 1000, maxSessionLife: MAX_SESSION_LIFE, }, stickySession: false, diff --git a/packages/replay/test/unit/util/isSessionExpired.test.ts b/packages/replay/test/unit/util/isSessionExpired.test.ts index 627105d322f0..38b24056d36f 100644 --- a/packages/replay/test/unit/util/isSessionExpired.test.ts +++ b/packages/replay/test/unit/util/isSessionExpired.test.ts @@ -1,4 +1,4 @@ -import { MAX_SESSION_LIFE } from '../../../src/constants'; +import { MAX_SESSION_LIFE, SESSION_IDLE_PAUSE_DURATION } from '../../../src/constants'; import { makeSession } from '../../../src/session/Session'; import { isSessionExpired } from '../../../src/util/isSessionExpired'; @@ -15,14 +15,28 @@ function createSession(extra?: Record) { describe('Unit | util | isSessionExpired', () => { it('session last activity is older than expiry time', function () { - expect(isSessionExpired(createSession(), { maxSessionLife: MAX_SESSION_LIFE, sessionIdle: 100 }, 200)).toBe(true); // Session expired at ts = 100 + expect( + isSessionExpired( + createSession(), + { + maxSessionLife: MAX_SESSION_LIFE, + sessionIdlePause: SESSION_IDLE_PAUSE_DURATION, + sessionIdleExpire: 100, + }, + 200, + ), + ).toBe(true); // Session expired at ts = 100 }); it('session last activity is not older than expiry time', function () { expect( isSessionExpired( createSession({ lastActivity: 100 }), - { maxSessionLife: MAX_SESSION_LIFE, sessionIdle: 150 }, + { + maxSessionLife: MAX_SESSION_LIFE, + sessionIdlePause: SESSION_IDLE_PAUSE_DURATION, + sessionIdleExpire: 150, + }, 200, ), ).toBe(false); // Session expires at ts >= 250 @@ -30,13 +44,29 @@ describe('Unit | util | isSessionExpired', () => { it('session age is not older than max session life', function () { expect( - isSessionExpired(createSession(), { maxSessionLife: MAX_SESSION_LIFE, sessionIdle: 1_800_000 }, 50_000), + isSessionExpired( + createSession(), + { + maxSessionLife: MAX_SESSION_LIFE, + sessionIdlePause: SESSION_IDLE_PAUSE_DURATION, + sessionIdleExpire: 1_800_000, + }, + 50_000, + ), ).toBe(false); }); it('session age is older than max session life', function () { expect( - isSessionExpired(createSession(), { maxSessionLife: MAX_SESSION_LIFE, sessionIdle: 1_800_000 }, 1_800_001), + isSessionExpired( + createSession(), + { + maxSessionLife: MAX_SESSION_LIFE, + sessionIdlePause: SESSION_IDLE_PAUSE_DURATION, + sessionIdleExpire: 1_800_000, + }, + 1_800_001, + ), ).toBe(true); // Session expires at ts >= 1_800_000 }); });