diff options
Diffstat (limited to 'toolkit/components/featuregates/test/unit/test_FeatureGateImplementation.js')
-rw-r--r-- | toolkit/components/featuregates/test/unit/test_FeatureGateImplementation.js | 182 |
1 files changed, 182 insertions, 0 deletions
diff --git a/toolkit/components/featuregates/test/unit/test_FeatureGateImplementation.js b/toolkit/components/featuregates/test/unit/test_FeatureGateImplementation.js new file mode 100644 index 0000000000..3b3f0c6393 --- /dev/null +++ b/toolkit/components/featuregates/test/unit/test_FeatureGateImplementation.js @@ -0,0 +1,182 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +ChromeUtils.import("resource://gre/modules/Services.jsm", this); +ChromeUtils.import("resource://featuregates/FeatureGate.jsm", this); +ChromeUtils.import( + "resource://featuregates/FeatureGateImplementation.jsm", + this +); +ChromeUtils.import("resource://testing-common/httpd.js", this); + +const kDefinitionDefaults = { + id: "test-feature", + title: "Test Feature", + description: "A feature for testing", + restartRequired: false, + type: "boolean", + preference: "test.feature", + defaultValue: false, + isPublic: false, +}; + +function definitionFactory(override = {}) { + return Object.assign({}, kDefinitionDefaults, override); +} + +class DefinitionServer { + constructor(definitionOverrides = []) { + this.server = new HttpServer(); + this.server.registerPathHandler("/definitions.json", this); + this.definitions = {}; + + for (const override of definitionOverrides) { + this.addDefinition(override); + } + + this.server.start(); + registerCleanupFunction( + () => new Promise(resolve => this.server.stop(resolve)) + ); + } + + // for nsIHttpRequestHandler + handle(request, response) { + // response.setHeader("Content-Type", "application/json"); + response.write(JSON.stringify(this.definitions)); + } + + get definitionsUrl() { + const { primaryScheme, primaryHost, primaryPort } = this.server.identity; + return `${primaryScheme}://${primaryHost}:${primaryPort}/definitions.json`; + } + + addDefinition(overrides = {}) { + const definition = definitionFactory(overrides); + // convert targeted values, used by fromId + definition.isPublic = { default: definition.isPublic }; + definition.defaultValue = { default: definition.defaultValue }; + this.definitions[definition.id] = definition; + return definition; + } +} + +// getValue should work +add_task(async function testGetValue() { + const preference = "test.pref"; + equal( + Services.prefs.getPrefType(preference), + Services.prefs.PREF_INVALID, + "Before creating the feature gate, the preference should not exist" + ); + const feature = new FeatureGateImplementation( + definitionFactory({ preference, defaultValue: false }) + ); + equal( + Services.prefs.getPrefType(preference), + Services.prefs.PREF_INVALID, + "Instantiating a feature gate should not set its default value" + ); + equal( + await feature.getValue(), + false, + "getValue() should return the feature gate's default" + ); + + Services.prefs.setBoolPref(preference, true); + equal( + await feature.getValue(), + true, + "getValue() should return the new value" + ); + + Services.prefs.setBoolPref(preference, false); + equal( + await feature.getValue(), + false, + "getValue() should return the third value" + ); + + // cleanup + Services.prefs.getDefaultBranch("").deleteBranch(preference); +}); + +// event observers should work +add_task(async function testGetValue() { + const preference = "test.pref"; + const feature = new FeatureGateImplementation( + definitionFactory({ preference, defaultValue: false }) + ); + const observer = { + onChange: sinon.stub(), + onEnable: sinon.stub(), + onDisable: sinon.stub(), + }; + + let rv = await feature.addObserver(observer); + equal(rv, false, "addObserver returns the current value"); + + Assert.deepEqual(observer.onChange.args, [], "onChange should not be called"); + Assert.deepEqual(observer.onEnable.args, [], "onEnable should not be called"); + Assert.deepEqual( + observer.onDisable.args, + [], + "onDisable should not be called" + ); + + Services.prefs.setBoolPref(preference, true); + await Promise.resolve(); // Allow events to be called async + Assert.deepEqual( + observer.onChange.args, + [[true]], + "onChange should be called with the new value" + ); + Assert.deepEqual(observer.onEnable.args, [[]], "onEnable should be called"); + Assert.deepEqual( + observer.onDisable.args, + [], + "onDisable should not be called" + ); + + Services.prefs.setBoolPref(preference, false); + await Promise.resolve(); // Allow events to be called async + Assert.deepEqual( + observer.onChange.args, + [[true], [false]], + "onChange should be called again with the new value" + ); + Assert.deepEqual( + observer.onEnable.args, + [[]], + "onEnable should not be called a second time" + ); + Assert.deepEqual( + observer.onDisable.args, + [[]], + "onDisable should be called for the first time" + ); + + Services.prefs.setBoolPref(preference, false); + await Promise.resolve(); // Allow events to be called async + Assert.deepEqual( + observer.onChange.args, + [[true], [false]], + "onChange should not be called if the value did not change" + ); + Assert.deepEqual( + observer.onEnable.args, + [[]], + "onEnable should not be called again if the value did not change" + ); + Assert.deepEqual( + observer.onDisable.args, + [[]], + "onDisable should not be called if the value did not change" + ); + + // cleanup + feature.removeAllObservers(); + Services.prefs.getDefaultBranch("").deleteBranch(preference); +}); |