Skip to content

Commit 5269a3e

Browse files
ryder-wendtsilesky
andauthored
Update LocalStorage error handling (#666)
Co-authored-by: Seth Silesky <[email protected]>
1 parent 9cc2cdf commit 5269a3e

File tree

3 files changed

+55
-38
lines changed

3 files changed

+55
-38
lines changed

.changeset/itchy-points-flash.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@segment/analytics-next': patch
3+
---
4+
5+
Do not throw errors if localStorage becomes unavailable

packages/browser/src/core/user/__tests__/index.test.ts

Lines changed: 31 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,17 @@ function clear(): void {
1111
localStorage.clear()
1212
}
1313

14+
let store: LocalStorage
15+
beforeEach(function () {
16+
store = new LocalStorage()
17+
clear()
18+
})
19+
1420
describe('user', () => {
1521
const cookieKey = User.defaults.cookie.key
1622
const localStorageKey = User.defaults.localStorage.key
17-
const store = new LocalStorage()
1823

1924
describe('()', () => {
20-
beforeEach(() => {
21-
clear()
22-
})
23-
2425
it('should pick the old "_sio" anonymousId', () => {
2526
jar.set('_sio', 'anonymous-id----user-id')
2627
const user = new User()
@@ -61,7 +62,6 @@ describe('user', () => {
6162

6263
beforeEach(() => {
6364
user = new User()
64-
clear()
6565
})
6666

6767
describe('when cookies are disabled', () => {
@@ -294,15 +294,13 @@ describe('user', () => {
294294

295295
beforeEach(() => {
296296
user = new User()
297-
clear()
298297
})
299298

300299
describe('when cookies are disabled', () => {
301300
beforeEach(() => {
302301
jest.spyOn(Cookie, 'available').mockReturnValueOnce(false)
303302

304303
user = new User()
305-
clear()
306304
})
307305

308306
it('should get an id from the store', () => {
@@ -332,7 +330,6 @@ describe('user', () => {
332330
jest.spyOn(Cookie, 'available').mockReturnValueOnce(false)
333331

334332
user = new User()
335-
clear()
336333
})
337334

338335
it('should get an id from memory', () => {
@@ -444,7 +441,6 @@ describe('user', () => {
444441

445442
beforeEach(() => {
446443
user = new User()
447-
clear()
448444
})
449445

450446
it('should get traits', () => {
@@ -537,7 +533,6 @@ describe('user', () => {
537533

538534
beforeEach(() => {
539535
user = new User()
540-
clear()
541536
})
542537

543538
it('should save an id to a cookie', () => {
@@ -604,7 +599,6 @@ describe('user', () => {
604599

605600
beforeEach(() => {
606601
user = new User()
607-
clear()
608602
})
609603

610604
it('should reset an id and traits', () => {
@@ -647,7 +641,6 @@ describe('user', () => {
647641

648642
beforeEach(() => {
649643
user = new User()
650-
clear()
651644
})
652645

653646
it('should save an id', () => {
@@ -704,7 +697,6 @@ describe('user', () => {
704697

705698
beforeEach(() => {
706699
user = new User()
707-
clear()
708700
})
709701

710702
it('should load an empty user', () => {
@@ -751,12 +743,6 @@ describe('user', () => {
751743
})
752744

753745
describe('group', () => {
754-
const store = new LocalStorage()
755-
756-
beforeEach(() => {
757-
clear()
758-
})
759-
760746
it('should not reset id and traits', () => {
761747
let group = new Group()
762748
group.id('gid')
@@ -865,19 +851,36 @@ describe('group', () => {
865851
})
866852

867853
describe('store', function () {
868-
const store = new LocalStorage()
869-
beforeEach(function () {
870-
clear()
871-
})
872-
873854
describe('#get', function () {
874-
it('should not not get an empty record', function () {
875-
expect(store.get('abc') === undefined)
855+
it('should return null if localStorage throws an error (or does not exist)', function () {
856+
const getItemSpy = jest
857+
.spyOn(global.Storage.prototype, 'getItem')
858+
.mockImplementationOnce(() => {
859+
throw new Error('getItem fail.')
860+
})
861+
store.set('foo', 'some value')
862+
expect(store.get('foo')).toBeNull()
863+
expect(getItemSpy).toBeCalledTimes(1)
864+
})
865+
866+
it('should not get an empty record', function () {
867+
expect(store.get('abc')).toBe(null)
876868
})
877869

878870
it('should get an existing record', function () {
879871
store.set('x', { a: 'b' })
872+
store.set('a', 'hello world')
873+
store.set('b', '')
874+
store.set('c', false)
875+
store.set('d', null)
876+
store.set('e', undefined)
877+
880878
expect(store.get('x')).toStrictEqual({ a: 'b' })
879+
expect(store.get('a')).toBe('hello world')
880+
expect(store.get('b')).toBe('')
881+
expect(store.get('c')).toBe(false)
882+
expect(store.get('d')).toBe(null)
883+
expect(store.get('e')).toBe('undefined')
881884
})
882885
})
883886

@@ -900,16 +903,12 @@ describe('store', function () {
900903
store.set('x', { a: 'b' })
901904
expect(store.get('x')).toStrictEqual({ a: 'b' })
902905
store.remove('x')
903-
expect(store.get('x') === undefined)
906+
expect(store.get('x')).toBe(null)
904907
})
905908
})
906909
})
907910

908911
describe('Custom cookie params', () => {
909-
beforeEach(() => {
910-
clear()
911-
})
912-
913912
it('allows for overriding keys', () => {
914913
const customUser = new User(
915914
{},

packages/browser/src/core/user/index.ts

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,10 @@ class NullStorage extends Store {
136136
remove = (_key: string): void => {}
137137
}
138138

139+
const localStorageWarning = (key: string, state: 'full' | 'unavailable') => {
140+
console.warn(`Unable to access ${key}, localStorage may be ${state}`)
141+
}
142+
139143
export class LocalStorage extends Store {
140144
static available(): boolean {
141145
const test = 'test'
@@ -149,29 +153,38 @@ export class LocalStorage extends Store {
149153
}
150154

151155
get<T>(key: string): T | null {
152-
const val = localStorage.getItem(key)
153-
if (val) {
156+
try {
157+
const val = localStorage.getItem(key)
158+
if (val === null) {
159+
return null
160+
}
154161
try {
155162
return JSON.parse(val)
156163
} catch (e) {
157-
return JSON.parse(JSON.stringify(val))
164+
return val as any as T
158165
}
166+
} catch (err) {
167+
localStorageWarning(key, 'unavailable')
168+
return null
159169
}
160-
return null
161170
}
162171

163172
set<T>(key: string, value: T): T | null {
164173
try {
165174
localStorage.setItem(key, JSON.stringify(value))
166175
} catch {
167-
console.warn(`Unable to set ${key} in localStorage, storage may be full.`)
176+
localStorageWarning(key, 'full')
168177
}
169178

170179
return value
171180
}
172181

173182
remove(key: string): void {
174-
return localStorage.removeItem(key)
183+
try {
184+
return localStorage.removeItem(key)
185+
} catch (err) {
186+
localStorageWarning(key, 'unavailable')
187+
}
175188
}
176189
}
177190

0 commit comments

Comments
 (0)