Skip to content

Commit fdfa9fe

Browse files
authored
Check to see whether storage is available before interacting with it (#9)
* Check to see whether storage is available before interacting with it * Version bump: 2.0.2
1 parent c565d22 commit fdfa9fe

File tree

5 files changed

+55
-2
lines changed

5 files changed

+55
-2
lines changed

__mocks__/lodash/once.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
2+
// Mock implementation returns the same function.
3+
module.exports = func => func

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "redux-sessions",
3-
"version": "2.0.1",
3+
"version": "2.0.2",
44
"description": "Redux action creators for storing session state",
55
"main": "lib/index.js",
66
"scripts": {

src/persistenceHelpers.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ function persistStorageKey (userType) {
3838
// Loads the redux state from local / session storage.
3939
// We use the storage prefix to figure out which values we've saved.
4040
export function loadSessionState () {
41+
if (!storage.isAvailable()) {
42+
// eslint-disable-next-line
43+
console.warn('Storage is unavailable, skipping session state load.')
44+
return {}
45+
}
4146
const storageKeys = storage.getAllKeys().filter(key => key.startsWith(STORAGE_PREFIX))
4247
const userTypes = uniq(storageKeys.map(getUserTypeFromStorageKey))
4348
const state = {}
@@ -50,6 +55,11 @@ export function loadSessionState () {
5055

5156
// Saves the redux state to local / session storage.
5257
export function saveSessionState (state) {
58+
if (!storage.isAvailable()) {
59+
// eslint-disable-next-line
60+
console.warn('Storage is unavailable, skipping session state save.')
61+
return
62+
}
5363
return map(state, ({ token, persist }, userType) => {
5464
storage.setItem(tokenStorageKey(userType), token, { persist })
5565
storage.setItem(persistStorageKey(userType), persist, { persist })

src/utils/storage.js

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,20 @@
1-
import { uniq, merge, keys } from 'lodash'
1+
import { uniq, merge, keys, once } from 'lodash'
22

33
// Unified interface for local and session storage
44

5+
// Returns true if local storage is available
6+
export const isAvailable = once(() => {
7+
try {
8+
const key = '__TEST__'
9+
localStorage.setItem(key, key)
10+
localStorage.getItem(key, key)
11+
localStorage.removeItem(key)
12+
return true
13+
} catch (e) {
14+
return false
15+
}
16+
})
17+
518
// Get item from local storage, falling back to session storage
619
export function getItem (key) {
720
return localStorage.getItem(key) || sessionStorage.getItem(key)

test/persistenceHelpers.test.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ beforeEach(() => {
66
sessionStorage.clear()
77
})
88

9+
// Note- lodash "once" is mocked to simulate each function being run in a new context.
10+
911
describe('loadSessionState()', () => {
1012
it('loads session state from localStorage and sessionStorage', () => {
1113
const advisorToken = 'foo'
@@ -23,6 +25,19 @@ describe('loadSessionState()', () => {
2325
client: { token: clientToken, persist: clientPersist }
2426
})
2527
})
28+
describe('with prohibited localStorage', () => {
29+
beforeAll(() => {
30+
saveSessionState({ advisor: { token: 'foo', persist: true } })
31+
window.getItem = Storage.prototype.getItem
32+
Storage.prototype.getItem = () => { throw new Error('No access.') }
33+
})
34+
it('fails gracefully', () => {
35+
expect(() => loadSessionState()).not.toThrow()
36+
})
37+
afterAll(() => {
38+
Storage.prototype.getItem = window.getItem
39+
})
40+
})
2641
})
2742

2843
describe('saveSessionState()', () => {
@@ -41,6 +56,18 @@ describe('saveSessionState()', () => {
4156
expect(sessionStorage.getItem('redux-sessions:token:client')).toEqual(clientToken)
4257
expect(sessionStorage.getItem('redux-sessions:persist:client')).toEqual(null)
4358
})
59+
describe('with prohibited localStorage', () => {
60+
beforeAll(() => {
61+
window.setItem = Storage.prototype.setItem
62+
Storage.prototype.setItem = () => { throw new Error('No access.') }
63+
})
64+
it('fails gracefully', () => {
65+
expect(() => saveSessionState({ advisor: { token: 'foo', persist: true } })).not.toThrow()
66+
})
67+
afterAll(() => {
68+
Storage.prototype.setItem = window.setItem
69+
})
70+
})
4471
})
4572

4673
test('persistence helpers are reciprocal', () => {

0 commit comments

Comments
 (0)