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