-
Notifications
You must be signed in to change notification settings - Fork 54
/
test_FeatureGateImplementation.js
182 lines (161 loc) · 5.01 KB
/
test_FeatureGateImplementation.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
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);
});