1- import { mockClock , getSessionState } from '../../../../test'
1+ import { ExperimentalFeature } from '../../../tools/experimentalFeatures'
2+ import { mockClock , getSessionState , registerCleanupTask , mockExperimentalFeatures } from '../../../../test'
23import { setCookie , deleteCookie , getCookie , getCurrentSite } from '../../../browser/cookie'
34import type { SessionState } from '../sessionState'
4- import type { Configuration } from '../../configuration'
5+ import { validateAndBuildConfiguration } from '../../configuration'
6+ import type { InitConfiguration } from '../../configuration'
57import { SESSION_COOKIE_EXPIRATION_DELAY , SESSION_EXPIRATION_DELAY , SESSION_TIME_OUT_DELAY } from '../sessionConstants'
68import { buildCookieOptions , selectCookieStrategy , initCookieStrategy } from './sessionInCookie'
7- import type { SessionStoreStrategy } from './sessionStoreStrategy'
89import { SESSION_STORE_KEY } from './sessionStoreStrategy'
910
10- export const DEFAULT_INIT_CONFIGURATION = { trackAnonymousUser : true } as Configuration
11- describe ( 'session in cookie strategy' , ( ) => {
12- const sessionState : SessionState = { id : '123' , created : '0' }
13- let cookieStorageStrategy : SessionStoreStrategy
11+ const DEFAULT_INIT_CONFIGURATION = { clientToken : 'abc' , trackAnonymousUser : true }
1412
15- beforeEach ( ( ) => {
16- cookieStorageStrategy = initCookieStrategy ( DEFAULT_INIT_CONFIGURATION , { } )
17- } )
13+ function setupCookieStrategy ( partialInitConfiguration : Partial < InitConfiguration > = { } ) {
14+ const initConfiguration = {
15+ ...DEFAULT_INIT_CONFIGURATION ,
16+ ...partialInitConfiguration ,
17+ } as InitConfiguration
1818
19- afterEach ( ( ) => {
20- deleteCookie ( SESSION_STORE_KEY )
21- } )
19+ const configuration = validateAndBuildConfiguration ( initConfiguration ) !
20+ const cookieOptions = buildCookieOptions ( initConfiguration ) !
21+
22+ registerCleanupTask ( ( ) => deleteCookie ( SESSION_STORE_KEY , cookieOptions ) )
23+
24+ return initCookieStrategy ( configuration , cookieOptions )
25+ }
26+
27+ describe ( 'session in cookie strategy' , ( ) => {
28+ const sessionState : SessionState = { id : '123' , created : '0' }
2229
2330 it ( 'should persist a session in a cookie' , ( ) => {
31+ const cookieStorageStrategy = setupCookieStrategy ( )
2432 cookieStorageStrategy . persistSession ( sessionState )
2533 const session = cookieStorageStrategy . retrieveSession ( )
2634 expect ( session ) . toEqual ( { ...sessionState } )
2735 expect ( getCookie ( SESSION_STORE_KEY ) ) . toBe ( 'id=123&created=0' )
2836 } )
2937
3038 it ( 'should set `isExpired=1` and `aid` to the cookie holding the session' , ( ) => {
39+ const cookieStorageStrategy = setupCookieStrategy ( )
3140 spyOn ( Math , 'random' ) . and . callFake ( ( ) => 0 )
3241 cookieStorageStrategy . persistSession ( sessionState )
3342 cookieStorageStrategy . expireSession ( sessionState )
@@ -37,6 +46,7 @@ describe('session in cookie strategy', () => {
3746 } )
3847
3948 it ( 'should return an empty object if session string is invalid' , ( ) => {
49+ const cookieStorageStrategy = setupCookieStrategy ( )
4050 setCookie ( SESSION_STORE_KEY , '{test:42}' , 1000 )
4151 const session = cookieStorageStrategy . retrieveSession ( )
4252 expect ( session ) . toEqual ( { } )
@@ -101,37 +111,69 @@ describe('session in cookie strategy', () => {
101111 } )
102112 } )
103113 } )
114+
115+ describe ( 'encode cookie options' , ( ) => {
116+ beforeEach ( ( ) => {
117+ mockExperimentalFeatures ( [ ExperimentalFeature . ENCODE_COOKIE_OPTIONS ] )
118+ } )
119+
120+ it ( 'should encode cookie options in the cookie value' , ( ) => {
121+ // Some older browsers don't support partitioned cross-site session cookies
122+ // so instead of testing the cookie value, we test the call to the cookie setter
123+ const cookieSetSpy = spyOnProperty ( document , 'cookie' , 'set' )
124+ const cookieStorageStrategy = setupCookieStrategy ( { usePartitionedCrossSiteSessionCookie : true } )
125+ cookieStorageStrategy . persistSession ( { id : '123' } )
126+
127+ const calls = cookieSetSpy . calls . all ( )
128+ const lastCall = calls [ calls . length - 1 ]
129+ expect ( lastCall . args [ 0 ] ) . toMatch ( / ^ _ d d _ s = i d = 1 2 3 & c = 1 / )
130+ } )
131+
132+ it ( 'should not encode cookie options in the cookie value if the session is empty (deleting the cookie)' , ( ) => {
133+ const cookieStorageStrategy = setupCookieStrategy ( { usePartitionedCrossSiteSessionCookie : true } )
134+ cookieStorageStrategy . persistSession ( { } )
135+
136+ expect ( getCookie ( SESSION_STORE_KEY ) ) . toBeUndefined ( )
137+ } )
138+
139+ it ( 'should return the correct session state from the cookies' , ( ) => {
140+ spyOnProperty ( document , 'cookie' , 'get' ) . and . returnValue ( '_dd_s=id=123&c=0;_dd_s=id=456&c=1;_dd_s=id=789&c=2' )
141+ const cookieStorageStrategy = setupCookieStrategy ( { usePartitionedCrossSiteSessionCookie : true } )
142+
143+ expect ( cookieStorageStrategy . retrieveSession ( ) ) . toEqual ( { id : '456' } )
144+ } )
145+
146+ it ( 'should return the session state from the first cookie if there is no match' , ( ) => {
147+ spyOnProperty ( document , 'cookie' , 'get' ) . and . returnValue ( '_dd_s=id=123&c=0;_dd_s=id=789&c=2' )
148+ const cookieStorageStrategy = setupCookieStrategy ( { usePartitionedCrossSiteSessionCookie : true } )
149+
150+ expect ( cookieStorageStrategy . retrieveSession ( ) ) . toEqual ( { id : '123' } )
151+ } )
152+ } )
104153} )
105154
106155describe ( 'session in cookie strategy when opt-in anonymous user tracking' , ( ) => {
107156 const anonymousId = 'device-123'
108157 const sessionState : SessionState = { id : '123' , created : '0' }
109- let cookieStorageStrategy : SessionStoreStrategy
110- beforeEach ( ( ) => {
111- cookieStorageStrategy = initCookieStrategy (
112- { ...DEFAULT_INIT_CONFIGURATION , trackAnonymousUser : true } as Configuration ,
113- { }
114- )
115- } )
116158
117- afterEach ( ( ) => {
118- deleteCookie ( SESSION_STORE_KEY )
119- } )
120159 it ( 'should persist with anonymous id' , ( ) => {
160+ const cookieStorageStrategy = setupCookieStrategy ( )
121161 cookieStorageStrategy . persistSession ( { ...sessionState , anonymousId } )
122162 const session = cookieStorageStrategy . retrieveSession ( )
123163 expect ( session ) . toEqual ( { ...sessionState , anonymousId } )
124164 expect ( getCookie ( SESSION_STORE_KEY ) ) . toBe ( 'id=123&created=0&aid=device-123' )
125165 } )
126166
127167 it ( 'should expire with anonymous id' , ( ) => {
168+ const cookieStorageStrategy = setupCookieStrategy ( )
128169 cookieStorageStrategy . expireSession ( { ...sessionState , anonymousId } )
129170 const session = cookieStorageStrategy . retrieveSession ( )
130171 expect ( session ) . toEqual ( { isExpired : '1' , anonymousId } )
131172 expect ( getCookie ( SESSION_STORE_KEY ) ) . toBe ( 'isExpired=1&aid=device-123' )
132173 } )
133174
134175 it ( 'should persist for one year when opt-in' , ( ) => {
176+ const cookieStorageStrategy = setupCookieStrategy ( )
135177 const cookieSetSpy = spyOnProperty ( document , 'cookie' , 'set' )
136178 const clock = mockClock ( )
137179 cookieStorageStrategy . persistSession ( { ...sessionState , anonymousId } )
@@ -141,6 +183,7 @@ describe('session in cookie strategy when opt-in anonymous user tracking', () =>
141183 } )
142184
143185 it ( 'should expire in one year when opt-in' , ( ) => {
186+ const cookieStorageStrategy = setupCookieStrategy ( )
144187 const cookieSetSpy = spyOnProperty ( document , 'cookie' , 'set' )
145188 const clock = mockClock ( )
146189 cookieStorageStrategy . expireSession ( { ...sessionState , anonymousId } )
@@ -153,30 +196,24 @@ describe('session in cookie strategy when opt-in anonymous user tracking', () =>
153196describe ( 'session in cookie strategy when opt-out anonymous user tracking' , ( ) => {
154197 const anonymousId = 'device-123'
155198 const sessionState : SessionState = { id : '123' , created : '0' }
156- let cookieStorageStrategy : SessionStoreStrategy
157-
158- beforeEach ( ( ) => {
159- cookieStorageStrategy = initCookieStrategy ( { trackAnonymousUser : false } as Configuration , { } )
160- } )
161-
162- afterEach ( ( ) => {
163- deleteCookie ( SESSION_STORE_KEY )
164- } )
165199
166200 it ( 'should not extend cookie expiration time when opt-out' , ( ) => {
201+ const cookieStorageStrategy = setupCookieStrategy ( { trackAnonymousUser : false } )
167202 const cookieSetSpy = spyOnProperty ( document , 'cookie' , 'set' )
168203 const clock = mockClock ( )
169204 cookieStorageStrategy . expireSession ( { ...sessionState , anonymousId } )
170205 expect ( cookieSetSpy . calls . argsFor ( 0 ) [ 0 ] ) . toContain ( new Date ( clock . timeStamp ( SESSION_TIME_OUT_DELAY ) ) . toUTCString ( ) )
171206 } )
172207
173208 it ( 'should not persist with one year when opt-out' , ( ) => {
209+ const cookieStorageStrategy = setupCookieStrategy ( { trackAnonymousUser : false } )
174210 const cookieSetSpy = spyOnProperty ( document , 'cookie' , 'set' )
175211 cookieStorageStrategy . persistSession ( { ...sessionState , anonymousId } )
176212 expect ( cookieSetSpy . calls . argsFor ( 0 ) [ 0 ] ) . toContain ( new Date ( Date . now ( ) + SESSION_EXPIRATION_DELAY ) . toUTCString ( ) )
177213 } )
178214
179215 it ( 'should not persist or expire a session with anonymous id when opt-out' , ( ) => {
216+ const cookieStorageStrategy = setupCookieStrategy ( { trackAnonymousUser : false } )
180217 cookieStorageStrategy . persistSession ( { ...sessionState , anonymousId } )
181218 cookieStorageStrategy . expireSession ( { ...sessionState , anonymousId } )
182219 const session = cookieStorageStrategy . retrieveSession ( )
0 commit comments