UNPKG

@hal313/settings-manager-chrome-extension-adapter

Version:

A backing repository for settings-manager, falling on chrome.storage.local (or sync) as an implementation.

489 lines (396 loc) 20.1 kB
export function runSpecs(SettingsManagerChromeExtensionAdapter) { // Test different paths for saving let paths = [ {use: null, expect: 'sm_ce'}, {use: undefined, expect: 'sm_ce'}, {use: '', expect: 'sm_ce'}, {use: 'site_prefix', expect: 'site_prefix'} ]; // Test 'sync' and 'local' let syncs = [ true, false ]; // Test error and success let errors = [ {message: 'failure'}, null ]; describe('Lifecycle', () => { it('should exist as a function', () => { expect.assertions(1); expect(SettingsManagerChromeExtensionAdapter).toEqual(expect.any(Function)); }); }); describe('API', () => { syncs.forEach(sync => { describe(`sync: ${sync}`, () => { paths.forEach(path => { describe(`path: ${JSON.stringify(path)}`, () => { errors.forEach(error => { describe(`error: ${JSON.stringify(error)}`, () => { // The settings manager adapter let settingsManagerChromeExtensionAdapter; // The storage API under test (sync or local) let currentStorageAPI; beforeEach(() => { // Setup the Chrome API global.chrome = { storage: { sync: new StorageAPI(), local: new StorageAPI() }, runtime: { lastError: error } }; // Assign the current storage API currentStorageAPI = !!sync ? global.chrome.storage.sync : global.chrome.storage.local; // Instantiate the settings manager instance settingsManagerChromeExtensionAdapter = new SettingsManagerChromeExtensionAdapter(sync, path.use); }); /** * Asserts that the "then" function was not invoked when an error condition was present. * * @param {*} result the value passed in * @returns {*} result */ function assertThen(result) { if (!!error) { // Simplest way to fail fail('"then" should not be invoked when an error is present'); } // Return the value for chaining return result; } /** * Asserts that the "catch" function was not invoked when no error condition was present. * * @param {*} thrown the error */ function assertCatch(thrown) { if (!!error && !!thrown) { // The API should never throw with a reason; if 'thrown' exists, the error probably // came from the previous 'then' blocks (in which case, the test should fail) expect(thrown).not.toBeDefined(); } if (!error) { // Easy way to fail fail('"catch" should not be invoked when no error is present'); } } describe('load', () => { it('should invoke the Chrome API load function', () => { // Change expectation count based on error state expect.assertions(!!error ? 1 : 2); return settingsManagerChromeExtensionAdapter.load() .then(assertThen) .then(settings => expect(settings).toBeDefined()) .catch(assertCatch) .then(() => expect(currentStorageAPI.get).toHaveBeenCalledWith([path.expect], expect.any(Function))); }); it('should return saved settings', () => { // Change expectation count based on error state expect.assertions(!!error ? 0 : 2); let savedSettings = { one: 1, two: 'two', bool: true, sub: { root: 'parent' } }; return settingsManagerChromeExtensionAdapter.clear() .then(assertThen) .then(() => settingsManagerChromeExtensionAdapter.load()) .then(settings => expect(settings).toEqual({})) .then(() => settingsManagerChromeExtensionAdapter.save(savedSettings)) .then(() => settingsManagerChromeExtensionAdapter.load()) .then(settings => expect(settings).toEqual(savedSettings)) .catch(assertCatch); }); }); describe('save', () => { it('should invoke the Chrome API set function', () => { expect.assertions(1); let settings = { p1: true, p2: 'false', p3: 1 } let expectedValue = {}; expectedValue[path.expect] = settings; return settingsManagerChromeExtensionAdapter.save(settings) .then(assertThen) .catch(assertCatch) .then(() => expect(currentStorageAPI.set).toHaveBeenCalledWith(expectedValue, expect.any(Function))); }); it('should save settings', () => { // Change expectation count based on error state expect.assertions(!!error ? 0 : 2); let savedSettings = { one: 1, two: 'two', bool: true, sub: { root: 'parent' } }; return settingsManagerChromeExtensionAdapter.clear() .then(assertThen) .then(() => settingsManagerChromeExtensionAdapter.load()) .then(settings => expect(settings).toEqual({})) .then(() => settingsManagerChromeExtensionAdapter.save(savedSettings)) .then(() => settingsManagerChromeExtensionAdapter.load()) .then(settings => expect(settings).toEqual(savedSettings)) .catch(assertCatch); }); it('should overwrite previously saved settings', () => { // Change expectation count based on error state expect.assertions(!!error ? 0 : 3); let originalSavedSettings = { one: 1, two: 'two', bool: true, sub: { root: 'parent' } }; let newSavedSettings = { three: 3, four: 'four', bool: false, sub: { none: null } }; return settingsManagerChromeExtensionAdapter.clear() .then(assertThen) .then(() => settingsManagerChromeExtensionAdapter.load()) .then(settings => expect(settings).toEqual({})) .then(() => settingsManagerChromeExtensionAdapter.save(originalSavedSettings)) .then(() => settingsManagerChromeExtensionAdapter.load()) .then(settings => expect(settings).toEqual(originalSavedSettings)) .then(() => settingsManagerChromeExtensionAdapter.save(newSavedSettings)) .then(() => settingsManagerChromeExtensionAdapter.load()) .then(settings => expect(settings).toEqual(newSavedSettings)) .catch(assertCatch); }); }); describe('clear', () => { it('should invoke the Chrome API set function with the correct payload path and a null value', () => { expect.assertions(1); let expectedValue = {}; expectedValue[path.expect] = null; return settingsManagerChromeExtensionAdapter.clear() .then(assertThen) .catch(assertCatch) .then(() => expect(currentStorageAPI.set).toHaveBeenCalledWith(expectedValue, expect.any(Function))); }); it('should clear settings', () => { // Change expectation count based on error state expect.assertions(!!error ? 0 : 3); let savedSettings = { one: 1, two: 'two', bool: true, sub: { root: 'parent' } }; return settingsManagerChromeExtensionAdapter.clear() .then(assertThen) .then(() => settingsManagerChromeExtensionAdapter.load()) .then(settings => expect(settings).toEqual({})) .then(() => settingsManagerChromeExtensionAdapter.save(savedSettings)) .then(() => settingsManagerChromeExtensionAdapter.load()) .then(settings => expect(settings).toEqual(savedSettings)) .then(() => settingsManagerChromeExtensionAdapter.clear()) .then(() => settingsManagerChromeExtensionAdapter.load()) .then(settings => expect(settings).toEqual({})) .catch(assertCatch); }); }); }); }); }); }); }); }); }); // Test the integration of this adapter with the actual SettingsManager describe('Integration', () => { // The SettingsManager let SettingsManager = require('@hal313/settings-manager').SettingsManager; // The settings manager adapter let settingsManagerChromeExtensionAdapter; // The settings manager let settingsManager; beforeEach(done => { // Setup the Chrome API global.chrome = { storage: { local: new StorageAPI() }, runtime: { lastError: undefined } }; // Instantiate the settings manager instance settingsManagerChromeExtensionAdapter = new SettingsManagerChromeExtensionAdapter(); settingsManager = new SettingsManager(settingsManagerChromeExtensionAdapter); // Ensure that the assertion is executed expect.assertions(1); // Start with a clean slate (empty settings) settingsManager.clear(() => { // Load the settings settingsManager.load(settings => { // Verify that there are no settings expect(settings).toEqual({}); done(); }); }); }); it('should load settings', done => { expect.assertions(2); let firstSettings = { one: 1, two: 'two', bool: true, sub: { root: 'parent' } }; // Save some settings settingsManager.save(firstSettings, () => { // Load the settings settingsManager.load(settings => { // Check to make sure that the settings are correct expect(settings).toEqual(firstSettings); // Done! done(); }); }); }); it('should save settings', done => { expect.assertions(2); let firstSettings = { one: 1, two: 'two', bool: true, sub: { root: 'parent' } }; // Save some settings settingsManager.save(firstSettings, () => { // Load the settings settingsManager.load(settings => { // Check that the settings are correct expect(settings).toEqual(firstSettings); // Done! done(); }); }); }); it('should clear settings', done => { expect.assertions(3); let firstSettings = { one: 1, two: 'two', bool: true, sub: { root: 'parent' } }; // Save some settings settingsManager.save(firstSettings, () => { // Load the settings settingsManager.load(settings => { expect(settings).toEqual(firstSettings); // Clear the settings settingsManager.clear(() => { // Load settings settingsManager.load(settings => { // Expect there to be no saved settings expect(settings).toEqual({}); done(); }); }); }); }); }); it('should merge settings', done => { expect.assertions(3); let firstSettings = { one: 1, two: 'two', bool: true, sub: { root: 'parent' } }; let secondSettings = { three: 3, four: 'four', bool: false, sub: { none: null } }; let mergedSettings = { one: 1, two: 'two', three: 3, four: 'four', bool: false, sub: { none: null, root: 'parent' } }; // Save some settings settingsManager.save(firstSettings, () => { // Load the settings to check that the settings were saved settingsManager.load(settings => { expect(settings).toEqual(firstSettings); // Save a second set of settings settingsManager.save(secondSettings, () => { // Load the settings to check that the settings were saved settingsManager.load(settings => { // Verify that the settings were merged expect(settings).toEqual(mergedSettings); done(); }); }); }); }); }); }); } // A mock of the Chrome Extension Storage API. class StorageAPI { constructor() { let value = { }; this.get = jest.fn(function getMock(path, callback) { if (!!callback) { callback(value[path]); } }); this.set = jest.fn(function setMock(newValue, callback) { value = newValue; if(!!callback) { callback(); } }); // The "clear" API call; not needed because the adapter implementation calls "set[key]=null" instead of clear // this.clear = jest.fn(function clearMock(callback) { // console.log('clear'); // value = {}; // if(!!callback) { // callback(); // } // }); } }