From 632598e2e8ea9d85298acf7f22ef941bc8900f1f Mon Sep 17 00:00:00 2001 From: Ed Sanders Date: Wed, 24 Apr 2019 13:46:40 +0100 Subject: [PATCH] mediawiki.storage: Add methods for storing plain objects as JSON Change-Id: I3cc1d5adfbce794e8345b7f1090c10fb0d42d150 --- resources/src/mediawiki.storage.js | 43 +++++++++++++- .../mediawiki/mediawiki.storage.test.js | 57 ++++++++++++++----- 2 files changed, 83 insertions(+), 17 deletions(-) diff --git a/resources/src/mediawiki.storage.js b/resources/src/mediawiki.storage.js index e9b2c9d329..3405ba7747 100644 --- a/resources/src/mediawiki.storage.js +++ b/resources/src/mediawiki.storage.js @@ -33,7 +33,7 @@ * * @param {string} key Key of item to retrieve * @return {string|null|boolean} String value, null if no value exists, or false - * if localStorage is not available. + * if storage is not available. */ SafeStorage.prototype.get = function ( key ) { try { @@ -47,7 +47,7 @@ * * @param {string} key Key name to store under * @param {string} value Value to be stored - * @return {boolean} Whether the save succeeded or not + * @return {boolean} The value was set */ SafeStorage.prototype.set = function ( key, value ) { try { @@ -61,7 +61,7 @@ * Remove a value from device storage. * * @param {string} key Key of item to remove - * @return {boolean} Whether the save succeeded or not + * @return {boolean} Whether the key was removed */ SafeStorage.prototype.remove = function ( key ) { try { @@ -71,6 +71,43 @@ return false; }; + /** + * Retrieve JSON object from device storage. + * + * @param {string} key Key of item to retrieve + * @return {Object|null|boolean} Object, null if no value exists or value + * is not JSON-parseable, or false if storage is not available. + */ + SafeStorage.prototype.getObject = function ( key ) { + var json = this.get( key ); + + if ( json === false ) { + return false; + } + + try { + return JSON.parse( json ); + } catch ( e ) {} + + return null; + }; + + /** + * Set an object value in device storage by JSON encoding + * + * @param {string} key Key name to store under + * @param {Object} value Object value to be stored + * @return {boolean} The value was set + */ + SafeStorage.prototype.setObject = function ( key, value ) { + var json; + try { + json = JSON.stringify( value ); + return this.set( key, json ); + } catch ( e ) {} + return false; + }; + /** * A wrapper for the HTML5 `localStorage` interface * that is safe to call on all browsers. diff --git a/tests/qunit/suites/resources/mediawiki/mediawiki.storage.test.js b/tests/qunit/suites/resources/mediawiki/mediawiki.storage.test.js index fab7e1fef1..82d5ea5b4d 100644 --- a/tests/qunit/suites/resources/mediawiki/mediawiki.storage.test.js +++ b/tests/qunit/suites/resources/mediawiki/mediawiki.storage.test.js @@ -1,23 +1,44 @@ ( function () { QUnit.module( 'mediawiki.storage' ); - QUnit.test( 'set/get with storage support', function ( assert ) { - var stub = { - setItem: this.sandbox.spy(), - getItem: this.sandbox.stub() - }; - stub.getItem.withArgs( 'foo' ).returns( 'test' ); - stub.getItem.returns( null ); + QUnit.test( 'set/get(Object) with storage support', function ( assert ) { + var data = {}, + object = { test: 'value' }, + stub = { + setItem: function ( k, v ) { + data[ k ] = v; + return true; + }, + getItem: function ( k ) { + return Object.prototype.hasOwnProperty.call( data, k ) ? data[ k ] : null; + }, + removeItem: function ( k ) { + delete data[ k ]; + return true; + } + }; + this.sandbox.stub( mw.storage, 'store', stub ); - mw.storage.set( 'foo', 'test' ); - assert.ok( stub.setItem.calledOnce ); + assert.strictEqual( mw.storage.set( 'foo', 'test' ), true, 'set returns true' ); + assert.strictEqual( mw.storage.get( 'foo' ), 'test', 'Check value gets stored' ); + assert.strictEqual( mw.storage.get( 'bar' ), null, 'Unset values are null' ); + assert.strictEqual( mw.storage.remove( 'foo' ), true, 'remove returns true' ); + assert.strictEqual( mw.storage.get( 'foo' ), null, 'Removed item is null' ); + + assert.strictEqual( mw.storage.setObject( 'baz', object ), true, 'setObject returns true' ); + assert.deepEqual( mw.storage.getObject( 'baz' ), object, 'Check value gets stored' ); + assert.notStrictEqual( mw.storage.getObject( 'baz' ), object, 'Retrieved value is a new object' ); + assert.strictEqual( mw.storage.getObject( 'quux' ), null, 'Unset values are null' ); + assert.strictEqual( mw.storage.remove( 'baz' ), true, 'remove returns true' ); + assert.strictEqual( mw.storage.getObject( 'baz' ), null, 'Removed item is null' ); + + mw.storage.set( 'baz', 'Non-JSON' ); + assert.strictEqual( mw.storage.getObject( 'baz' ), null, 'Non-JSON values are null' ); - assert.strictEqual( mw.storage.get( 'foo' ), 'test', 'Check value gets stored.' ); - assert.strictEqual( mw.storage.get( 'bar' ), null, 'Unset values are null.' ); } ); - QUnit.test( 'set/get with storage methods disabled', function ( assert ) { + QUnit.test( 'set/get(Object) with storage methods disabled', function ( assert ) { // This covers browsers where storage is disabled // (quota full, or security/privacy settings). // On most browsers, these interface will be accessible with @@ -34,10 +55,14 @@ assert.strictEqual( mw.storage.get( 'foo' ), false ); assert.strictEqual( mw.storage.set( 'foo', 'test' ), false ); - assert.strictEqual( mw.storage.remove( 'foo', 'test' ), false ); + assert.strictEqual( mw.storage.remove( 'foo' ), false ); + + assert.strictEqual( mw.storage.getObject( 'bar' ), false ); + assert.strictEqual( mw.storage.setObject( 'bar', { test: 'value' } ), false ); + assert.strictEqual( mw.storage.remove( 'bar' ), false ); } ); - QUnit.test( 'set/get with storage object disabled', function ( assert ) { + QUnit.test( 'set/get(Object) with storage object disabled', function ( assert ) { // On other browsers, these entire object is disabled. // `'localStorage' in window` would be true (and pass feature test) // but trying to read the object as window.localStorage would throw @@ -50,6 +75,10 @@ assert.strictEqual( mw.storage.set( 'foo', 'test' ), false ); assert.strictEqual( mw.storage.remove( 'foo', 'test' ), false ); + assert.strictEqual( mw.storage.getObject( 'bar' ), false ); + assert.strictEqual( mw.storage.setObject( 'bar', { test: 'value' } ), false ); + assert.strictEqual( mw.storage.remove( 'bar' ), false ); + mw.storage.store = old; } ); -- 2.20.1