1// Copyright (c) 2011 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5// Stub out the `chrome.experimental.proxy` API 6chrome.experimental = chrome.experimental || { 7 proxy: { 8 settings: { 9 get: function() {}, 10 clear: function() {}, 11 set: function() {} 12 } 13 } 14}; 15// Stub out i18n 16chrome.i18n = chrome.i18n || { 17 getMessage: function(x) { return x; } 18}; 19// Stub out messaging and access. 20chrome.extension = chrome.extension || { 21 sendRequest: function() {}, 22 isAllowedIncognitoAccess: function(funk) { 23 funk(true); 24 } 25}; 26var fixture = document.getElementById('fixture'); 27var baselineHTML = fixture.innerHTML; 28var groupIDs = [ProxyFormController.ProxyTypes.DIRECT, 29 ProxyFormController.ProxyTypes.SYSTEM, 30 ProxyFormController.ProxyTypes.PAC, 31 ProxyFormController.ProxyTypes.FIXED]; 32 33var mockFunctionFactory = function(returnValue, logging) { 34 var called = []; 35 returnValue = returnValue || null; 36 37 var funky = function() { 38 called.push(arguments); 39 if (arguments[1] && typeof(arguments[1]) === 'function') { 40 var funk = arguments[1]; 41 funk(returnValue); 42 } 43 return returnValue; 44 }; 45 funky.getCallList = function() { return called; }; 46 funky.getValue = function() { return returnValue; }; 47 return funky; 48}; 49 50var chrome = chrome || {}; 51 52var proxyform = new Test.Unit.Runner({ 53 setup: function() { 54 fixture.innerHTML = baselineHTML; 55 this.controller_ = new ProxyFormController('proxyForm'); 56 this.clickEvent_ = document.createEvent('MouseEvents'); 57 this.clickEvent_.initMouseEvent('click', true, true, window, 58 0, 0, 0, 0, 0, false, false, false, false, 0, null); 59 // Reset mock functions. 60 chrome.experimental = { 61 proxy: { 62 settings: { 63 get: mockFunctionFactory({ 64 value: {mode: 'system' }, 65 levelOfControl: 'ControllableByThisExtension' }), 66 clear: mockFunctionFactory({ 67 value: {mode: 'system' }, 68 levelOfControl: 'ControllableByThisExtension' }), 69 set: mockFunctionFactory({ 70 value: {mode: 'system' }, 71 levelOfControl: 'ControllableByThisExtension' }) 72 } 73 } 74 }; 75 }, 76 77 teardown: function() { 78 fixture.removeChild(fixture.childNodes[0]); 79 delete(this.controller_); 80 }, 81 82 // Clicking on various bits of the interface should set correct classes, 83 // and select correct radio buttons. 84 testActivationClicks: function() { 85 var self = this; 86 var i; 87 groupIDs.forEach(function(id) { 88 var group = document.getElementById(id); 89 var all = group.querySelectorAll('*'); 90 for (i = 0; i < all.length; i++) { 91 group.classList.remove('active'); 92 all[i].dispatchEvent(self.clickEvent_); 93 self.assert(group.classList.contains('active')); 94 } 95 }); 96 }, 97 98 // Elements inside an active group should not be disabled, and vice versa 99 testDisabledElements: function() { 100 var self = this; 101 var i, j; 102 groupIDs.forEach(function(id) { 103 var group = document.getElementById(id); 104 var all = group.querySelectorAll('*'); 105 // First, check that activating a group enables its form elements 106 for (i = 0; i < all.length; i++) { 107 group.classList.remove('active'); 108 var inputs = group.querySelectorAll('input:not([type="radio"]),select'); 109 for (j = 0; j < inputs.length; j++) { 110 inputs[j].setAttribute('disabled', 'disabled'); 111 } 112 all[i].dispatchEvent(self.clickEvent_); 113 for (j = 0; j < inputs.length; j++) { 114 self.assert(!inputs[j].hasAttribute('disabled')); 115 } 116 } 117 }); 118 }, 119 120 // Clicking the "Use single proxy" checkbox should set the correct 121 // classes on the form. 122 testSingleProxyToggle: function() { 123 var group = document.getElementById( 124 ProxyFormController.ProxyTypes.FIXED); 125 var checkbox = document.getElementById('singleProxyForEverything'); 126 var section = checkbox.parentNode.parentNode; 127 // Checkbox only works in active group, `testActivationClicks` tests 128 // the inactive click behavior. 129 group.classList.add('active'); 130 131 checkbox.checked = false; 132 checkbox.dispatchEvent(this.clickEvent_); 133 this.assert(section.classList.contains('single')); 134 checkbox.dispatchEvent(this.clickEvent_); 135 this.assert(!section.classList.contains('single')); 136 }, 137 138 // On instantiation, ProxyFormController should read the current state 139 // from `chrome.experimental.getCurrentProxySettings`, and react 140 // accordingly. Let's see if that happens with the next four sets of 141 // assertsions. 142 testSetupFormSystem: function() { 143 chrome.experimental.proxy.settings.get = mockFunctionFactory({ 144 value: {mode: 'system'}, 145 levelOfControl: 'ControllableByThisExtension' 146 }); 147 148 fixture.innerHTML = baselineHTML; 149 this.controller_ = new ProxyFormController('proxyForm'); 150 // Wait for async calls to fire 151 this.wait(100, function() { 152 this.assertEqual( 153 2, 154 chrome.experimental.proxy.settings.get.getCallList().length); 155 this.assert( 156 document.getElementById(ProxyFormController.ProxyTypes.SYSTEM) 157 .classList.contains('active')); 158 }); 159 }, 160 161 testSetupFormDirect: function() { 162 chrome.experimental.proxy.settings.get = 163 mockFunctionFactory({value: {mode: 'direct'}, 164 levelOfControl: 'ControllableByThisExtension'}, true); 165 166 fixture.innerHTML = baselineHTML; 167 this.controller_ = new ProxyFormController('proxyForm'); 168 // Wait for async calls to fire 169 this.wait(100, function() { 170 this.assertEqual( 171 2, 172 chrome.experimental.proxy.settings.get.getCallList().length); 173 this.assert( 174 document.getElementById(ProxyFormController.ProxyTypes.DIRECT) 175 .classList.contains('active')); 176 }); 177 }, 178 179 testSetupFormPac: function() { 180 chrome.experimental.proxy.settings.get = 181 mockFunctionFactory({value: {mode: 'pac_script' }, 182 levelOfControl: 'ControllableByThisExtension'}); 183 184 fixture.innerHTML = baselineHTML; 185 this.controller_ = new ProxyFormController('proxyForm'); 186 // Wait for async calls to fire 187 this.wait(100, function() { 188 this.assertEqual( 189 2, 190 chrome.experimental.proxy.settings.get.getCallList().length); 191 this.assert( 192 document.getElementById(ProxyFormController.ProxyTypes.PAC) 193 .classList.contains('active')); 194 }); 195 }, 196 197 testSetupFormFixed: function() { 198 chrome.experimental.proxy.settings.get = 199 mockFunctionFactory({value: {mode: 'fixed_servers' }, 200 levelOfControl: 'ControllableByThisExtension'}); 201 202 fixture.innerHTML = baselineHTML; 203 this.controller_ = new ProxyFormController('proxyForm'); 204 // Wait for async calls to fire 205 this.wait(100, function() { 206 this.assertEqual( 207 2, 208 chrome.experimental.proxy.settings.get.getCallList().length); 209 this.assert( 210 document.getElementById(ProxyFormController.ProxyTypes.FIXED) 211 .classList.contains('active')); 212 }); 213 }, 214 215 // Test that `recalcFormValues_` correctly sets DOM field values when 216 // given a `ProxyConfig` structure 217 testRecalcFormValuesGroups: function() { 218 // Test `AUTO` normalization to `PAC` 219 this.controller_.recalcFormValues_({ 220 mode: ProxyFormController.ProxyTypes.AUTO, 221 rules: {}, 222 pacScript: '' 223 }); 224 this.assert( 225 document.getElementById(ProxyFormController.ProxyTypes.PAC) 226 .classList.contains('active')); 227 228 // DIRECT 229 this.controller_.recalcFormValues_({ 230 mode: ProxyFormController.ProxyTypes.DIRECT, 231 rules: {}, 232 pacScript: '' 233 }); 234 this.assert( 235 document.getElementById(ProxyFormController.ProxyTypes.DIRECT) 236 .classList.contains('active')); 237 238 // FIXED 239 this.controller_.recalcFormValues_({ 240 mode: ProxyFormController.ProxyTypes.FIXED, 241 rules: {}, 242 pacScript: '' 243 }); 244 this.assert( 245 document.getElementById(ProxyFormController.ProxyTypes.FIXED) 246 .classList.contains('active')); 247 248 // PAC 249 this.controller_.recalcFormValues_({ 250 mode: ProxyFormController.ProxyTypes.PAC, 251 rules: {}, 252 pacScript: '' 253 }); 254 this.assert( 255 document.getElementById(ProxyFormController.ProxyTypes.PAC) 256 .classList.contains('active')); 257 258 // SYSTEM 259 this.controller_.recalcFormValues_({ 260 mode: ProxyFormController.ProxyTypes.SYSTEM, 261 rules: {}, 262 pacScript: '' 263 }); 264 this.assert( 265 document.getElementById(ProxyFormController.ProxyTypes.SYSTEM) 266 .classList.contains('active')); 267 }, 268 269 testRecalcFormValuesFixedSingle: function() { 270 this.controller_.recalcFormValues_({ 271 mode: ProxyFormController.ProxyTypes.FIXED, 272 rules: { 273 singleProxy: { 274 scheme: 'socks5', 275 host: 'singleproxy.example.com', 276 port: '1234' 277 } 278 } 279 }); 280 var single = this.controller_.singleProxy; 281 this.assertEqual('socks5', single.scheme); 282 this.assertEqual('singleproxy.example.com', single.host); 283 this.assertEqual(1234, single.port); 284 }, 285 286 testRecalcFormValuesPacScript: function() { 287 this.controller_.recalcFormValues_({ 288 mode: ProxyFormController.ProxyTypes.PAC, 289 rules: {}, 290 pacScript: {url: 'http://example.com/this/is/a/pac.script'} 291 }); 292 this.assertEqual( 293 'http://example.com/this/is/a/pac.script', 294 document.getElementById('autoconfigURL').value); 295 }, 296 297 testRecalcFormValuesSingle: function() { 298 this.controller_.recalcFormValues_({ 299 mode: ProxyFormController.ProxyTypes.FIXED, 300 rules: { 301 singleProxy: { 302 scheme: 'https', 303 host: 'example.com', 304 port: 80 305 } 306 } 307 }); 308 // Single! 309 this.assert( 310 document.querySelector('#' + ProxyFormController.ProxyTypes.FIXED + 311 ' > section').classList.contains('single')); 312 313 var single = this.controller_.singleProxy; 314 this.assertEqual('https', single.scheme); 315 this.assertEqual('example.com', single.host); 316 this.assertEqual(80, single.port); 317 }, 318 319 testRecalcFormValuesMultiple: function() { 320 this.controller_.recalcFormValues_({ 321 mode: ProxyFormController.ProxyTypes.FIXED, 322 rules: { 323 proxyForHttp: { 324 scheme: 'http', 325 host: 'http.example.com', 326 port: 1 327 }, 328 proxyForHttps: { 329 scheme: 'https', 330 host: 'https.example.com', 331 port: 2 332 }, 333 proxyForFtp: { 334 scheme: 'socks4', 335 host: 'socks4.example.com', 336 port: 3 337 }, 338 fallbackProxy: { 339 scheme: 'socks5', 340 host: 'socks5.example.com', 341 port: 4 342 } 343 } 344 }); 345 // Not Single! 346 this.assert( 347 !document.querySelector('#' + ProxyFormController.ProxyTypes.FIXED 348 + ' > section').classList.contains('single')); 349 var server = this.controller_.singleProxy; 350 this.assertNull(server); 351 352 server = this.controller_.httpProxy; 353 this.assertEqual('http', server.scheme); 354 this.assertEqual('http.example.com', server.host); 355 this.assertEqual(1, server.port); 356 357 server = this.controller_.httpsProxy; 358 this.assertEqual('https', server.scheme); 359 this.assertEqual('https.example.com', server.host); 360 this.assertEqual(2, server.port); 361 362 server = this.controller_.ftpProxy; 363 this.assertEqual('socks4', server.scheme); 364 this.assertEqual('socks4.example.com', server.host); 365 this.assertEqual(3, server.port); 366 367 server = this.controller_.fallbackProxy; 368 this.assertEqual('socks5', server.scheme); 369 this.assertEqual('socks5.example.com', server.host); 370 this.assertEqual(4, server.port); 371 }, 372 373 testBypassList: function() { 374 this.controller_.bypassList = ['1.example.com', 375 '2.example.com', 376 '3.example.com']; 377 this.assertEnumEqual( 378 document.getElementById('bypassList').value, 379 '1.example.com, 2.example.com, 3.example.com'); 380 this.assertEnumEqual( 381 this.controller_.bypassList, 382 ['1.example.com', '2.example.com', '3.example.com']); 383 }, 384 385 // Test that "system" rules are correctly generated 386 testProxyRulesGenerationSystem: function() { 387 this.controller_.changeActive_( 388 document.getElementById(ProxyFormController.ProxyTypes.SYSTEM)); 389 390 this.assertHashEqual( 391 {mode: 'system'}, 392 this.controller_.generateProxyConfig_()); 393 }, 394 395 // Test that "direct" rules are correctly generated 396 testProxyRulesGenerationDirect: function() { 397 this.controller_.changeActive_( 398 document.getElementById(ProxyFormController.ProxyTypes.DIRECT)); 399 400 this.assertHashEqual( 401 {mode: 'direct'}, 402 this.controller_.generateProxyConfig_()); 403 }, 404 405 // Test that auto detection rules are correctly generated when "automatic" 406 // is selected, and no PAC file URL is given 407 testProxyRulesGenerationAuto: function() { 408 this.controller_.changeActive_( 409 document.getElementById(ProxyFormController.ProxyTypes.PAC)); 410 411 this.assertHashEqual( 412 {mode: 'auto_detect'}, 413 this.controller_.generateProxyConfig_()); 414 }, 415 416 // Test that PAC URL rules are correctly generated when "automatic" 417 // is selected, and a PAC file URL is given 418 testProxyRulesGenerationPacURL: function() { 419 this.controller_.changeActive_( 420 document.getElementById(ProxyFormController.ProxyTypes.PAC)); 421 this.controller_.pacURL = 'http://example.com/pac.pac'; 422 var result = this.controller_.generateProxyConfig_(); 423 this.assertEqual('pac_script', result.mode); 424 this.assertEqual('http://example.com/pac.pac', result.pacScript.url); 425 }, 426 427 // Manual PAC definitions 428 testProxyRulesGenerationPacData: function() { 429 var pacData = 'function FindProxyForURL(url,host) { return "DIRECT"; }'; 430 this.controller_.changeActive_( 431 document.getElementById(ProxyFormController.ProxyTypes.PAC)); 432 this.controller_.manualPac = pacData; 433 var result = this.controller_.generateProxyConfig_(); 434 this.assertEqual('pac_script', result.mode); 435 this.assertEqual(pacData, result.pacScript.data); 436 }, 437 438 // PAC URLs override manual PAC definitions 439 testProxyRulesGenerationPacURLOverridesData: function() { 440 this.controller_.changeActive_( 441 document.getElementById(ProxyFormController.ProxyTypes.PAC)); 442 this.controller_.pacURL = 'http://example.com/pac.pac'; 443 this.controller_.manualPac = 444 'function FindProxyForURL(url,host) { return "DIRECT"; }'; 445 var result = this.controller_.generateProxyConfig_(); 446 this.assertEqual('pac_script', result.mode); 447 this.assertEqual('http://example.com/pac.pac', result.pacScript.url); 448 }, 449 450 // Test that fixed, manual servers are correctly transformed into a 451 // `ProxyRules` structure. 452 testProxyRulesGenerationSingle: function() { 453 this.controller_.changeActive_( 454 document.getElementById(ProxyFormController.ProxyTypes.FIXED)); 455 456 this.controller_.singleProxy = { 457 scheme: 'http', 458 host: 'example.com', 459 port: '80' 460 }; 461 462 var result = this.controller_.generateProxyConfig_(); 463 this.assertEqual('fixed_servers', result.mode); 464 this.assertEqual('http', result.rules.singleProxy.scheme); 465 this.assertEqual('example.com', result.rules.singleProxy.host); 466 this.assertEqual(80, result.rules.singleProxy.port); 467 this.assertEqual(undefined, result.rules.proxyForHttp); 468 this.assertEqual(undefined, result.rules.proxyForHttps); 469 this.assertEqual(undefined, result.rules.proxyForFtp); 470 this.assertEqual(undefined, result.rules.fallbackProxy); 471 }, 472 473 // Test that proxy configuration rules are correctly generated 474 // for separate manually entered servers. 475 testProxyRulesGenerationSeparate: function() { 476 this.controller_.changeActive_( 477 document.getElementById(ProxyFormController.ProxyTypes.FIXED)); 478 479 this.controller_.singleProxy = false; 480 this.controller_.httpProxy = { 481 scheme: 'http', 482 host: 'http.example.com', 483 port: 80 484 }; 485 this.controller_.httpsProxy = { 486 scheme: 'https', 487 host: 'https.example.com', 488 port: 443 489 }; 490 this.controller_.ftpProxy = { 491 scheme: 'socks4', 492 host: 'ftp.example.com', 493 port: 80 494 }; 495 this.controller_.fallbackProxy = { 496 scheme: 'socks5', 497 host: 'fallback.example.com', 498 port: 80 499 }; 500 501 var result = this.controller_.generateProxyConfig_(); 502 this.assertEqual('fixed_servers', result.mode); 503 this.assertEqual(undefined, result.rules.singleProxy); 504 this.assertEqual('http', result.rules.proxyForHttp.scheme); 505 this.assertEqual('http.example.com', result.rules.proxyForHttp.host); 506 this.assertEqual('80', result.rules.proxyForHttp.port); 507 this.assertEqual('https', result.rules.proxyForHttps.scheme); 508 this.assertEqual('https.example.com', result.rules.proxyForHttps.host); 509 this.assertEqual('443', result.rules.proxyForHttps.port); 510 this.assertEqual('socks4', result.rules.proxyForFtp.scheme); 511 this.assertEqual('ftp.example.com', result.rules.proxyForFtp.host); 512 this.assertEqual('80', result.rules.proxyForFtp.port); 513 this.assertEqual('socks5', result.rules.fallbackProxy.scheme); 514 this.assertEqual('fallback.example.com', result.rules.fallbackProxy.host); 515 this.assertEqual('80', result.rules.fallbackProxy.port); 516 } 517}, { testLog: 'proxyformcontrollerlog' }); 518 519var c = new ProxyFormController('proxyForm'); 520