OLD | NEW |
1 /* | 1 /* |
2 * This file is part of Adblock Plus <https://adblockplus.org/>, | 2 * This file is part of Adblock Plus <https://adblockplus.org/>, |
3 * Copyright (C) 2006-present eyeo GmbH | 3 * Copyright (C) 2006-present eyeo GmbH |
4 * | 4 * |
5 * Adblock Plus is free software: you can redistribute it and/or modify | 5 * Adblock Plus is free software: you can redistribute it and/or modify |
6 * it under the terms of the GNU General Public License version 3 as | 6 * it under the terms of the GNU General Public License version 3 as |
7 * published by the Free Software Foundation. | 7 * published by the Free Software Foundation. |
8 * | 8 * |
9 * Adblock Plus is distributed in the hope that it will be useful, | 9 * Adblock Plus is distributed in the hope that it will be useful, |
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of | 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 * GNU General Public License for more details. | 12 * GNU General Public License for more details. |
13 * | 13 * |
14 * You should have received a copy of the GNU General Public License | 14 * You should have received a copy of the GNU General Public License |
15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. | 15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. |
16 */ | 16 */ |
17 | 17 |
18 "use strict"; | 18 "use strict"; |
19 | 19 |
| 20 const assert = require("assert"); |
| 21 |
20 let { | 22 let { |
21 createSandbox, setupTimerAndXMLHttp, setupRandomResult, unexpectedError, Cr | 23 createSandbox, setupTimerAndXMLHttp, setupRandomResult, Cr |
22 } = require("./_common"); | 24 } = require("./_common"); |
23 | 25 |
24 let Prefs = null; | 26 let Prefs = null; |
25 let Utils = null; | 27 let Utils = null; |
26 let Notification = null; | 28 let Notification = null; |
27 | 29 |
28 exports.setUp = function(callback) | 30 describe("Notifications", () => |
29 { | 31 { |
30 // Inject our Array and JSON to make sure that instanceof checks on arrays | 32 let runner = {}; |
31 // within the sandbox succeed even with data passed in from outside. | 33 |
32 let globals = Object.assign({Array, JSON}, | 34 beforeEach(() => |
33 setupTimerAndXMLHttp.call(this), setupRandomResult.call(this)); | 35 { |
34 | 36 runner = {}; |
35 let sandboxedRequire = createSandbox({globals}); | 37 // Inject our Array and JSON to make sure that instanceof checks on arrays |
36 ( | 38 // within the sandbox succeed even with data passed in from outside. |
37 {Prefs} = sandboxedRequire("./stub-modules/prefs"), | 39 let globals = Object.assign({Array, JSON}, |
38 {Utils} = sandboxedRequire("./stub-modules/utils"), | 40 setupTimerAndXMLHttp.call(runner), setupRandomResult.call(runner)); |
39 {Notification} = sandboxedRequire("../lib/notification") | 41 |
40 ); | 42 let sandboxedRequire = createSandbox({globals}); |
41 | 43 ( |
42 callback(); | 44 {Prefs} = sandboxedRequire("./stub-modules/prefs"), |
43 }; | 45 {Utils} = sandboxedRequire("./stub-modules/utils"), |
44 | 46 {Notification} = sandboxedRequire("../lib/notification") |
45 function showNotifications(url) | 47 ); |
46 { | 48 }); |
47 let shownNotifications = []; | 49 |
48 function showListener(notification) | 50 function showNotifications(url) |
49 { | 51 { |
50 shownNotifications.push(notification); | 52 let shownNotifications = []; |
51 Notification.markAsShown(notification.id); | 53 function showListener(notification) |
| 54 { |
| 55 shownNotifications.push(notification); |
| 56 Notification.markAsShown(notification.id); |
| 57 } |
| 58 Notification.addShowListener(showListener); |
| 59 Notification.showNext(url); |
| 60 Notification.removeShowListener(showListener); |
| 61 return shownNotifications; |
52 } | 62 } |
53 Notification.addShowListener(showListener); | 63 |
54 Notification.showNext(url); | 64 function* pairs(array) |
55 Notification.removeShowListener(showListener); | 65 { |
56 return shownNotifications; | 66 for (let element1 of array) |
57 } | 67 { |
58 | 68 for (let element2 of array) |
59 function* pairs(array) | 69 { |
60 { | 70 if (element1 != element2) |
61 for (let element1 of array) | 71 yield [element1, element2]; |
62 { | 72 } |
63 for (let element2 of array) | |
64 { | |
65 if (element1 != element2) | |
66 yield [element1, element2]; | |
67 } | 73 } |
68 } | 74 } |
69 } | 75 |
70 | 76 function registerHandler(notifications, checkCallback) |
71 function registerHandler(notifications, checkCallback) | 77 { |
72 { | 78 runner.registerHandler("/notification.json", metadata => |
73 this.registerHandler("/notification.json", metadata => | 79 { |
74 { | 80 if (checkCallback) |
75 if (checkCallback) | 81 checkCallback(metadata); |
76 checkCallback(metadata); | 82 |
77 | 83 let notification = { |
78 let notification = { | 84 version: 55, |
79 version: 55, | 85 notifications |
80 notifications | 86 }; |
81 }; | 87 |
82 | 88 return [Cr.NS_OK, 200, JSON.stringify(notification)]; |
83 return [Cr.NS_OK, 200, JSON.stringify(notification)]; | 89 }); |
84 }); | 90 } |
85 } | 91 |
86 | 92 it("No Data", () => |
87 exports.testNoData = function(test) | 93 { |
88 { | 94 assert.deepEqual(showNotifications(), [], "No notifications should be return
ed if there is no data"); |
89 test.deepEqual(showNotifications(), [], "No notifications should be returned i
f there is no data"); | 95 }); |
90 test.done(); | 96 |
91 }; | 97 it("Single Notificaion", () => |
92 | 98 { |
93 exports.testSingleNotification = function(test) | |
94 { | |
95 let information = { | |
96 id: 1, | |
97 type: "information", | |
98 message: {"en-US": "Information"} | |
99 }; | |
100 | |
101 registerHandler.call(this, [information]); | |
102 this.runScheduledTasks(1).then(() => | |
103 { | |
104 test.deepEqual(showNotifications(), [information], "The notification is show
n"); | |
105 test.deepEqual(showNotifications(), [], "Informational notifications aren't
shown more than once"); | |
106 }).catch(unexpectedError.bind(test)).then(() => test.done()); | |
107 }; | |
108 | |
109 exports.testInformationAndCritical = function(test) | |
110 { | |
111 let information = { | |
112 id: 1, | |
113 type: "information", | |
114 message: {"en-US": "Information"} | |
115 }; | |
116 let critical = { | |
117 id: 2, | |
118 type: "critical", | |
119 message: {"en-US": "Critical"} | |
120 }; | |
121 | |
122 registerHandler.call(this, [information, critical]); | |
123 this.runScheduledTasks(1).then(() => | |
124 { | |
125 test.deepEqual(showNotifications(), [critical], "The critical notification i
s given priority"); | |
126 test.deepEqual(showNotifications(), [critical], "Critical notifications can
be shown multiple times"); | |
127 }).catch(unexpectedError.bind(test)).then(() => test.done()); | |
128 }; | |
129 | |
130 exports.testNoType = function(test) | |
131 { | |
132 let information = { | |
133 id: 1, | |
134 message: {"en-US": "Information"} | |
135 }; | |
136 | |
137 registerHandler.call(this, [information]); | |
138 this.runScheduledTasks(1).then(() => | |
139 { | |
140 test.deepEqual(showNotifications(), [information], "The notification is show
n"); | |
141 test.deepEqual(showNotifications(), [], "Notification is treated as type inf
ormation"); | |
142 }).catch(unexpectedError.bind(test)).then(() => test.done()); | |
143 }; | |
144 | |
145 function testTargetSelectionFunc(propName, value, result) | |
146 { | |
147 return function(test) | |
148 { | |
149 let targetInfo = {}; | |
150 targetInfo[propName] = value; | |
151 | |
152 let information = { | 99 let information = { |
153 id: 1, | 100 id: 1, |
154 type: "information", | 101 type: "information", |
155 message: {"en-US": "Information"}, | 102 message: {"en-US": "Information"} |
156 targets: [targetInfo] | 103 }; |
157 }; | 104 |
158 | 105 registerHandler.call(runner, [information]); |
159 registerHandler.call(this, [information]); | 106 return runner.runScheduledTasks(1).then(() => |
160 this.runScheduledTasks(1).then(() => | 107 { |
161 { | 108 assert.deepEqual(showNotifications(), [information], "The notification is
shown"); |
162 let expected = (result ? [information] : []); | 109 assert.deepEqual(showNotifications(), [], "Informational notifications are
n't shown more than once"); |
163 test.deepEqual(showNotifications(), expected, "Selected notification for "
+ JSON.stringify(information.targets)); | 110 }); |
164 test.deepEqual(showNotifications(), [], "No notification on second call"); | 111 }); |
165 }).catch(unexpectedError.bind(test)).then(() => test.done()); | 112 |
166 }; | 113 it("Information and Critical", () => |
167 } | 114 { |
168 | |
169 exports.testTargetSelection = {}; | |
170 | |
171 for (let [propName, value, result] of [ | |
172 ["extension", "adblockpluschrome", true], | |
173 ["extension", "adblockplus", false], | |
174 ["extension", "adblockpluschrome2", false], | |
175 ["extensionMinVersion", "1.4", true], | |
176 ["extensionMinVersion", "1.4.1", true], | |
177 ["extensionMinVersion", "1.5", false], | |
178 ["extensionMaxVersion", "1.5", true], | |
179 ["extensionMaxVersion", "1.4.1", true], | |
180 ["extensionMaxVersion", "1.4.*", true], | |
181 ["extensionMaxVersion", "1.4", false], | |
182 ["application", "chrome", true], | |
183 ["application", "firefox", false], | |
184 ["applicationMinVersion", "27.0", true], | |
185 ["applicationMinVersion", "27", true], | |
186 ["applicationMinVersion", "26", true], | |
187 ["applicationMinVersion", "28", false], | |
188 ["applicationMinVersion", "27.1", false], | |
189 ["applicationMinVersion", "27.0b1", true], | |
190 ["applicationMaxVersion", "27.0", true], | |
191 ["applicationMaxVersion", "27", true], | |
192 ["applicationMaxVersion", "28", true], | |
193 ["applicationMaxVersion", "26", false], | |
194 ["applicationMaxVersion", "27.0b1", false], | |
195 ["platform", "chromium", true], | |
196 ["platform", "gecko", false], | |
197 ["platformMinVersion", "12.0", true], | |
198 ["platformMinVersion", "12", true], | |
199 ["platformMinVersion", "11", true], | |
200 ["platformMinVersion", "13", false], | |
201 ["platformMinVersion", "12.1", false], | |
202 ["platformMinVersion", "12.0b1", true], | |
203 ["platformMaxVersion", "12.0", true], | |
204 ["platformMaxVersion", "12", true], | |
205 ["platformMaxVersion", "13", true], | |
206 ["platformMaxVersion", "11", false], | |
207 ["platformMaxVersion", "12.0b1", false], | |
208 ["blockedTotalMin", "11", false], | |
209 ["blockedTotalMin", "10", true], | |
210 ["blockedTotalMax", "10", true], | |
211 ["blockedTotalMax", "1", false], | |
212 ["locales", ["en-US"], true], | |
213 ["locales", ["en-US", "de-DE"], true], | |
214 ["locales", ["de-DE"], false], | |
215 ["locales", ["en-GB", "de-DE"], false] | |
216 ]) | |
217 { | |
218 exports.testTargetSelection[`${propName}=${value}`] = testTargetSelectionFunc(
propName, value, result); | |
219 } | |
220 | |
221 exports.testTargetSelectionNoShowStats = { | |
222 | |
223 setUp(callback) | |
224 { | |
225 this.show_statsinpopup_orig = Prefs.show_statsinpopup; | |
226 Prefs.show_statsinpopup = false; | |
227 callback(); | |
228 }, | |
229 tearDown(callback) | |
230 { | |
231 Prefs.show_statsinpopup = this.show_statsinpopup_orig; | |
232 callback(); | |
233 } | |
234 }; | |
235 for (let [propName, value, result] of [ | |
236 ["blockedTotalMin", "10", false], | |
237 ["blockedTotalMax", "10", false] | |
238 ]) | |
239 { | |
240 exports.testTargetSelectionNoShowStats[`${propName}=${value}`] = testTargetSel
ectionFunc(propName, value, result); | |
241 } | |
242 | |
243 exports.testMultipleTargets = {}; | |
244 | |
245 for (let [[propName1, value1, result1], [propName2, value2, result2]] of pairs([ | |
246 ["extension", "adblockpluschrome", true], | |
247 ["extension", "adblockplus", false], | |
248 ["extensionMinVersion", "1.4", true], | |
249 ["extensionMinVersion", "1.5", false], | |
250 ["application", "chrome", true], | |
251 ["application", "firefox", false], | |
252 ["applicationMinVersion", "27", true], | |
253 ["applicationMinVersion", "28", false], | |
254 ["platform", "chromium", true], | |
255 ["platform", "gecko", false], | |
256 ["platformMinVersion", "12", true], | |
257 ["platformMinVersion", "13", false], | |
258 ["unknown", "unknown", false] | |
259 ])) | |
260 { | |
261 exports.testMultipleTargets[`${propName1}=${value1},${propName2}=${value2}`] =
function(test) | |
262 { | |
263 let targetInfo1 = {}; | |
264 targetInfo1[propName1] = value1; | |
265 let targetInfo2 = {}; | |
266 targetInfo2[propName2] = value2; | |
267 | |
268 let information = { | 115 let information = { |
269 id: 1, | 116 id: 1, |
270 type: "information", | 117 type: "information", |
271 message: {"en-US": "Information"}, | 118 message: {"en-US": "Information"} |
272 targets: [targetInfo1, targetInfo2] | 119 }; |
| 120 let critical = { |
| 121 id: 2, |
| 122 type: "critical", |
| 123 message: {"en-US": "Critical"} |
| 124 }; |
| 125 |
| 126 registerHandler.call(this, [information, critical]); |
| 127 return runner.runScheduledTasks(1).then(() => |
| 128 { |
| 129 assert.deepEqual(showNotifications(), [critical], "The critical notificati
on is given priority"); |
| 130 assert.deepEqual(showNotifications(), [critical], "Critical notifications
can be shown multiple times"); |
| 131 }); |
| 132 }); |
| 133 |
| 134 it("No Type", () => |
| 135 { |
| 136 let information = { |
| 137 id: 1, |
| 138 message: {"en-US": "Information"} |
273 }; | 139 }; |
274 | 140 |
275 registerHandler.call(this, [information]); | 141 registerHandler.call(this, [information]); |
276 this.runScheduledTasks(1).then(() => | 142 return runner.runScheduledTasks(1).then(() => |
277 { | 143 { |
278 let expected = (result1 || result2 ? [information] : []); | 144 assert.deepEqual(showNotifications(), [information], "The notification is
shown"); |
279 test.deepEqual(showNotifications(), expected, "Selected notification for "
+ JSON.stringify(information.targets)); | 145 assert.deepEqual(showNotifications(), [], "Notification is treated as type
information"); |
280 }).catch(unexpectedError.bind(test)).then(() => test.done()); | 146 }); |
281 }; | 147 }); |
282 } | 148 |
283 | 149 function testTargetSelectionFunc(propName, value, result) |
284 exports.testParametersSent = function(test) | 150 { |
285 { | 151 return function() |
286 Prefs.notificationdata = { | 152 { |
287 data: { | 153 let targetInfo = {}; |
288 version: "3" | 154 targetInfo[propName] = value; |
289 } | 155 |
290 }; | 156 let information = { |
291 | 157 id: 1, |
292 let parameters = null; | 158 type: "information", |
293 registerHandler.call(this, [], metadata => | 159 message: {"en-US": "Information"}, |
294 { | 160 targets: [targetInfo] |
295 parameters = decodeURI(metadata.queryString); | 161 }; |
296 }); | 162 |
297 this.runScheduledTasks(1).then(() => | 163 registerHandler.call(this, [information]); |
298 { | 164 return runner.runScheduledTasks(1).then(() => |
299 test.equal(parameters, | 165 { |
300 "addonName=adblockpluschrome&addonVersion=1.4.1&application=chrome&app
licationVersion=27.0&platform=chromium&platformVersion=12.0&lastVersion=3&downlo
adCount=0", | 166 let expected = (result ? [information] : []); |
301 "The correct parameters are sent to the server"); | 167 assert.deepEqual(showNotifications(), expected, "Selected notification f
or " + JSON.stringify(information.targets)); |
302 }).catch(unexpectedError.bind(test)).then(() => test.done()); | 168 assert.deepEqual(showNotifications(), [], "No notification on second cal
l"); |
303 }; | 169 }); |
304 | 170 }; |
305 exports.testExpirationInterval = {}; | |
306 | |
307 let initialDelay = 1 / 60; | |
308 for (let currentTest of [ | |
309 { | |
310 randomResult: 0.5, | |
311 requests: [initialDelay, initialDelay + 24, initialDelay + 48] | |
312 }, | |
313 { | |
314 randomResult: 0, // Changes interval by factor 0.8 (19.2 hours) | |
315 requests: [initialDelay, initialDelay + 20, initialDelay + 40] | |
316 }, | |
317 { | |
318 randomResult: 1, // Changes interval by factor 1.2 (28.8 hours) | |
319 requests: [initialDelay, initialDelay + 29, initialDelay + 58] | |
320 }, | |
321 { | |
322 randomResult: 0.25, // Changes interval by factor 0.9 (21.6 hours) | |
323 requests: [initialDelay, initialDelay + 22, initialDelay + 44] | |
324 }, | |
325 { | |
326 randomResult: 0.5, | |
327 skipAfter: initialDelay + 5, | |
328 skip: 10, // Short break should not increase soft expiration | |
329 requests: [initialDelay, initialDelay + 24] | |
330 }, | |
331 { | |
332 randomResult: 0.5, | |
333 skipAfter: initialDelay + 5, | |
334 skip: 30, // Long break should increase soft expiration, hitti
ng hard expiration | |
335 requests: [initialDelay, initialDelay + 48] | |
336 } | 171 } |
337 ]) | 172 |
338 { | 173 describe("Target selection", () => |
339 let testId = "Math.random() returning " + currentTest.randomResult; | 174 { |
340 if (typeof currentTest.skip != "number") | 175 for (let [propName, value, result] of [ |
341 testId += " skipping " + currentTest.skip + " hours after " + currentTest.sk
ipAfter + " hours"; | 176 ["extension", "adblockpluschrome", true], |
342 exports.testExpirationInterval[testId] = function(test) | 177 ["extension", "adblockplus", false], |
343 { | 178 ["extension", "adblockpluschrome2", false], |
344 let requests = []; | 179 ["extensionMinVersion", "1.4", true], |
345 registerHandler.call(this, [], metadata => requests.push(this.getTimeOffset(
))); | 180 ["extensionMinVersion", "1.4.1", true], |
346 | 181 ["extensionMinVersion", "1.5", false], |
347 this.randomResult = currentTest.randomResult; | 182 ["extensionMaxVersion", "1.5", true], |
348 | 183 ["extensionMaxVersion", "1.4.1", true], |
349 let maxHours = Math.round(Math.max.apply(null, currentTest.requests)) + 1; | 184 ["extensionMaxVersion", "1.4.*", true], |
350 this.runScheduledTasks(maxHours, currentTest.skipAfter, currentTest.skip).th
en(() => | 185 ["extensionMaxVersion", "1.4", false], |
351 { | 186 ["application", "chrome", true], |
352 test.deepEqual(requests, currentTest.requests, "Requests"); | 187 ["application", "firefox", false], |
353 }).catch(unexpectedError.bind(test)).then(() => test.done()); | 188 ["applicationMinVersion", "27.0", true], |
354 }; | 189 ["applicationMinVersion", "27", true], |
355 } | 190 ["applicationMinVersion", "26", true], |
356 | 191 ["applicationMinVersion", "28", false], |
357 exports.testUsingSeverityInsteadOfType = function(test) | 192 ["applicationMinVersion", "27.1", false], |
358 { | 193 ["applicationMinVersion", "27.0b1", true], |
359 let severityNotification = { | 194 ["applicationMaxVersion", "27.0", true], |
360 id: 1, | 195 ["applicationMaxVersion", "27", true], |
361 severity: "information", | 196 ["applicationMaxVersion", "28", true], |
362 message: {"en-US": "Information"} | 197 ["applicationMaxVersion", "26", false], |
363 }; | 198 ["applicationMaxVersion", "27.0b1", false], |
364 | 199 ["platform", "chromium", true], |
365 function listener(name) | 200 ["platform", "gecko", false], |
366 { | 201 ["platformMinVersion", "12.0", true], |
367 if (name !== "notificationdata") | 202 ["platformMinVersion", "12", true], |
368 return; | 203 ["platformMinVersion", "11", true], |
369 | 204 ["platformMinVersion", "13", false], |
370 Prefs.removeListener(listener); | 205 ["platformMinVersion", "12.1", false], |
371 let notification = Prefs.notificationdata.data.notifications[0]; | 206 ["platformMinVersion", "12.0b1", true], |
372 test.ok(!("severity" in notification), "Severity property was removed"); | 207 ["platformMaxVersion", "12.0", true], |
373 test.ok("type" in notification, "Type property was added"); | 208 ["platformMaxVersion", "12", true], |
374 test.equal(notification.type, severityNotification.severity, "Type property
has correct value"); | 209 ["platformMaxVersion", "13", true], |
375 test.done(); | 210 ["platformMaxVersion", "11", false], |
376 } | 211 ["platformMaxVersion", "12.0b1", false], |
377 Prefs.addListener(listener); | 212 ["blockedTotalMin", "11", false], |
378 | 213 ["blockedTotalMin", "10", true], |
379 let responseText = JSON.stringify({ | 214 ["blockedTotalMax", "10", true], |
380 notifications: [severityNotification] | 215 ["blockedTotalMax", "1", false], |
381 }); | 216 ["locales", ["en-US"], true], |
382 Notification._onDownloadSuccess({}, responseText, () => {}, () => {}); | 217 ["locales", ["en-US", "de-DE"], true], |
383 }; | 218 ["locales", ["de-DE"], false], |
384 | 219 ["locales", ["en-GB", "de-DE"], false] |
385 exports.testURLSpecificNotification = function(test) | 220 ]) |
386 { | 221 { |
387 let withURLFilterFoo = { | 222 it(`Target ${propName}=${value}`, testTargetSelectionFunc(propName, value,
result)); |
388 id: 1, | 223 } |
389 urlFilters: ["foo.com$document"] | 224 }); |
390 }; | 225 |
391 let withoutURLFilter = { | 226 describe("No show stats", () => |
392 id: 2 | 227 { |
393 }; | 228 beforeEach(() => |
394 let withURLFilterBar = { | 229 { |
395 id: 3, | 230 runner.show_statsinpopup_orig = Prefs.show_statsinpopup; |
396 urlFilters: ["bar.com$document"] | 231 Prefs.show_statsinpopup = false; |
397 }; | 232 }); |
398 let subdomainURLFilter = { | 233 |
399 id: 4, | 234 afterEach(() => |
400 urlFilters: ["||example.com$document"] | 235 { |
401 }; | 236 Prefs.show_statsinpopup = runner.show_statsinpopup_orig; |
402 | 237 }); |
403 registerHandler.call(this, [ | 238 |
404 withURLFilterFoo, | 239 for (let [propName, value, result] of [ |
405 withoutURLFilter, | 240 ["blockedTotalMin", "10", false], |
406 withURLFilterBar, | 241 ["blockedTotalMax", "10", false] |
407 subdomainURLFilter | 242 ]) |
408 ]); | 243 { |
409 this.runScheduledTasks(1).then(() => | 244 it(`Target ${propName}=${value}`, testTargetSelectionFunc(propName, value,
result)); |
410 { | 245 } |
411 test.deepEqual(showNotifications(), [withoutURLFilter], "URL-specific notifi
cations are skipped"); | 246 }); |
412 test.deepEqual(showNotifications("http://foo.com"), [withURLFilterFoo], "URL
-specific notification is retrieved"); | 247 |
413 test.deepEqual(showNotifications("http://foo.com"), [], "URL-specific notifi
cation is not retrieved"); | 248 describe("Multiple Targets", () => |
414 test.deepEqual(showNotifications("http://www.example.com"), [subdomainURLFil
ter], "URL-specific notification matches subdomain"); | 249 { |
415 }).catch(unexpectedError.bind(test)).then(() => test.done()); | 250 for (let [[propName1, value1, result1], [propName2, value2, result2]] of pai
rs([ |
416 }; | 251 ["extension", "adblockpluschrome", true], |
417 | 252 ["extension", "adblockplus", false], |
418 exports.testInterval = function(test) | 253 ["extensionMinVersion", "1.4", true], |
419 { | 254 ["extensionMinVersion", "1.5", false], |
420 let relentless = { | 255 ["application", "chrome", true], |
421 id: 3, | 256 ["application", "firefox", false], |
422 type: "relentless", | 257 ["applicationMinVersion", "27", true], |
423 interval: 100 | 258 ["applicationMinVersion", "28", false], |
424 }; | 259 ["platform", "chromium", true], |
425 | 260 ["platform", "gecko", false], |
426 registerHandler.call(this, [relentless]); | 261 ["platformMinVersion", "12", true], |
427 this.runScheduledTasks(1).then(() => | 262 ["platformMinVersion", "13", false], |
428 { | 263 ["unknown", "unknown", false] |
429 test.deepEqual(showNotifications(), [relentless], "Relentless notifications
are shown initially"); | 264 ])) |
430 }).then(() => | 265 { |
431 { | 266 it(`Targets ${propName1}=${value1},${propName2}=${value2}`, () => |
432 test.deepEqual(showNotifications(), [], "Relentless notifications are not sh
own before the interval"); | 267 { |
433 }).then(() => | 268 let targetInfo1 = {}; |
434 { | 269 targetInfo1[propName1] = value1; |
435 // Date always returns a fixed time (see setupTimerAndXMLHttp) so we | 270 let targetInfo2 = {}; |
436 // manipulate the shown data manually. | 271 targetInfo2[propName2] = value2; |
437 Prefs.notificationdata.shown[relentless.id] -= relentless.interval; | 272 |
438 test.deepEqual(showNotifications(), [relentless], "Relentless notifications
are shown after the interval"); | 273 let information = { |
439 }).catch(unexpectedError.bind(test)).then(() => test.done()); | 274 id: 1, |
440 }; | 275 type: "information", |
441 | 276 message: {"en-US": "Information"}, |
442 exports.testRelentlessNotification = function(test) | 277 targets: [targetInfo1, targetInfo2] |
443 { | 278 }; |
444 let relentless = { | 279 |
445 id: 3, | 280 registerHandler.call(this, [information]); |
446 type: "relentless", | 281 return runner.runScheduledTasks(1).then(() => |
447 interval: 100, | 282 { |
448 urlFilters: ["foo.com$document", "bar.foo$document"] | 283 let expected = (result1 || result2 ? [information] : []); |
449 }; | 284 assert.deepEqual(showNotifications(), expected, "Selected notification
for " + JSON.stringify(information.targets)); |
450 | 285 }); |
451 registerHandler.call(this, [relentless]); | 286 }); |
452 this.runScheduledTasks(1).then(() => | 287 } |
453 { | 288 }); |
454 test.deepEqual(showNotifications(), [], "Relentless notification is not show
n without URL"); | 289 |
455 test.deepEqual(showNotifications("http://bar.com"), [], "Relentless notifica
tion is not shown for a non-matching URL"); | 290 it("Parameters Sent", () => |
456 test.deepEqual(showNotifications("http://foo.com"), [relentless], "Relentles
s notification is shown for a matching URL"); | 291 { |
457 }).then(() => | 292 Prefs.notificationdata = { |
458 { | 293 data: { |
459 test.deepEqual(showNotifications("http://foo.com"), [], "Relentless notifica
tions are not shown before the interval"); | 294 version: "3" |
460 }).then(() => | 295 } |
461 { | 296 }; |
462 // Date always returns a fixed time (see setupTimerAndXMLHttp) so we | 297 |
463 // manipulate the shown data manually. | 298 let parameters = null; |
464 Prefs.notificationdata.shown[relentless.id] -= relentless.interval; | 299 registerHandler.call(this, [], metadata => |
465 test.deepEqual(showNotifications(), [], "Relentless notifications are not sh
own after the interval without URL"); | 300 { |
466 test.deepEqual(showNotifications("http://bar.com"), [], "Relentless notifica
tions are not shown after the interval for a non-matching URL"); | 301 parameters = decodeURI(metadata.queryString); |
467 test.deepEqual(showNotifications("http://bar.foo.com"), [relentless], "Relen
tless notifications are shown after the interval for a matching URL"); | 302 }); |
468 }).catch(unexpectedError.bind(test)).then(() => test.done()); | 303 return runner.runScheduledTasks(1).then(() => |
469 }; | 304 { |
470 | 305 assert.equal(parameters, |
471 exports.testGlobalOptOut = function(test) | 306 "addonName=adblockpluschrome&addonVersion=1.4.1&application=chr
ome&applicationVersion=27.0&platform=chromium&platformVersion=12.0&lastVersion=3
&downloadCount=0", |
472 { | 307 "The correct parameters are sent to the server"); |
473 Notification.toggleIgnoreCategory("*", true); | 308 }); |
474 test.ok(Prefs.notifications_ignoredcategories.indexOf("*") != -1, "Force enabl
e global opt-out"); | 309 }); |
475 Notification.toggleIgnoreCategory("*", true); | 310 |
476 test.ok(Prefs.notifications_ignoredcategories.indexOf("*") != -1, "Force enabl
e global opt-out (again)"); | 311 describe("Expiration Interval", () => |
477 Notification.toggleIgnoreCategory("*", false); | 312 { |
478 test.ok(Prefs.notifications_ignoredcategories.indexOf("*") == -1, "Force disab
le global opt-out"); | 313 let initialDelay = 1 / 60; |
479 Notification.toggleIgnoreCategory("*", false); | 314 for (let currentTest of [ |
480 test.ok(Prefs.notifications_ignoredcategories.indexOf("*") == -1, "Force disab
le global opt-out (again)"); | 315 { |
481 Notification.toggleIgnoreCategory("*"); | 316 randomResult: 0.5, |
482 test.ok(Prefs.notifications_ignoredcategories.indexOf("*") != -1, "Toggle enab
le global opt-out"); | 317 requests: [initialDelay, initialDelay + 24, initialDelay + 48] |
483 Notification.toggleIgnoreCategory("*"); | 318 }, |
484 test.ok(Prefs.notifications_ignoredcategories.indexOf("*") == -1, "Toggle disa
ble global opt-out"); | 319 { |
485 | 320 randomResult: 0, // Changes interval by factor 0.8 (19.2 hours) |
486 Prefs.notifications_showui = false; | 321 requests: [initialDelay, initialDelay + 20, initialDelay + 40] |
487 Notification.toggleIgnoreCategory("*", false); | 322 }, |
488 test.ok(!Prefs.notifications_showui, "Opt-out UI will not be shown if global o
pt-out hasn't been enabled yet"); | 323 { |
489 Notification.toggleIgnoreCategory("*", true); | 324 randomResult: 1, // Changes interval by factor 1.2 (28.8 hours) |
490 test.ok(Prefs.notifications_showui, "Opt-out UI will be shown after enabling g
lobal opt-out"); | 325 requests: [initialDelay, initialDelay + 29, initialDelay + 58] |
491 Notification.toggleIgnoreCategory("*", false); | 326 }, |
492 test.ok(Prefs.notifications_showui, "Opt-out UI will be shown after enabling g
lobal opt-out even if it got disabled again"); | 327 { |
493 | 328 randomResult: 0.25, // Changes interval by factor 0.9 (21.6 hours) |
494 let information = { | 329 requests: [initialDelay, initialDelay + 22, initialDelay + 44] |
495 id: 1, | 330 }, |
496 type: "information" | 331 { |
497 }; | 332 randomResult: 0.5, |
498 let critical = { | 333 skipAfter: initialDelay + 5, |
499 id: 2, | 334 skip: 10, // Short break should not increase soft expirati
on |
500 type: "critical" | 335 requests: [initialDelay, initialDelay + 24] |
501 }; | 336 }, |
502 let relentless = { | 337 { |
503 id: 3, | 338 randomResult: 0.5, |
504 type: "relentless" | 339 skipAfter: initialDelay + 5, |
505 }; | 340 skip: 30, // Long break should increase soft expiration, h
itting hard expiration |
506 | 341 requests: [initialDelay, initialDelay + 48] |
507 Notification.toggleIgnoreCategory("*", true); | 342 } |
508 registerHandler.call(this, [information]); | 343 ]) |
509 this.runScheduledTasks(1).then(() => | 344 { |
510 { | 345 let testId = `Math.random() returning ${currentTest.randomResult}`; |
511 test.deepEqual(showNotifications(), [], "Information notifications are ignor
ed after enabling global opt-out"); | 346 if (typeof currentTest.skip == "number") |
| 347 testId += ` skipping ${currentTest.skip} hours after ${currentTest.skipA
fter} hours`; |
| 348 |
| 349 it(testId, () => |
| 350 { |
| 351 let requests = []; |
| 352 registerHandler.call(this, [], metadata => requests.push(runner.getTimeO
ffset())); |
| 353 |
| 354 runner.randomResult = currentTest.randomResult; |
| 355 |
| 356 let maxHours = Math.round(Math.max.apply(null, currentTest.requests)) +
1; |
| 357 return runner.runScheduledTasks(maxHours, currentTest.skipAfter, current
Test.skip).then(() => |
| 358 { |
| 359 assert.deepEqual(requests, currentTest.requests, "Requests"); |
| 360 }); |
| 361 }); |
| 362 } |
| 363 }); |
| 364 |
| 365 it("Using severity instead of type", done => |
| 366 { |
| 367 let severityNotification = { |
| 368 id: 1, |
| 369 severity: "information", |
| 370 message: {"en-US": "Information"} |
| 371 }; |
| 372 |
| 373 function listener(name) |
| 374 { |
| 375 if (name !== "notificationdata") |
| 376 return; |
| 377 |
| 378 Prefs.removeListener(listener); |
| 379 let notification = Prefs.notificationdata.data.notifications[0]; |
| 380 assert.ok(!("severity" in notification), "Severity property was removed"); |
| 381 assert.ok("type" in notification, "Type property was added"); |
| 382 assert.equal(notification.type, severityNotification.severity, "Type prope
rty has correct value"); |
| 383 done(); |
| 384 } |
| 385 Prefs.addListener(listener); |
| 386 |
| 387 let responseText = JSON.stringify({ |
| 388 notifications: [severityNotification] |
| 389 }); |
| 390 Notification._onDownloadSuccess({}, responseText, () => {}, () => {}); |
| 391 }); |
| 392 |
| 393 it("URL Specific Notification", () => |
| 394 { |
| 395 let withURLFilterFoo = { |
| 396 id: 1, |
| 397 urlFilters: ["foo.com$document"] |
| 398 }; |
| 399 let withoutURLFilter = { |
| 400 id: 2 |
| 401 }; |
| 402 let withURLFilterBar = { |
| 403 id: 3, |
| 404 urlFilters: ["bar.com$document"] |
| 405 }; |
| 406 let subdomainURLFilter = { |
| 407 id: 4, |
| 408 urlFilters: ["||example.com$document"] |
| 409 }; |
| 410 |
| 411 registerHandler.call(this, [ |
| 412 withURLFilterFoo, |
| 413 withoutURLFilter, |
| 414 withURLFilterBar, |
| 415 subdomainURLFilter |
| 416 ]); |
| 417 return runner.runScheduledTasks(1).then(() => |
| 418 { |
| 419 assert.deepEqual(showNotifications(), [withoutURLFilter], "URL-specific no
tifications are skipped"); |
| 420 assert.deepEqual(showNotifications("http://foo.com"), [withURLFilterFoo],
"URL-specific notification is retrieved"); |
| 421 assert.deepEqual(showNotifications("http://foo.com"), [], "URL-specific no
tification is not retrieved"); |
| 422 assert.deepEqual(showNotifications("http://www.example.com"), [subdomainUR
LFilter], "URL-specific notification matches subdomain"); |
| 423 }); |
| 424 }); |
| 425 |
| 426 it("Interval", () => |
| 427 { |
| 428 let relentless = { |
| 429 id: 3, |
| 430 type: "relentless", |
| 431 interval: 100 |
| 432 }; |
| 433 |
| 434 registerHandler.call(this, [relentless]); |
| 435 return runner.runScheduledTasks(1).then(() => |
| 436 { |
| 437 assert.deepEqual(showNotifications(), [relentless], "Relentless notificati
ons are shown initially"); |
| 438 }).then(() => |
| 439 { |
| 440 assert.deepEqual(showNotifications(), [], "Relentless notifications are no
t shown before the interval"); |
| 441 }).then(() => |
| 442 { |
| 443 // Date always returns a fixed time (see setupTimerAndXMLHttp) so we |
| 444 // manipulate the shown data manually. |
| 445 Prefs.notificationdata.shown[relentless.id] -= relentless.interval; |
| 446 assert.deepEqual(showNotifications(), [relentless], "Relentless notificati
ons are shown after the interval"); |
| 447 }); |
| 448 }); |
| 449 |
| 450 it("Relentless notification", () => |
| 451 { |
| 452 let relentless = { |
| 453 id: 3, |
| 454 type: "relentless", |
| 455 interval: 100, |
| 456 urlFilters: ["foo.com$document", "bar.foo$document"] |
| 457 }; |
| 458 |
| 459 registerHandler.call(this, [relentless]); |
| 460 return runner.runScheduledTasks(1).then(() => |
| 461 { |
| 462 assert.deepEqual(showNotifications(), [], "Relentless notification is not
shown without URL"); |
| 463 assert.deepEqual(showNotifications("http://bar.com"), [], "Relentless noti
fication is not shown for a non-matching URL"); |
| 464 assert.deepEqual(showNotifications("http://foo.com"), [relentless], "Relen
tless notification is shown for a matching URL"); |
| 465 }).then(() => |
| 466 { |
| 467 assert.deepEqual(showNotifications("http://foo.com"), [], "Relentless noti
fications are not shown before the interval"); |
| 468 }).then(() => |
| 469 { |
| 470 // Date always returns a fixed time (see setupTimerAndXMLHttp) so we |
| 471 // manipulate the shown data manually. |
| 472 Prefs.notificationdata.shown[relentless.id] -= relentless.interval; |
| 473 assert.deepEqual(showNotifications(), [], "Relentless notifications are no
t shown after the interval without URL"); |
| 474 assert.deepEqual(showNotifications("http://bar.com"), [], "Relentless noti
fications are not shown after the interval for a non-matching URL"); |
| 475 assert.deepEqual(showNotifications("http://bar.foo.com"), [relentless], "R
elentless notifications are shown after the interval for a matching URL"); |
| 476 }); |
| 477 }); |
| 478 |
| 479 it("Global opt-out", () => |
| 480 { |
| 481 Notification.toggleIgnoreCategory("*", true); |
| 482 assert.ok(Prefs.notifications_ignoredcategories.indexOf("*") != -1, "Force e
nable global opt-out"); |
| 483 Notification.toggleIgnoreCategory("*", true); |
| 484 assert.ok(Prefs.notifications_ignoredcategories.indexOf("*") != -1, "Force e
nable global opt-out (again)"); |
512 Notification.toggleIgnoreCategory("*", false); | 485 Notification.toggleIgnoreCategory("*", false); |
513 test.deepEqual(showNotifications(), [information], "Information notification
s are shown after disabling global opt-out"); | 486 assert.ok(Prefs.notifications_ignoredcategories.indexOf("*") == -1, "Force d
isable global opt-out"); |
514 | 487 Notification.toggleIgnoreCategory("*", false); |
| 488 assert.ok(Prefs.notifications_ignoredcategories.indexOf("*") == -1, "Force d
isable global opt-out (again)"); |
| 489 Notification.toggleIgnoreCategory("*"); |
| 490 assert.ok(Prefs.notifications_ignoredcategories.indexOf("*") != -1, "Toggle
enable global opt-out"); |
| 491 Notification.toggleIgnoreCategory("*"); |
| 492 assert.ok(Prefs.notifications_ignoredcategories.indexOf("*") == -1, "Toggle
disable global opt-out"); |
| 493 |
| 494 Prefs.notifications_showui = false; |
| 495 Notification.toggleIgnoreCategory("*", false); |
| 496 assert.ok(!Prefs.notifications_showui, "Opt-out UI will not be shown if glob
al opt-out hasn't been enabled yet"); |
515 Notification.toggleIgnoreCategory("*", true); | 497 Notification.toggleIgnoreCategory("*", true); |
516 Prefs.notificationdata = {}; | 498 assert.ok(Prefs.notifications_showui, "Opt-out UI will be shown after enabli
ng global opt-out"); |
517 registerHandler.call(this, [critical]); | 499 Notification.toggleIgnoreCategory("*", false); |
518 return this.runScheduledTasks(1); | 500 assert.ok(Prefs.notifications_showui, "Opt-out UI will be shown after enabli
ng global opt-out even if it got disabled again"); |
519 }).then(() => | 501 |
520 { | 502 let information = { |
521 test.deepEqual(showNotifications(), [critical], "Critical notifications are
not ignored"); | 503 id: 1, |
522 | 504 type: "information" |
523 Prefs.notificationdata = {}; | 505 }; |
524 registerHandler.call(this, [relentless]); | 506 let critical = { |
525 return this.runScheduledTasks(1); | 507 id: 2, |
526 }).then(() => | 508 type: "critical" |
527 { | 509 }; |
528 test.deepEqual(showNotifications(), [relentless], "Relentless notifications
are not ignored"); | 510 let relentless = { |
529 }).catch(unexpectedError.bind(test)).then(() => test.done()); | 511 id: 3, |
530 }; | 512 type: "relentless" |
531 | 513 }; |
532 exports.testMessageWithoutLocalization = function(test) | 514 |
533 { | 515 Notification.toggleIgnoreCategory("*", true); |
534 let notification = {message: "non-localized"}; | 516 registerHandler.call(this, [information]); |
535 let texts = Notification.getLocalizedTexts(notification); | 517 return runner.runScheduledTasks(1).then(() => |
536 test.equal(texts.message, "non-localized"); | 518 { |
537 test.done(); | 519 assert.deepEqual(showNotifications(), [], "Information notifications are i
gnored after enabling global opt-out"); |
538 }; | 520 Notification.toggleIgnoreCategory("*", false); |
539 | 521 assert.deepEqual(showNotifications(), [information], "Information notifica
tions are shown after disabling global opt-out"); |
540 exports.testLanguageOnly = function(test) | 522 |
541 { | 523 Notification.toggleIgnoreCategory("*", true); |
542 let notification = {message: {fr: "fr"}}; | 524 Prefs.notificationdata = {}; |
543 Utils.appLocale = "fr"; | 525 registerHandler.call(this, [critical]); |
544 let texts = Notification.getLocalizedTexts(notification); | 526 return runner.runScheduledTasks(1); |
545 test.equal(texts.message, "fr"); | 527 }).then(() => |
546 Utils.appLocale = "fr-CA"; | 528 { |
547 texts = Notification.getLocalizedTexts(notification); | 529 assert.deepEqual(showNotifications(), [critical], "Critical notifications
are not ignored"); |
548 test.equal(texts.message, "fr"); | 530 |
549 test.done(); | 531 Prefs.notificationdata = {}; |
550 }; | 532 registerHandler.call(this, [relentless]); |
551 | 533 return runner.runScheduledTasks(1); |
552 exports.testLanguageAndCountry = function(test) | 534 }).then(() => |
553 { | 535 { |
554 let notification = {message: {"fr": "fr", "fr-CA": "fr-CA"}}; | 536 assert.deepEqual(showNotifications(), [relentless], "Relentless notificati
ons are not ignored"); |
555 Utils.appLocale = "fr-CA"; | 537 }); |
556 let texts = Notification.getLocalizedTexts(notification); | 538 }); |
557 test.equal(texts.message, "fr-CA"); | 539 |
558 Utils.appLocale = "fr"; | 540 it("Message without localization", () => |
559 texts = Notification.getLocalizedTexts(notification); | 541 { |
560 test.equal(texts.message, "fr"); | 542 let notification = {message: "non-localized"}; |
561 test.done(); | 543 let texts = Notification.getLocalizedTexts(notification); |
562 }; | 544 assert.equal(texts.message, "non-localized"); |
563 | 545 }); |
564 exports.testMissingTranslation = function(test) | 546 |
565 { | 547 it("Language only", () => |
566 let notification = {message: {"en-US": "en-US"}}; | 548 { |
567 Utils.appLocale = "fr"; | 549 let notification = {message: {fr: "fr"}}; |
568 let texts = Notification.getLocalizedTexts(notification); | 550 Utils.appLocale = "fr"; |
569 test.equal(texts.message, "en-US"); | 551 let texts = Notification.getLocalizedTexts(notification); |
570 test.done(); | 552 assert.equal(texts.message, "fr"); |
571 }; | 553 Utils.appLocale = "fr-CA"; |
| 554 texts = Notification.getLocalizedTexts(notification); |
| 555 assert.equal(texts.message, "fr"); |
| 556 }); |
| 557 |
| 558 it("Language and Country", () => |
| 559 { |
| 560 let notification = {message: {"fr": "fr", "fr-CA": "fr-CA"}}; |
| 561 Utils.appLocale = "fr-CA"; |
| 562 let texts = Notification.getLocalizedTexts(notification); |
| 563 assert.equal(texts.message, "fr-CA"); |
| 564 Utils.appLocale = "fr"; |
| 565 texts = Notification.getLocalizedTexts(notification); |
| 566 assert.equal(texts.message, "fr"); |
| 567 }); |
| 568 |
| 569 it("Missing translation", () => |
| 570 { |
| 571 let notification = {message: {"en-US": "en-US"}}; |
| 572 Utils.appLocale = "fr"; |
| 573 let texts = Notification.getLocalizedTexts(notification); |
| 574 assert.equal(texts.message, "en-US"); |
| 575 }); |
| 576 }); |
OLD | NEW |