From afe177cdba8947d997ccd679b8c7951ba161363c Mon Sep 17 00:00:00 2001 From: David Kaltschmidt Date: Mon, 14 Nov 2016 17:16:46 +0100 Subject: [PATCH] Support for feature flags --- .../utils/__tests__/feature-utils-test.js | 30 +++++++++++++++++++ client/app/scripts/utils/feature-utils.js | 18 +++++++++++ client/app/scripts/utils/storage-utils.js | 25 +++++++++++++++- client/package.json | 4 +++ client/test/support/localStorage.js | 17 +++++++++++ 5 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 client/app/scripts/utils/__tests__/feature-utils-test.js create mode 100644 client/app/scripts/utils/feature-utils.js create mode 100644 client/test/support/localStorage.js diff --git a/client/app/scripts/utils/__tests__/feature-utils-test.js b/client/app/scripts/utils/__tests__/feature-utils-test.js new file mode 100644 index 000000000..2395236e4 --- /dev/null +++ b/client/app/scripts/utils/__tests__/feature-utils-test.js @@ -0,0 +1,30 @@ + +const FU = require('../feature-utils'); + +describe('FeatureUtils', () => { + const FEATURE_X_KEY = 'my feature 1'; + const FEATURE_Y_KEY = 'my feature 2'; + + beforeEach(() => { + FU.clearFeatures(); + }); + + describe('Setting of features', () => { + it('should not have any features by default', () => { + expect(FU.featureIsEnabled(FEATURE_X_KEY)).toBeFalsy(); + expect(FU.featureIsEnabled(FEATURE_Y_KEY)).toBeFalsy(); + }); + + it('should work with enabling one feature', () => { + let success; + expect(FU.featureIsEnabled(FEATURE_X_KEY)).toBeFalsy(); + success = FU.setFeature(FEATURE_X_KEY, true); + expect(success).toBeTruthy(); + expect(FU.featureIsEnabled(FEATURE_X_KEY)).toBeTruthy(); + expect(FU.featureIsEnabled(FEATURE_Y_KEY)).toBeFalsy(); + success = FU.setFeature(FEATURE_X_KEY, false); + expect(success).toBeTruthy(); + expect(FU.featureIsEnabled(FEATURE_X_KEY)).toBeFalsy(); + }); + }); +}); diff --git a/client/app/scripts/utils/feature-utils.js b/client/app/scripts/utils/feature-utils.js new file mode 100644 index 000000000..abd4ae3b5 --- /dev/null +++ b/client/app/scripts/utils/feature-utils.js @@ -0,0 +1,18 @@ +import { storageGetObject, storageSetObject } from './storage-utils'; + +const STORAGE_FEATURE_KEY = 'scopeFeatureFlags'; + +export function featureIsEnabled(feature) { + const features = storageGetObject(STORAGE_FEATURE_KEY, {}); + return features[feature]; +} + +export function setFeature(feature, isEnabled) { + const features = storageGetObject(STORAGE_FEATURE_KEY, {}); + features[feature] = isEnabled; + return storageSetObject(STORAGE_FEATURE_KEY, features); +} + +export function clearFeatures() { + return storageSetObject(STORAGE_FEATURE_KEY, {}); +} diff --git a/client/app/scripts/utils/storage-utils.js b/client/app/scripts/utils/storage-utils.js index 0ff92ff99..1556fa403 100644 --- a/client/app/scripts/utils/storage-utils.js +++ b/client/app/scripts/utils/storage-utils.js @@ -6,7 +6,7 @@ const log = debug('scope:storage-utils'); const storage = typeof(Storage) !== 'undefined' ? window.localStorage : null; export function storageGet(key, defaultValue) { - if (storage && storage[key] !== undefined) { + if (storage && storage.getItem(key) !== undefined) { return storage.getItem(key); } return defaultValue; @@ -16,8 +16,31 @@ export function storageSet(key, value) { if (storage) { try { storage.setItem(key, value); + return true; } catch (e) { log('Error storing value in storage. Maybe full? Could not store key.', key); } } + return false; +} + +export function storageGetObject(key, defaultValue) { + const value = storageGet(key); + if (value) { + try { + return JSON.parse(value); + } catch (e) { + log('Error getting object for key.', key); + } + } + return defaultValue; +} + +export function storageSetObject(key, obj) { + try { + return storageSet(key, JSON.stringify(obj)); + } catch (e) { + log('Error encoding object for key', key); + } + return false; } diff --git a/client/package.json b/client/package.json index c5505b938..549ff7390 100644 --- a/client/package.json +++ b/client/package.json @@ -90,6 +90,10 @@ }, "jest": { "transform": {".*": "/node_modules/babel-jest"}, + "scriptPreprocessor": "/node_modules/babel-jest", + "setupFiles": [ + "/test/support/localStorage.js" + ], "testPathDirs": [ "/app/scripts" ], diff --git a/client/test/support/localStorage.js b/client/test/support/localStorage.js new file mode 100644 index 000000000..d153bdf8a --- /dev/null +++ b/client/test/support/localStorage.js @@ -0,0 +1,17 @@ +const localStorageMock = (function() { + let store = {}; + return { + store, + getItem: function(key) { + return store[key]; + }, + setItem: function(key, value) { + store[key] = value.toString(); + }, + clear: function() { + store = {}; + } + }; +})(); +Object.defineProperty(window, 'Storage', { value: localStorageMock }); +Object.defineProperty(window, 'localStorage', { value: localStorageMock });