11import type { Clock } from '../../test'
22import { collectAsyncCalls , mockClock , registerCleanupTask , replaceMockable } from '../../test'
33import type { Configuration } from '../domain/configuration'
4+ import { display } from '../tools/display'
45import { detectVersion , isChromium } from '../tools/utils/browserDetection'
56import { dateNow , ONE_MINUTE } from '../tools/utils/timeUtils'
67import type { CookieOptions } from './cookie'
78import { deleteCookie , getCookie , setCookie } from './cookie'
89import type { CookieStoreWindow } from './browser.types'
9- import { createCookieAccess , WATCH_COOKIE_INTERVAL_DELAY } from './cookieAccess'
10+ import type { CookieAccess } from './cookieAccess'
11+ import {
12+ areCookiesAuthorized ,
13+ createCookieStoreAccess ,
14+ createDocumentCookieAccess ,
15+ WATCH_COOKIE_INTERVAL_DELAY ,
16+ } from './cookieAccess'
1017
1118const COOKIE_NAME = 'test_cookie'
1219const COOKIE_OPTIONS = { secure : false , crossSite : false , partitioned : false }
@@ -18,6 +25,7 @@ function disableCookieStore() {
1825
1926interface setupResult {
2027 clock : Clock
28+ createCookieAccess : ( name : string , options : CookieOptions ) => CookieAccess
2129 flushObservable : ( spy : jasmine . Spy ) => Promise < void >
2230 setCookieWithCleanup : (
2331 this : void ,
@@ -38,6 +46,7 @@ describe('cookieAccess', () => {
3846
3947 return {
4048 clock,
49+ createCookieAccess : ( name : string , options : CookieOptions ) => createDocumentCookieAccess ( name , options ) ,
4150 flushObservable ( this : void , _spy : jasmine . Spy ) {
4251 clock . tick ( WATCH_COOKIE_INTERVAL_DELAY )
4352 return Promise . resolve ( )
@@ -68,6 +77,8 @@ describe('cookieAccess', () => {
6877
6978 return {
7079 clock,
80+ createCookieAccess : ( name : string , options : CookieOptions ) =>
81+ createCookieStoreAccess ( name , options , MOCK_CONFIGURATION ) ,
7182 async flushObservable ( this : void , spy : jasmine . Spy ) {
7283 await collectAsyncCalls ( spy , 1 )
7384 // Reset the spy calls to avoid throwing on unexpected calls during teardown
@@ -107,10 +118,10 @@ describe('cookieAccess', () => {
107118 describe ( title , ( ) => {
108119 describe ( 'getAllAndSet' , ( ) => {
109120 it ( 'should pass current cookie values to callback' , async ( ) => {
110- const { setCookieWithCleanup } = setup ( )
121+ const { createCookieAccess , setCookieWithCleanup } = setup ( )
111122 await setCookieWithCleanup ( COOKIE_NAME , 'value1' , ONE_MINUTE )
112123
113- const cookieAccess = createCookieAccess ( COOKIE_NAME , MOCK_CONFIGURATION , COOKIE_OPTIONS )
124+ const cookieAccess = createCookieAccess ( COOKIE_NAME , COOKIE_OPTIONS )
114125
115126 let capturedValues : string [ ] | undefined
116127 await cookieAccess . getAllAndSet ( ( values ) => {
@@ -122,8 +133,8 @@ describe('cookieAccess', () => {
122133 } )
123134
124135 it ( 'should pass empty array when cookie does not exist' , async ( ) => {
125- setup ( )
126- const cookieAccess = createCookieAccess ( COOKIE_NAME , MOCK_CONFIGURATION , COOKIE_OPTIONS )
136+ const { createCookieAccess } = setup ( )
137+ const cookieAccess = createCookieAccess ( COOKIE_NAME , COOKIE_OPTIONS )
127138
128139 let capturedValues : string [ ] | undefined
129140 await cookieAccess . getAllAndSet ( ( values ) => {
@@ -135,8 +146,8 @@ describe('cookieAccess', () => {
135146 } )
136147
137148 it ( 'should write the value returned by the callback' , async ( ) => {
138- setup ( )
139- const cookieAccess = createCookieAccess ( COOKIE_NAME , MOCK_CONFIGURATION , COOKIE_OPTIONS )
149+ const { createCookieAccess } = setup ( )
150+ const cookieAccess = createCookieAccess ( COOKIE_NAME , COOKIE_OPTIONS )
140151
141152 await cookieAccess . getAllAndSet ( ( ) => ( { value : 'hello' , expireDelay : ONE_MINUTE } ) )
142153
@@ -149,11 +160,11 @@ describe('cookieAccess', () => {
149160 pending ( 'Only Recent Chromium supports multiple cookies with the same name with different options' )
150161 }
151162
152- const { setCookieWithCleanup } = setup ( )
163+ const { createCookieAccess , setCookieWithCleanup } = setup ( )
153164 await setCookieWithCleanup ( COOKIE_NAME , 'value1' , ONE_MINUTE )
154165 await setCookieWithCleanup ( COOKIE_NAME , 'value2' , ONE_MINUTE , { secure : true , partitioned : true } )
155166
156- const cookieAccess = createCookieAccess ( COOKIE_NAME , MOCK_CONFIGURATION , COOKIE_OPTIONS )
167+ const cookieAccess = createCookieAccess ( COOKIE_NAME , COOKIE_OPTIONS )
157168
158169 let capturedValues : string [ ] | undefined
159170 await cookieAccess . getAllAndSet ( ( values ) => {
@@ -167,8 +178,8 @@ describe('cookieAccess', () => {
167178
168179 describe ( 'observable' , ( ) => {
169180 it ( 'should notify when cookie is changed externally' , async ( ) => {
170- const { flushObservable, setCookieWithCleanup } = setup ( )
171- const cookieAccess = createCookieAccess ( COOKIE_NAME , MOCK_CONFIGURATION , COOKIE_OPTIONS )
181+ const { createCookieAccess , flushObservable, setCookieWithCleanup } = setup ( )
182+ const cookieAccess = createCookieAccess ( COOKIE_NAME , COOKIE_OPTIONS )
172183 const spy = jasmine . createSpy ( 'observer' )
173184 const subscription = cookieAccess . observable . subscribe ( spy )
174185 registerCleanupTask ( ( ) => subscription . unsubscribe ( ) )
@@ -180,10 +191,10 @@ describe('cookieAccess', () => {
180191 } )
181192
182193 it ( 'should notify when cookie is deleted externally' , async ( ) => {
183- const { flushObservable, setCookieWithCleanup } = setup ( )
194+ const { createCookieAccess , flushObservable, setCookieWithCleanup } = setup ( )
184195 await setCookieWithCleanup ( COOKIE_NAME , 'existing' , ONE_MINUTE )
185196
186- const cookieAccess = createCookieAccess ( COOKIE_NAME , MOCK_CONFIGURATION , COOKIE_OPTIONS )
197+ const cookieAccess = createCookieAccess ( COOKIE_NAME , COOKIE_OPTIONS )
187198 const spy = jasmine . createSpy ( 'observer' )
188199 const subscription = cookieAccess . observable . subscribe ( spy )
189200 registerCleanupTask ( ( ) => subscription . unsubscribe ( ) )
@@ -195,10 +206,10 @@ describe('cookieAccess', () => {
195206 } )
196207
197208 it ( 'should not notify when cookie value is unchanged' , async ( ) => {
198- const { clock, setCookieWithCleanup } = setup ( )
209+ const { createCookieAccess , clock, setCookieWithCleanup } = setup ( )
199210 await setCookieWithCleanup ( COOKIE_NAME , 'stable' , ONE_MINUTE )
200211
201- const cookieAccess = createCookieAccess ( COOKIE_NAME , MOCK_CONFIGURATION , COOKIE_OPTIONS )
212+ const cookieAccess = createCookieAccess ( COOKIE_NAME , COOKIE_OPTIONS )
202213 const spy = jasmine . createSpy ( 'observer' )
203214 const subscription = cookieAccess . observable . subscribe ( spy )
204215 registerCleanupTask ( ( ) => subscription . unsubscribe ( ) )
@@ -209,8 +220,8 @@ describe('cookieAccess', () => {
209220 } )
210221
211222 it ( 'should notify the observable after writing' , async ( ) => {
212- const { flushObservable } = setup ( )
213- const cookieAccess = createCookieAccess ( COOKIE_NAME , MOCK_CONFIGURATION , COOKIE_OPTIONS )
223+ const { createCookieAccess , flushObservable } = setup ( )
224+ const cookieAccess = createCookieAccess ( COOKIE_NAME , COOKIE_OPTIONS )
214225 const spy = jasmine . createSpy ( 'observer' )
215226 const subscription = cookieAccess . observable . subscribe ( spy )
216227 registerCleanupTask ( ( ) => subscription . unsubscribe ( ) )
@@ -223,4 +234,85 @@ describe('cookieAccess', () => {
223234 } )
224235 } )
225236 }
237+
238+ describe ( 'areCookiesAuthorized' , ( ) => {
239+ it ( 'returns true when the access can write and read back the test cookie' , async ( ) => {
240+ const access : CookieAccess = {
241+ getAll : jasmine . createSpy ( 'getAll' ) . and . returnValue ( Promise . resolve ( [ 'test' ] ) ) ,
242+ getAllAndSet : jasmine . createSpy ( 'getAllAndSet' ) . and . returnValue ( Promise . resolve ( ) ) ,
243+ observable : null as any ,
244+ }
245+ const factory = jasmine . createSpy ( 'factory' ) . and . returnValue ( access )
246+
247+ const result = await areCookiesAuthorized ( factory , COOKIE_OPTIONS , MOCK_CONFIGURATION )
248+
249+ expect ( result ) . toBe ( true )
250+ expect ( factory ) . toHaveBeenCalledWith ( jasmine . any ( String ) , COOKIE_OPTIONS , MOCK_CONFIGURATION )
251+ } )
252+
253+ it ( 'returns false when the access cannot read back the test cookie' , async ( ) => {
254+ const access : CookieAccess = {
255+ getAll : ( ) => Promise . resolve ( [ ] ) ,
256+ getAllAndSet : ( ) => Promise . resolve ( ) ,
257+ observable : null as any ,
258+ }
259+
260+ const result = await areCookiesAuthorized ( ( ) => access , COOKIE_OPTIONS , MOCK_CONFIGURATION )
261+
262+ expect ( result ) . toBe ( false )
263+ } )
264+
265+ it ( 'returns false and logs when the access throws' , async ( ) => {
266+ const displayErrorSpy = spyOn ( display , 'error' )
267+ const access : CookieAccess = {
268+ getAll : ( ) => Promise . resolve ( [ ] ) ,
269+ getAllAndSet : ( ) => Promise . reject ( new Error ( 'boom' ) ) ,
270+ observable : null as any ,
271+ }
272+
273+ const result = await areCookiesAuthorized ( ( ) => access , COOKIE_OPTIONS , MOCK_CONFIGURATION )
274+
275+ expect ( result ) . toBe ( false )
276+ expect ( displayErrorSpy ) . toHaveBeenCalled ( )
277+ } )
278+
279+ it ( 'cleans up the test cookie after the check' , async ( ) => {
280+ const calls : Array < { value : string ; expireDelay : number } > = [ ]
281+ const access : CookieAccess = {
282+ getAll : ( ) => Promise . resolve ( [ 'test' ] ) ,
283+ getAllAndSet : ( cb ) => {
284+ calls . push ( cb ( [ ] ) )
285+ return Promise . resolve ( )
286+ } ,
287+ observable : null as any ,
288+ }
289+
290+ await areCookiesAuthorized ( ( ) => access , COOKIE_OPTIONS , MOCK_CONFIGURATION )
291+
292+ expect ( calls ) . toEqual ( [
293+ { value : 'test' , expireDelay : jasmine . any ( Number ) as unknown as number } ,
294+ { value : '' , expireDelay : 0 } ,
295+ ] )
296+ } )
297+
298+ it ( 'works with the real createDocumentCookieAccess' , async ( ) => {
299+ disableCookieStore ( )
300+ const result = await areCookiesAuthorized ( createDocumentCookieAccess , COOKIE_OPTIONS , MOCK_CONFIGURATION )
301+ expect ( result ) . toBe ( true )
302+ } )
303+
304+ it ( 'works with the real createCookieStoreAccess' , async ( ) => {
305+ if ( ! ( window as CookieStoreWindow ) . cookieStore ) {
306+ pending ( 'CookieStore API not available' )
307+ }
308+ const result = await areCookiesAuthorized ( createCookieStoreAccess , COOKIE_OPTIONS , MOCK_CONFIGURATION )
309+ expect ( result ) . toBe ( true )
310+ } )
311+
312+ it ( 'returns false when document.cookie is empty' , async ( ) => {
313+ spyOnProperty ( document , 'cookie' , 'get' ) . and . returnValue ( '' )
314+ const result = await areCookiesAuthorized ( createDocumentCookieAccess , COOKIE_OPTIONS , MOCK_CONFIGURATION )
315+ expect ( result ) . toBe ( false )
316+ } )
317+ } )
226318} )
0 commit comments