From f6a962eaaafeddae92a4831dd8ea9bcbebe787de Mon Sep 17 00:00:00 2001 From: German Lena Date: Wed, 14 Dec 2016 17:15:44 -0300 Subject: [PATCH 1/5] storage failover to cookies --- src/helper/cookies.js | 45 ++++++++++++++++++++++++++++++++++ src/helper/storage.js | 18 ++++---------- src/helper/storage/cookie.js | 18 ++++++++++++++ src/helper/storage/dummy.js | 9 +++++++ src/helper/storage/handler.js | 46 +++++++++++++++++++++++++++++++++++ 5 files changed, 123 insertions(+), 13 deletions(-) create mode 100644 src/helper/cookies.js create mode 100644 src/helper/storage/cookie.js create mode 100644 src/helper/storage/dummy.js create mode 100644 src/helper/storage/handler.js diff --git a/src/helper/cookies.js b/src/helper/cookies.js new file mode 100644 index 00000000..b1a6a21b --- /dev/null +++ b/src/helper/cookies.js @@ -0,0 +1,45 @@ +var windowHandler = require('./window'); + +function create(name, value, days) { + var date; + var expires; + + if (days) { + date = new Date(); + date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); + expires = '; expires=' + date.toGMTString(); + } else { + expires = ''; + } + + windowHandler.getDocument().cookie = name + '+' + value + expires + '; path=/'; +} + +function read(name) { + var i; + var cookie; + var nameEQ = name + '='; + var cookies = windowHandler.getDocument().cookie.split(';'); + + for (i = 0; i < cookies.length; i++) { + cookie = cookies[i]; + while (cookie.charAt(0) === ' ') { + cookie = cookie.substring(1, cookie.length); + } + if (cookie.indexOf(nameEQ) === 0) { + return cookie.substring(nameEQ.length, cookie.length); + } + } + + return null; +} + +function erase(name) { + create(name, '', -1); +} + +module.exports = { + create: create, + read: read, + erase: erase +}; diff --git a/src/helper/storage.js b/src/helper/storage.js index cbea77b5..6a94a107 100644 --- a/src/helper/storage.js +++ b/src/helper/storage.js @@ -1,24 +1,16 @@ -var windowHandler = require('./window'); - -function DummyStorage() {} -DummyStorage.prototype.getItem = function (key) { return null; }; -DummyStorage.prototype.removeItem = function (key) {}; -DummyStorage.prototype.setItem = function (key, value) {}; - -function getStorage() { - return windowHandler.getWindow().localStorage || new DummyStorage(); -} +var StorageHandler = require('./storage/handler'); +var storage = new StorageHandler(); module.exports = { getItem: function (key) { - var value = getStorage().getItem(key); + var value = storage.getItem(key); return JSON.parse(value); }, removeItem: function (key) { - return getStorage().removeItem(key); + return storage.removeItem(key); }, setItem: function (key, value) { var json = JSON.stringify(value); - return getStorage().setItem(key, json); + return storage.setItem(key, json); } }; diff --git a/src/helper/storage/cookie.js b/src/helper/storage/cookie.js new file mode 100644 index 00000000..57cf44ea --- /dev/null +++ b/src/helper/storage/cookie.js @@ -0,0 +1,18 @@ +var windowHandler = require('./window'); +var cookies = require('./cookies'); + +function CookieStorage() {} + +CookieStorage.prototype.getItem = function (key) { + return cookies.read(key); +}; + +CookieStorage.prototype.removeItem = function (key) { + cookies.erase(key); +}; + +CookieStorage.prototype.setItem = function (key, value) { + cookies.read(key, value, 1); +}; + +module.exports = CookieStorage; \ No newline at end of file diff --git a/src/helper/storage/dummy.js b/src/helper/storage/dummy.js new file mode 100644 index 00000000..ceadbc8d --- /dev/null +++ b/src/helper/storage/dummy.js @@ -0,0 +1,9 @@ +function DummyStorage() {} + +DummyStorage.prototype.getItem = function (key) { return null; }; + +DummyStorage.prototype.removeItem = function (key) {}; + +DummyStorage.prototype.setItem = function (key, value) {}; + +module.exports = DummyStorage; \ No newline at end of file diff --git a/src/helper/storage/handler.js b/src/helper/storage/handler.js new file mode 100644 index 00000000..f060b4dc --- /dev/null +++ b/src/helper/storage/handler.js @@ -0,0 +1,46 @@ +var windowHandler = require('../window'); +var DummyStorage = require('./dummy'); +var CookieStorage = require('./cookie'); + +function StorageHandler() { + this.storage = windowHandler.getWindow().localStorage || new DummyStorage(); +} + +StorageHandler.prototype.failover = function () { + if (this.storage instanceof DummyStorage) { + return; + } else if (this.storage instanceof CookieStorage) { + this.storage = new DummyStorage(); + } else { + this.storage = new CookieStorage(); + } +}; + +StorageHandler.prototype.getItem = function (key) { + try { + return this.storage.getItem(key); + } catch (e) { + this.failover(); + return this.getItem(key); + } +}; + +StorageHandler.prototype.removeItem = function (key) { + try { + return this.storage.removeItem(key); + } catch (e) { + this.failover(); + return this.removeItem(key); + } +}; + +StorageHandler.prototype.setItem = function (key, value) { + try { + return this.storage.setItem(key, value); + } catch (e) { + this.failover(); + return this.setItem(key, value); + } +}; + +module.exports = StorageHandler; From 9449887ab3312421142ac00fbd226a848b884ccd Mon Sep 17 00:00:00 2001 From: German Lena Date: Wed, 14 Dec 2016 17:23:45 -0300 Subject: [PATCH 2/5] refactors --- src/helper/storage.js | 15 +++++++++++---- src/helper/storage/cookie.js | 4 ++-- src/helper/storage/handler.js | 3 ++- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/helper/storage.js b/src/helper/storage.js index 6a94a107..929acd79 100644 --- a/src/helper/storage.js +++ b/src/helper/storage.js @@ -1,16 +1,23 @@ var StorageHandler = require('./storage/handler'); -var storage = new StorageHandler(); +var storage; + +function getStorage() { + if (!storage) { + storage = new StorageHandler(); + } + return storage; +} module.exports = { getItem: function (key) { - var value = storage.getItem(key); + var value = getStorage().getItem(key); return JSON.parse(value); }, removeItem: function (key) { - return storage.removeItem(key); + return getStorage().removeItem(key); }, setItem: function (key, value) { var json = JSON.stringify(value); - return storage.setItem(key, json); + return getStorage().setItem(key, json); } }; diff --git a/src/helper/storage/cookie.js b/src/helper/storage/cookie.js index 57cf44ea..87d38a9c 100644 --- a/src/helper/storage/cookie.js +++ b/src/helper/storage/cookie.js @@ -1,5 +1,5 @@ -var windowHandler = require('./window'); -var cookies = require('./cookies'); +var windowHandler = require('../window'); +var cookies = require('../cookies'); function CookieStorage() {} diff --git a/src/helper/storage/handler.js b/src/helper/storage/handler.js index f060b4dc..9366f95e 100644 --- a/src/helper/storage/handler.js +++ b/src/helper/storage/handler.js @@ -3,7 +3,7 @@ var DummyStorage = require('./dummy'); var CookieStorage = require('./cookie'); function StorageHandler() { - this.storage = windowHandler.getWindow().localStorage || new DummyStorage(); + this.storage = windowHandler.getWindow().localStorage || new CookieStorage(); } StorageHandler.prototype.failover = function () { @@ -20,6 +20,7 @@ StorageHandler.prototype.getItem = function (key) { try { return this.storage.getItem(key); } catch (e) { + console.log(e); this.failover(); return this.getItem(key); } From 857eba195941643d23e2f6be1ed3259759ab7fe6 Mon Sep 17 00:00:00 2001 From: German Lena Date: Thu, 15 Dec 2016 10:42:03 -0300 Subject: [PATCH 3/5] fixes + tests --- src/helper/cookies.js | 16 +++++++++++-- src/helper/storage.js | 9 ++++--- src/helper/storage/cookie.js | 2 +- src/helper/storage/handler.js | 6 ++++- test/helper/storage.test.js | 44 ++++++++++++++++++++++++++++++++-- test/web-auth/web-auth.test.js | 3 +++ 6 files changed, 71 insertions(+), 9 deletions(-) diff --git a/src/helper/cookies.js b/src/helper/cookies.js index b1a6a21b..742a7810 100644 --- a/src/helper/cookies.js +++ b/src/helper/cookies.js @@ -4,6 +4,11 @@ function create(name, value, days) { var date; var expires; + if (windowHandler.getDocument().cookie === undefined + || windowHandler.getDocument().cookie === null) { + throw new Error('cookie storage not available'); + } + if (days) { date = new Date(); date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); @@ -12,14 +17,21 @@ function create(name, value, days) { expires = ''; } - windowHandler.getDocument().cookie = name + '+' + value + expires + '; path=/'; + windowHandler.getDocument().cookie = name + '=' + value + expires + '; path=/'; } function read(name) { var i; var cookie; + var cookies; var nameEQ = name + '='; - var cookies = windowHandler.getDocument().cookie.split(';'); + + if (windowHandler.getDocument().cookie === undefined + || windowHandler.getDocument().cookie === null) { + throw new Error('cookie storage not available'); + } + + cookies = windowHandler.getDocument().cookie.split(';'); for (i = 0; i < cookies.length; i++) { cookie = cookies[i]; diff --git a/src/helper/storage.js b/src/helper/storage.js index 929acd79..4e08c6ff 100644 --- a/src/helper/storage.js +++ b/src/helper/storage.js @@ -1,8 +1,8 @@ var StorageHandler = require('./storage/handler'); var storage; -function getStorage() { - if (!storage) { +function getStorage(force) { + if (!storage || force) { storage = new StorageHandler(); } return storage; @@ -11,7 +11,7 @@ function getStorage() { module.exports = { getItem: function (key) { var value = getStorage().getItem(key); - return JSON.parse(value); + return value ? JSON.parse(value) : value; }, removeItem: function (key) { return getStorage().removeItem(key); @@ -19,5 +19,8 @@ module.exports = { setItem: function (key, value) { var json = JSON.stringify(value); return getStorage().setItem(key, json); + }, + reload: function() { + getStorage(true); } }; diff --git a/src/helper/storage/cookie.js b/src/helper/storage/cookie.js index 87d38a9c..e34a8721 100644 --- a/src/helper/storage/cookie.js +++ b/src/helper/storage/cookie.js @@ -12,7 +12,7 @@ CookieStorage.prototype.removeItem = function (key) { }; CookieStorage.prototype.setItem = function (key, value) { - cookies.read(key, value, 1); + cookies.create(key, value, 1); }; module.exports = CookieStorage; \ No newline at end of file diff --git a/src/helper/storage/handler.js b/src/helper/storage/handler.js index 9366f95e..d93a9cc8 100644 --- a/src/helper/storage/handler.js +++ b/src/helper/storage/handler.js @@ -1,8 +1,10 @@ var windowHandler = require('../window'); var DummyStorage = require('./dummy'); var CookieStorage = require('./cookie'); +var Warn = require('../warn'); function StorageHandler() { + this.warn = new Warn({}); this.storage = windowHandler.getWindow().localStorage || new CookieStorage(); } @@ -20,7 +22,7 @@ StorageHandler.prototype.getItem = function (key) { try { return this.storage.getItem(key); } catch (e) { - console.log(e); + this.warn.warning(e); this.failover(); return this.getItem(key); } @@ -30,6 +32,7 @@ StorageHandler.prototype.removeItem = function (key) { try { return this.storage.removeItem(key); } catch (e) { + this.warn.warning(e); this.failover(); return this.removeItem(key); } @@ -39,6 +42,7 @@ StorageHandler.prototype.setItem = function (key, value) { try { return this.storage.setItem(key, value); } catch (e) { + this.warn.warning(e); this.failover(); return this.setItem(key, value); } diff --git a/test/helper/storage.test.js b/test/helper/storage.test.js index 86af80f5..7f1956e7 100644 --- a/test/helper/storage.test.js +++ b/test/helper/storage.test.js @@ -5,8 +5,11 @@ var windowHandler = require('../../src/helper/window'); var storage = require('../../src/helper/storage'); describe('helpers storage', function () { - describe('with localstorage', function () { + beforeEach(function(){ + storage.reload(); + }) + describe('with localstorage', function () { before(function(){ var data = {}; stub(windowHandler, 'getWindow', function () { @@ -33,15 +36,52 @@ describe('helpers storage', function () { }); }); - describe('without localstorage', function () { + describe('without localstorage and with cookies', function () { before(function(){ + var document = { + cookie: '' + }; stub(windowHandler, 'getWindow', function () { return { + localStorage: { + getItem: function(key) { throw new Error('localStorage not available') } + } }; }); + stub(windowHandler, 'getDocument', function () { + return document; + }); + }); + + after(function(){ + windowHandler.getDocument.restore(); + windowHandler.getWindow.restore(); + }); + + it('should store stuff', function () { + expect(storage.getItem('data')).to.be(null); + storage.setItem('data', 'text'); + expect(storage.getItem('data')).to.eql('text'); + storage.removeItem('data'); + // Cookies mock does not delete the cookie since it works as a variable not an actual method. + // When it depetes the cookie it stays as an empty string. The browser should delete it + // for real and return null instead + expect(storage.getItem('data')).to.be(''); + }); + }); + + describe('with dummy storage', function () { + before(function(){ + stub(windowHandler, 'getWindow', function () { + return {}; + }); + stub(windowHandler, 'getDocument', function () { + return {}; + }); }); after(function(){ + windowHandler.getDocument.restore(); windowHandler.getWindow.restore(); }); diff --git a/test/web-auth/web-auth.test.js b/test/web-auth/web-auth.test.js index 5039dca8..e8e66e00 100644 --- a/test/web-auth/web-auth.test.js +++ b/test/web-auth/web-auth.test.js @@ -2,6 +2,7 @@ var expect = require('expect.js'); var stub = require('sinon').stub; var request = require('superagent'); +var storage = require('../../src/helper/storage'); var IframeHandler = require('../../src/helper/iframe-handler'); var RequestMock = require('../mock/request-mock'); @@ -29,6 +30,7 @@ describe('auth0.WebAuth', function () { appState: null }); }; + storage.reload(); }) it('should fail if the nonce is not valid', function (done) { @@ -55,6 +57,7 @@ describe('auth0.WebAuth', function () { }; webAuth.renewAuth(options, function (err, data) { + console.log(err, data); expect(err).to.eql({ error: 'invalid_token', errorDescription: 'Nonce does not match' From 41e4088d20240e60a67d6591e665aa3dadb14aac Mon Sep 17 00:00:00 2001 From: German Lena Date: Thu, 15 Dec 2016 11:02:51 -0300 Subject: [PATCH 4/5] removed log --- test/web-auth/web-auth.test.js | 1 - 1 file changed, 1 deletion(-) diff --git a/test/web-auth/web-auth.test.js b/test/web-auth/web-auth.test.js index e8e66e00..e9e1a238 100644 --- a/test/web-auth/web-auth.test.js +++ b/test/web-auth/web-auth.test.js @@ -57,7 +57,6 @@ describe('auth0.WebAuth', function () { }; webAuth.renewAuth(options, function (err, data) { - console.log(err, data); expect(err).to.eql({ error: 'invalid_token', errorDescription: 'Nonce does not match' From 6962ff2f42c45d04d55998e420f07124dc06d2f9 Mon Sep 17 00:00:00 2001 From: German Lena Date: Thu, 15 Dec 2016 12:59:06 -0300 Subject: [PATCH 5/5] fixes + tests --- src/helper/cookies.js | 5 +- src/helper/storage/handler.js | 3 + test/helper/cookies.test.js | 108 +++++++++++++++++++++ test/helper/storage-handler.test.js | 145 ++++++++++++++++++++++++++++ 4 files changed, 259 insertions(+), 2 deletions(-) create mode 100644 test/helper/cookies.test.js create mode 100644 test/helper/storage-handler.test.js diff --git a/src/helper/cookies.js b/src/helper/cookies.js index 742a7810..56ffb036 100644 --- a/src/helper/cookies.js +++ b/src/helper/cookies.js @@ -1,4 +1,5 @@ var windowHandler = require('./window'); +var base64Url = require('./base64_url'); function create(name, value, days) { var date; @@ -17,7 +18,7 @@ function create(name, value, days) { expires = ''; } - windowHandler.getDocument().cookie = name + '=' + value + expires + '; path=/'; + windowHandler.getDocument().cookie = name + '=' + base64Url.encode(value) + expires + '; path=/'; } function read(name) { @@ -39,7 +40,7 @@ function read(name) { cookie = cookie.substring(1, cookie.length); } if (cookie.indexOf(nameEQ) === 0) { - return cookie.substring(nameEQ.length, cookie.length); + return base64Url.decode(cookie.substring(nameEQ.length, cookie.length)); } } diff --git a/src/helper/storage/handler.js b/src/helper/storage/handler.js index d93a9cc8..d27c40fe 100644 --- a/src/helper/storage/handler.js +++ b/src/helper/storage/handler.js @@ -10,10 +10,13 @@ function StorageHandler() { StorageHandler.prototype.failover = function () { if (this.storage instanceof DummyStorage) { + this.warn.warning('DummyStorage: ignore failover'); return; } else if (this.storage instanceof CookieStorage) { + this.warn.warning('CookieStorage: failing over DummyStorage'); this.storage = new DummyStorage(); } else { + this.warn.warning('LocalStorage: failing over CookieStorage'); this.storage = new CookieStorage(); } }; diff --git a/test/helper/cookies.test.js b/test/helper/cookies.test.js new file mode 100644 index 00000000..f29c6901 --- /dev/null +++ b/test/helper/cookies.test.js @@ -0,0 +1,108 @@ +var expect = require('expect.js'); +var stub = require('sinon').stub; + +var windowHandler = require('../../src/helper/window'); +var cookies = require('../../src/helper/cookies'); + +describe('helpers cookies', function () { + + beforeEach(function(){ + var document = { + cookie: '' + } + + stub(windowHandler, 'getDocument', function (message) { + return document; + }); + stub(Date.prototype, 'getTime', function (message) { + return 0; + }); + }) + + afterEach(function(){ + windowHandler.getDocument.restore(); + Date.prototype.getTime.restore(); + }) + + it('create a cookie with exp', function () { + + cookies.create('cookie_name', 'cookie value', 1); + expect(windowHandler.getDocument().cookie).to.be.eql('cookie_name=Y29va2llIHZhbHVl; expires=Fri, 02 Jan 1970 00:00:00 GMT; path=/') + + }); + + it('create a cookie without exp', function () { + + cookies.create('cookie_name', 'cookie value'); + expect(windowHandler.getDocument().cookie).to.be.eql('cookie_name=Y29va2llIHZhbHVl; path=/') + + }); + + it('returns null if the cookie does not exist', function () { + + var value = cookies.read('cookie_name'); + expect(value).to.be.eql(null); + + }); + + it('returns the cookie value', function () { + + cookies.create('cookie_name', 'cookie value'); + expect(windowHandler.getDocument().cookie).to.be.eql('cookie_name=Y29va2llIHZhbHVl; path=/') + + var value = cookies.read('cookie_name'); + expect(value).to.be.eql('cookie value') + + }); + + + it('should handle multiple cookies', function () { + + windowHandler.getDocument.restore(); + stub(windowHandler, 'getDocument', function (message) { + return { + cookie: 'cookie_name=Y29va2llIHZhbHVl; path=/; cookie_name2=Y29va2llIHZhbHVlMg; path=/' + }; + }); + + var value = cookies.read('cookie_name2'); + expect(value).to.be.eql('cookie value2') + }); + + it('returns the cookie value (with ;)', function () { + + cookies.create('cookie_name', 'cookie; value'); + expect(windowHandler.getDocument().cookie).to.be.eql('cookie_name=Y29va2llOyB2YWx1ZQ; path=/') + + var value = cookies.read('cookie_name'); + expect(value).to.be.eql('cookie; value') + + }); + + it('should reset the expiration', function () { + + cookies.create('cookie_name', 'cookie; value'); + expect(windowHandler.getDocument().cookie).to.be.eql('cookie_name=Y29va2llOyB2YWx1ZQ; path=/') + + cookies.erase('cookie_name'); + + expect(windowHandler.getDocument().cookie).to.be.eql('cookie_name=; expires=Wed, 31 Dec 1969 00:00:00 GMT; path=/') + }); + + it('handle cookie not available', function () { + + windowHandler.getDocument.restore(); + stub(windowHandler, 'getDocument', function (message) { + return {}; + }); + expect(function () { + cookies.create('cookie_name', 'cookie; value'); + }).to.throwError(); + + expect(function () { + cookies.read('cookie_name'); + }).to.throwError(); + + }); + +}); diff --git a/test/helper/storage-handler.test.js b/test/helper/storage-handler.test.js new file mode 100644 index 00000000..0edd9aa9 --- /dev/null +++ b/test/helper/storage-handler.test.js @@ -0,0 +1,145 @@ +var expect = require('expect.js'); +var stub = require('sinon').stub; + +var windowHandler = require('../../src/helper/window'); +var StorageHandler = require('../../src/helper/storage/handler'); +var CookieStorage = require('../../src/helper/storage/cookie'); +var DummyStorage = require('../../src/helper/storage/dummy'); + +function MockLocalStorage() {} +MockLocalStorage.prototype.getItem = function() { throw new Error('fail'); } +MockLocalStorage.prototype.removeItem = function() { throw new Error('fail'); } +MockLocalStorage.prototype.setItem = function() { throw new Error('fail'); } + +describe('helpers storage handler', function () { + it('should use localStorage by default', function () { + stub(windowHandler, 'getWindow', function (message) { + return { + localStorage: new MockLocalStorage() + }; + }); + + var handler = new StorageHandler(); + expect(handler.storage).to.be.a(MockLocalStorage); + + windowHandler.getWindow.restore(); + }); + + it('should use cookie storage is localstorage is not available', function () { + stub(windowHandler, 'getWindow', function (message) { + return {}; + }); + + var handler = new StorageHandler(); + expect(handler.storage).to.be.a(CookieStorage); + + windowHandler.getWindow.restore(); + }); + + it('should use cookie storage is localstorage fails with getItem', function () { + stub(windowHandler, 'getWindow', function (message) { + return { + localStorage: new MockLocalStorage() + }; + }); + stub(windowHandler, 'getDocument', function (message) { + return { + cookie: '' + }; + }); + + var handler = new StorageHandler(); + + expect(handler.storage).to.be.a(MockLocalStorage); + handler.getItem('pepe'); + + expect(handler.storage).to.be.a(CookieStorage); + + windowHandler.getWindow.restore(); + windowHandler.getDocument.restore(); + }); + + it('should use cookie storage is localstorage fails with setItem', function () { + var document = { + cookie: '' + }; + stub(windowHandler, 'getWindow', function (message) { + return { + localStorage: new MockLocalStorage() + }; + }); + stub(windowHandler, 'getDocument', function (message) { + return document; + }); + stub(Date.prototype, 'getTime', function (message) { + return 0; + }); + + var handler = new StorageHandler(); + + expect(handler.storage).to.be.a(MockLocalStorage); + handler.setItem('some', 'value'); + + expect(handler.storage).to.be.a(CookieStorage); + expect(document.cookie).to.be('some=dmFsdWU; expires=Fri, 02 Jan 1970 00:00:00 GMT; path=/'); + + windowHandler.getWindow.restore(); + windowHandler.getDocument.restore(); + Date.prototype.getTime.restore(); + }); + + it('should use cookie storage is localstorage fails with removeItem', function () { + var document = { + cookie: '' + }; + stub(windowHandler, 'getWindow', function (message) { + return { + localStorage: new MockLocalStorage() + }; + }); + stub(windowHandler, 'getDocument', function (message) { + return document; + }); + stub(Date.prototype, 'getTime', function (message) { + return 0; + }); + + var handler = new StorageHandler(); + + expect(handler.storage).to.be.a(MockLocalStorage); + handler.removeItem('some'); + + expect(handler.storage).to.be.a(CookieStorage); + expect(document.cookie).to.be('some=; expires=Wed, 31 Dec 1969 00:00:00 GMT; path=/'); + + windowHandler.getWindow.restore(); + windowHandler.getDocument.restore(); + Date.prototype.getTime.restore(); + }); + + it('should failover to dummy', function () { + var document = { + cookie: '' + }; + stub(windowHandler, 'getWindow', function (message) { + return { + localStorage: new MockLocalStorage() + }; + }); + + var handler = new StorageHandler(); + + expect(handler.storage).to.be.a(MockLocalStorage); + handler.failover(); + + expect(handler.storage).to.be.a(CookieStorage); + handler.failover(); + + expect(handler.storage).to.be.a(DummyStorage); + handler.failover(); + + expect(handler.storage).to.be.a(DummyStorage); + + windowHandler.getWindow.restore(); + }); +});