@@ -5,9 +5,41 @@ async function loadStorageModule() {
55 return import ( "../src/storage" ) ;
66}
77
8+ const storageDescriptors = {
9+ localStorage : Object . getOwnPropertyDescriptor ( globalThis , "localStorage" ) ,
10+ sessionStorage : Object . getOwnPropertyDescriptor ( globalThis , "sessionStorage" ) ,
11+ } ;
12+
13+ function restoreStorageDescriptor ( name : "localStorage" | "sessionStorage" ) {
14+ const descriptor = storageDescriptors [ name ] ;
15+ if ( descriptor ) {
16+ Object . defineProperty ( globalThis , name , descriptor ) ;
17+ } else {
18+ delete ( globalThis as Record < string , unknown > ) [ name ] ;
19+ }
20+ }
21+
22+ function defineThrowingStorage (
23+ name : "localStorage" | "sessionStorage" ,
24+ errorName = "SecurityError" ,
25+ ) {
26+ Object . defineProperty ( globalThis , name , {
27+ configurable : true ,
28+ get ( ) {
29+ const error = new Error ( "Access is denied for this document." ) ;
30+ error . name = errorName ;
31+ throw error ;
32+ } ,
33+ } ) ;
34+ }
35+
836describe ( "storage adapters" , ( ) => {
937 afterEach ( ( ) => {
1038 vi . unstubAllGlobals ( ) ;
39+ restoreStorageDescriptor ( "localStorage" ) ;
40+ restoreStorageDescriptor ( "sessionStorage" ) ;
41+ localStorage . clear ( ) ;
42+ sessionStorage . clear ( ) ;
1143 } ) ;
1244
1345 it ( "noop adapter ignores writes" , async ( ) => {
@@ -22,14 +54,34 @@ describe("storage adapters", () => {
2254 expect ( await adapter . getItem ( "key" ) ) . toBeNull ( ) ;
2355 } ) ;
2456
25- it ( "localStorage adapter throws when localStorage is unavailable" , async ( ) => {
57+ it ( "localStorage adapter falls back to sessionStorage when localStorage is unavailable" , async ( ) => {
2658 const { getLocalStorageAdapter } = await loadStorageModule ( ) ;
2759 vi . stubGlobal ( "localStorage" , undefined ) ;
28- expect ( ( ) => getLocalStorageAdapter ( ) ) . toThrowError (
29- "localStorage is not available. Provide a custom storage adapter." ,
30- ) ;
60+
61+ const adapter = getLocalStorageAdapter ( ) ;
62+
63+ await adapter . setItem ( "fallback" , "ok" ) ;
64+ expect ( sessionStorage . getItem ( "fallback" ) ) . toBe ( "ok" ) ;
65+ expect ( await adapter . getItem ( "fallback" ) ) . toBe ( "ok" ) ;
66+
67+ await adapter . removeItem ?.( "fallback" ) ;
68+ expect ( sessionStorage . getItem ( "fallback" ) ) . toBeNull ( ) ;
3169 } ) ;
3270
71+ it . each ( [ "SecurityError" , "NS_ERROR_FAILURE" , "NS_ERROR_ABORT" ] ) (
72+ "localStorage adapter ignores %s when storage access is denied" ,
73+ async ( errorName ) => {
74+ const { getLocalStorageAdapter } = await loadStorageModule ( ) ;
75+ defineThrowingStorage ( "localStorage" , errorName ) ;
76+
77+ const adapter = getLocalStorageAdapter ( ) ;
78+
79+ expect ( await adapter . getItem ( "key" ) ) . toBeNull ( ) ;
80+ await expect ( adapter . setItem ( "key" , "value" ) ) . resolves . toBeUndefined ( ) ;
81+ await expect ( adapter . removeItem ?.( "key" ) ) . resolves . toBeUndefined ( ) ;
82+ } ,
83+ ) ;
84+
3385 it ( "default adapter falls back to noop on server runtimes" , async ( ) => {
3486 vi . resetModules ( ) ;
3587 vi . stubGlobal ( "window" , undefined ) ;
0 commit comments