Home | History | Annotate | Download | only in js
      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