OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 /** @fileoverview Suite of tests for cr-settings-prefs. */ | |
6 cr.define('cr_settings_prefs', function() { | |
7 /** | |
8 * Mock of chrome.settingsPrivate API. | |
9 * @constructor | |
10 * @extends {chrome.settingsPrivate} | |
11 */ | |
12 function MockSettingsApi() { | |
13 this.prefs = {}; | |
14 this.listener_ = null; | |
15 | |
16 // Hack alert: bind this instance's onPrefsChanged members to this. | |
17 this.onPrefsChanged = { | |
18 addListener: this.onPrefsChanged.addListener.bind(this), | |
19 removeListener: this.onPrefsChanged.removeListener.bind(this), | |
20 }; | |
21 | |
22 this.addBooleanPref_('top_level_pref', true); | |
23 this.addBooleanPref_('browser.enable_flash', false); | |
24 this.addBooleanPref_('browser.enable_html5', true); | |
25 this.addNumberPref_('device.overclock', .6); | |
26 this.addStringPref_('homepage', 'example.com'); | |
27 } | |
28 | |
29 MockSettingsApi.prototype = { | |
30 // chrome.settingsPrivate overrides. | |
31 onPrefsChanged: { | |
32 addListener: function(listener) { | |
33 this.listener_ = listener; | |
34 }, | |
35 | |
36 removeListener: function(listener) { | |
37 expectNotEquals(null, this.listener_); | |
Dan Beam
2015/09/11 21:49:13
^ why?
michaelpg
2015/09/13 03:01:06
why not?
I don't much care; nobody calls this any
| |
38 this.listener_ = null; | |
39 }, | |
40 }, | |
41 | |
42 getAllPrefs: function(callback) { | |
43 // Send a copy of prefs to keep our internal state private. | |
44 var prefs = []; | |
45 for (var key in this.prefs) | |
46 prefs.push(this.createCopy_(this.prefs[key])); | |
47 | |
48 setTimeout(callback.bind(null, prefs)); | |
49 }, | |
50 | |
51 setPref: function(key, value, pageId, callback) { | |
52 var pref = this.prefs[key]; | |
53 assertNotEquals(undefined, pref); | |
54 assertEquals(typeof value, typeof pref.value); | |
55 assertEquals(Array.isArray(value), Array.isArray(pref.value)); | |
56 | |
57 if (this.failNextSetPref_) { | |
58 setTimeout(callback.bind(null, false)); | |
59 this.failNextSetPref_ = false; | |
60 return; | |
61 } | |
62 | |
63 // TODO(michaelpg): support list and dict prefs. | |
64 var changed = this.prefs[key].value != value; | |
65 this.prefs[key].value = value; | |
66 setTimeout(function() { | |
67 callback(true); | |
68 // Like chrome.settingsPrivate, send a notification when prefs change. | |
69 if (changed) | |
70 this.sendPrefChanges([key], [value]); | |
71 }.bind(this)); | |
72 }, | |
73 | |
74 getPref: function(key, callback) { | |
75 var pref = this.prefs[key]; | |
76 assertNotEquals(undefined, pref); | |
77 | |
78 setTimeout(callback.bind(null, this.createCopy_(pref))); | |
79 }, | |
80 | |
81 // Functions used by tests. | |
82 | |
83 /** Instructs the API to fail when setPref is next called. */ | |
84 failNextSetPref: function() { | |
85 this.failNextSetPref_ = true; | |
86 }, | |
87 | |
88 /** | |
89 * Notifies the listener of pref changes. | |
90 * @param {!Array<string>} keys | |
91 * @param {!Array<*>} values | |
92 */ | |
93 sendPrefChanges: function(keys, values) { | |
94 assertNotEquals(null, this.listener_); | |
Dan Beam
2015/09/11 21:49:12
assert(this.listener_); (or just remove this becau
michaelpg
2015/09/13 03:01:06
I can't figure out why, but assert doesn't play ni
| |
95 | |
96 var prefs = []; | |
97 for (var i = 0; i < keys.length; i++) { | |
98 var key = keys[i]; | |
99 var pref = this.prefs[key]; | |
100 pref.value = values[i]; | |
101 prefs.push(this.createCopy_(pref)); | |
102 } | |
103 | |
104 setTimeout(this.listener_.bind(null, prefs)); | |
105 }, | |
106 | |
107 // Private methods for use by the mock API. | |
108 | |
109 /** | |
110 * @param {!chrome.settingsPrivate.PrefType} type | |
111 * @param {string} key | |
112 * @param {*} value | |
113 */ | |
114 addPref_: function(type, key, value) { | |
115 this.prefs[key] = { | |
116 type: type, | |
117 key: key, | |
118 value: value, | |
119 }; | |
120 }, | |
121 | |
122 /** | |
123 * @param {string} key | |
124 * @param {boolean} value | |
125 */ | |
126 addBooleanPref_: function(key, value) { | |
127 this.addPref_('BOOLEAN', key, value); | |
stevenjb
2015/09/11 21:03:47
chrome.settingsPrivate.PrefType.BOOLEAN (throughou
Dan Beam
2015/09/11 21:49:12
are these values ('BOOLEAN', 'STRING', 'NUMBER') i
michaelpg
2015/09/13 03:01:06
Done.
michaelpg
2015/09/13 03:01:06
Done.
| |
128 }, | |
129 | |
130 /** | |
131 * @param {string} key | |
132 * @param {number} value | |
133 */ | |
134 addNumberPref_: function(key, value) { | |
135 this.addPref_('NUMBER', key, value); | |
136 }, | |
137 | |
138 /** | |
139 * @param {string} key | |
140 * @param {string} value | |
141 */ | |
142 addStringPref_: function(key, value) { | |
143 this.addPref_('STRING', key, value); | |
144 }, | |
145 | |
146 /** | |
147 * Creates a deep copy of the preference. | |
148 * @param {chrome.settingsPrivate.PrefObject} pref | |
149 * @return {chrome.settingsPrivate.PrefObject} | |
150 */ | |
151 createCopy_: function(pref) { | |
152 return JSON.parse(JSON.stringify(pref)); | |
stevenjb
2015/09/11 21:03:47
Can we use Object.Assign here?
https://developer.m
Dan Beam
2015/09/11 21:49:12
I guess using Object.create() here would be too mu
michaelpg
2015/09/13 03:01:06
Done.
michaelpg
2015/09/13 03:01:06
Used Object.assign instead.
| |
153 }, | |
154 }; | |
155 | |
156 /** | |
157 * Helper function to deal with asynchronicity by repeatedly queueing | |
158 * tasks to check if the callback returns true. | |
159 * @param {function(): boolean} callback | |
160 * @return {Promise} | |
161 */ | |
162 function waitUntilTrue(callback) { | |
163 return new Promise(function(resolve, reject) { | |
164 var interval = setInterval(function() { | |
Dan Beam
2015/09/11 21:49:12
ew
michaelpg
2015/09/13 03:01:06
agreed. not sure how to solve this, though, withou
| |
165 if (callback.call()) { | |
166 clearInterval(interval); | |
167 resolve(); | |
168 } | |
169 }); | |
Dan Beam
2015/09/11 21:49:12
wait, do this every 0ms?
michaelpg
2015/09/13 03:01:06
Yeah, it posts a task each time. If a function pos
| |
170 }); | |
171 } | |
172 | |
173 function registerTests() { | |
174 suite('CrSettingsPrefs', function() { | |
175 /** | |
176 * Prefs instance created before each test. | |
177 * @type {CrSettingsPrefs} | |
178 */ | |
179 var prefs; | |
180 | |
181 /** @type {MockSettingsApi} */ | |
182 var mockApi = null; | |
183 | |
184 // Initialize a <cr-settings-prefs> element before each test. | |
185 setup(function(done) { | |
186 mockApi = new MockSettingsApi(); | |
187 // TODO(michaelpg): don't use global variables to inject the API. | |
188 window.mockApi = mockApi; | |
189 | |
190 // Create and attach the <cr-settings-prefs> element. | |
191 PolymerTest.clearBody(); | |
192 prefs = document.createElement('cr-settings-prefs'); | |
193 document.body.appendChild(prefs); | |
Dan Beam
2015/09/11 21:49:13
damn it http://jsfiddle.net/11hefdtv/
michaelpg
2015/09/13 03:01:06
Yyyyeeeppp. The browser creates and initializes ev
| |
194 | |
195 window.mockApi = undefined; | |
196 | |
197 // Wait for CrSettingsPrefs.INITIALIZED. | |
198 if (!CrSettingsPrefs.isInitialized) { | |
Dan Beam
2015/09/11 21:49:12
can haz promise instead? CrSettingsPrefs.initiali
michaelpg
2015/09/13 03:01:06
Good idea, added the todo to prefs_types.js.
| |
199 var listener = function() { | |
200 document.removeEventListener(CrSettingsPrefs.INITIALIZED, listener); | |
201 done(); | |
202 }; | |
203 document.addEventListener(CrSettingsPrefs.INITIALIZED, listener); | |
204 return; | |
205 } | |
206 | |
207 done(); | |
208 }); | |
209 | |
210 test('receives and caches prefs', function() { | |
211 // Test that each pref has been successfully copied to the Polymer | |
212 // |prefs| property. | |
213 for (var key in mockApi.prefs) { | |
214 var expectedPref = mockApi.prefs[key]; | |
215 var actualPref = prefs.prefs; | |
216 // Find the preference. | |
217 var path = key.split('.'); | |
218 var skipPref = false; | |
219 for (var part of path) { | |
220 actualPref = actualPref[part]; | |
221 if (!expectNotEquals(undefined, actualPref)) { | |
222 // We've already registered an error, so skip the pref. | |
223 skipPref = true; | |
224 break; | |
225 } | |
226 } | |
stevenjb
2015/09/11 21:03:47
nit: Maybe wrap the above in a function to avoid t
michaelpg
2015/09/13 03:01:06
Done.
| |
227 if (skipPref) | |
228 continue; | |
229 | |
230 expectEquals(expectedPref.key, actualPref.key); | |
231 expectEquals(expectedPref.type, actualPref.type); | |
232 if (expectedPref.type != chrome.settingsPrivate.LIST) { | |
233 expectEquals(expectedPref.value, actualPref.value); | |
234 } else { | |
235 expectEquals(JSON.stringify(expectedPref.value), | |
236 JSON.stringify(actualPref.value)); | |
Dan Beam
2015/09/11 21:49:12
just do this for numbers/integers as well?
michaelpg
2015/09/13 03:01:06
but but but... it's perf season!
| |
237 } | |
238 } | |
239 }); | |
240 | |
241 test('forwards pref changes to API', function() { | |
242 // Test that cr-settings-prefs uses the setPref API. | |
243 prefs.set('prefs.browser.enable_flash.value', true); | |
244 expectTrue(mockApi.prefs['browser.enable_flash'].value); | |
Dan Beam
2015/09/11 21:49:13
\n
michaelpg
2015/09/13 03:01:06
Done.
| |
245 prefs.set('prefs.device.overclock.value', 1.2); | |
246 expectEquals(1.2, mockApi.prefs['device.overclock'].value); | |
Dan Beam
2015/09/11 21:49:12
\n
michaelpg
2015/09/13 03:01:06
Done.
| |
247 prefs.set('prefs.homepage.value', 'chromium.org'); | |
248 expectEquals('chromium.org', mockApi.prefs['homepage'].value); | |
249 | |
250 // Test that when setPref fails, the pref is reverted locally. | |
251 mockApi.failNextSetPref(); | |
252 prefs.set('prefs.browser.enable_flash.value', false); | |
Dan Beam
2015/09/11 21:49:13
\n
michaelpg
2015/09/13 03:01:06
Done.
| |
253 mockApi.failNextSetPref(); | |
254 prefs.set('prefs.device.overclock.value', 2.0); | |
Dan Beam
2015/09/11 21:49:13
\n
michaelpg
2015/09/13 03:01:06
Done.
| |
255 mockApi.failNextSetPref(); | |
256 prefs.set('prefs.homepage.value', 'invalid-url'); | |
257 | |
258 // Multiple asynchronous round trips need to happen. | |
259 return Promise.all([ | |
260 waitUntilTrue(function() { | |
261 return prefs.prefs.browser.enable_flash.value; | |
262 }), | |
263 waitUntilTrue(function() { | |
264 return prefs.prefs.device.overclock.value == 1.2; | |
265 }), | |
266 waitUntilTrue(function() { | |
267 return prefs.prefs.homepage.value == 'chromium.org'; | |
268 }), | |
269 ]); | |
270 }); | |
271 | |
272 test('responds to API changes', function(done) { | |
273 mockApi.sendPrefChanges(['top_level_pref'], [false]); | |
274 PolymerTest.async(function() { | |
275 expectEquals(false, prefs.prefs.top_level_pref.value); | |
276 | |
277 mockApi.sendPrefChanges(['top_level_pref'], [true]); | |
278 }).async(function() { | |
Dan Beam
2015/09/11 21:49:12
can't PolymerTest.async() just return a promise an
michaelpg
2015/09/13 03:01:06
no, we want to chain this logic more than once ("c
| |
279 expectEquals(true, prefs.prefs.top_level_pref.value); | |
280 done(); | |
281 }); | |
282 }); | |
283 }); | |
284 } | |
285 | |
286 return { | |
287 registerTests: registerTests, | |
288 }; | |
289 }); | |
OLD | NEW |