@openobserve/browser-core 0.3.3 → 0.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cjs/boot/init.js +1 -1
- package/cjs/domain/configuration/endpointBuilder.js +1 -1
- package/cjs/domain/tags.js +1 -1
- package/cjs/domain/telemetry/telemetry.js +1 -1
- package/cjs/domain/telemetry/telemetryEvent.types.d.ts +1 -1
- package/esm/boot/init.js +1 -1
- package/esm/domain/configuration/endpointBuilder.js +1 -1
- package/esm/domain/tags.js +1 -1
- package/esm/domain/telemetry/telemetry.js +1 -1
- package/esm/domain/telemetry/telemetryEvent.types.d.ts +1 -1
- package/package.json +2 -2
- package/src/boot/displayAlreadyInitializedError.spec.ts +18 -0
- package/src/boot/init.spec.ts +50 -0
- package/src/browser/addEventListener.spec.ts +141 -0
- package/src/browser/cookie.spec.ts +53 -0
- package/src/browser/fetchObservable.spec.ts +334 -0
- package/src/browser/pageMayExitObservable.spec.ts +56 -0
- package/src/browser/xhrObservable.spec.ts +405 -0
- package/src/domain/allowedTrackingOrigins.spec.ts +210 -0
- package/src/domain/bufferedData.spec.ts +34 -0
- package/src/domain/configuration/configuration.spec.ts +236 -0
- package/src/domain/configuration/endpointBuilder.spec.ts +173 -0
- package/src/domain/configuration/transportConfiguration.spec.ts +130 -0
- package/src/domain/connectivity/connectivity.spec.ts +50 -0
- package/src/domain/console/consoleObservable.spec.ts +151 -0
- package/src/domain/context/contextManager.spec.ts +152 -0
- package/src/domain/context/contextUtils.spec.ts +26 -0
- package/src/domain/context/storeContextManager.spec.ts +103 -0
- package/src/domain/contexts/accountContext.spec.ts +99 -0
- package/src/domain/contexts/globalContext.spec.ts +88 -0
- package/src/domain/contexts/userContext.spec.ts +156 -0
- package/src/domain/error/error.spec.ts +294 -0
- package/src/domain/error/trackRuntimeError.spec.ts +343 -0
- package/src/domain/eventRateLimiter/createEventRateLimiter.spec.ts +119 -0
- package/src/domain/extension/extensionUtils.spec.ts +47 -0
- package/src/domain/report/reportObservable.spec.ts +71 -0
- package/src/domain/session/oldCookiesMigration.spec.ts +76 -0
- package/src/domain/session/sessionManager.spec.ts +684 -0
- package/src/domain/session/sessionState.spec.ts +88 -0
- package/src/domain/session/sessionStore.spec.ts +647 -0
- package/src/domain/session/sessionStoreOperations.spec.ts +234 -0
- package/src/domain/session/storeStrategies/sessionInCookie.spec.ts +228 -0
- package/src/domain/session/storeStrategies/sessionInLocalStorage.spec.ts +75 -0
- package/src/domain/synthetics/syntheticsWorkerValues.spec.ts +82 -0
- package/src/domain/tags.spec.ts +74 -0
- package/src/domain/telemetry/telemetry.spec.ts +494 -0
- package/src/domain/telemetry/telemetryEvent.types.ts +1 -1
- package/src/domain/trackingConsent.spec.ts +44 -0
- package/src/tools/abstractHooks.spec.ts +76 -0
- package/src/tools/abstractLifeCycle.spec.ts +46 -0
- package/src/tools/boundedBuffer.spec.ts +40 -0
- package/src/tools/catchUserErrors.spec.ts +20 -0
- package/src/tools/encoder.spec.ts +112 -0
- package/src/tools/experimentalFeatures.spec.ts +60 -0
- package/src/tools/getZoneJsOriginalValue.spec.ts +36 -0
- package/src/tools/instrumentMethod.spec.ts +381 -0
- package/src/tools/matchOption.spec.ts +41 -0
- package/src/tools/mergeInto.spec.ts +198 -0
- package/src/tools/monitor.spec.ts +172 -0
- package/src/tools/observable.spec.ts +259 -0
- package/src/tools/queueMicrotask.spec.ts +24 -0
- package/src/tools/readBytesFromStream.spec.ts +62 -0
- package/src/tools/requestIdleCallback.spec.ts +65 -0
- package/src/tools/serialisation/jsonStringify.spec.ts +76 -0
- package/src/tools/serialisation/sanitize.spec.ts +284 -0
- package/src/tools/stackTrace/capturedExceptions.specHelper.ts +316 -0
- package/src/tools/stackTrace/computeStackTrace.spec.ts +1007 -0
- package/src/tools/stackTrace/handlingStack.spec.ts +20 -0
- package/src/tools/taskQueue.spec.ts +60 -0
- package/src/tools/timer.spec.ts +76 -0
- package/src/tools/utils/browserDetection.spec.ts +120 -0
- package/src/tools/utils/byteUtils.spec.ts +29 -0
- package/src/tools/utils/functionUtils.spec.ts +229 -0
- package/src/tools/utils/numberUtils.spec.ts +27 -0
- package/src/tools/utils/stringUtils.spec.ts +74 -0
- package/src/tools/utils/typeUtils.spec.ts +25 -0
- package/src/tools/utils/urlPolyfill.spec.ts +55 -0
- package/src/tools/valueHistory.spec.ts +180 -0
- package/src/transport/batch.spec.ts +261 -0
- package/src/transport/eventBridge.spec.ts +90 -0
- package/src/transport/flushController.spec.ts +267 -0
- package/src/transport/httpRequest.spec.ts +400 -0
- package/src/transport/sendWithRetryStrategy.spec.ts +393 -0
|
@@ -0,0 +1,647 @@
|
|
|
1
|
+
import type { Clock } from '../../../test'
|
|
2
|
+
import { mockClock, createFakeSessionStoreStrategy } from '../../../test'
|
|
3
|
+
import type { InitConfiguration, Configuration } from '../configuration'
|
|
4
|
+
import { display } from '../../tools/display'
|
|
5
|
+
import type { SessionStore } from './sessionStore'
|
|
6
|
+
import { STORAGE_POLL_DELAY, startSessionStore, selectSessionStoreStrategyType } from './sessionStore'
|
|
7
|
+
import {
|
|
8
|
+
SESSION_EXPIRATION_DELAY,
|
|
9
|
+
SESSION_NOT_TRACKED,
|
|
10
|
+
SESSION_TIME_OUT_DELAY,
|
|
11
|
+
SessionPersistence,
|
|
12
|
+
} from './sessionConstants'
|
|
13
|
+
import type { SessionState } from './sessionState'
|
|
14
|
+
|
|
15
|
+
const enum FakeTrackingType {
|
|
16
|
+
TRACKED = 'tracked',
|
|
17
|
+
NOT_TRACKED = SESSION_NOT_TRACKED,
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const PRODUCT_KEY = 'product'
|
|
21
|
+
const FIRST_ID = 'first'
|
|
22
|
+
const SECOND_ID = 'second'
|
|
23
|
+
const IS_EXPIRED = '1'
|
|
24
|
+
const DEFAULT_INIT_CONFIGURATION: InitConfiguration = { clientToken: 'abc' }
|
|
25
|
+
const DEFAULT_CONFIGURATION = { trackAnonymousUser: true } as Configuration
|
|
26
|
+
|
|
27
|
+
const EMPTY_SESSION_STATE: SessionState = {}
|
|
28
|
+
|
|
29
|
+
function createSessionState(
|
|
30
|
+
trackingType: FakeTrackingType = FakeTrackingType.TRACKED,
|
|
31
|
+
id?: string,
|
|
32
|
+
expire?: number
|
|
33
|
+
): SessionState {
|
|
34
|
+
return {
|
|
35
|
+
[PRODUCT_KEY]: trackingType,
|
|
36
|
+
created: `${Date.now()}`,
|
|
37
|
+
expire: `${expire || Date.now() + SESSION_EXPIRATION_DELAY}`,
|
|
38
|
+
...(id ? { id } : {}),
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
let sessionStoreStrategy: ReturnType<typeof createFakeSessionStoreStrategy>
|
|
43
|
+
|
|
44
|
+
function getSessionStoreState(): SessionState {
|
|
45
|
+
return sessionStoreStrategy.retrieveSession()
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function expectTrackedSessionToBeInStore(id?: string) {
|
|
49
|
+
expect(getSessionStoreState().id).toEqual(id ? id : jasmine.any(String))
|
|
50
|
+
expect(getSessionStoreState().isExpired).toBeUndefined()
|
|
51
|
+
expect(getSessionStoreState()[PRODUCT_KEY]).toEqual(FakeTrackingType.TRACKED)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function expectNotTrackedSessionToBeInStore() {
|
|
55
|
+
expect(getSessionStoreState().id).toBeUndefined()
|
|
56
|
+
expect(getSessionStoreState().isExpired).toBeUndefined()
|
|
57
|
+
expect(getSessionStoreState()[PRODUCT_KEY]).toEqual(FakeTrackingType.NOT_TRACKED)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function expectSessionToBeExpiredInStore() {
|
|
61
|
+
expect(getSessionStoreState().isExpired).toEqual(IS_EXPIRED)
|
|
62
|
+
expect(getSessionStoreState().id).toBeUndefined()
|
|
63
|
+
expect(getSessionStoreState()[PRODUCT_KEY]).toBeUndefined()
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function getStoreExpiration() {
|
|
67
|
+
return getSessionStoreState().expire
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function resetSessionInStore() {
|
|
71
|
+
sessionStoreStrategy.expireSession()
|
|
72
|
+
sessionStoreStrategy.expireSession.calls.reset()
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function setSessionInStore(sessionState: SessionState) {
|
|
76
|
+
sessionStoreStrategy.persistSession(sessionState)
|
|
77
|
+
sessionStoreStrategy.persistSession.calls.reset()
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
describe('session store', () => {
|
|
81
|
+
describe('selectSessionStoreStrategyType', () => {
|
|
82
|
+
describe('sessionPersistence: cookie (default)', () => {
|
|
83
|
+
it('returns cookie strategy when cookies are available', () => {
|
|
84
|
+
const sessionStoreStrategyType = selectSessionStoreStrategyType(DEFAULT_INIT_CONFIGURATION)
|
|
85
|
+
expect(sessionStoreStrategyType).toEqual(jasmine.objectContaining({ type: SessionPersistence.COOKIE }))
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
it('returns undefined when cookies are not available', () => {
|
|
89
|
+
disableCookies()
|
|
90
|
+
const sessionStoreStrategyType = selectSessionStoreStrategyType(DEFAULT_INIT_CONFIGURATION)
|
|
91
|
+
expect(sessionStoreStrategyType).toBeUndefined()
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
it('returns cookie strategy when sessionPersistence is cookie', () => {
|
|
95
|
+
const sessionStoreStrategyType = selectSessionStoreStrategyType({
|
|
96
|
+
...DEFAULT_INIT_CONFIGURATION,
|
|
97
|
+
sessionPersistence: SessionPersistence.COOKIE,
|
|
98
|
+
})
|
|
99
|
+
expect(sessionStoreStrategyType).toEqual(jasmine.objectContaining({ type: SessionPersistence.COOKIE }))
|
|
100
|
+
})
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
describe('sessionPersistence: local-storage', () => {
|
|
104
|
+
it('returns local storage strategy when sessionPersistence is local storage', () => {
|
|
105
|
+
const sessionStoreStrategyType = selectSessionStoreStrategyType({
|
|
106
|
+
...DEFAULT_INIT_CONFIGURATION,
|
|
107
|
+
sessionPersistence: SessionPersistence.LOCAL_STORAGE,
|
|
108
|
+
})
|
|
109
|
+
expect(sessionStoreStrategyType).toEqual(jasmine.objectContaining({ type: SessionPersistence.LOCAL_STORAGE }))
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
it('returns undefined when local storage is not available', () => {
|
|
113
|
+
disableLocalStorage()
|
|
114
|
+
const sessionStoreStrategyType = selectSessionStoreStrategyType({
|
|
115
|
+
...DEFAULT_INIT_CONFIGURATION,
|
|
116
|
+
sessionPersistence: SessionPersistence.LOCAL_STORAGE,
|
|
117
|
+
})
|
|
118
|
+
expect(sessionStoreStrategyType).toBeUndefined()
|
|
119
|
+
})
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
it('returns undefined when sessionPersistence is invalid', () => {
|
|
123
|
+
const displayErrorSpy = spyOn(display, 'error')
|
|
124
|
+
|
|
125
|
+
const sessionStoreStrategyType = selectSessionStoreStrategyType({
|
|
126
|
+
...DEFAULT_INIT_CONFIGURATION,
|
|
127
|
+
sessionPersistence: 'invalid' as SessionPersistence,
|
|
128
|
+
})
|
|
129
|
+
expect(sessionStoreStrategyType).toBeUndefined()
|
|
130
|
+
expect(displayErrorSpy).toHaveBeenCalledOnceWith("Invalid session persistence 'invalid'")
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
describe('allowFallbackToLocalStorage (deprecated)', () => {
|
|
134
|
+
it('should return a type cookie when cookies are available', () => {
|
|
135
|
+
const sessionStoreStrategyType = selectSessionStoreStrategyType({
|
|
136
|
+
...DEFAULT_INIT_CONFIGURATION,
|
|
137
|
+
allowFallbackToLocalStorage: true,
|
|
138
|
+
})
|
|
139
|
+
expect(sessionStoreStrategyType).toEqual(jasmine.objectContaining({ type: SessionPersistence.COOKIE }))
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
it('should report undefined when cookies are not available, and fallback is not allowed', () => {
|
|
143
|
+
disableCookies()
|
|
144
|
+
const sessionStoreStrategyType = selectSessionStoreStrategyType({
|
|
145
|
+
...DEFAULT_INIT_CONFIGURATION,
|
|
146
|
+
allowFallbackToLocalStorage: false,
|
|
147
|
+
})
|
|
148
|
+
expect(sessionStoreStrategyType).toBeUndefined()
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
it('should fallback to localStorage when cookies are not available', () => {
|
|
152
|
+
disableCookies()
|
|
153
|
+
const sessionStoreStrategyType = selectSessionStoreStrategyType({
|
|
154
|
+
...DEFAULT_INIT_CONFIGURATION,
|
|
155
|
+
allowFallbackToLocalStorage: true,
|
|
156
|
+
})
|
|
157
|
+
expect(sessionStoreStrategyType).toEqual({ type: SessionPersistence.LOCAL_STORAGE })
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
it('should report undefined when no storage is available', () => {
|
|
161
|
+
disableLocalStorage()
|
|
162
|
+
disableCookies()
|
|
163
|
+
const sessionStoreStrategyType = selectSessionStoreStrategyType({
|
|
164
|
+
...DEFAULT_INIT_CONFIGURATION,
|
|
165
|
+
allowFallbackToLocalStorage: true,
|
|
166
|
+
})
|
|
167
|
+
expect(sessionStoreStrategyType).toBeUndefined()
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
it('does not fallback to localStorage when sessionPersistence is set to cookie', () => {
|
|
171
|
+
disableCookies()
|
|
172
|
+
const sessionStoreStrategyType = selectSessionStoreStrategyType({
|
|
173
|
+
...DEFAULT_INIT_CONFIGURATION,
|
|
174
|
+
sessionPersistence: SessionPersistence.COOKIE,
|
|
175
|
+
allowFallbackToLocalStorage: true,
|
|
176
|
+
})
|
|
177
|
+
expect(sessionStoreStrategyType).toBeUndefined()
|
|
178
|
+
})
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
function disableCookies() {
|
|
182
|
+
spyOnProperty(document, 'cookie', 'get').and.returnValue('')
|
|
183
|
+
}
|
|
184
|
+
function disableLocalStorage() {
|
|
185
|
+
spyOn(Storage.prototype, 'getItem').and.throwError('unavailable')
|
|
186
|
+
}
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
describe('session lifecyle mechanism', () => {
|
|
190
|
+
let expireSpy: () => void
|
|
191
|
+
let renewSpy: () => void
|
|
192
|
+
let sessionStoreManager: SessionStore
|
|
193
|
+
let clock: Clock
|
|
194
|
+
|
|
195
|
+
function setupSessionStore(
|
|
196
|
+
initialState: SessionState = {},
|
|
197
|
+
computeTrackingType: (rawTrackingType?: string) => FakeTrackingType = () => FakeTrackingType.TRACKED
|
|
198
|
+
) {
|
|
199
|
+
const sessionStoreStrategyType = selectSessionStoreStrategyType(DEFAULT_INIT_CONFIGURATION)
|
|
200
|
+
if (sessionStoreStrategyType?.type !== SessionPersistence.COOKIE) {
|
|
201
|
+
fail('Unable to initialize cookie storage')
|
|
202
|
+
return
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
sessionStoreStrategy = createFakeSessionStoreStrategy({ isLockEnabled: true, initialSession: initialState })
|
|
206
|
+
|
|
207
|
+
sessionStoreManager = startSessionStore(
|
|
208
|
+
sessionStoreStrategyType,
|
|
209
|
+
DEFAULT_CONFIGURATION,
|
|
210
|
+
PRODUCT_KEY,
|
|
211
|
+
computeTrackingType,
|
|
212
|
+
sessionStoreStrategy
|
|
213
|
+
)
|
|
214
|
+
sessionStoreStrategy.persistSession.calls.reset()
|
|
215
|
+
sessionStoreManager.expireObservable.subscribe(expireSpy)
|
|
216
|
+
sessionStoreManager.renewObservable.subscribe(renewSpy)
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
beforeEach(() => {
|
|
220
|
+
expireSpy = jasmine.createSpy('expire session')
|
|
221
|
+
renewSpy = jasmine.createSpy('renew session')
|
|
222
|
+
clock = mockClock()
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
afterEach(() => {
|
|
226
|
+
resetSessionInStore()
|
|
227
|
+
sessionStoreManager.stop()
|
|
228
|
+
})
|
|
229
|
+
|
|
230
|
+
describe('initialize session', () => {
|
|
231
|
+
it('when session not in store, should initialize a new session', () => {
|
|
232
|
+
setupSessionStore()
|
|
233
|
+
expect(sessionStoreManager.getSession().isExpired).toEqual(IS_EXPIRED)
|
|
234
|
+
expect(sessionStoreManager.getSession().anonymousId).toEqual(jasmine.any(String))
|
|
235
|
+
})
|
|
236
|
+
|
|
237
|
+
it('when tracked session in store, should do nothing ', () => {
|
|
238
|
+
setupSessionStore(createSessionState(FakeTrackingType.TRACKED, FIRST_ID))
|
|
239
|
+
|
|
240
|
+
expect(sessionStoreManager.getSession().id).toBe(FIRST_ID)
|
|
241
|
+
expect(sessionStoreManager.getSession().isExpired).toBeUndefined()
|
|
242
|
+
expect(sessionStoreManager.getSession()[PRODUCT_KEY]).toBeDefined()
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
it('when not tracked session in store, should do nothing ', () => {
|
|
246
|
+
setupSessionStore(createSessionState(FakeTrackingType.NOT_TRACKED))
|
|
247
|
+
|
|
248
|
+
expect(sessionStoreManager.getSession().id).toBeUndefined()
|
|
249
|
+
expect(sessionStoreManager.getSession().isExpired).toBeUndefined()
|
|
250
|
+
expect(sessionStoreManager.getSession()[PRODUCT_KEY]).toBeDefined()
|
|
251
|
+
})
|
|
252
|
+
|
|
253
|
+
it('should generate an anonymousId if not present', () => {
|
|
254
|
+
setupSessionStore()
|
|
255
|
+
expect(sessionStoreManager.getSession().anonymousId).toBeDefined()
|
|
256
|
+
})
|
|
257
|
+
})
|
|
258
|
+
|
|
259
|
+
describe('expand or renew session', () => {
|
|
260
|
+
it(
|
|
261
|
+
'when session not in cache, session not in store and new session tracked, ' +
|
|
262
|
+
'should create new session and trigger renew session ',
|
|
263
|
+
() => {
|
|
264
|
+
setupSessionStore()
|
|
265
|
+
|
|
266
|
+
sessionStoreManager.expandOrRenewSession()
|
|
267
|
+
|
|
268
|
+
expect(sessionStoreManager.getSession().id).toBeDefined()
|
|
269
|
+
expectTrackedSessionToBeInStore()
|
|
270
|
+
expect(expireSpy).not.toHaveBeenCalled()
|
|
271
|
+
expect(renewSpy).toHaveBeenCalledTimes(1)
|
|
272
|
+
}
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
it(
|
|
276
|
+
'when session not in cache, session not in store and new session not tracked, ' +
|
|
277
|
+
'should store not tracked session and trigger renew session',
|
|
278
|
+
() => {
|
|
279
|
+
setupSessionStore(EMPTY_SESSION_STATE, () => FakeTrackingType.NOT_TRACKED)
|
|
280
|
+
|
|
281
|
+
sessionStoreManager.expandOrRenewSession()
|
|
282
|
+
|
|
283
|
+
expect(sessionStoreManager.getSession().id).toBeUndefined()
|
|
284
|
+
expectNotTrackedSessionToBeInStore()
|
|
285
|
+
expect(expireSpy).not.toHaveBeenCalled()
|
|
286
|
+
expect(renewSpy).toHaveBeenCalledTimes(1)
|
|
287
|
+
}
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
it('when session not in cache and session in store, should expand session and trigger renew session', () => {
|
|
291
|
+
setupSessionStore()
|
|
292
|
+
setSessionInStore(createSessionState(FakeTrackingType.TRACKED, FIRST_ID))
|
|
293
|
+
|
|
294
|
+
sessionStoreManager.expandOrRenewSession()
|
|
295
|
+
|
|
296
|
+
expect(sessionStoreManager.getSession().id).toBe(FIRST_ID)
|
|
297
|
+
expectTrackedSessionToBeInStore(FIRST_ID)
|
|
298
|
+
expect(expireSpy).not.toHaveBeenCalled()
|
|
299
|
+
expect(renewSpy).toHaveBeenCalledTimes(1)
|
|
300
|
+
})
|
|
301
|
+
|
|
302
|
+
it(
|
|
303
|
+
'when session in cache, session not in store and new session tracked, ' +
|
|
304
|
+
'should expire session, create a new one and trigger renew session',
|
|
305
|
+
() => {
|
|
306
|
+
setupSessionStore(createSessionState(FakeTrackingType.TRACKED, FIRST_ID))
|
|
307
|
+
resetSessionInStore()
|
|
308
|
+
|
|
309
|
+
sessionStoreManager.expandOrRenewSession()
|
|
310
|
+
|
|
311
|
+
const sessionId = sessionStoreManager.getSession().id
|
|
312
|
+
expect(sessionId).toBeDefined()
|
|
313
|
+
expect(sessionId).not.toBe(FIRST_ID)
|
|
314
|
+
expectTrackedSessionToBeInStore(sessionId)
|
|
315
|
+
expect(expireSpy).toHaveBeenCalled()
|
|
316
|
+
expect(renewSpy).toHaveBeenCalledTimes(1)
|
|
317
|
+
}
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
it(
|
|
321
|
+
'when session in cache, session not in store and new session not tracked, ' +
|
|
322
|
+
'should expire session, store not tracked session and trigger renew session',
|
|
323
|
+
() => {
|
|
324
|
+
setupSessionStore(createSessionState(FakeTrackingType.TRACKED, FIRST_ID), () => FakeTrackingType.NOT_TRACKED)
|
|
325
|
+
resetSessionInStore()
|
|
326
|
+
|
|
327
|
+
sessionStoreManager.expandOrRenewSession()
|
|
328
|
+
|
|
329
|
+
expect(sessionStoreManager.getSession().id).toBeUndefined()
|
|
330
|
+
expect(sessionStoreManager.getSession()[PRODUCT_KEY]).toBeDefined()
|
|
331
|
+
expectNotTrackedSessionToBeInStore()
|
|
332
|
+
expect(expireSpy).toHaveBeenCalled()
|
|
333
|
+
expect(renewSpy).toHaveBeenCalledTimes(1)
|
|
334
|
+
}
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
it(
|
|
338
|
+
'when session not tracked in cache, session not in store and new session not tracked, ' +
|
|
339
|
+
'should expire session, store not tracked session and trigger renew session',
|
|
340
|
+
() => {
|
|
341
|
+
setupSessionStore(createSessionState(FakeTrackingType.NOT_TRACKED), () => FakeTrackingType.NOT_TRACKED)
|
|
342
|
+
resetSessionInStore()
|
|
343
|
+
|
|
344
|
+
sessionStoreManager.expandOrRenewSession()
|
|
345
|
+
|
|
346
|
+
expect(sessionStoreManager.getSession().id).toBeUndefined()
|
|
347
|
+
expect(sessionStoreManager.getSession()[PRODUCT_KEY]).toBeDefined()
|
|
348
|
+
expectNotTrackedSessionToBeInStore()
|
|
349
|
+
expect(expireSpy).toHaveBeenCalled()
|
|
350
|
+
expect(renewSpy).toHaveBeenCalledTimes(1)
|
|
351
|
+
}
|
|
352
|
+
)
|
|
353
|
+
|
|
354
|
+
it('when session in cache is same session than in store, should expand session', () => {
|
|
355
|
+
setupSessionStore(createSessionState(FakeTrackingType.TRACKED, FIRST_ID))
|
|
356
|
+
|
|
357
|
+
clock.tick(10)
|
|
358
|
+
sessionStoreManager.expandOrRenewSession()
|
|
359
|
+
|
|
360
|
+
expect(sessionStoreManager.getSession().id).toBe(FIRST_ID)
|
|
361
|
+
expect(sessionStoreManager.getSession().expire).toBe(getStoreExpiration())
|
|
362
|
+
expectTrackedSessionToBeInStore(FIRST_ID)
|
|
363
|
+
expect(expireSpy).not.toHaveBeenCalled()
|
|
364
|
+
expect(renewSpy).not.toHaveBeenCalled()
|
|
365
|
+
})
|
|
366
|
+
|
|
367
|
+
it(
|
|
368
|
+
'when session in cache is different session than in store and store session is tracked, ' +
|
|
369
|
+
'should expire session, expand store session and trigger renew',
|
|
370
|
+
() => {
|
|
371
|
+
setupSessionStore(createSessionState(FakeTrackingType.TRACKED, FIRST_ID))
|
|
372
|
+
setSessionInStore(createSessionState(FakeTrackingType.TRACKED, SECOND_ID))
|
|
373
|
+
|
|
374
|
+
sessionStoreManager.expandOrRenewSession()
|
|
375
|
+
|
|
376
|
+
expect(sessionStoreManager.getSession().id).toBe(SECOND_ID)
|
|
377
|
+
expectTrackedSessionToBeInStore(SECOND_ID)
|
|
378
|
+
expect(expireSpy).toHaveBeenCalled()
|
|
379
|
+
expect(renewSpy).toHaveBeenCalledTimes(1)
|
|
380
|
+
}
|
|
381
|
+
)
|
|
382
|
+
|
|
383
|
+
it(
|
|
384
|
+
'when session in cache is different session than in store and store session is not tracked, ' +
|
|
385
|
+
'should expire session, store not tracked session and trigger renew',
|
|
386
|
+
() => {
|
|
387
|
+
setupSessionStore(createSessionState(FakeTrackingType.TRACKED, FIRST_ID), (rawTrackingType) =>
|
|
388
|
+
rawTrackingType === FakeTrackingType.TRACKED ? FakeTrackingType.TRACKED : FakeTrackingType.NOT_TRACKED
|
|
389
|
+
)
|
|
390
|
+
setSessionInStore(createSessionState(FakeTrackingType.NOT_TRACKED, ''))
|
|
391
|
+
|
|
392
|
+
sessionStoreManager.expandOrRenewSession()
|
|
393
|
+
|
|
394
|
+
expect(sessionStoreManager.getSession().id).toBeUndefined()
|
|
395
|
+
expectNotTrackedSessionToBeInStore()
|
|
396
|
+
expect(expireSpy).toHaveBeenCalled()
|
|
397
|
+
expect(renewSpy).toHaveBeenCalledTimes(1)
|
|
398
|
+
}
|
|
399
|
+
)
|
|
400
|
+
|
|
401
|
+
it('when throttled, expandOrRenewSession() should not renew the session if expire() is called right after', () => {
|
|
402
|
+
setupSessionStore(createSessionState(FakeTrackingType.TRACKED, FIRST_ID))
|
|
403
|
+
|
|
404
|
+
// The first call is not throttled (leading execution)
|
|
405
|
+
sessionStoreManager.expandOrRenewSession()
|
|
406
|
+
|
|
407
|
+
sessionStoreManager.expandOrRenewSession()
|
|
408
|
+
sessionStoreManager.expire()
|
|
409
|
+
|
|
410
|
+
clock.tick(STORAGE_POLL_DELAY)
|
|
411
|
+
|
|
412
|
+
expectSessionToBeExpiredInStore()
|
|
413
|
+
expect(sessionStoreManager.getSession().id).toBeUndefined()
|
|
414
|
+
expect(renewSpy).not.toHaveBeenCalled()
|
|
415
|
+
})
|
|
416
|
+
})
|
|
417
|
+
|
|
418
|
+
describe('expand session', () => {
|
|
419
|
+
it('when session not in cache and session not in store, should do nothing', () => {
|
|
420
|
+
setupSessionStore()
|
|
421
|
+
|
|
422
|
+
sessionStoreManager.expandSession()
|
|
423
|
+
|
|
424
|
+
expect(sessionStoreManager.getSession().id).toBeUndefined()
|
|
425
|
+
expect(expireSpy).not.toHaveBeenCalled()
|
|
426
|
+
})
|
|
427
|
+
|
|
428
|
+
it('when session not in cache and session in store, should do nothing', () => {
|
|
429
|
+
setupSessionStore()
|
|
430
|
+
setSessionInStore(createSessionState(FakeTrackingType.TRACKED, FIRST_ID))
|
|
431
|
+
|
|
432
|
+
sessionStoreManager.expandSession()
|
|
433
|
+
|
|
434
|
+
expect(sessionStoreManager.getSession().id).toBeUndefined()
|
|
435
|
+
expect(expireSpy).not.toHaveBeenCalled()
|
|
436
|
+
})
|
|
437
|
+
|
|
438
|
+
it('when session in cache and session not in store, should expire session', () => {
|
|
439
|
+
setupSessionStore(createSessionState(FakeTrackingType.TRACKED, FIRST_ID))
|
|
440
|
+
resetSessionInStore()
|
|
441
|
+
|
|
442
|
+
sessionStoreManager.expandSession()
|
|
443
|
+
|
|
444
|
+
expectSessionToBeExpiredInStore()
|
|
445
|
+
expect(sessionStoreManager.getSession().id).toBeUndefined()
|
|
446
|
+
expect(expireSpy).toHaveBeenCalled()
|
|
447
|
+
})
|
|
448
|
+
|
|
449
|
+
it('when session in cache is same session than in store, should expand session', () => {
|
|
450
|
+
setupSessionStore(createSessionState(FakeTrackingType.TRACKED, FIRST_ID))
|
|
451
|
+
|
|
452
|
+
clock.tick(10)
|
|
453
|
+
sessionStoreManager.expandSession()
|
|
454
|
+
|
|
455
|
+
expect(sessionStoreManager.getSession().id).toBe(FIRST_ID)
|
|
456
|
+
expect(sessionStoreManager.getSession().expire).toBe(getStoreExpiration())
|
|
457
|
+
expect(expireSpy).not.toHaveBeenCalled()
|
|
458
|
+
})
|
|
459
|
+
|
|
460
|
+
it('when session in cache is different session than in store, should expire session', () => {
|
|
461
|
+
setupSessionStore(createSessionState(FakeTrackingType.TRACKED, FIRST_ID))
|
|
462
|
+
setSessionInStore(createSessionState(FakeTrackingType.TRACKED, SECOND_ID))
|
|
463
|
+
|
|
464
|
+
sessionStoreManager.expandSession()
|
|
465
|
+
|
|
466
|
+
expect(sessionStoreManager.getSession().id).toBeUndefined()
|
|
467
|
+
expectTrackedSessionToBeInStore(SECOND_ID)
|
|
468
|
+
expect(expireSpy).toHaveBeenCalled()
|
|
469
|
+
})
|
|
470
|
+
})
|
|
471
|
+
|
|
472
|
+
describe('regular watch', () => {
|
|
473
|
+
it('when session not in cache and session not in store, should store the expired session', () => {
|
|
474
|
+
setupSessionStore()
|
|
475
|
+
|
|
476
|
+
clock.tick(STORAGE_POLL_DELAY)
|
|
477
|
+
|
|
478
|
+
expectSessionToBeExpiredInStore()
|
|
479
|
+
expect(sessionStoreManager.getSession().id).toBeUndefined()
|
|
480
|
+
expect(expireSpy).not.toHaveBeenCalled()
|
|
481
|
+
expect(sessionStoreStrategy.persistSession).toHaveBeenCalled()
|
|
482
|
+
})
|
|
483
|
+
|
|
484
|
+
it('when session in cache and session not in store, should expire session', () => {
|
|
485
|
+
setupSessionStore(createSessionState(FakeTrackingType.TRACKED, FIRST_ID))
|
|
486
|
+
resetSessionInStore()
|
|
487
|
+
|
|
488
|
+
clock.tick(STORAGE_POLL_DELAY)
|
|
489
|
+
|
|
490
|
+
expect(sessionStoreManager.getSession().id).toBeUndefined()
|
|
491
|
+
expectSessionToBeExpiredInStore()
|
|
492
|
+
expect(expireSpy).toHaveBeenCalled()
|
|
493
|
+
expect(sessionStoreStrategy.persistSession).toHaveBeenCalled()
|
|
494
|
+
})
|
|
495
|
+
|
|
496
|
+
it('when session not in cache and session in store, should do nothing', () => {
|
|
497
|
+
setupSessionStore()
|
|
498
|
+
setSessionInStore(createSessionState(FakeTrackingType.TRACKED, FIRST_ID))
|
|
499
|
+
|
|
500
|
+
clock.tick(STORAGE_POLL_DELAY)
|
|
501
|
+
|
|
502
|
+
expect(sessionStoreManager.getSession().id).toBeUndefined()
|
|
503
|
+
expect(expireSpy).not.toHaveBeenCalled()
|
|
504
|
+
expect(sessionStoreStrategy.persistSession).not.toHaveBeenCalled()
|
|
505
|
+
})
|
|
506
|
+
|
|
507
|
+
it('when session in cache is same session than in store, should synchronize session', () => {
|
|
508
|
+
setupSessionStore(createSessionState(FakeTrackingType.TRACKED, FIRST_ID))
|
|
509
|
+
setSessionInStore(
|
|
510
|
+
createSessionState(FakeTrackingType.TRACKED, FIRST_ID, Date.now() + SESSION_TIME_OUT_DELAY + 10)
|
|
511
|
+
)
|
|
512
|
+
|
|
513
|
+
clock.tick(STORAGE_POLL_DELAY)
|
|
514
|
+
|
|
515
|
+
expect(sessionStoreManager.getSession().id).toBe(FIRST_ID)
|
|
516
|
+
expect(sessionStoreManager.getSession().expire).toBe(getStoreExpiration())
|
|
517
|
+
expect(expireSpy).not.toHaveBeenCalled()
|
|
518
|
+
expect(sessionStoreStrategy.persistSession).not.toHaveBeenCalled()
|
|
519
|
+
})
|
|
520
|
+
|
|
521
|
+
it('when session id in cache is different than session id in store, should expire session and not touch the store', () => {
|
|
522
|
+
setupSessionStore(createSessionState(FakeTrackingType.TRACKED, FIRST_ID))
|
|
523
|
+
setSessionInStore(createSessionState(FakeTrackingType.TRACKED, SECOND_ID))
|
|
524
|
+
|
|
525
|
+
clock.tick(STORAGE_POLL_DELAY)
|
|
526
|
+
|
|
527
|
+
expect(sessionStoreManager.getSession().id).toBeUndefined()
|
|
528
|
+
expect(expireSpy).toHaveBeenCalled()
|
|
529
|
+
expect(sessionStoreStrategy.persistSession).not.toHaveBeenCalled()
|
|
530
|
+
})
|
|
531
|
+
|
|
532
|
+
it('when session in store is expired first and then get updated by another tab, should expire session in cache and not touch the store', () => {
|
|
533
|
+
setupSessionStore(createSessionState(FakeTrackingType.TRACKED, FIRST_ID))
|
|
534
|
+
resetSessionInStore()
|
|
535
|
+
|
|
536
|
+
// Simulate a new session being written to the store by another tab during the watch.
|
|
537
|
+
// Watch is reading the cookie twice so we need to plan the write of the cookie at the right index
|
|
538
|
+
sessionStoreStrategy.planRetrieveSession(1, createSessionState(FakeTrackingType.TRACKED, SECOND_ID))
|
|
539
|
+
|
|
540
|
+
clock.tick(STORAGE_POLL_DELAY)
|
|
541
|
+
|
|
542
|
+
// expires session in cache
|
|
543
|
+
expect(sessionStoreManager.getSession().id).toBeUndefined()
|
|
544
|
+
expect(expireSpy).toHaveBeenCalled()
|
|
545
|
+
|
|
546
|
+
// Does not touch the store
|
|
547
|
+
// The two calls to persist session are for the lock management, these can be ignored
|
|
548
|
+
expect(sessionStoreStrategy.persistSession).toHaveBeenCalledTimes(2)
|
|
549
|
+
expect(sessionStoreStrategy.expireSession).not.toHaveBeenCalled()
|
|
550
|
+
})
|
|
551
|
+
|
|
552
|
+
it('when session type in cache is different than session type in store, should expire session and not touch the store', () => {
|
|
553
|
+
setupSessionStore(createSessionState(FakeTrackingType.NOT_TRACKED, FIRST_ID))
|
|
554
|
+
setSessionInStore(createSessionState(FakeTrackingType.TRACKED, FIRST_ID))
|
|
555
|
+
|
|
556
|
+
clock.tick(STORAGE_POLL_DELAY)
|
|
557
|
+
|
|
558
|
+
expect(sessionStoreManager.getSession().id).toBeUndefined()
|
|
559
|
+
expect(expireSpy).toHaveBeenCalled()
|
|
560
|
+
expect(sessionStoreStrategy.persistSession).not.toHaveBeenCalled()
|
|
561
|
+
})
|
|
562
|
+
})
|
|
563
|
+
|
|
564
|
+
describe('reinitialize session', () => {
|
|
565
|
+
it('when session not in store, should reinitialize the store', () => {
|
|
566
|
+
setupSessionStore()
|
|
567
|
+
|
|
568
|
+
sessionStoreManager.restartSession()
|
|
569
|
+
|
|
570
|
+
expect(sessionStoreManager.getSession().isExpired).toEqual(IS_EXPIRED)
|
|
571
|
+
expect(sessionStoreManager.getSession().anonymousId).toEqual(jasmine.any(String))
|
|
572
|
+
})
|
|
573
|
+
|
|
574
|
+
it('when session in store, should do nothing', () => {
|
|
575
|
+
setupSessionStore(createSessionState(FakeTrackingType.TRACKED, FIRST_ID))
|
|
576
|
+
|
|
577
|
+
sessionStoreManager.restartSession()
|
|
578
|
+
|
|
579
|
+
expect(sessionStoreManager.getSession().id).toBe(FIRST_ID)
|
|
580
|
+
expect(sessionStoreManager.getSession().isExpired).toBeUndefined()
|
|
581
|
+
})
|
|
582
|
+
|
|
583
|
+
it('restart session should generate an anonymousId if not present', () => {
|
|
584
|
+
setupSessionStore()
|
|
585
|
+
sessionStoreManager.restartSession()
|
|
586
|
+
expect(sessionStoreManager.getSession().anonymousId).toBeDefined()
|
|
587
|
+
})
|
|
588
|
+
})
|
|
589
|
+
})
|
|
590
|
+
|
|
591
|
+
describe('session update and synchronisation', () => {
|
|
592
|
+
let updateSpy: jasmine.Spy<jasmine.Func>
|
|
593
|
+
let otherUpdateSpy: jasmine.Spy<jasmine.Func>
|
|
594
|
+
let clock: Clock
|
|
595
|
+
|
|
596
|
+
function setupSessionStore(initialState: SessionState = {}, updateSpy: () => void) {
|
|
597
|
+
const computeTrackingType: (rawTrackingType?: string) => FakeTrackingType = () => FakeTrackingType.TRACKED
|
|
598
|
+
const sessionStoreStrategyType = selectSessionStoreStrategyType(DEFAULT_INIT_CONFIGURATION)
|
|
599
|
+
|
|
600
|
+
sessionStoreStrategy = createFakeSessionStoreStrategy({ isLockEnabled: true, initialSession: initialState })
|
|
601
|
+
|
|
602
|
+
const sessionStoreManager = startSessionStore(
|
|
603
|
+
sessionStoreStrategyType!,
|
|
604
|
+
DEFAULT_CONFIGURATION,
|
|
605
|
+
PRODUCT_KEY,
|
|
606
|
+
computeTrackingType,
|
|
607
|
+
sessionStoreStrategy
|
|
608
|
+
)
|
|
609
|
+
sessionStoreManager.sessionStateUpdateObservable.subscribe(updateSpy)
|
|
610
|
+
|
|
611
|
+
return sessionStoreManager
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
let sessionStoreManager: SessionStore
|
|
615
|
+
let otherSessionStoreManager: SessionStore
|
|
616
|
+
|
|
617
|
+
beforeEach(() => {
|
|
618
|
+
updateSpy = jasmine.createSpy()
|
|
619
|
+
otherUpdateSpy = jasmine.createSpy()
|
|
620
|
+
clock = mockClock()
|
|
621
|
+
})
|
|
622
|
+
|
|
623
|
+
afterEach(() => {
|
|
624
|
+
resetSessionInStore()
|
|
625
|
+
sessionStoreManager.stop()
|
|
626
|
+
otherSessionStoreManager.stop()
|
|
627
|
+
})
|
|
628
|
+
|
|
629
|
+
it('should synchronise all stores and notify update observables of all stores', () => {
|
|
630
|
+
const initialState = createSessionState(FakeTrackingType.TRACKED, FIRST_ID)
|
|
631
|
+
sessionStoreManager = setupSessionStore(initialState, updateSpy)
|
|
632
|
+
otherSessionStoreManager = setupSessionStore(initialState, otherUpdateSpy)
|
|
633
|
+
|
|
634
|
+
sessionStoreManager.updateSessionState({ extra: 'extra' })
|
|
635
|
+
|
|
636
|
+
expect(updateSpy).toHaveBeenCalledTimes(1)
|
|
637
|
+
|
|
638
|
+
const callArgs = updateSpy.calls.argsFor(0)[0]
|
|
639
|
+
expect(callArgs!.previousState.extra).toBeUndefined()
|
|
640
|
+
expect(callArgs.newState.extra).toBe('extra')
|
|
641
|
+
|
|
642
|
+
// Need to wait until watch is triggered
|
|
643
|
+
clock.tick(STORAGE_POLL_DELAY)
|
|
644
|
+
expect(otherUpdateSpy).toHaveBeenCalled()
|
|
645
|
+
})
|
|
646
|
+
})
|
|
647
|
+
})
|