LEFT | RIGHT |
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-2017 eyeo GmbH | 3 * Copyright (C) 2006-2017 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 let Filter = require("filterClasses").Filter; | 20 let Filter = require("filterClasses").Filter; |
21 let ContentBlockerList = require("../lib/abp2blocklist.js").ContentBlockerList; | 21 let ContentBlockerList = require("../lib/abp2blocklist.js").ContentBlockerList; |
22 | 22 |
23 function testRules(test, filters, expected, transformFunction, | 23 function runTest(test, assertions) |
24 {merge = false} = {}) | |
25 { | 24 { |
26 let blockerList = new ContentBlockerList(); | 25 // All the assertions are run in parallel but we wait for all of them to |
| 26 // finish before moving on to the next test. |
| 27 test.expect(assertions.length); |
| 28 Promise.all(assertions).then(() => test.done()); |
| 29 } |
| 30 |
| 31 function testRules(test, filters, expected, transformFunction, options) |
| 32 { |
| 33 let blockerList = new ContentBlockerList(options); |
27 for (let filter of filters) | 34 for (let filter of filters) |
28 blockerList.addFilter(Filter.fromText(filter)); | 35 blockerList.addFilter(Filter.fromText(filter)); |
29 | 36 |
30 let rules = blockerList.generateRules({merge}); | 37 return blockerList.generateRules().then(rules => |
31 if (transformFunction) | 38 { |
32 rules = transformFunction(rules); | 39 if (transformFunction) |
33 | 40 rules = transformFunction(rules); |
34 test.deepEqual(rules, expected); | 41 |
| 42 test.deepEqual(rules, expected); |
| 43 }); |
35 } | 44 } |
36 | 45 |
37 exports.generateRules = { | 46 exports.generateRules = { |
38 testElementHiding: function(test) | 47 testElementHiding: function(test) |
39 { | 48 { |
40 testRules(test, ["##.whatever"], [ | 49 runTest(test, [ |
41 {trigger: {"url-filter": "^https?://", | 50 testRules(test, ["##.whatever"], [ |
42 "url-filter-is-case-sensitive": true}, | 51 {trigger: {"url-filter": "^https?://", |
43 action: {type: "css-display-none", selector: ".whatever"}} | 52 "url-filter-is-case-sensitive": true}, |
44 ]); | 53 action: {type: "css-display-none", selector: ".whatever"}} |
45 testRules(test, ["test.com##.whatever"], [ | 54 ]), |
46 {trigger: {"url-filter": "^https?://([^/:]*\\.)?test\\.com[/:]", | 55 testRules(test, ["test.com##.whatever"], [ |
47 "url-filter-is-case-sensitive": true}, | 56 {trigger: {"url-filter": "^https?://([^/:]*\\.)?test\\.com[/:]", |
48 action: {type: "css-display-none", selector: ".whatever"}} | 57 "url-filter-is-case-sensitive": true}, |
49 ]); | 58 action: {type: "css-display-none", selector: ".whatever"}} |
50 | 59 ]) |
51 test.done(); | 60 ]); |
52 }, | 61 }, |
53 | 62 |
54 testElementHidingExceptions: function(test) | 63 testElementHidingExceptions: function(test) |
55 { | 64 { |
56 testRules(test, ["#@#whatever"], []); | 65 runTest(test, [ |
57 testRules(test, ["test.com#@#whatever"], []); | 66 testRules(test, [ |
58 testRules(test, ["~test.com#@#whatever"], []); | 67 "##.whatever", |
59 | 68 "test.com,anothertest.com###something", |
60 // We currently completely ignore any element hiding filters that have the | 69 "@@||special.test.com^$elemhide", |
61 // same selector as an element hiding exception. In these examples #whatever | 70 "@@||test.com^$generichide", |
62 // should be hidden for all domains not ending in test.com instead of | 71 "@@||anothertest.com^$elemhide", |
63 // nowhere! | 72 "@@^something^$elemhide", |
64 testRules(test, ["test.com#@#whatever", "##whatever"], []); | 73 "@@^anything^$generichide" |
65 testRules(test, ["~test.com##whatever"], []); | 74 ], [ |
66 | 75 ["^https?://", ["*test.com", "*special.test.com", "*anothertest.com"]], |
67 test.done(); | 76 ["^https?://([^/:]*\\.)?test\\.com[/:]", ["*special.test.com"]] |
| 77 ], rules => rules.map(rule => [rule.trigger["url-filter"], |
| 78 rule.trigger["unless-domain"]])), |
| 79 |
| 80 testRules(test, ["#@#whatever"], []), |
| 81 testRules(test, ["test.com#@#whatever"], []), |
| 82 testRules(test, ["~test.com#@#whatever"], []), |
| 83 |
| 84 // We currently completely ignore any element hiding filters that have the |
| 85 // same selector as an element hiding exception. In these examples |
| 86 // #whatever should be hidden for all domains not ending in test.com |
| 87 // instead of nowhere! |
| 88 testRules(test, ["test.com#@#whatever", "##whatever"], []), |
| 89 testRules(test, ["~test.com##whatever"], []) |
| 90 ]); |
68 }, | 91 }, |
69 | 92 |
70 testRequestFilters: function(test) | 93 testRequestFilters: function(test) |
71 { | 94 { |
72 testRules(test, ["/foo", "||test.com", "http://example.com/foo"], [ | 95 runTest(test, [ |
73 {trigger: {"url-filter": "^https?://.*/foo", | 96 testRules(test, [ |
74 "resource-type": ["image", "style-sheet", "script", "font", | 97 "/foo", "||test.com^", "http://example.com/foo", "^foo^" |
75 "media", "raw", "document"]}, | 98 ], [ |
76 action: {type: "block"}}, | 99 { |
77 {trigger: {"url-filter": "^https?://([^/]+\\.)?test\\.com", | 100 trigger: { |
78 "url-filter-is-case-sensitive": true, | 101 "url-filter": "^[^:]+:(//)?.*/foo", |
79 "resource-type": ["image", "style-sheet", "script", "font", | 102 "resource-type": ["image", "style-sheet", "script", "font", |
80 "media", "raw", "document"]}, | 103 "media", "raw"] |
81 action: {type: "block"}}, | 104 }, |
82 {trigger: {"url-filter": "http://example\\.com/foo", | 105 action: {type: "block"} |
83 "resource-type": ["image", "style-sheet", "script", "font", | 106 }, |
84 "media", "raw", "document"]}, | 107 { |
85 action: {type: "block"}} | 108 trigger: { |
86 ]); | 109 "url-filter": |
87 | 110 "^[^:]+:(//)?([^/]+\\.)?test\\.com([^-_.%a-z0-9].*)?$", |
88 testRules(test, ["||example.com"], [ | 111 "url-filter-is-case-sensitive": true, |
89 {trigger: {"url-filter": "^https?://([^/]+\\.)?example\\.com", | 112 "resource-type": ["image", "style-sheet", "script", "font", |
90 "url-filter-is-case-sensitive": true, | 113 "media", "raw", "document"], |
91 "resource-type": ["image", "style-sheet", "script", "font", | 114 "unless-top-url": [ |
92 "media", "raw", "document"]}, | 115 "^[^:]+:(//)?([^/]+\\.)?test\\.com([^-_.%a-z0-9].*)?$" |
93 | 116 ], |
94 action: {type: "block"}} | 117 "top-url-filter-is-case-sensitive": true |
95 ]); | 118 }, |
96 | 119 action: {type: "block"} |
97 // Rules which would match no resource-types shouldn't be generated. | 120 }, |
98 testRules(test, ["foo$document", "||foo.com$document"], []); | 121 { |
99 | 122 trigger: { |
100 test.done(); | 123 "url-filter": "^http://example\\.com/foo", |
| 124 "resource-type": ["image", "style-sheet", "script", "font", |
| 125 "media", "raw", "document"], |
| 126 "unless-top-url": ["^http://example\\.com/foo"] |
| 127 }, |
| 128 action: {type: "block"} |
| 129 }, |
| 130 { |
| 131 trigger: { |
| 132 "url-filter": "^[^:]+:(//)?.*http://example\\.com/foo", |
| 133 "resource-type": ["image", "style-sheet", "script", "font", |
| 134 "media", "raw", "document"], |
| 135 "unless-top-url": ["^[^:]+:(//)?.*http://example\\.com/foo"] |
| 136 }, |
| 137 action: {type: "block"} |
| 138 }, |
| 139 { |
| 140 trigger: { |
| 141 "url-filter": |
| 142 "^[^:]+:(//)?(.*[^-_.%A-Za-z0-9])?foo([^-_.%A-Za-z0-9].*)?$", |
| 143 "resource-type": ["image", "style-sheet", "script", "font", |
| 144 "media", "raw"] |
| 145 }, |
| 146 action: {type: "block"} |
| 147 } |
| 148 ]), |
| 149 |
| 150 testRules(test, ["||example.com"], [ |
| 151 {trigger: {"url-filter": "^[^:]+:(//)?([^/]+\\.)?example\\.com", |
| 152 "url-filter-is-case-sensitive": true, |
| 153 "resource-type": ["image", "style-sheet", "script", "font", |
| 154 "media", "raw", "document"], |
| 155 "unless-top-url": ["^[^:]+:(//)?([^/]+\\.)?example\\.com"], |
| 156 "top-url-filter-is-case-sensitive": true}, |
| 157 |
| 158 action: {type: "block"}} |
| 159 ]), |
| 160 |
| 161 // Rules which would match no resource-types shouldn't be generated. |
| 162 testRules(test, ["foo$document", "||foo.com$document"], []) |
| 163 ]); |
101 }, | 164 }, |
102 | 165 |
103 testRequestFilterExceptions: function(test) | 166 testRequestFilterExceptions: function(test) |
104 { | 167 { |
105 testRules(test, ["@@example.com"], [ | 168 runTest(test, [ |
106 {trigger: {"url-filter": "^https?://.*example\\.com", | 169 testRules(test, ["@@example.com"], [ |
107 "resource-type": ["image", "style-sheet", "script", "font", | 170 {trigger: {"url-filter": "^[^:]+:(//)?.*example\\.com", |
108 "media", "raw", "document"]}, | 171 "resource-type": ["image", "style-sheet", "script", "font", |
109 action: {type: "ignore-previous-rules"}} | 172 "media", "raw", "document"]}, |
110 ]); | 173 action: {type: "ignore-previous-rules"}} |
111 | 174 ]), |
112 testRules(test, ["@@||example.com"], [ | 175 |
113 {trigger: {"url-filter": "^https?://([^/]+\\.)?example\\.com", | 176 testRules(test, ["@@||example.com"], [ |
114 "url-filter-is-case-sensitive": true, | 177 {trigger: {"url-filter": "^[^:]+:(//)?([^/]+\\.)?example\\.com", |
115 "resource-type": ["image", "style-sheet", "script", "font", | 178 "url-filter-is-case-sensitive": true, |
116 "media", "raw", "document"]}, | 179 "resource-type": ["image", "style-sheet", "script", "font", |
117 action: {type: "ignore-previous-rules"}} | 180 "media", "raw", "document"]}, |
118 ]); | 181 action: {type: "ignore-previous-rules"}} |
119 | 182 ]) |
120 test.done(); | 183 ]); |
121 }, | 184 }, |
122 | 185 |
123 testElementIDattributeFormat: function(test) | 186 testElementIDattributeFormat: function(test) |
124 { | 187 { |
125 testRules(test, | 188 runTest(test, [ |
126 ["###example", "test.com###EXAMPLE"], | 189 testRules(test, |
127 ["[id=example]", "[id=EXAMPLE]"], | 190 ["###example", "test.com###EXAMPLE"], |
128 rules => rules.map(rule => rule.action.selector)); | 191 ["[id=example]", "[id=EXAMPLE]"], |
129 | 192 rules => rules.map(rule => rule.action.selector)) |
130 test.done(); | 193 ]); |
131 }, | 194 }, |
132 | 195 |
133 testDomainWhitelisting: function(test) | 196 testDomainWhitelisting: function(test) |
134 { | 197 { |
135 testRules(test, ["@@||example.com^$document"], [ | 198 runTest(test, [ |
136 {trigger: {"url-filter": ".*", | 199 testRules(test, ["@@||example.com^$document"], [ |
137 "if-domain": ["example.com", "www.example.com"]}, | 200 { |
138 action: {type: "ignore-previous-rules"}} | 201 trigger: { |
139 ]); | 202 "url-filter": ".*", |
140 testRules(test, ["@@||example.com^$document,image"], [ | 203 "if-domain": ["*example.com"] |
141 {trigger: {"url-filter": ".*", | 204 }, |
142 "if-domain": ["example.com", "www.example.com"]}, | 205 action: {type: "ignore-previous-rules"} |
143 action: {type: "ignore-previous-rules"}}, | 206 } |
144 {trigger: {"url-filter": "^https?://([^/]+\\.)?example\\.com", | 207 ]), |
145 "url-filter-is-case-sensitive": true, | 208 testRules(test, ["@@||example.com^$document,image"], [ |
146 "resource-type": ["image"]}, | 209 { |
147 action: {type: "ignore-previous-rules"}} | 210 trigger: { |
148 ]); | 211 "url-filter": ".*", |
149 testRules(test, ["@@||example.com/path^$font,document"], [ | 212 "if-domain": ["*example.com"] |
150 {trigger: {"url-filter": "^https?://([^/]+\\.)?example\\.com/path", | 213 }, |
151 "resource-type": ["font"]}, | 214 action: {type: "ignore-previous-rules"} |
152 action: {type: "ignore-previous-rules"}} | 215 }, |
153 ]); | 216 { |
154 | 217 trigger: { |
155 test.done(); | 218 "url-filter": |
| 219 "^https?://([^/]+\\.)?example\\.com([^-_.%a-z0-9].*)?$", |
| 220 "url-filter-is-case-sensitive": true, |
| 221 "resource-type": ["image"] |
| 222 }, |
| 223 action: {type: "ignore-previous-rules"} |
| 224 } |
| 225 ]), |
| 226 testRules(test, ["@@||example.com/path^$font,document"], [ |
| 227 { |
| 228 trigger: { |
| 229 "url-filter": |
| 230 "^https?://([^/]+\\.)?example\\.com/path([^-_.%A-Za-z0-9].*)?$", |
| 231 "resource-type": ["font"] |
| 232 }, |
| 233 action: {type: "ignore-previous-rules"} |
| 234 } |
| 235 ]) |
| 236 ]); |
| 237 }, |
| 238 |
| 239 testGenericblockExceptions: function(test) |
| 240 { |
| 241 runTest(test, [ |
| 242 testRules(test, ["^ad.jpg|", "@@||example.com^$genericblock"], |
| 243 [[undefined, ["*example.com"]]], |
| 244 rules => rules.map(rule => [rule.trigger["if-domain"], |
| 245 rule.trigger["unless-domain"]])), |
| 246 testRules(test, ["^ad.jpg|$domain=test.com", |
| 247 "@@||example.com^$genericblock"], |
| 248 [[["*test.com"], undefined]], |
| 249 rules => rules.map(rule => [rule.trigger["if-domain"], |
| 250 rule.trigger["unless-domain"]])), |
| 251 testRules(test, ["^ad.jpg|$domain=~test.com", |
| 252 "@@||example.com^$genericblock"], |
| 253 [[undefined, ["*test.com", "*example.com"]]], |
| 254 rules => rules.map(rule => [rule.trigger["if-domain"], |
| 255 rule.trigger["unless-domain"]])) |
| 256 ]); |
156 }, | 257 }, |
157 | 258 |
158 testRuleOrdering: function(test) | 259 testRuleOrdering: function(test) |
159 { | 260 { |
160 testRules( | 261 runTest(test, [ |
161 test, | 262 testRules( |
162 ["/ads.jpg", "@@example.com", "test.com#@#foo", "##bar"], | 263 test, |
163 ["css-display-none", "block", "ignore-previous-rules"], | 264 ["/ads.jpg", "@@example.com", "test.com#@#foo", "##bar"], |
164 rules => rules.map(rule => rule.action.type) | 265 ["css-display-none", "block", "ignore-previous-rules"], |
165 ); | 266 rules => rules.map(rule => rule.action.type) |
166 testRules( | 267 ), |
167 test, | 268 testRules( |
168 ["@@example.com", "##bar", "/ads.jpg", "test.com#@#foo"], | 269 test, |
169 ["css-display-none", "block", "ignore-previous-rules"], | 270 ["@@example.com", "##bar", "/ads.jpg", "test.com#@#foo"], |
170 rules => rules.map(rule => rule.action.type) | 271 ["css-display-none", "block", "ignore-previous-rules"], |
171 ); | 272 rules => rules.map(rule => rule.action.type) |
172 | 273 ) |
173 test.done(); | 274 ]); |
174 }, | 275 }, |
175 | 276 |
176 testRequestTypeMapping: function(test) | 277 testRequestTypeMapping: function(test) |
177 { | 278 { |
178 testRules( | 279 runTest(test, [ |
179 test, | 280 testRules( |
180 ["1", "2$image", "3$stylesheet", "4$script", "5$font", "6$media", | 281 test, |
181 "7$popup", "8$object", "9$object_subrequest", "10$xmlhttprequest", | 282 ["1", "2$image", "3$stylesheet", "4$script", "5$font", "6$media", |
182 "11$ping", "12$subdocument", "13$other", "14$IMAGE", | 283 "7$popup", "8$object", "9$object_subrequest", "10$xmlhttprequest", |
183 "15$script,PING,Popup", "16$~image"], | 284 "11$websocket", "12$webrtc", |
184 [["image", "style-sheet", "script", "font", "media", "raw", "document" ], | 285 "13$ping", "14$subdocument", "15$other", "16$IMAGE", |
185 ["image"], | 286 "17$script,PING,Popup", "18$~image"], |
186 ["style-sheet"], | 287 [["image", "style-sheet", "script", "font", "media", "raw"], |
187 ["script"], | 288 ["image"], |
188 ["font"], | 289 ["style-sheet"], |
189 ["media"], | 290 ["script"], |
190 ["popup"], | 291 ["font"], |
191 ["media"], | 292 ["media"], |
192 ["raw"], | 293 ["popup"], |
193 ["raw"], | 294 ["media"], |
194 ["raw"], | 295 ["raw"], |
195 ["document"], | 296 ["raw"], |
196 ["raw"], | 297 ["raw"], // WebSocket |
197 ["image"], | 298 ["raw"], // WebRTC: STUN |
198 ["script", "popup", "raw" ], | 299 ["raw"], // WebRTC: TURN |
199 ["style-sheet", "script", "font", "media", "raw", "document"]], | 300 ["raw"], |
200 rules => rules.map(rule => rule.trigger["resource-type"]) | 301 ["raw"], |
201 ); | 302 ["image"], |
202 | 303 ["script", "popup", "raw" ], |
203 test.done(); | 304 ["style-sheet", "script", "font", "media", "raw"]], |
| 305 rules => rules.map(rule => rule.trigger["resource-type"]) |
| 306 ) |
| 307 ]); |
204 }, | 308 }, |
205 | 309 |
206 testUnsupportedfilters: function(test) | 310 testUnsupportedfilters: function(test) |
207 { | 311 { |
208 // These types of filters are currently completely unsupported. | 312 runTest(test, [ |
209 testRules(test, ["foo$sitekey=bar", "@@foo$genericblock", | 313 // These types of filters are currently completely unsupported. |
210 "@@bar$generichide"], []); | 314 testRules(test, ["foo$sitekey=bar"], []) |
211 | 315 ]); |
212 test.done(); | |
213 }, | 316 }, |
214 | 317 |
215 testFilterOptions: function(test) | 318 testFilterOptions: function(test) |
216 { | 319 { |
217 testRules(test, ["1$domain=foo.com"], ["foo.com", "www.foo.com"], | 320 runTest(test, [ |
218 rules => rules[0]["trigger"]["if-domain"]); | 321 testRules(test, ["1$domain=foo.com"], ["*foo.com"], |
219 testRules(test, ["2$domain=third-party"], ["third-party"], | 322 rules => rules[0]["trigger"]["if-domain"]), |
220 rules => rules[0]["trigger"]["if-domain"]); | 323 testRules(test, ["2$third-party"], ["third-party"], |
221 testRules(test, ["foo$match_case"], true, | 324 rules => rules[0]["trigger"]["load-type"]), |
222 rules => rules[0]["trigger"]["url-filter-is-case-sensitive"]); | 325 testRules(test, ["foo$match_case"], true, |
223 | 326 rules => rules[0]["trigger"]["url-filter-is-case-sensitive"]), |
224 test.done(); | 327 |
| 328 // Test subdomain exceptions. |
| 329 testRules(test, ["1$domain=foo.com|~bar.foo.com"], |
| 330 ["foo.com", "www.foo.com"], |
| 331 rules => rules[0]["trigger"]["if-domain"]), |
| 332 testRules(test, ["1$domain=foo.com|~www.foo.com"], |
| 333 ["foo.com"], |
| 334 rules => rules[0]["trigger"]["if-domain"]) |
| 335 ]); |
225 }, | 336 }, |
226 | 337 |
227 testUnicode: function(test) | 338 testUnicode: function(test) |
228 { | 339 { |
229 testRules(test, ["$domain=🐈.cat"], ["xn--zn8h.cat", "www.xn--zn8h.cat"], | 340 runTest(test, [ |
230 rules => rules[0]["trigger"]["if-domain"]); | 341 testRules(test, ["$domain=🐈.cat"], ["*xn--zn8h.cat"], |
231 testRules(test, ["🐈$domain=🐈.cat"], []); | 342 rules => rules[0]["trigger"]["if-domain"]), |
232 testRules(test, ["###🐈"], []); | 343 testRules(test, ["||🐈"], "^[^:]+:(//)?([^/]+\\.)?xn--zn8h", |
233 | 344 rules => rules[0]["trigger"]["url-filter"]), |
234 test.done(); | 345 testRules(test, ["🐈$domain=🐈.cat"], "^[^:]+:(//)?.*%F0%9F%90%88", |
| 346 rules => rules[0]["trigger"]["url-filter"]), |
| 347 testRules(test, ["🐈%F0%9F%90%88$domain=🐈.cat"], |
| 348 "^[^:]+:(//)?.*%F0%9F%90%88%F0%9F%90%88", |
| 349 rules => rules[0]["trigger"]["url-filter"]), |
| 350 testRules(test, ["###🐈"], "[id=🐈]", |
| 351 rules => rules[0]["action"]["selector"]) |
| 352 ]); |
| 353 }, |
| 354 |
| 355 testWebSocket: function(test) |
| 356 { |
| 357 runTest(test, [ |
| 358 testRules(test, ["foo$websocket"], [ |
| 359 {trigger: {"url-filter": "^wss?://.*foo", "resource-type": ["raw"]}, |
| 360 action: {type: "block"}} |
| 361 ]) |
| 362 ]); |
| 363 }, |
| 364 |
| 365 testWebRTC: function(test) |
| 366 { |
| 367 runTest(test, [ |
| 368 testRules(test, ["foo$webrtc"], [ |
| 369 {trigger: {"url-filter": "^stuns?:.*foo", "resource-type": ["raw"]}, |
| 370 action: {type: "block"}}, |
| 371 {trigger: {"url-filter": "^turns?:.*foo", "resource-type": ["raw"]}, |
| 372 action: {type: "block"}} |
| 373 ]) |
| 374 ]); |
235 }, | 375 }, |
236 | 376 |
237 testMerging: function(test) | 377 testMerging: function(test) |
238 { | 378 { |
239 // Single character substitutions, deletions, and insertions. | 379 runTest(test, [ |
240 testRules(test, ["/ads", "/adv"], ["^https?://.*/ad[sv]"], | 380 // Single character substitutions, deletions, and insertions. |
241 rules => rules.map(rule => rule.trigger["url-filter"]), | 381 testRules(test, ["/ads", "/adv"], ["^[^:]+:(//)?.*/ad[sv]"], |
242 {merge: true}); | 382 rules => rules.map(rule => rule.trigger["url-filter"]), |
243 testRules(test, ["/ad", "/ads"], ["^https?://.*/ads?"], | 383 {merge: "all"}), |
244 rules => rules.map(rule => rule.trigger["url-filter"]), | 384 testRules(test, ["/ads", "/advs"], ["^[^:]+:(//)?.*/adv?s"], |
245 {merge: true}); | 385 rules => rules.map(rule => rule.trigger["url-filter"]), |
246 testRules(test, ["/ads", "/ad"], ["^https?://.*/ads?"], | 386 {merge: "all"}), |
247 rules => rules.map(rule => rule.trigger["url-filter"]), | 387 testRules(test, ["/advs", "/ads"], ["^[^:]+:(//)?.*/adv?s"], |
248 {merge: true}); | 388 rules => rules.map(rule => rule.trigger["url-filter"]), |
249 testRules(test, ["/ads", "/adv", "/ad"], ["^https?://.*/ad[sv]?"], | 389 {merge: "all"}), |
250 rules => rules.map(rule => rule.trigger["url-filter"]), | 390 testRules(test, ["/adts", "/advs", "/ads"], ["^[^:]+:(//)?.*/ad[tv]?s"], |
251 {merge: true}); | 391 rules => rules.map(rule => rule.trigger["url-filter"]), |
252 testRules(test, ["/ad", "/ads", "/adv"], ["^https?://.*/ad[sv]?"], | 392 {merge: "all"}), |
253 rules => rules.map(rule => rule.trigger["url-filter"]), | 393 testRules(test, ["/ads", "/adts", "/advs"], ["^[^:]+:(//)?.*/ad[tv]?s"], |
254 {merge: true}); | 394 rules => rules.map(rule => rule.trigger["url-filter"]), |
255 testRules(test, ["/ads", "/ad", "/adv"], ["^https?://.*/ad[sv]?"], | 395 {merge: "all"}), |
256 rules => rules.map(rule => rule.trigger["url-filter"]), | 396 testRules(test, ["/adts", "/ads", "/advs"], ["^[^:]+:(//)?.*/ad[tv]?s"], |
257 {merge: true}); | 397 rules => rules.map(rule => rule.trigger["url-filter"]), |
258 testRules(test, ["/a", "/ad", "/ads", "/adv"], | 398 {merge: "all"}), |
259 ["^https?://.*/a", "^https?://.*/ad[sv]?"], | 399 testRules(test, ["/ax", "/adx", "/adsx", "/advx"], |
260 rules => rules.map(rule => rule.trigger["url-filter"]), | 400 ["^[^:]+:(//)?.*/ax", "^[^:]+:(//)?.*/ad[sv]?x"], |
261 {merge: true}); | 401 rules => rules.map(rule => rule.trigger["url-filter"]), |
262 testRules(test, ["/ad", "/a", "/ads", "/adv"], | 402 {merge: "all"}), |
263 ["^https?://.*/a", "^https?://.*/ad[sv]?"], | 403 testRules(test, ["/adx", "/ax", "/adsx", "/advx"], |
264 rules => rules.map(rule => rule.trigger["url-filter"]), | 404 ["^[^:]+:(//)?.*/ax", "^[^:]+:(//)?.*/ad[sv]?x"], |
265 {merge: true}); | 405 rules => rules.map(rule => rule.trigger["url-filter"]), |
266 testRules(test, ["/ads", "/adv", "/ad", "/a"], | 406 {merge: "all"}), |
267 ["^https?://.*/ad[sv]?", "^https?://.*/a"], | 407 testRules(test, ["/adsx", "/advx", "/adx", "/ax"], |
268 rules => rules.map(rule => rule.trigger["url-filter"]), | 408 ["^[^:]+:(//)?.*/ad[sv]?x", "^[^:]+:(//)?.*/ax"], |
269 {merge: true}); | 409 rules => rules.map(rule => rule.trigger["url-filter"]), |
270 testRules(test, ["/ads", "/adv", "/a", "/ad"], | 410 {merge: "all"}), |
271 ["^https?://.*/ad[sv]?", "^https?://.*/a"], | 411 testRules(test, ["/adsx", "/advx", "/ax", "/adx"], |
272 rules => rules.map(rule => rule.trigger["url-filter"]), | 412 ["^[^:]+:(//)?.*/ad[sv]?x", "^[^:]+:(//)?.*/ax"], |
273 {merge: true}); | 413 rules => rules.map(rule => rule.trigger["url-filter"]), |
274 testRules(test, ["/ad", "/a", "/ads", "/adv", "/adx"], | 414 {merge: "all"}), |
275 ["^https?://.*/a", "^https?://.*/ad[svx]?"], | 415 testRules(test, ["/ad-", "/a-", "/ads-", "/adv-", "/adx-"], |
276 rules => rules.map(rule => rule.trigger["url-filter"]), | 416 ["^[^:]+:(//)?.*/a-", "^[^:]+:(//)?.*/ad[svx]?-"], |
277 {merge: true}); | 417 rules => rules.map(rule => rule.trigger["url-filter"]), |
278 testRules(test, ["/ads", "/a", "/ad", "/adv", "/adx"], | 418 {merge: "all"}), |
279 ["^https?://.*/ad[svx]?", "^https?://.*/a"], | 419 testRules(test, ["/ads-", "/a-", "/ad-", "/adv-", "/adx-"], |
280 rules => rules.map(rule => rule.trigger["url-filter"]), | 420 ["^[^:]+:(//)?.*/ad[svx]?-", "^[^:]+:(//)?.*/a-"], |
281 {merge: true}); | 421 rules => rules.map(rule => rule.trigger["url-filter"]), |
282 | 422 {merge: "all"}), |
283 // Multiple character deletions and insertions. | 423 |
284 testRules(test, ["/ad", "/adxsi"], | 424 // Multiple character deletions and insertions. |
285 ["^https?://.*/ad(xsi)?"], | 425 testRules(test, ["/ads", "/adxis"], |
286 rules => rules.map(rule => rule.trigger["url-filter"]), | 426 ["^[^:]+:(//)?.*/ad(xi)?s"], |
287 {merge: true}); | 427 rules => rules.map(rule => rule.trigger["url-filter"]), |
288 testRules(test, ["/adxsi", "/xsi"], | 428 {merge: "all"}), |
289 ["^https?://.*/(ad)?xsi"], | 429 testRules(test, ["/adxsi", "/xsi"], |
290 rules => rules.map(rule => rule.trigger["url-filter"]), | 430 ["^[^:]+:(//)?.*/(ad)?xsi"], |
291 {merge: true}); | 431 rules => rules.map(rule => rule.trigger["url-filter"]), |
292 testRules(test, ["/adxsi", "/ai"], | 432 {merge: "all"}), |
293 ["^https?://.*/a(dxs)?i"], | 433 testRules(test, ["/adxsi", "/ai"], |
294 rules => rules.map(rule => rule.trigger["url-filter"]), | 434 ["^[^:]+:(//)?.*/a(dxs)?i"], |
295 {merge: true}); | 435 rules => rules.map(rule => rule.trigger["url-filter"]), |
296 | 436 {merge: "all"}), |
297 // Both single and multiple character edits combined. | 437 |
298 testRules(test, ["/ad", "/adxsi", "/xsi", "/axsi", "/bxsi"], | 438 // Both single and multiple character edits combined. |
299 ["^https?://.*/ad(xsi)?", "^https?://.*/[ab]?xsi"], | 439 testRules(test, ["/adq", "/adxsiq", "/xsiq", "/axsiq", "/bxsiq"], |
300 rules => rules.map(rule => rule.trigger["url-filter"]), | 440 ["^[^:]+:(//)?.*/ad(xsi)?q", "^[^:]+:(//)?.*/[ab]?xsiq"], |
301 {merge: true}); | 441 rules => rules.map(rule => rule.trigger["url-filter"]), |
302 | 442 {merge: "all"}), |
303 testRules(test, ["/ads", "/a", "/ad", "/adv", "/adx", "/adxs"], | 443 |
304 ["^https?://.*/ad[svx]?", "^https?://.*/a(dxs)?"], | 444 testRules(test, ["/adsq", "/aq", "/adq", "/advq", "/adxq", "/adxsq"], |
305 rules => rules.map(rule => rule.trigger["url-filter"]), | 445 ["^[^:]+:(//)?.*/ad[svx]?q", "^[^:]+:(//)?.*/a(dxs)?q"], |
306 {merge: true}); | 446 rules => rules.map(rule => rule.trigger["url-filter"]), |
307 testRules(test, ["/adxs", "/a", "/ad", "/ads", "/adv", "/adx"], | 447 {merge: "all"}), |
308 ["^https?://.*/a(dxs)?", "^https?://.*/ad[svx]?"], | 448 testRules(test, ["/adxsq", "/aq", "/adq", "/adsq", "/advq", "/adxq"], |
309 rules => rules.map(rule => rule.trigger["url-filter"]), | 449 ["^[^:]+:(//)?.*/a(dxs)?q", "^[^:]+:(//)?.*/ad[svx]?q"], |
310 {merge: true}); | 450 rules => rules.map(rule => rule.trigger["url-filter"]), |
311 testRules(test, ["/adxs", "/a", "/ads", "/ad", "/adv", "/adx"], | 451 {merge: "all"}), |
312 ["^https?://.*/a(dxs)?", "^https?://.*/ad[svx]?"], | 452 testRules(test, ["/adxsq", "/aq", "/adsq", "/adq", "/advq", "/adxq"], |
313 rules => rules.map(rule => rule.trigger["url-filter"]), | 453 ["^[^:]+:(//)?.*/a(dxs)?q", "^[^:]+:(//)?.*/ad[svx]?q"], |
314 {merge: true}); | 454 rules => rules.map(rule => rule.trigger["url-filter"]), |
315 testRules(test, ["/adxs", "/a", "/ads", "/adv", "/ad", "/adx"], | 455 {merge: "all"}), |
316 ["^https?://.*/a(dxs)?", "^https?://.*/ad[svx]?"], | 456 testRules(test, ["/adxsq", "/aq", "/adsq", "/advq", "/adq", "/adxq"], |
317 rules => rules.map(rule => rule.trigger["url-filter"]), | 457 ["^[^:]+:(//)?.*/a(dxs)?q", "^[^:]+:(//)?.*/ad[svx]?q"], |
318 {merge: true}); | 458 rules => rules.map(rule => rule.trigger["url-filter"]), |
319 testRules(test, ["/adsxi", "/adxs", "/a", "/ads", "/adv", "/ad", "/adx"], | 459 {merge: "all"}), |
320 ["^https?://.*/a(dsxi)?", "^https?://.*/adxs", | 460 testRules(test, ["/adsxiq", "/adxsq", "/aq", "/adsq", "/advq", "/adq", |
321 "^https?://.*/ad[svx]?"], | 461 "/adxq"], |
322 rules => rules.map(rule => rule.trigger["url-filter"]), | 462 ["^[^:]+:(//)?.*/a(dsxi)?q", "^[^:]+:(//)?.*/adxsq", |
323 {merge: true}); | 463 "^[^:]+:(//)?.*/ad[svx]?q"], |
324 testRules(test, ["/adxsi", "/adsxi", "/adxs", "/a", "/ads", "/adv", "/ad", | 464 rules => rules.map(rule => rule.trigger["url-filter"]), |
325 "/adx"], | 465 {merge: "all"}), |
326 ["^https?://.*/adxsi?", "^https?://.*/a(dsxi)?", | 466 testRules(test, ["/adxsiq", "/adsxiq", "/adxsq", "/aq", "/adsq", "/advq", |
327 "^https?://.*/ad[svx]?"], | 467 "/adq", "/adxq"], |
328 rules => rules.map(rule => rule.trigger["url-filter"]), | 468 ["^[^:]+:(//)?.*/adxsi?q", "^[^:]+:(//)?.*/a(dsxi)?q", |
329 {merge: true}); | 469 "^[^:]+:(//)?.*/ad[svx]?q"], |
330 | 470 rules => rules.map(rule => rule.trigger["url-filter"]), |
331 // Given the 6 rules "ads", "bds", "adv", "bdv", "adx", and "bdx", we want | 471 {merge: "all"}), |
332 // the 2 rules "ad[svx]" and "bd[svx]", not the 3 rules "[ab]ds", "[ab]dv", | 472 |
333 // and "[ab]dx" | 473 // Given the 6 rules "adsi", "bdsi", "advi", "bdvi", "adxi", and "bdxi", |
334 testRules(test, ["/ads", "/bds", "/adv", "/bdv", "/adx", "/bdx"], | 474 // we want the 2 rules "ad[svx]i" and "bd[svx]i", not the 3 rules |
335 ["^https?://.*/ad[svx]", "^https?://.*/bd[svx]"], | 475 // "[ab]dsi", "[ab]dvi", and "[ab]dxi" |
336 rules => rules.map(rule => rule.trigger["url-filter"]), | 476 testRules(test, ["/adsi", "/bdsi", "/advi", "/bdvi", "/adxi", "/bdxi"], |
337 {merge: true}); | 477 ["^[^:]+:(//)?.*/ad[svx]i", "^[^:]+:(//)?.*/bd[svx]i"], |
338 testRules(test, ["/ads", "/bds", "/adv", "/bdv", "/bdx"], | 478 rules => rules.map(rule => rule.trigger["url-filter"]), |
339 ["^https?://.*/ad[sv]", "^https?://.*/bd[svx]"], | 479 {merge: "all"}), |
340 rules => rules.map(rule => rule.trigger["url-filter"]), | 480 testRules(test, ["/adsi", "/bdsi", "/advi", "/bdvi", "/bdxi"], |
341 {merge: true}); | 481 ["^[^:]+:(//)?.*/ad[sv]i", "^[^:]+:(//)?.*/bd[svx]i"], |
342 | 482 rules => rules.map(rule => rule.trigger["url-filter"]), |
343 // Make sure there's no merge where there are special characters in the | 483 {merge: "all"}), |
344 // delta. | 484 |
345 testRules(test, ["/ads?", "/ads"], | 485 // Make sure there's no merge where there are special characters in the |
346 ["^https?://.*/ads\\?", "^https?://.*/ads"], | 486 // delta. |
347 rules => rules.map(rule => rule.trigger["url-filter"]), | 487 testRules(test, ["/ads?q", "/adsq"], |
348 {merge: true}); | 488 ["^[^:]+:(//)?.*/ads\\?q", "^[^:]+:(//)?.*/adsq"], |
349 testRules(test, ["/ads?", "/ads-"], | 489 rules => rules.map(rule => rule.trigger["url-filter"]), |
350 ["^https?://.*/ads\\?", "^https?://.*/ads-"], | 490 {merge: "all"}), |
351 rules => rules.map(rule => rule.trigger["url-filter"]), | 491 testRules(test, ["/ads?", "/ads-"], |
352 {merge: true}); | 492 ["^[^:]+:(//)?.*/ads\\?", "^[^:]+:(//)?.*/ads-"], |
353 testRules(test, ["/ads?-", "/ads-"], | 493 rules => rules.map(rule => rule.trigger["url-filter"]), |
354 ["^https?://.*/ads\\?-", "^https?://.*/ads-"], | 494 {merge: "all"}), |
355 rules => rules.map(rule => rule.trigger["url-filter"]), | 495 testRules(test, ["/ads?-", "/ads-"], |
356 {merge: true}); | 496 ["^[^:]+:(//)?.*/ads\\?-", "^[^:]+:(//)?.*/ads-"], |
357 | 497 rules => rules.map(rule => rule.trigger["url-filter"]), |
358 test.done(); | 498 {merge: "all"}), |
| 499 |
| 500 // Redundant rules should be discarded. |
| 501 testRules(test, ["/ad", "/ads", "/advertisement"], |
| 502 ["^[^:]+:(//)?.*/ad"], |
| 503 rules => rules.map(rule => rule.trigger["url-filter"]), |
| 504 {merge: "all"}) |
| 505 ]); |
359 } | 506 } |
360 }; | 507 }; |
LEFT | RIGHT |