1/* 2 * Copyright (C) 2010 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 32/** 33 * @fileoverview This file contains small testing framework along with the 34 * test suite for the frontend. These tests are a part of the continues build 35 * and are executed by the devtools_sanity_unittest.cc as a part of the 36 * Interactive UI Test suite. 37 * FIXME: change field naming style to use trailing underscore. 38 */ 39 40if (window.domAutomationController) { 41 42var ___interactiveUiTestsMode = true; 43 44/** 45 * Test suite for interactive UI tests. 46 * @constructor 47 */ 48TestSuite = function() 49{ 50 this.controlTaken_ = false; 51 this.timerId_ = -1; 52}; 53 54 55/** 56 * Reports test failure. 57 * @param {string} message Failure description. 58 */ 59TestSuite.prototype.fail = function(message) 60{ 61 if (this.controlTaken_) 62 this.reportFailure_(message); 63 else 64 throw message; 65}; 66 67 68/** 69 * Equals assertion tests that expected === actual. 70 * @param {!Object} expected Expected object. 71 * @param {!Object} actual Actual object. 72 * @param {string} opt_message User message to print if the test fails. 73 */ 74TestSuite.prototype.assertEquals = function(expected, actual, opt_message) 75{ 76 if (expected !== actual) { 77 var message = "Expected: '" + expected + "', but was '" + actual + "'"; 78 if (opt_message) 79 message = opt_message + "(" + message + ")"; 80 this.fail(message); 81 } 82}; 83 84/** 85 * True assertion tests that value == true. 86 * @param {!Object} value Actual object. 87 * @param {string} opt_message User message to print if the test fails. 88 */ 89TestSuite.prototype.assertTrue = function(value, opt_message) 90{ 91 this.assertEquals(true, !!value, opt_message); 92}; 93 94 95/** 96 * HasKey assertion tests that object has given key. 97 * @param {!Object} object 98 * @param {string} key 99 */ 100TestSuite.prototype.assertHasKey = function(object, key) 101{ 102 if (!object.hasOwnProperty(key)) 103 this.fail("Expected object to contain key '" + key + "'"); 104}; 105 106 107/** 108 * Contains assertion tests that string contains substring. 109 * @param {string} string Outer. 110 * @param {string} substring Inner. 111 */ 112TestSuite.prototype.assertContains = function(string, substring) 113{ 114 if (string.indexOf(substring) === -1) 115 this.fail("Expected to: '" + string + "' to contain '" + substring + "'"); 116}; 117 118 119/** 120 * Takes control over execution. 121 */ 122TestSuite.prototype.takeControl = function() 123{ 124 this.controlTaken_ = true; 125 // Set up guard timer. 126 var self = this; 127 this.timerId_ = setTimeout(function() { 128 self.reportFailure_("Timeout exceeded: 20 sec"); 129 }, 20000); 130}; 131 132 133/** 134 * Releases control over execution. 135 */ 136TestSuite.prototype.releaseControl = function() 137{ 138 if (this.timerId_ !== -1) { 139 clearTimeout(this.timerId_); 140 this.timerId_ = -1; 141 } 142 this.reportOk_(); 143}; 144 145 146/** 147 * Async tests use this one to report that they are completed. 148 */ 149TestSuite.prototype.reportOk_ = function() 150{ 151 window.domAutomationController.send("[OK]"); 152}; 153 154 155/** 156 * Async tests use this one to report failures. 157 */ 158TestSuite.prototype.reportFailure_ = function(error) 159{ 160 if (this.timerId_ !== -1) { 161 clearTimeout(this.timerId_); 162 this.timerId_ = -1; 163 } 164 window.domAutomationController.send("[FAILED] " + error); 165}; 166 167 168/** 169 * Runs all global functions starting with "test" as unit tests. 170 */ 171TestSuite.prototype.runTest = function(testName) 172{ 173 try { 174 this[testName](); 175 if (!this.controlTaken_) 176 this.reportOk_(); 177 } catch (e) { 178 this.reportFailure_(e); 179 } 180}; 181 182 183/** 184 * @param {string} panelName Name of the panel to show. 185 */ 186TestSuite.prototype.showPanel = function(panelName) 187{ 188 // Open Scripts panel. 189 var button = document.getElementById("tab-" + panelName); 190 button.selectTabForTest(); 191 this.assertEquals(WebInspector.panels[panelName], WebInspector.inspectorView.currentPanel()); 192}; 193 194 195/** 196 * Overrides the method with specified name until it's called first time. 197 * @param {!Object} receiver An object whose method to override. 198 * @param {string} methodName Name of the method to override. 199 * @param {!Function} override A function that should be called right after the 200 * overridden method returns. 201 * @param {boolean} opt_sticky Whether restore original method after first run 202 * or not. 203 */ 204TestSuite.prototype.addSniffer = function(receiver, methodName, override, opt_sticky) 205{ 206 var orig = receiver[methodName]; 207 if (typeof orig !== "function") 208 this.fail("Cannot find method to override: " + methodName); 209 var test = this; 210 receiver[methodName] = function(var_args) { 211 try { 212 var result = orig.apply(this, arguments); 213 } finally { 214 if (!opt_sticky) 215 receiver[methodName] = orig; 216 } 217 // In case of exception the override won't be called. 218 try { 219 override.apply(this, arguments); 220 } catch (e) { 221 test.fail("Exception in overriden method '" + methodName + "': " + e); 222 } 223 return result; 224 }; 225}; 226 227/** 228 * Waits for current throttler invocations, if any. 229 * @param {!WebInspector.Throttler} throttler 230 * @param {!function} callback 231 */ 232TestSuite.prototype.waitForThrottler = function(throttler, callback) 233{ 234 var test = this; 235 var scheduleShouldFail = true; 236 test.addSniffer(throttler, "schedule", onSchedule); 237 238 function hasSomethingScheduled() 239 { 240 return throttler._isRunningProcess || throttler._process; 241 } 242 243 function checkState() 244 { 245 if (!hasSomethingScheduled()) { 246 scheduleShouldFail = false; 247 callback(); 248 return; 249 } 250 251 test.addSniffer(throttler, "_processCompletedForTests", checkState); 252 } 253 254 function onSchedule() 255 { 256 if (scheduleShouldFail) 257 test.fail("Unexpected Throttler.schedule"); 258 } 259 260 checkState(); 261}; 262 263// UI Tests 264 265 266/** 267 * Tests that scripts tab can be open and populated with inspected scripts. 268 */ 269TestSuite.prototype.testShowScriptsTab = function() 270{ 271 this.showPanel("sources"); 272 var test = this; 273 // There should be at least main page script. 274 this._waitUntilScriptsAreParsed(["debugger_test_page.html"], 275 function() { 276 test.releaseControl(); 277 }); 278 // Wait until all scripts are added to the debugger. 279 this.takeControl(); 280}; 281 282 283/** 284 * Tests that scripts tab is populated with inspected scripts even if it 285 * hadn't been shown by the moment inspected paged refreshed. 286 * @see http://crbug.com/26312 287 */ 288TestSuite.prototype.testScriptsTabIsPopulatedOnInspectedPageRefresh = function() 289{ 290 var test = this; 291 this.assertEquals(WebInspector.panels.elements, WebInspector.inspectorView.currentPanel(), "Elements panel should be current one."); 292 293 WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.GlobalObjectCleared, waitUntilScriptIsParsed); 294 295 // Reload inspected page. It will reset the debugger agent. 296 test.evaluateInConsole_("window.location.reload(true);", function(resultText) {}); 297 298 function waitUntilScriptIsParsed() 299 { 300 WebInspector.debuggerModel.removeEventListener(WebInspector.DebuggerModel.Events.GlobalObjectCleared, waitUntilScriptIsParsed); 301 test.showPanel("sources"); 302 test._waitUntilScriptsAreParsed(["debugger_test_page.html"], 303 function() { 304 test.releaseControl(); 305 }); 306 } 307 308 // Wait until all scripts are added to the debugger. 309 this.takeControl(); 310}; 311 312 313/** 314 * Tests that scripts list contains content scripts. 315 */ 316TestSuite.prototype.testContentScriptIsPresent = function() 317{ 318 this.showPanel("sources"); 319 var test = this; 320 321 test._waitUntilScriptsAreParsed( 322 ["page_with_content_script.html", "simple_content_script.js"], 323 function() { 324 test.releaseControl(); 325 }); 326 327 // Wait until all scripts are added to the debugger. 328 this.takeControl(); 329}; 330 331 332/** 333 * Tests that scripts are not duplicaed on Scripts tab switch. 334 */ 335TestSuite.prototype.testNoScriptDuplicatesOnPanelSwitch = function() 336{ 337 var test = this; 338 339 // There should be two scripts: one for the main page and another 340 // one which is source of console API(see 341 // InjectedScript._ensureCommandLineAPIInstalled). 342 var expectedScriptsCount = 2; 343 var parsedScripts = []; 344 345 this.showPanel("sources"); 346 347 function switchToElementsTab() { 348 test.showPanel("elements"); 349 setTimeout(switchToScriptsTab, 0); 350 } 351 352 function switchToScriptsTab() { 353 test.showPanel("sources"); 354 setTimeout(checkScriptsPanel, 0); 355 } 356 357 function checkScriptsPanel() { 358 test.assertTrue(test._scriptsAreParsed(["debugger_test_page.html"]), "Some scripts are missing."); 359 checkNoDuplicates(); 360 test.releaseControl(); 361 } 362 363 function checkNoDuplicates() { 364 var uiSourceCodes = test.nonAnonymousUISourceCodes_(); 365 for (var i = 0; i < uiSourceCodes.length; i++) { 366 var scriptName = uiSourceCodes[i].url; 367 for (var j = i + 1; j < uiSourceCodes.length; j++) 368 test.assertTrue(scriptName !== uiSourceCodes[j].url, "Found script duplicates: " + test.uiSourceCodesToString_(uiSourceCodes)); 369 } 370 } 371 372 test._waitUntilScriptsAreParsed( 373 ["debugger_test_page.html"], 374 function() { 375 checkNoDuplicates(); 376 setTimeout(switchToElementsTab, 0); 377 }); 378 379 380 // Wait until all scripts are added to the debugger. 381 this.takeControl(); 382}; 383 384 385// Tests that debugger works correctly if pause event occurs when DevTools 386// frontend is being loaded. 387TestSuite.prototype.testPauseWhenLoadingDevTools = function() 388{ 389 this.showPanel("sources"); 390 391 // Script execution can already be paused. 392 if (WebInspector.debuggerModel.debuggerPausedDetails) 393 return; 394 395 this._waitForScriptPause(this.releaseControl.bind(this)); 396 this.takeControl(); 397}; 398 399 400// Tests that pressing "Pause" will pause script execution if the script 401// is already running. 402TestSuite.prototype.testPauseWhenScriptIsRunning = function() 403{ 404 this.showPanel("sources"); 405 406 this.evaluateInConsole_( 407 'setTimeout("handleClick()" , 0)', 408 didEvaluateInConsole.bind(this)); 409 410 function didEvaluateInConsole(resultText) { 411 this.assertTrue(!isNaN(resultText), "Failed to get timer id: " + resultText); 412 // Wait for some time to make sure that inspected page is running the 413 // infinite loop. 414 setTimeout(testScriptPause.bind(this), 300); 415 } 416 417 function testScriptPause() { 418 // The script should be in infinite loop. Click "Pause" button to 419 // pause it and wait for the result. 420 WebInspector.panels.sources._pauseButton.element.click(); 421 422 this._waitForScriptPause(this.releaseControl.bind(this)); 423 } 424 425 this.takeControl(); 426}; 427 428 429/** 430 * Tests network size. 431 */ 432TestSuite.prototype.testNetworkSize = function() 433{ 434 var test = this; 435 436 function finishResource(resource, finishTime) 437 { 438 test.assertEquals(219, resource.transferSize, "Incorrect total encoded data length"); 439 test.assertEquals(25, resource.resourceSize, "Incorrect total data length"); 440 test.releaseControl(); 441 } 442 443 this.addSniffer(WebInspector.NetworkDispatcher.prototype, "_finishNetworkRequest", finishResource); 444 445 // Reload inspected page to sniff network events 446 test.evaluateInConsole_("window.location.reload(true);", function(resultText) {}); 447 448 this.takeControl(); 449}; 450 451 452/** 453 * Tests network sync size. 454 */ 455TestSuite.prototype.testNetworkSyncSize = function() 456{ 457 var test = this; 458 459 function finishResource(resource, finishTime) 460 { 461 test.assertEquals(219, resource.transferSize, "Incorrect total encoded data length"); 462 test.assertEquals(25, resource.resourceSize, "Incorrect total data length"); 463 test.releaseControl(); 464 } 465 466 this.addSniffer(WebInspector.NetworkDispatcher.prototype, "_finishNetworkRequest", finishResource); 467 468 // Send synchronous XHR to sniff network events 469 test.evaluateInConsole_("var xhr = new XMLHttpRequest(); xhr.open(\"GET\", \"chunked\", false); xhr.send(null);", function() {}); 470 471 this.takeControl(); 472}; 473 474 475/** 476 * Tests network raw headers text. 477 */ 478TestSuite.prototype.testNetworkRawHeadersText = function() 479{ 480 var test = this; 481 482 function finishResource(resource, finishTime) 483 { 484 if (!resource.responseHeadersText) 485 test.fail("Failure: resource does not have response headers text"); 486 test.assertEquals(164, resource.responseHeadersText.length, "Incorrect response headers text length"); 487 test.releaseControl(); 488 } 489 490 this.addSniffer(WebInspector.NetworkDispatcher.prototype, "_finishNetworkRequest", finishResource); 491 492 // Reload inspected page to sniff network events 493 test.evaluateInConsole_("window.location.reload(true);", function(resultText) {}); 494 495 this.takeControl(); 496}; 497 498 499/** 500 * Tests network timing. 501 */ 502TestSuite.prototype.testNetworkTiming = function() 503{ 504 var test = this; 505 506 function finishResource(resource, finishTime) 507 { 508 // Setting relaxed expectations to reduce flakiness. 509 // Server sends headers after 100ms, then sends data during another 100ms. 510 // We expect these times to be measured at least as 70ms. 511 test.assertTrue(resource.timing.receiveHeadersEnd - resource.timing.connectStart >= 70, 512 "Time between receiveHeadersEnd and connectStart should be >=70ms, but was " + 513 "receiveHeadersEnd=" + resource.timing.receiveHeadersEnd + ", connectStart=" + resource.timing.connectStart + "."); 514 test.assertTrue(resource.responseReceivedTime - resource.startTime >= 0.07, 515 "Time between responseReceivedTime and startTime should be >=0.07s, but was " + 516 "responseReceivedTime=" + resource.responseReceivedTime + ", startTime=" + resource.startTime + "."); 517 test.assertTrue(resource.endTime - resource.startTime >= 0.14, 518 "Time between endTime and startTime should be >=0.14s, but was " + 519 "endtime=" + resource.endTime + ", startTime=" + resource.startTime + "."); 520 521 test.releaseControl(); 522 } 523 524 this.addSniffer(WebInspector.NetworkDispatcher.prototype, "_finishNetworkRequest", finishResource); 525 526 // Reload inspected page to sniff network events 527 test.evaluateInConsole_("window.location.reload(true);", function(resultText) {}); 528 529 this.takeControl(); 530}; 531 532 533TestSuite.prototype.testConsoleOnNavigateBack = function() 534{ 535 if (WebInspector.multitargetConsoleModel.messages().length === 1) 536 firstConsoleMessageReceived.call(this); 537 else 538 WebInspector.multitargetConsoleModel.addEventListener(WebInspector.ConsoleModel.Events.MessageAdded, firstConsoleMessageReceived, this); 539 540 function firstConsoleMessageReceived() { 541 WebInspector.multitargetConsoleModel.removeEventListener(WebInspector.ConsoleModel.Events.MessageAdded, firstConsoleMessageReceived, this); 542 this.evaluateInConsole_("clickLink();", didClickLink.bind(this)); 543 } 544 545 function didClickLink() { 546 // Check that there are no new messages(command is not a message). 547 this.assertEquals(3, WebInspector.multitargetConsoleModel.messages().length); 548 this.evaluateInConsole_("history.back();", didNavigateBack.bind(this)); 549 } 550 551 function didNavigateBack() 552 { 553 // Make sure navigation completed and possible console messages were pushed. 554 this.evaluateInConsole_("void 0;", didCompleteNavigation.bind(this)); 555 } 556 557 function didCompleteNavigation() { 558 this.assertEquals(7, WebInspector.multitargetConsoleModel.messages().length); 559 this.releaseControl(); 560 } 561 562 this.takeControl(); 563}; 564 565TestSuite.prototype.testReattachAfterCrash = function() 566{ 567 PageAgent.navigate("about:crash"); 568 PageAgent.navigate("about:blank"); 569 WebInspector.runtimeModel.addEventListener(WebInspector.RuntimeModel.Events.ExecutionContextCreated, this.releaseControl, this); 570}; 571 572 573TestSuite.prototype.testSharedWorker = function() 574{ 575 function didEvaluateInConsole(resultText) { 576 this.assertEquals("2011", resultText); 577 this.releaseControl(); 578 } 579 this.evaluateInConsole_("globalVar", didEvaluateInConsole.bind(this)); 580 this.takeControl(); 581}; 582 583 584TestSuite.prototype.testPauseInSharedWorkerInitialization = function() 585{ 586 if (WebInspector.debuggerModel.debuggerPausedDetails) 587 return; 588 this._waitForScriptPause(this.releaseControl.bind(this)); 589 this.takeControl(); 590}; 591 592TestSuite.prototype.enableTouchEmulation = function() 593{ 594 WebInspector.targetManager.mainTarget().domModel.emulateTouchEventObjects(true); 595}; 596 597 598// Regression test for crbug.com/370035. 599TestSuite.prototype.testDeviceMetricsOverrides = function() 600{ 601 const dumpPageMetrics = function() 602 { 603 return JSON.stringify({ 604 width: window.innerWidth, 605 height: window.innerHeight, 606 deviceScaleFactor: window.devicePixelRatio 607 }); 608 }; 609 610 var test = this; 611 612 function testOverrides(params, metrics, callback) 613 { 614 PageAgent.invoke_setDeviceMetricsOverride(params, getMetrics); 615 616 function getMetrics() 617 { 618 test.evaluateInConsole_("(" + dumpPageMetrics.toString() + ")()", checkMetrics); 619 } 620 621 function checkMetrics(consoleResult) 622 { 623 test.assertEquals('"' + JSON.stringify(metrics) + '"', consoleResult, "Wrong metrics for params: " + JSON.stringify(params)); 624 callback(); 625 } 626 } 627 628 function step1() 629 { 630 testOverrides({width: 1200, height: 1000, deviceScaleFactor: 1, mobile: false, fitWindow: true}, {width: 1200, height: 1000, deviceScaleFactor: 1}, step2); 631 } 632 633 function step2() 634 { 635 testOverrides({width: 1200, height: 1000, deviceScaleFactor: 1, mobile: false, fitWindow: false}, {width: 1200, height: 1000, deviceScaleFactor: 1}, step3); 636 } 637 638 function step3() 639 { 640 testOverrides({width: 1200, height: 1000, deviceScaleFactor: 3, mobile: false, fitWindow: true}, {width: 1200, height: 1000, deviceScaleFactor: 3}, step4); 641 } 642 643 function step4() 644 { 645 testOverrides({width: 1200, height: 1000, deviceScaleFactor: 3, mobile: false, fitWindow: false}, {width: 1200, height: 1000, deviceScaleFactor: 3}, finish); 646 } 647 648 function finish() 649 { 650 test.releaseControl(); 651 } 652 653 WebInspector.overridesSupport._deviceMetricsChangedListenerMuted = true; 654 test.takeControl(); 655 this.waitForThrottler(WebInspector.overridesSupport._deviceMetricsThrottler, step1); 656}; 657 658TestSuite.prototype.waitForTestResultsInConsole = function() 659{ 660 var messages = WebInspector.multitargetConsoleModel.messages(); 661 for (var i = 0; i < messages.length; ++i) { 662 var text = messages[i].messageText; 663 if (text === "PASS") 664 return; 665 else if (/^FAIL/.test(text)) 666 this.fail(text); // This will throw. 667 } 668 // Neither PASS nor FAIL, so wait for more messages. 669 function onConsoleMessage(event) 670 { 671 var text = event.data.messageText; 672 if (text === "PASS") 673 this.releaseControl(); 674 else if (/^FAIL/.test(text)) 675 this.fail(text); 676 } 677 678 WebInspector.multitargetConsoleModel.addEventListener(WebInspector.ConsoleModel.Events.MessageAdded, onConsoleMessage, this); 679 this.takeControl(); 680}; 681 682TestSuite.prototype.checkLogAndErrorMessages = function() 683{ 684 var messages = WebInspector.multitargetConsoleModel.messages(); 685 686 var matchesCount = 0; 687 function validMessage(message) 688 { 689 if (message.text === "log" && message.level === WebInspector.ConsoleMessage.MessageLevel.Log) { 690 ++matchesCount; 691 return true; 692 } 693 694 if (message.text === "error" && message.level === WebInspector.ConsoleMessage.MessageLevel.Error) { 695 ++matchesCount; 696 return true; 697 } 698 return false; 699 } 700 701 for (var i = 0; i < messages.length; ++i) { 702 if (validMessage(messages[i])) 703 continue; 704 this.fail(messages[i].text + ":" + messages[i].level); // This will throw. 705 } 706 707 if (matchesCount === 2) 708 return; 709 710 // Wait for more messages. 711 function onConsoleMessage(event) 712 { 713 var message = event.data; 714 if (validMessage(message)) { 715 if (matchesCount === 2) { 716 this.releaseControl(); 717 return; 718 } 719 } else 720 this.fail(message.text + ":" + messages[i].level); 721 } 722 723 WebInspector.multitargetConsoleModel.addEventListener(WebInspector.ConsoleModel.Events.MessageAdded, onConsoleMessage, this); 724 this.takeControl(); 725}; 726 727/** 728 * Serializes array of uiSourceCodes to string. 729 * @param {!Array.<!WebInspectorUISourceCode>} uiSourceCodes 730 * @return {string} 731 */ 732TestSuite.prototype.uiSourceCodesToString_ = function(uiSourceCodes) 733{ 734 var names = []; 735 for (var i = 0; i < uiSourceCodes.length; i++) 736 names.push('"' + uiSourceCodes[i].url + '"'); 737 return names.join(","); 738}; 739 740 741/** 742 * Returns all loaded non anonymous uiSourceCodes. 743 * @return {!Array.<!WebInspectorUISourceCode>} 744 */ 745TestSuite.prototype.nonAnonymousUISourceCodes_ = function() 746{ 747 function filterOutAnonymous(uiSourceCode) 748 { 749 return !!uiSourceCode.url; 750 } 751 752 function filterOutService(uiSourceCode) 753 { 754 return !uiSourceCode.project().isServiceProject(); 755 } 756 757 var uiSourceCodes = WebInspector.workspace.uiSourceCodes(); 758 uiSourceCodes = uiSourceCodes.filter(filterOutService); 759 return uiSourceCodes.filter(filterOutAnonymous); 760}; 761 762 763/* 764 * Evaluates the code in the console as if user typed it manually and invokes 765 * the callback when the result message is received and added to the console. 766 * @param {string} code 767 * @param {function(string)} callback 768 */ 769TestSuite.prototype.evaluateInConsole_ = function(code, callback) 770{ 771 function innerEvaluate() 772 { 773 WebInspector.console.show(); 774 var consoleView = WebInspector.ConsolePanel._view(); 775 consoleView._prompt.text = code; 776 consoleView._promptElement.dispatchEvent(TestSuite.createKeyEvent("Enter")); 777 778 this.addSniffer(WebInspector.ConsoleView.prototype, "_showConsoleMessage", 779 function(viewMessage) { 780 callback(viewMessage.toMessageElement().textContent); 781 }.bind(this)); 782 } 783 784 if (!WebInspector.context.flavor(WebInspector.ExecutionContext)) { 785 WebInspector.context.addFlavorChangeListener(WebInspector.ExecutionContext, innerEvaluate, this); 786 return; 787 } 788 789 innerEvaluate.call(this); 790}; 791 792/** 793 * Checks that all expected scripts are present in the scripts list 794 * in the Scripts panel. 795 * @param {!Array.<string>} expected Regular expressions describing 796 * expected script names. 797 * @return {boolean} Whether all the scripts are in "scripts-files" select 798 * box 799 */ 800TestSuite.prototype._scriptsAreParsed = function(expected) 801{ 802 var uiSourceCodes = this.nonAnonymousUISourceCodes_(); 803 // Check that at least all the expected scripts are present. 804 var missing = expected.slice(0); 805 for (var i = 0; i < uiSourceCodes.length; ++i) { 806 for (var j = 0; j < missing.length; ++j) { 807 if (uiSourceCodes[i].name().search(missing[j]) !== -1) { 808 missing.splice(j, 1); 809 break; 810 } 811 } 812 } 813 return missing.length === 0; 814}; 815 816 817/** 818 * Waits for script pause, checks expectations, and invokes the callback. 819 * @param {function():void} callback 820 */ 821TestSuite.prototype._waitForScriptPause = function(callback) 822{ 823 function pauseListener(event) { 824 WebInspector.debuggerModel.removeEventListener(WebInspector.DebuggerModel.Events.DebuggerPaused, pauseListener, this); 825 callback(); 826 } 827 WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.DebuggerPaused, pauseListener, this); 828}; 829 830 831/** 832 * Waits until all the scripts are parsed and asynchronously executes the code 833 * in the inspected page. 834 */ 835TestSuite.prototype._executeCodeWhenScriptsAreParsed = function(code, expectedScripts) 836{ 837 var test = this; 838 839 function executeFunctionInInspectedPage() { 840 // Since breakpoints are ignored in evals' calculate() function is 841 // execute after zero-timeout so that the breakpoint is hit. 842 test.evaluateInConsole_( 843 'setTimeout("' + code + '" , 0)', 844 function(resultText) { 845 test.assertTrue(!isNaN(resultText), "Failed to get timer id: " + resultText + ". Code: " + code); 846 }); 847 } 848 849 test._waitUntilScriptsAreParsed(expectedScripts, executeFunctionInInspectedPage); 850}; 851 852 853/** 854 * Waits until all the scripts are parsed and invokes the callback. 855 */ 856TestSuite.prototype._waitUntilScriptsAreParsed = function(expectedScripts, callback) 857{ 858 var test = this; 859 860 function waitForAllScripts() { 861 if (test._scriptsAreParsed(expectedScripts)) 862 callback(); 863 else 864 test.addSniffer(WebInspector.panels.sources.sourcesView(), "_addUISourceCode", waitForAllScripts); 865 } 866 867 waitForAllScripts(); 868}; 869 870 871/** 872 * Key event with given key identifier. 873 */ 874TestSuite.createKeyEvent = function(keyIdentifier) 875{ 876 var evt = document.createEvent("KeyboardEvent"); 877 evt.initKeyboardEvent("keydown", true /* can bubble */, true /* can cancel */, null /* view */, keyIdentifier, ""); 878 return evt; 879}; 880 881 882/** 883 * Test runner for the test suite. 884 */ 885var uiTests = {}; 886 887 888/** 889 * Run each test from the test suit on a fresh instance of the suite. 890 */ 891uiTests.runAllTests = function() 892{ 893 // For debugging purposes. 894 for (var name in TestSuite.prototype) { 895 if (name.substring(0, 4) === "test" && typeof TestSuite.prototype[name] === "function") 896 uiTests.runTest(name); 897 } 898}; 899 900 901/** 902 * Run specified test on a fresh instance of the test suite. 903 * @param {string} name Name of a test method from TestSuite class. 904 */ 905uiTests.runTest = function(name) 906{ 907 if (uiTests._populatedInterface) 908 new TestSuite().runTest(name); 909 else 910 uiTests._pendingTestName = name; 911}; 912 913(function() { 914 915function runTests() 916{ 917 uiTests._populatedInterface = true; 918 var name = uiTests._pendingTestName; 919 delete uiTests._pendingTestName; 920 if (name) 921 new TestSuite().runTest(name); 922} 923 924WebInspector.notifications.addEventListener(WebInspector.NotificationService.Events.InspectorAgentEnabledForTests, runTests); 925 926})(); 927 928} 929