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 40 if (window.domAutomationController) { 41 42 var ___interactiveUiTestsMode = true; 43 44 /** 45 * Test suite for interactive UI tests. 46 * @constructor 47 */ 48 TestSuite = 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 */ 59 TestSuite.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 */ 74 TestSuite.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 */ 89 TestSuite.prototype.assertTrue = function(value, opt_message) 90 { 91 this.assertEquals(true, !!value, opt_message); 92 }; 93 94 95 /** 96 * Contains assertion tests that string contains substring. 97 * @param {string} string Outer. 98 * @param {string} substring Inner. 99 */ 100 TestSuite.prototype.assertContains = function(string, substring) 101 { 102 if (string.indexOf(substring) === -1) 103 this.fail("Expected to: '" + string + "' to contain '" + substring + "'"); 104 }; 105 106 107 /** 108 * Takes control over execution. 109 */ 110 TestSuite.prototype.takeControl = function() 111 { 112 this.controlTaken_ = true; 113 // Set up guard timer. 114 var self = this; 115 this.timerId_ = setTimeout(function() { 116 self.reportFailure_("Timeout exceeded: 20 sec"); 117 }, 20000); 118 }; 119 120 121 /** 122 * Releases control over execution. 123 */ 124 TestSuite.prototype.releaseControl = function() 125 { 126 if (this.timerId_ !== -1) { 127 clearTimeout(this.timerId_); 128 this.timerId_ = -1; 129 } 130 this.reportOk_(); 131 }; 132 133 134 /** 135 * Async tests use this one to report that they are completed. 136 */ 137 TestSuite.prototype.reportOk_ = function() 138 { 139 window.domAutomationController.send("[OK]"); 140 }; 141 142 143 /** 144 * Async tests use this one to report failures. 145 */ 146 TestSuite.prototype.reportFailure_ = function(error) 147 { 148 if (this.timerId_ !== -1) { 149 clearTimeout(this.timerId_); 150 this.timerId_ = -1; 151 } 152 window.domAutomationController.send("[FAILED] " + error); 153 }; 154 155 156 /** 157 * Runs all global functions starting with "test" as unit tests. 158 */ 159 TestSuite.prototype.runTest = function(testName) 160 { 161 try { 162 this[testName](); 163 if (!this.controlTaken_) 164 this.reportOk_(); 165 } catch (e) { 166 this.reportFailure_(e); 167 } 168 }; 169 170 171 /** 172 * @param {string} panelName Name of the panel to show. 173 */ 174 TestSuite.prototype.showPanel = function(panelName) 175 { 176 // Open Scripts panel. 177 var toolbar = document.getElementById("toolbar"); 178 var button = toolbar.getElementsByClassName(panelName)[0]; 179 button.click(); 180 this.assertEquals(WebInspector.panels[panelName], WebInspector.currentPanel); 181 }; 182 183 184 /** 185 * Overrides the method with specified name until it's called first time. 186 * @param {Object} receiver An object whose method to override. 187 * @param {string} methodName Name of the method to override. 188 * @param {Function} override A function that should be called right after the 189 * overriden method returns. 190 * @param {boolean} opt_sticky Whether restore original method after first run 191 * or not. 192 */ 193 TestSuite.prototype.addSniffer = function(receiver, methodName, override, opt_sticky) 194 { 195 var orig = receiver[methodName]; 196 if (typeof orig !== "function") 197 this.fail("Cannot find method to override: " + methodName); 198 var test = this; 199 receiver[methodName] = function(var_args) { 200 try { 201 var result = orig.apply(this, arguments); 202 } finally { 203 if (!opt_sticky) 204 receiver[methodName] = orig; 205 } 206 // In case of exception the override won't be called. 207 try { 208 override.apply(this, arguments); 209 } catch (e) { 210 test.fail("Exception in overriden method '" + methodName + "': " + e); 211 } 212 return result; 213 }; 214 }; 215 216 217 TestSuite.prototype.testEnableResourcesTab = function() 218 { 219 // FIXME once reference is removed downstream. 220 } 221 222 TestSuite.prototype.testCompletionOnPause = function() 223 { 224 // FIXME once reference is removed downstream. 225 } 226 227 // UI Tests 228 229 230 /** 231 * Tests that profiler works. 232 */ 233 TestSuite.prototype.testProfilerTab = function() 234 { 235 this.showPanel("profiles"); 236 237 var panel = WebInspector.panels.profiles; 238 var test = this; 239 240 function findDisplayedNode() { 241 var node = panel.visibleView.profileDataGridTree.children[0]; 242 if (!node) { 243 // Profile hadn't been queried yet, re-schedule. 244 window.setTimeout(findDisplayedNode, 100); 245 return; 246 } 247 248 // Iterate over displayed functions and search for a function 249 // that is called "fib" or "eternal_fib". If found, this will mean 250 // that we actually have profiled page's code. 251 while (node) { 252 if (node.functionName.indexOf("fib") !== -1) 253 test.releaseControl(); 254 node = node.traverseNextNode(true, null, true); 255 } 256 257 test.fail(); 258 } 259 260 function findVisibleView() { 261 if (!panel.visibleView) { 262 setTimeout(findVisibleView, 0); 263 return; 264 } 265 setTimeout(findDisplayedNode, 0); 266 } 267 268 findVisibleView(); 269 this.takeControl(); 270 }; 271 272 273 /** 274 * Tests that heap profiler works. 275 */ 276 TestSuite.prototype.testHeapProfiler = function() 277 { 278 this.showPanel("profiles"); 279 280 var panel = WebInspector.panels.profiles; 281 var test = this; 282 283 function findDisplayedNode() { 284 var node = panel.visibleView.dataGrid.children[0]; 285 if (!node) { 286 // Profile hadn't been queried yet, re-schedule. 287 window.setTimeout(findDisplayedNode, 100); 288 return; 289 } 290 291 // Iterate over displayed functions and find node called "A" 292 // If found, this will mean that we actually have taken heap snapshot. 293 while (node) { 294 if (node.constructorName.indexOf("A") !== -1) { 295 test.releaseControl(); 296 return; 297 } 298 node = node.traverseNextNode(false, null, true); 299 } 300 301 test.fail(); 302 } 303 304 function findVisibleView() { 305 if (!panel.visibleView) { 306 setTimeout(findVisibleView, 0); 307 return; 308 } 309 setTimeout(findDisplayedNode, 0); 310 } 311 312 WebInspector.HeapSnapshotProfileType.prototype.buttonClicked(); 313 findVisibleView(); 314 this.takeControl(); 315 }; 316 317 318 /** 319 * Tests that scripts tab can be open and populated with inspected scripts. 320 */ 321 TestSuite.prototype.testShowScriptsTab = function() 322 { 323 this.showPanel("scripts"); 324 var test = this; 325 // There should be at least main page script. 326 this._waitUntilScriptsAreParsed(["debugger_test_page.html"], 327 function() { 328 test.releaseControl(); 329 }); 330 // Wait until all scripts are added to the debugger. 331 this.takeControl(); 332 }; 333 334 335 /** 336 * Tests that scripts tab is populated with inspected scripts even if it 337 * hadn't been shown by the moment inspected paged refreshed. 338 * @see http://crbug.com/26312 339 */ 340 TestSuite.prototype.testScriptsTabIsPopulatedOnInspectedPageRefresh = function() 341 { 342 var test = this; 343 this.assertEquals(WebInspector.panels.elements, WebInspector.currentPanel, "Elements panel should be current one."); 344 345 this.addSniffer(WebInspector.panels.scripts, "reset", waitUntilScriptIsParsed); 346 347 // Reload inspected page. It will reset the debugger agent. 348 test.evaluateInConsole_( 349 "window.location.reload(true);", 350 function(resultText) {}); 351 352 function waitUntilScriptIsParsed() { 353 test.showPanel("scripts"); 354 test._waitUntilScriptsAreParsed(["debugger_test_page.html"], 355 function() { 356 test.releaseControl(); 357 }); 358 } 359 360 // Wait until all scripts are added to the debugger. 361 this.takeControl(); 362 }; 363 364 365 /** 366 * Tests that scripts list contains content scripts. 367 */ 368 TestSuite.prototype.testContentScriptIsPresent = function() 369 { 370 this.showPanel("scripts"); 371 var test = this; 372 373 test._waitUntilScriptsAreParsed( 374 ["page_with_content_script.html", "simple_content_script.js"], 375 function() { 376 test.releaseControl(); 377 }); 378 379 // Wait until all scripts are added to the debugger. 380 this.takeControl(); 381 }; 382 383 384 /** 385 * Tests that scripts are not duplicaed on Scripts tab switch. 386 */ 387 TestSuite.prototype.testNoScriptDuplicatesOnPanelSwitch = function() 388 { 389 var test = this; 390 391 // There should be two scripts: one for the main page and another 392 // one which is source of console API(see 393 // InjectedScript._ensureCommandLineAPIInstalled). 394 var expectedScriptsCount = 2; 395 var parsedScripts = []; 396 397 this.showPanel("scripts"); 398 399 400 function switchToElementsTab() { 401 test.showPanel("elements"); 402 setTimeout(switchToScriptsTab, 0); 403 } 404 405 function switchToScriptsTab() { 406 test.showPanel("scripts"); 407 setTimeout(checkScriptsPanel, 0); 408 } 409 410 function checkScriptsPanel() { 411 test.assertTrue(!!WebInspector.panels.scripts.visibleView, "No visible script view."); 412 test.assertTrue(test._scriptsAreParsed(["debugger_test_page.html"]), "Some scripts are missing."); 413 checkNoDuplicates(); 414 test.releaseControl(); 415 } 416 417 function checkNoDuplicates() { 418 var scriptSelect = document.getElementById("scripts-files"); 419 var options = scriptSelect.options; 420 for (var i = 0; i < options.length; i++) { 421 var scriptName = options[i].text; 422 for (var j = i + 1; j < options.length; j++) 423 test.assertTrue(scriptName !== options[j].text, "Found script duplicates: " + test.optionsToString_(options)); 424 } 425 } 426 427 test._waitUntilScriptsAreParsed( 428 ["debugger_test_page.html"], 429 function() { 430 checkNoDuplicates(); 431 setTimeout(switchToElementsTab, 0); 432 }); 433 434 435 // Wait until all scripts are added to the debugger. 436 this.takeControl(); 437 }; 438 439 440 // Tests that debugger works correctly if pause event occurs when DevTools 441 // frontend is being loaded. 442 TestSuite.prototype.testPauseWhenLoadingDevTools = function() 443 { 444 this.showPanel("scripts"); 445 var test = this; 446 447 var expectations = { 448 functionsOnStack: ["callDebugger"], 449 lineNumber: 8, 450 lineText: " debugger;" 451 }; 452 453 454 // Script execution can already be paused. 455 if (WebInspector.currentPanel.paused) { 456 var callFrame = WebInspector.currentPanel._presentationModel.selectedCallFrame; 457 this.assertEquals(expectations.functionsOnStack[0], callFrame.functionName); 458 var callbackInvoked = false; 459 this._checkSourceFrameWhenLoaded(expectations, function() { 460 callbackInvoked = true; 461 if (test.controlTaken_) 462 test.releaseControl(); 463 }); 464 if (!callbackInvoked) { 465 test.takeControl(); 466 } 467 return; 468 } 469 470 this._waitForScriptPause( 471 { 472 functionsOnStack: ["callDebugger"], 473 lineNumber: 8, 474 lineText: " debugger;" 475 }, 476 function() { 477 test.releaseControl(); 478 }); 479 this.takeControl(); 480 }; 481 482 483 // Tests that pressing "Pause" will pause script execution if the script 484 // is already running. 485 TestSuite.prototype.testPauseWhenScriptIsRunning = function() 486 { 487 this.showPanel("scripts"); 488 var test = this; 489 490 test.evaluateInConsole_( 491 'setTimeout("handleClick()" , 0)', 492 function(resultText) { 493 test.assertTrue(!isNaN(resultText), "Failed to get timer id: " + resultText); 494 testScriptPauseAfterDelay(); 495 }); 496 497 // Wait for some time to make sure that inspected page is running the 498 // infinite loop. 499 function testScriptPauseAfterDelay() { 500 setTimeout(testScriptPause, 300); 501 } 502 503 function testScriptPause() { 504 // The script should be in infinite loop. Click "Pause" button to 505 // pause it and wait for the result. 506 WebInspector.panels.scripts.pauseButton.click(); 507 508 test._waitForScriptPause( 509 { 510 functionsOnStack: ["handleClick", ""], 511 lineNumber: 5, 512 lineText: " while(true) {" 513 }, 514 function() { 515 test.releaseControl(); 516 }); 517 } 518 519 this.takeControl(); 520 }; 521 522 523 /** 524 * Tests network size. 525 */ 526 TestSuite.prototype.testNetworkSize = function() 527 { 528 var test = this; 529 530 function finishResource(resource, finishTime) 531 { 532 test.assertEquals(221, resource.transferSize, "Incorrect total encoded data length"); 533 test.assertEquals(25, resource.resourceSize, "Incorrect total data length"); 534 test.releaseControl(); 535 } 536 537 this.addSniffer(WebInspector.NetworkDispatcher.prototype, "_finishResource", finishResource); 538 539 // Reload inspected page to sniff network events 540 test.evaluateInConsole_("window.location.reload(true);", function(resultText) {}); 541 542 this.takeControl(); 543 }; 544 545 546 /** 547 * Tests network sync size. 548 */ 549 TestSuite.prototype.testNetworkSyncSize = function() 550 { 551 var test = this; 552 553 function finishResource(resource, finishTime) 554 { 555 test.assertEquals(221, resource.transferSize, "Incorrect total encoded data length"); 556 test.assertEquals(25, resource.resourceSize, "Incorrect total data length"); 557 test.releaseControl(); 558 } 559 560 this.addSniffer(WebInspector.NetworkDispatcher.prototype, "_finishResource", finishResource); 561 562 // Send synchronous XHR to sniff network events 563 test.evaluateInConsole_("var xhr = new XMLHttpRequest(); xhr.open(\"GET\", \"chunked\", false); xhr.send(null);", function() {}); 564 565 this.takeControl(); 566 }; 567 568 569 /** 570 * Tests network raw headers text. 571 */ 572 TestSuite.prototype.testNetworkRawHeadersText = function() 573 { 574 var test = this; 575 576 function finishResource(resource, finishTime) 577 { 578 var rawResponseHeadersText = resource.rawResponseHeadersText 579 if (!rawResponseHeadersText) 580 test.fail("Failure: resource does not have raw response header text"); 581 test.assertEquals(166, resource.rawResponseHeadersText.length, "Incorrect raw response header text length"); 582 test.releaseControl(); 583 } 584 585 this.addSniffer(WebInspector.NetworkDispatcher.prototype, "_finishResource", finishResource); 586 587 // Reload inspected page to sniff network events 588 test.evaluateInConsole_("window.location.reload(true);", function(resultText) {}); 589 590 this.takeControl(); 591 }; 592 593 594 /** 595 * Tests network timing. 596 */ 597 TestSuite.prototype.testNetworkTiming = function() 598 { 599 var test = this; 600 601 function finishResource(resource, finishTime) 602 { 603 test.assertTrue(resource.timing.receiveHeadersEnd - resource.timing.connectStart >= 100, 604 "Time between receiveHeadersEnd and connectStart should be >=100ms, but was " + 605 "receiveHeadersEnd=" + resource.timing.receiveHeadersEnd + ", connectStart=" + resource.timing.connectStart + "."); 606 test.assertTrue(resource.endTime - resource.startTime >= 0.2, 607 "Time between endTime and startTime should be >=200ms, but was " + 608 "endtime=" + resource.endTime + ", startTime=" + resource.startTime + "."); 609 610 test.releaseControl(); 611 } 612 613 this.addSniffer(WebInspector.NetworkDispatcher.prototype, "_finishResource", finishResource); 614 615 // Reload inspected page to sniff network events 616 test.evaluateInConsole_("window.location.reload(true);", function(resultText) {}); 617 618 this.takeControl(); 619 }; 620 621 622 /** 623 * Serializes options collection to string. 624 * @param {HTMLOptionsCollection} options 625 * @return {string} 626 */ 627 TestSuite.prototype.optionsToString_ = function(options) 628 { 629 var names = []; 630 for (var i = 0; i < options.length; i++) 631 names.push('"' + options[i].text + '"'); 632 return names.join(","); 633 }; 634 635 636 /** 637 * Ensures that main HTML resource is selected in Scripts panel and that its 638 * source frame is setup. Invokes the callback when the condition is satisfied. 639 * @param {HTMLOptionsCollection} options 640 * @param {function(WebInspector.SourceView,string)} callback 641 */ 642 TestSuite.prototype.showMainPageScriptSource_ = function(scriptName, callback) 643 { 644 var test = this; 645 646 var scriptSelect = document.getElementById("scripts-files"); 647 var options = scriptSelect.options; 648 649 test.assertTrue(options.length, "Scripts list is empty"); 650 651 // Select page's script if it's not current option. 652 var scriptResource; 653 if (options[scriptSelect.selectedIndex].text === scriptName) 654 scriptResource = options[scriptSelect.selectedIndex].representedObject; 655 else { 656 var pageScriptIndex = -1; 657 for (var i = 0; i < options.length; i++) { 658 if (options[i].text === scriptName) { 659 pageScriptIndex = i; 660 break; 661 } 662 } 663 test.assertTrue(-1 !== pageScriptIndex, "Script with url " + scriptName + " not found among " + test.optionsToString_(options)); 664 scriptResource = options[pageScriptIndex].representedObject; 665 666 // Current panel is "Scripts". 667 WebInspector.currentPanel._showScriptOrResource(scriptResource); 668 test.assertEquals(pageScriptIndex, scriptSelect.selectedIndex, "Unexpected selected option index."); 669 } 670 671 test.assertTrue(scriptResource instanceof WebInspector.Resource, 672 "Unexpected resource class."); 673 test.assertTrue(!!scriptResource.url, "Resource URL is null."); 674 test.assertTrue(scriptResource.url.search(scriptName + "$") !== -1, "Main HTML resource should be selected."); 675 676 var scriptsPanel = WebInspector.panels.scripts; 677 678 var view = scriptsPanel.visibleView; 679 test.assertTrue(view instanceof WebInspector.SourceView); 680 681 if (!view.sourceFrame._loaded) { 682 test.addSniffer(view, "_sourceFrameSetupFinished", function(event) { 683 callback(view, scriptResource.url); 684 }); 685 } else 686 callback(view, scriptResource.url); 687 }; 688 689 690 /* 691 * Evaluates the code in the console as if user typed it manually and invokes 692 * the callback when the result message is received and added to the console. 693 * @param {string} code 694 * @param {function(string)} callback 695 */ 696 TestSuite.prototype.evaluateInConsole_ = function(code, callback) 697 { 698 WebInspector.showConsole(); 699 WebInspector.console.prompt.text = code; 700 WebInspector.console.promptElement.dispatchEvent( TestSuite.createKeyEvent("Enter")); 701 702 this.addSniffer(WebInspector.ConsoleView.prototype, "addMessage", 703 function(commandResult) { 704 callback(commandResult.toMessageElement().textContent); 705 }); 706 }; 707 708 709 /** 710 * Checks current execution line against expectations. 711 * @param {WebInspector.SourceFrame} sourceFrame 712 * @param {number} lineNumber Expected line number 713 * @param {string} lineContent Expected line text 714 */ 715 TestSuite.prototype._checkExecutionLine = function(sourceFrame, lineNumber, lineContent) 716 { 717 this.assertEquals(lineNumber, sourceFrame._executionLineNumber + 1, "Unexpected execution line number."); 718 this.assertEquals(lineContent, sourceFrame._textModel.line(lineNumber - 1), "Unexpected execution line text."); 719 } 720 721 722 /** 723 * Checks that all expected scripts are present in the scripts list 724 * in the Scripts panel. 725 * @param {Array.<string>} expected Regular expressions describing 726 * expected script names. 727 * @return {boolean} Whether all the scripts are in "scripts-files" select 728 * box 729 */ 730 TestSuite.prototype._scriptsAreParsed = function(expected) 731 { 732 var scriptSelect = document.getElementById("scripts-files"); 733 var options = scriptSelect.options; 734 735 // Check that at least all the expected scripts are present. 736 var missing = expected.slice(0); 737 for (var i = 0 ; i < options.length; i++) { 738 for (var j = 0; j < missing.length; j++) { 739 if (options[i].text.search(missing[j]) !== -1) { 740 missing.splice(j, 1); 741 break; 742 } 743 } 744 } 745 return missing.length === 0; 746 }; 747 748 749 /** 750 * Waits for script pause, checks expectations, and invokes the callback. 751 * @param {Object} expectations Dictionary of expectations 752 * @param {function():void} callback 753 */ 754 TestSuite.prototype._waitForScriptPause = function(expectations, callback) 755 { 756 var test = this; 757 // Wait until script is paused. 758 test.addSniffer( 759 WebInspector.debuggerModel, 760 "_pausedScript", 761 function(details) { 762 var callFrames = details.callFrames; 763 var functionsOnStack = []; 764 for (var i = 0; i < callFrames.length; i++) 765 functionsOnStack.push(callFrames[i].functionName); 766 767 test.assertEquals(expectations.functionsOnStack.join(","), functionsOnStack.join(","), "Unexpected stack."); 768 769 // Check that execution line where the script is paused is 770 // expected one. 771 test._checkSourceFrameWhenLoaded(expectations, callback); 772 }); 773 }; 774 775 776 /** 777 * Waits for current source frame to load, checks expectations, and invokes 778 * the callback. 779 * @param {Object} expectations Dictionary of expectations 780 * @param {function():void} callback 781 */ 782 TestSuite.prototype._checkSourceFrameWhenLoaded = function(expectations, callback) 783 { 784 var test = this; 785 786 var frame = WebInspector.currentPanel.visibleView; 787 788 if (frame._textViewer) 789 checkExecLine(); 790 else { 791 setTimeout(function() { 792 test._checkSourceFrameWhenLoaded(expectations, callback); 793 }, 100); 794 } 795 function checkExecLine() { 796 test._checkExecutionLine(frame, expectations.lineNumber, expectations.lineText); 797 callback(); 798 } 799 }; 800 801 802 /** 803 * Waits until all the scripts are parsed and asynchronously executes the code 804 * in the inspected page. 805 */ 806 TestSuite.prototype._executeCodeWhenScriptsAreParsed = function(code, expectedScripts) 807 { 808 var test = this; 809 810 function executeFunctionInInspectedPage() { 811 // Since breakpoints are ignored in evals' calculate() function is 812 // execute after zero-timeout so that the breakpoint is hit. 813 test.evaluateInConsole_( 814 'setTimeout("' + code + '" , 0)', 815 function(resultText) { 816 test.assertTrue(!isNaN(resultText), "Failed to get timer id: " + resultText + ". Code: " + code); 817 }); 818 } 819 820 test._waitUntilScriptsAreParsed(expectedScripts, executeFunctionInInspectedPage); 821 }; 822 823 824 /** 825 * Waits until all the scripts are parsed and invokes the callback. 826 */ 827 TestSuite.prototype._waitUntilScriptsAreParsed = function(expectedScripts, callback) 828 { 829 var test = this; 830 831 function waitForAllScripts() { 832 if (test._scriptsAreParsed(expectedScripts)) 833 callback(); 834 else 835 test.addSniffer(WebInspector.panels.scripts, "_addOptionToFilesSelect", waitForAllScripts); 836 } 837 838 waitForAllScripts(); 839 }; 840 841 842 /** 843 * Key event with given key identifier. 844 */ 845 TestSuite.createKeyEvent = function(keyIdentifier) 846 { 847 var evt = document.createEvent("KeyboardEvent"); 848 evt.initKeyboardEvent("keydown", true /* can bubble */, true /* can cancel */, null /* view */, keyIdentifier, ""); 849 return evt; 850 }; 851 852 853 /** 854 * Test runner for the test suite. 855 */ 856 var uiTests = {}; 857 858 859 /** 860 * Run each test from the test suit on a fresh instance of the suite. 861 */ 862 uiTests.runAllTests = function() 863 { 864 // For debugging purposes. 865 for (var name in TestSuite.prototype) { 866 if (name.substring(0, 4) === "test" && typeof TestSuite.prototype[name] === "function") 867 uiTests.runTest(name); 868 } 869 }; 870 871 872 /** 873 * Run specified test on a fresh instance of the test suite. 874 * @param {string} name Name of a test method from TestSuite class. 875 */ 876 uiTests.runTest = function(name) 877 { 878 if (uiTests._populatedInterface) 879 new TestSuite().runTest(name); 880 else 881 uiTests._pendingTestName = name; 882 }; 883 884 (function() { 885 886 function runTests() 887 { 888 uiTests._populatedInterface = true; 889 var name = uiTests._pendingTestName; 890 delete uiTests._pendingTestName; 891 if (name) 892 new TestSuite().runTest(name); 893 } 894 895 var oldShowElementsPanel = WebInspector.showElementsPanel; 896 WebInspector.showElementsPanel = function() 897 { 898 oldShowElementsPanel.call(this); 899 runTests(); 900 } 901 902 var oldShowPanel = WebInspector.showPanel; 903 WebInspector.showPanel = function(name) 904 { 905 oldShowPanel.call(this, name); 906 runTests(); 907 } 908 909 })(); 910 911 } 912