WebKit/chromium/src/js/Tests.js
changeset 0 4f2f89ce4247
equal deleted inserted replaced
-1:000000000000 0:4f2f89ce4247
       
     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 /**
       
    86  * True assertion tests that value == true.
       
    87  * @param {Object} value Actual object.
       
    88  * @param {string} opt_message User message to print if the test fails.
       
    89  */
       
    90 TestSuite.prototype.assertTrue = function(value, opt_message)
       
    91 {
       
    92     this.assertEquals(true, !!value, opt_message);
       
    93 };
       
    94 
       
    95 
       
    96 /**
       
    97  * Contains assertion tests that string contains substring.
       
    98  * @param {string} string Outer.
       
    99  * @param {string} substring Inner.
       
   100  */
       
   101 TestSuite.prototype.assertContains = function(string, substring)
       
   102 {
       
   103     if (string.indexOf(substring) === -1)
       
   104         this.fail("Expected to: '" + string + "' to contain '" + substring + "'");
       
   105 };
       
   106 
       
   107 
       
   108 /**
       
   109  * Takes control over execution.
       
   110  */
       
   111 TestSuite.prototype.takeControl = function()
       
   112 {
       
   113     this.controlTaken_ = true;
       
   114     // Set up guard timer.
       
   115     var self = this;
       
   116     this.timerId_ = setTimeout(function() {
       
   117         self.reportFailure_("Timeout exceeded: 20 sec");
       
   118     }, 20000);
       
   119 };
       
   120 
       
   121 
       
   122 /**
       
   123  * Releases control over execution.
       
   124  */
       
   125 TestSuite.prototype.releaseControl = function()
       
   126 {
       
   127     if (this.timerId_ !== -1) {
       
   128         clearTimeout(this.timerId_);
       
   129         this.timerId_ = -1;
       
   130     }
       
   131     this.reportOk_();
       
   132 };
       
   133 
       
   134 
       
   135 /**
       
   136  * Async tests use this one to report that they are completed.
       
   137  */
       
   138 TestSuite.prototype.reportOk_ = function()
       
   139 {
       
   140     window.domAutomationController.send("[OK]");
       
   141 };
       
   142 
       
   143 
       
   144 /**
       
   145  * Async tests use this one to report failures.
       
   146  */
       
   147 TestSuite.prototype.reportFailure_ = function(error)
       
   148 {
       
   149     if (this.timerId_ !== -1) {
       
   150         clearTimeout(this.timerId_);
       
   151         this.timerId_ = -1;
       
   152     }
       
   153     window.domAutomationController.send("[FAILED] " + error);
       
   154 };
       
   155 
       
   156 
       
   157 /**
       
   158  * Runs all global functions starting with "test" as unit tests.
       
   159  */
       
   160 TestSuite.prototype.runTest = function(testName)
       
   161 {
       
   162     try {
       
   163         this[testName]();
       
   164         if (!this.controlTaken_)
       
   165             this.reportOk_();
       
   166     } catch (e) {
       
   167         this.reportFailure_(e);
       
   168     }
       
   169 };
       
   170 
       
   171 
       
   172 /**
       
   173  * @param {string} panelName Name of the panel to show.
       
   174  */
       
   175 TestSuite.prototype.showPanel = function(panelName)
       
   176 {
       
   177     // Open Scripts panel.
       
   178     var toolbar = document.getElementById("toolbar");
       
   179     var button = toolbar.getElementsByClassName(panelName)[0];
       
   180     button.click();
       
   181     this.assertEquals(WebInspector.panels[panelName], WebInspector.currentPanel);
       
   182 };
       
   183 
       
   184 
       
   185 /**
       
   186  * Overrides the method with specified name until it's called first time.
       
   187  * @param {Object} receiver An object whose method to override.
       
   188  * @param {string} methodName Name of the method to override.
       
   189  * @param {Function} override A function that should be called right after the
       
   190  *     overriden method returns.
       
   191  * @param {boolean} opt_sticky Whether restore original method after first run
       
   192  *     or not.
       
   193  */
       
   194 TestSuite.prototype.addSniffer = function(receiver, methodName, override, opt_sticky)
       
   195 {
       
   196     var orig = receiver[methodName];
       
   197     if (typeof orig !== "function")
       
   198         this.fail("Cannot find method to override: " + methodName);
       
   199     var test = this;
       
   200     receiver[methodName] = function(var_args) {
       
   201         try {
       
   202             var result = orig.apply(this, arguments);
       
   203         } finally {
       
   204             if (!opt_sticky)
       
   205                 receiver[methodName] = orig;
       
   206         }
       
   207         // In case of exception the override won't be called.
       
   208         try {
       
   209             override.apply(this, arguments);
       
   210         } catch (e) {
       
   211             test.fail("Exception in overriden method '" + methodName + "': " + e);
       
   212         }
       
   213         return result;
       
   214     };
       
   215 };
       
   216 
       
   217 
       
   218 // UI Tests
       
   219 
       
   220 
       
   221 /**
       
   222  * Tests that the real injected host is present in the context.
       
   223  */
       
   224 TestSuite.prototype.testHostIsPresent = function()
       
   225 {
       
   226     this.assertTrue(typeof InspectorFrontendHost === "object" && !InspectorFrontendHost.isStub);
       
   227 };
       
   228 
       
   229 
       
   230 /**
       
   231  * Tests elements tree has an "HTML" root.
       
   232  */
       
   233 TestSuite.prototype.testElementsTreeRoot = function()
       
   234 {
       
   235     var doc = WebInspector.domAgent.document;
       
   236     this.assertEquals("HTML", doc.documentElement.nodeName);
       
   237     this.assertTrue(doc.documentElement.hasChildNodes());
       
   238 };
       
   239 
       
   240 
       
   241 /**
       
   242  * Tests that main resource is present in the system and that it is
       
   243  * the only resource.
       
   244  */
       
   245 TestSuite.prototype.testMainResource = function()
       
   246 {
       
   247     var tokens = [];
       
   248     var resources = WebInspector.resources;
       
   249     for (var id in resources)
       
   250         tokens.push(resources[id].lastPathComponent);
       
   251     this.assertEquals("simple_page.html", tokens.join(","));
       
   252 };
       
   253 
       
   254 
       
   255 /**
       
   256  * Tests that resources tab is enabled when corresponding item is selected.
       
   257  */
       
   258 TestSuite.prototype.testEnableResourcesTab = function()
       
   259 {
       
   260     this.showPanel("resources");
       
   261 
       
   262     var test = this;
       
   263     this.addSniffer(WebInspector, "updateResource",
       
   264         function(identifier, payload) {
       
   265             test.assertEquals("simple_page.html", payload.lastPathComponent);
       
   266             WebInspector.panels.resources.refresh();
       
   267             WebInspector.panels.resources.revealAndSelectItem(WebInspector.resources[identifier]);
       
   268 
       
   269             test.releaseControl();
       
   270         });
       
   271 
       
   272     // Following call should lead to reload that we capture in the
       
   273     // addResource override.
       
   274     WebInspector.panels.resources._enableResourceTracking();
       
   275 
       
   276     // We now have some time to report results to controller.
       
   277     this.takeControl();
       
   278 };
       
   279 
       
   280 
       
   281 /**
       
   282  * Tests that correct content length is reported for resources.
       
   283  */
       
   284 TestSuite.prototype.testResourceContentLength = function()
       
   285 {
       
   286     this.showPanel("resources");
       
   287     var test = this;
       
   288 
       
   289     var png = false;
       
   290     var html = false;
       
   291     this.addSniffer(WebInspector, "updateResource",
       
   292         function(identifier, payload) {
       
   293             if (!payload.didLengthChange)
       
   294                 return;
       
   295             var resource = WebInspector.resources[identifier];
       
   296             if (!resource || !resource.url)
       
   297                 return;
       
   298             if (resource.url.search("image.html") !== -1) {
       
   299               var expectedLength = 87;
       
   300               test.assertTrue(
       
   301                   resource.resourceSize <= expectedLength,
       
   302                   "image.html content length is greater thatn expected.");
       
   303               if (expectedLength === resource.resourceSize)
       
   304                   html = true;
       
   305             } else if (resource.url.search("image.png") !== -1) {
       
   306               var expectedLength = 257796;
       
   307               test.assertTrue(
       
   308                   resource.resourceSize <= expectedLength,
       
   309                   "image.png content length is greater than expected.");
       
   310               if (expectedLength === resource.resourceSize)
       
   311                   png = true;
       
   312             }
       
   313             if (html && png) {
       
   314               // Wait 1 second before releasing control to check that the content
       
   315               // lengths are not updated anymore.
       
   316               setTimeout(function() {
       
   317                   test.releaseControl();
       
   318               }, 1000);
       
   319             }
       
   320         }, true);
       
   321 
       
   322     // Make sure resource tracking is on.
       
   323     WebInspector.panels.resources._enableResourceTracking();
       
   324     // Reload inspected page to update all resources.
       
   325     test.evaluateInConsole_(
       
   326         "window.location.reload(true);",
       
   327          function(resultText) {
       
   328              test.assertEquals("undefined", resultText, "Unexpected result of reload().");
       
   329          });
       
   330 
       
   331     // We now have some time to report results to controller.
       
   332     this.takeControl();
       
   333 };
       
   334 
       
   335 
       
   336 /**
       
   337  * Tests resource headers.
       
   338  */
       
   339 TestSuite.prototype.testResourceHeaders = function()
       
   340 {
       
   341     this.showPanel("resources");
       
   342 
       
   343     var test = this;
       
   344 
       
   345     var responseOk = false;
       
   346     var timingOk = false;
       
   347 
       
   348     this.addSniffer(WebInspector, "updateResource",
       
   349         function(identifier, payload) {
       
   350             var resource = this.resources[identifier];
       
   351             if (!resource || resource.mainResource) {
       
   352                 // We are only interested in secondary resources in this test.
       
   353                 return;
       
   354             }
       
   355 
       
   356             var requestHeaders = JSON.stringify(resource.requestHeaders);
       
   357             test.assertContains(requestHeaders, "Accept");
       
   358 
       
   359             if (payload.didResponseChange) {
       
   360                 var responseHeaders = JSON.stringify(resource.responseHeaders);
       
   361                 test.assertContains(responseHeaders, "Content-type");
       
   362                 test.assertContains(responseHeaders, "Content-Length");
       
   363                 test.assertTrue(typeof resource.responseReceivedTime !== "undefined");
       
   364                 responseOk = true;
       
   365             }
       
   366 
       
   367             if (payload.didTimingChange) {
       
   368                 test.assertTrue(typeof resource.startTime !== "undefined");
       
   369                 timingOk = true;
       
   370             }
       
   371 
       
   372             if (payload.didCompletionChange) {
       
   373                 test.assertTrue(responseOk);
       
   374                 test.assertTrue(timingOk);
       
   375                 test.assertTrue(typeof resource.endTime !== "undefined");
       
   376                 test.releaseControl();
       
   377             }
       
   378         }, true);
       
   379 
       
   380     WebInspector.panels.resources._enableResourceTracking();
       
   381     this.takeControl();
       
   382 };
       
   383 
       
   384 
       
   385 /**
       
   386  * Tests the mime type of a cached (HTTP 304) resource.
       
   387  */
       
   388 TestSuite.prototype.testCachedResourceMimeType = function()
       
   389 {
       
   390     this.showPanel("resources");
       
   391 
       
   392     var test = this;
       
   393     var hasReloaded = false;
       
   394 
       
   395     this.addSniffer(WebInspector, "updateResource",
       
   396         function(identifier, payload) {
       
   397             var resource = this.resources[identifier];
       
   398             if (!resource || resource.mainResource) {
       
   399                 // We are only interested in secondary resources in this test.
       
   400                 return;
       
   401             }
       
   402 
       
   403             if (payload.didResponseChange) {
       
   404                 // Test server uses a default mime type for JavaScript files.
       
   405                 test.assertEquals("text/html", payload.mimeType);
       
   406                 if (!hasReloaded) {
       
   407                     hasReloaded = true;
       
   408                     // Reload inspected page to update all resources.
       
   409                     test.evaluateInConsole_("window.location.reload(true);", function() {});
       
   410                 } else
       
   411                     test.releaseControl();
       
   412             }
       
   413 
       
   414         }, true);
       
   415 
       
   416     WebInspector.panels.resources._enableResourceTracking();
       
   417     this.takeControl();
       
   418 };
       
   419 
       
   420 
       
   421 /**
       
   422  * Tests that profiler works.
       
   423  */
       
   424 TestSuite.prototype.testProfilerTab = function()
       
   425 {
       
   426     this.showPanel("profiles");
       
   427 
       
   428     var panel = WebInspector.panels.profiles;
       
   429     var test = this;
       
   430 
       
   431     function findDisplayedNode() {
       
   432         var node = panel.visibleView.profileDataGridTree.children[0];
       
   433         if (!node) {
       
   434             // Profile hadn't been queried yet, re-schedule.
       
   435             window.setTimeout(findDisplayedNode, 100);
       
   436             return;
       
   437         }
       
   438 
       
   439         // Iterate over displayed functions and search for a function
       
   440         // that is called "fib" or "eternal_fib". If found, this will mean
       
   441         // that we actually have profiled page's code.
       
   442         while (node) {
       
   443             if (node.functionName.indexOf("fib") !== -1)
       
   444                 test.releaseControl();
       
   445             node = node.traverseNextNode(true, null, true);
       
   446         }
       
   447 
       
   448         test.fail();
       
   449     }
       
   450 
       
   451     function findVisibleView() {
       
   452         if (!panel.visibleView) {
       
   453             setTimeout(findVisibleView, 0);
       
   454             return;
       
   455         }
       
   456         setTimeout(findDisplayedNode, 0);
       
   457     }
       
   458 
       
   459     findVisibleView();
       
   460     this.takeControl();
       
   461 };
       
   462 
       
   463 
       
   464 /**
       
   465  * Tests that heap profiler works.
       
   466  */
       
   467 TestSuite.prototype.testHeapProfiler = function()
       
   468 {
       
   469     this.showPanel("profiles");
       
   470 
       
   471     var panel = WebInspector.panels.profiles;
       
   472     var test = this;
       
   473 
       
   474     function findDisplayedNode() {
       
   475         var node = panel.visibleView.dataGrid.children[0];
       
   476         if (!node) {
       
   477             // Profile hadn't been queried yet, re-schedule.
       
   478             window.setTimeout(findDisplayedNode, 100);
       
   479             return;
       
   480         }
       
   481 
       
   482         // Iterate over displayed functions and find node called "A"
       
   483         // If found, this will mean that we actually have taken heap snapshot.
       
   484         while (node) {
       
   485             if (node.constructorName.indexOf("A") !== -1) {
       
   486                 test.releaseControl();
       
   487                 return;
       
   488             }
       
   489             node = node.traverseNextNode(false, null, true);
       
   490         }
       
   491 
       
   492         test.fail();
       
   493     }
       
   494 
       
   495     function findVisibleView() {
       
   496         if (!panel.visibleView) {
       
   497             setTimeout(findVisibleView, 0);
       
   498             return;
       
   499         }
       
   500         setTimeout(findDisplayedNode, 0);
       
   501     }
       
   502 
       
   503     WebInspector.HeapSnapshotProfileType.prototype.buttonClicked();
       
   504     findVisibleView();
       
   505     this.takeControl();
       
   506 };
       
   507 
       
   508 
       
   509 /**
       
   510  * Tests that scripts tab can be open and populated with inspected scripts.
       
   511  */
       
   512 TestSuite.prototype.testShowScriptsTab = function()
       
   513 {
       
   514     this.showPanel("scripts");
       
   515     var test = this;
       
   516     // There should be at least main page script.
       
   517     this._waitUntilScriptsAreParsed(["debugger_test_page.html"],
       
   518         function() {
       
   519             test.releaseControl();
       
   520         });
       
   521     // Wait until all scripts are added to the debugger.
       
   522     this.takeControl();
       
   523 };
       
   524 
       
   525 
       
   526 /**
       
   527  * Tests that scripts tab is populated with inspected scripts even if it
       
   528  * hadn't been shown by the moment inspected paged refreshed.
       
   529  * @see http://crbug.com/26312
       
   530  */
       
   531 TestSuite.prototype.testScriptsTabIsPopulatedOnInspectedPageRefresh = function()
       
   532 {
       
   533     var test = this;
       
   534     this.assertEquals(WebInspector.panels.elements, WebInspector.currentPanel, "Elements panel should be current one.");
       
   535 
       
   536     this.addSniffer(WebInspector.panels.scripts, "reset", waitUntilScriptIsParsed);
       
   537 
       
   538     // Reload inspected page. It will reset the debugger agent.
       
   539     test.evaluateInConsole_(
       
   540         "window.location.reload(true);",
       
   541         function(resultText) {});
       
   542 
       
   543     function waitUntilScriptIsParsed() {
       
   544         test.showPanel("scripts");
       
   545         test._waitUntilScriptsAreParsed(["debugger_test_page.html"],
       
   546             function() {
       
   547                 test.releaseControl();
       
   548             });
       
   549     }
       
   550 
       
   551     // Wait until all scripts are added to the debugger.
       
   552     this.takeControl();
       
   553 };
       
   554 
       
   555 
       
   556 /**
       
   557  * Tests that scripts list contains content scripts.
       
   558  */
       
   559 TestSuite.prototype.testContentScriptIsPresent = function()
       
   560 {
       
   561     this.showPanel("scripts");
       
   562     var test = this;
       
   563 
       
   564     test._waitUntilScriptsAreParsed(
       
   565         ["page_with_content_script.html", "simple_content_script.js"],
       
   566         function() {
       
   567           test.releaseControl();
       
   568         });
       
   569 
       
   570     // Wait until all scripts are added to the debugger.
       
   571     this.takeControl();
       
   572 };
       
   573 
       
   574 
       
   575 /**
       
   576  * Tests that scripts are not duplicaed on Scripts tab switch.
       
   577  */
       
   578 TestSuite.prototype.testNoScriptDuplicatesOnPanelSwitch = function()
       
   579 {
       
   580     var test = this;
       
   581 
       
   582     // There should be two scripts: one for the main page and another
       
   583     // one which is source of console API(see
       
   584     // InjectedScript._ensureCommandLineAPIInstalled).
       
   585     var expectedScriptsCount = 2;
       
   586     var parsedScripts = [];
       
   587 
       
   588     this.showPanel("scripts");
       
   589 
       
   590 
       
   591     function switchToElementsTab() {
       
   592         test.showPanel("elements");
       
   593         setTimeout(switchToScriptsTab, 0);
       
   594     }
       
   595 
       
   596     function switchToScriptsTab() {
       
   597         test.showPanel("scripts");
       
   598         setTimeout(checkScriptsPanel, 0);
       
   599     }
       
   600 
       
   601     function checkScriptsPanel() {
       
   602         test.assertTrue(!!WebInspector.panels.scripts.visibleView, "No visible script view.");
       
   603         test.assertTrue(test._scriptsAreParsed(["debugger_test_page.html"]), "Some scripts are missing.");
       
   604         checkNoDuplicates();
       
   605         test.releaseControl();
       
   606     }
       
   607 
       
   608     function checkNoDuplicates() {
       
   609         var scriptSelect = document.getElementById("scripts-files");
       
   610         var options = scriptSelect.options;
       
   611         for (var i = 0; i < options.length; i++) {
       
   612             var scriptName = options[i].text;
       
   613             for (var j = i + 1; j < options.length; j++)
       
   614                 test.assertTrue(scriptName !== options[j].text, "Found script duplicates: " + test.optionsToString_(options));
       
   615         }
       
   616     }
       
   617 
       
   618     test._waitUntilScriptsAreParsed(
       
   619         ["debugger_test_page.html"],
       
   620         function() {
       
   621             checkNoDuplicates();
       
   622             setTimeout(switchToElementsTab, 0);
       
   623         });
       
   624 
       
   625 
       
   626     // Wait until all scripts are added to the debugger.
       
   627     this.takeControl();
       
   628 };
       
   629 
       
   630 
       
   631 /**
       
   632  * Tests that a breakpoint can be set.
       
   633  */
       
   634 TestSuite.prototype.testSetBreakpoint = function()
       
   635 {
       
   636     var test = this;
       
   637     this.showPanel("scripts");
       
   638 
       
   639     var breakpointLine = 16
       
   640 
       
   641     this._waitUntilScriptsAreParsed(["debugger_test_page.html"],
       
   642         function() {
       
   643           test.showMainPageScriptSource_(
       
   644               "debugger_test_page.html",
       
   645               function(view, url) {
       
   646                 view._addBreakpoint(breakpointLine);
       
   647 
       
   648                 test.evaluateInConsole_(
       
   649                     'setTimeout("calculate()" , 0)',
       
   650                     function(resultText) {
       
   651                       test.assertTrue(!isNaN(resultText), "Failed to get timer id: " + resultText);
       
   652                     });
       
   653               });
       
   654         });
       
   655 
       
   656     this._waitForScriptPause(
       
   657         {
       
   658             functionsOnStack: ["calculate", ""],
       
   659             lineNumber: breakpointLine,
       
   660             lineText: "  result = fib(lastVal++);"
       
   661         },
       
   662         function() {
       
   663             test.releaseControl();
       
   664         });
       
   665 
       
   666     this.takeControl();
       
   667 };
       
   668 
       
   669 
       
   670 /**
       
   671  * Tests that pause on exception works.
       
   672  */
       
   673 TestSuite.prototype.testPauseOnException = function()
       
   674 {
       
   675     this.showPanel("scripts");
       
   676     var test = this;
       
   677 
       
   678     InspectorBackend.setPauseOnExceptionsState(WebInspector.ScriptsPanel.PauseOnExceptionsState.PauseOnUncaughtExceptions);
       
   679 
       
   680     this._executeCodeWhenScriptsAreParsed("handleClick()", ["pause_on_exception.html"]);
       
   681 
       
   682     this._waitForScriptPause(
       
   683         {
       
   684             functionsOnStack: ["throwAnException", "handleClick", ""],
       
   685             lineNumber: 6,
       
   686             lineText: "  return unknown_var;"
       
   687         },
       
   688         function() {
       
   689             test.releaseControl();
       
   690         });
       
   691 
       
   692     this.takeControl();
       
   693 };
       
   694 
       
   695 
       
   696 // Tests that debugger works correctly if pause event occurs when DevTools
       
   697 // frontend is being loaded.
       
   698 TestSuite.prototype.testPauseWhenLoadingDevTools = function()
       
   699 {
       
   700     this.showPanel("scripts");
       
   701     var test = this;
       
   702 
       
   703     var expectations = {
       
   704             functionsOnStack: ["callDebugger"],
       
   705             lineNumber: 8,
       
   706             lineText: "  debugger;"
       
   707         };
       
   708 
       
   709 
       
   710     // Script execution can already be paused.
       
   711     if (WebInspector.currentPanel.paused) {
       
   712         var callFrame = WebInspector.currentPanel.sidebarPanes.callstack.selectedCallFrame;
       
   713         this.assertEquals(expectations.functionsOnStack[0], callFrame.functionName);
       
   714         var callbackInvoked = false;
       
   715         this._checkSourceFrameWhenLoaded(expectations, function() {
       
   716                 callbackInvoked = true;
       
   717                 if (test.controlTaken_)
       
   718                     test.releaseControl();
       
   719             });
       
   720         if (!callbackInvoked) {
       
   721             test.takeControl();
       
   722         }
       
   723         return;
       
   724     }
       
   725 
       
   726     this._waitForScriptPause(
       
   727         {
       
   728             functionsOnStack: ["callDebugger"],
       
   729             lineNumber: 8,
       
   730             lineText: "  debugger;"
       
   731         },
       
   732         function() {
       
   733             test.releaseControl();
       
   734         });
       
   735     this.takeControl();
       
   736 };
       
   737 
       
   738 
       
   739 // Tests that pressing "Pause" will pause script execution if the script
       
   740 // is already running.
       
   741 TestSuite.prototype.testPauseWhenScriptIsRunning = function()
       
   742 {
       
   743     this.showPanel("scripts");
       
   744     var test = this;
       
   745 
       
   746     test.evaluateInConsole_(
       
   747         'setTimeout("handleClick()" , 0)',
       
   748         function(resultText) {
       
   749           test.assertTrue(!isNaN(resultText), "Failed to get timer id: " + resultText);
       
   750           testScriptPauseAfterDelay();
       
   751         });
       
   752 
       
   753     // Wait for some time to make sure that inspected page is running the
       
   754     // infinite loop.
       
   755     function testScriptPauseAfterDelay() {
       
   756         setTimeout(testScriptPause, 300);
       
   757     }
       
   758 
       
   759     function testScriptPause() {
       
   760         // The script should be in infinite loop. Click "Pause" button to
       
   761         // pause it and wait for the result.
       
   762         WebInspector.panels.scripts.pauseButton.click();
       
   763 
       
   764         test._waitForScriptPause(
       
   765             {
       
   766                 functionsOnStack: ["handleClick", ""],
       
   767                 lineNumber: 5,
       
   768                 lineText: "  while(true) {"
       
   769             },
       
   770             function() {
       
   771                 test.releaseControl();
       
   772             });
       
   773     }
       
   774 
       
   775     this.takeControl();
       
   776 };
       
   777 
       
   778 
       
   779 /**
       
   780  * Serializes options collection to string.
       
   781  * @param {HTMLOptionsCollection} options
       
   782  * @return {string}
       
   783  */
       
   784 TestSuite.prototype.optionsToString_ = function(options)
       
   785 {
       
   786     var names = [];
       
   787     for (var i = 0; i < options.length; i++)
       
   788         names.push('"' + options[i].text + '"');
       
   789     return names.join(",");
       
   790 };
       
   791 
       
   792 
       
   793 /**
       
   794  * Ensures that main HTML resource is selected in Scripts panel and that its
       
   795  * source frame is setup. Invokes the callback when the condition is satisfied.
       
   796  * @param {HTMLOptionsCollection} options
       
   797  * @param {function(WebInspector.SourceView,string)} callback
       
   798  */
       
   799 TestSuite.prototype.showMainPageScriptSource_ = function(scriptName, callback)
       
   800 {
       
   801     var test = this;
       
   802 
       
   803     var scriptSelect = document.getElementById("scripts-files");
       
   804     var options = scriptSelect.options;
       
   805 
       
   806     test.assertTrue(options.length, "Scripts list is empty");
       
   807 
       
   808     // Select page's script if it's not current option.
       
   809     var scriptResource;
       
   810     if (options[scriptSelect.selectedIndex].text === scriptName)
       
   811         scriptResource = options[scriptSelect.selectedIndex].representedObject;
       
   812     else {
       
   813         var pageScriptIndex = -1;
       
   814         for (var i = 0; i < options.length; i++) {
       
   815             if (options[i].text === scriptName) {
       
   816                 pageScriptIndex = i;
       
   817                 break;
       
   818             }
       
   819         }
       
   820         test.assertTrue(-1 !== pageScriptIndex, "Script with url " + scriptName + " not found among " + test.optionsToString_(options));
       
   821         scriptResource = options[pageScriptIndex].representedObject;
       
   822 
       
   823         // Current panel is "Scripts".
       
   824         WebInspector.currentPanel._showScriptOrResource(scriptResource);
       
   825         test.assertEquals(pageScriptIndex, scriptSelect.selectedIndex, "Unexpected selected option index.");
       
   826     }
       
   827 
       
   828     test.assertTrue(scriptResource instanceof WebInspector.Resource,
       
   829                     "Unexpected resource class.");
       
   830     test.assertTrue(!!scriptResource.url, "Resource URL is null.");
       
   831     test.assertTrue(scriptResource.url.search(scriptName + "$") !== -1, "Main HTML resource should be selected.");
       
   832 
       
   833     var scriptsPanel = WebInspector.panels.scripts;
       
   834 
       
   835     var view = scriptsPanel.visibleView;
       
   836     test.assertTrue(view instanceof WebInspector.SourceView);
       
   837 
       
   838     if (!view.sourceFrame._loaded) {
       
   839         test.addSniffer(view, "_sourceFrameSetupFinished", function(event) {
       
   840             callback(view, scriptResource.url);
       
   841         });
       
   842     } else
       
   843         callback(view, scriptResource.url);
       
   844 };
       
   845 
       
   846 
       
   847 /*
       
   848  * Evaluates the code in the console as if user typed it manually and invokes
       
   849  * the callback when the result message is received and added to the console.
       
   850  * @param {string} code
       
   851  * @param {function(string)} callback
       
   852  */
       
   853 TestSuite.prototype.evaluateInConsole_ = function(code, callback)
       
   854 {
       
   855     WebInspector.showConsole();
       
   856     WebInspector.console.prompt.text = code;
       
   857     WebInspector.console.promptElement.dispatchEvent( TestSuite.createKeyEvent("Enter"));
       
   858 
       
   859     this.addSniffer(WebInspector.ConsoleView.prototype, "addMessage",
       
   860         function(commandResult) {
       
   861             callback(commandResult.toMessageElement().textContent);
       
   862         });
       
   863 };
       
   864 
       
   865 
       
   866 /**
       
   867  * Tests eval on call frame.
       
   868  */
       
   869 TestSuite.prototype.testEvalOnCallFrame = function()
       
   870 {
       
   871     this.showPanel("scripts");
       
   872 
       
   873     var breakpointLine = 16;
       
   874 
       
   875     var test = this;
       
   876     this._waitUntilScriptsAreParsed(["debugger_test_page.html"],
       
   877         function() {
       
   878           test.showMainPageScriptSource_(
       
   879               "debugger_test_page.html",
       
   880               function(view, url) {
       
   881                   view._addBreakpoint(breakpointLine);
       
   882 
       
   883                   // Since breakpoints are ignored in evals' calculate() function is
       
   884                   // execute after zero-timeout so that the breakpoint is hit.
       
   885                   test.evaluateInConsole_(
       
   886                       'setTimeout("calculate(123)" , 0)',
       
   887                       function(resultText) {
       
   888                           test.assertTrue(!isNaN(resultText), "Failed to get timer id: " + resultText);
       
   889                           waitForBreakpointHit();
       
   890                       });
       
   891               });
       
   892         });
       
   893 
       
   894     function waitForBreakpointHit() {
       
   895       test.addSniffer(WebInspector,
       
   896           "pausedScript",
       
   897           function(callFrames) {
       
   898             test.assertEquals(2, callFrames.length, "Unexpected stack depth on the breakpoint. " + JSON.stringify(callFrames, null, 4));
       
   899             test.assertEquals("calculate", callFrames[0].functionName, "Unexpected top frame function.");
       
   900             // Evaluate "e+1" where "e" is an argument of "calculate" function.
       
   901             test.evaluateInConsole_(
       
   902                 "e+1",
       
   903                 function(resultText) {
       
   904                     test.assertEquals("124", resultText, 'Unexpected "e+1" value.');
       
   905                     test.releaseControl();
       
   906                 });
       
   907           });
       
   908     }
       
   909 
       
   910     this.takeControl();
       
   911 };
       
   912 
       
   913 
       
   914 /**
       
   915  * Tests that console auto completion works when script execution is paused.
       
   916  */
       
   917 TestSuite.prototype.testCompletionOnPause = function()
       
   918 {
       
   919     this.showPanel("scripts");
       
   920     var test = this;
       
   921     this._executeCodeWhenScriptsAreParsed("handleClick()", ["completion_on_pause.html"]);
       
   922 
       
   923     this._waitForScriptPause(
       
   924         {
       
   925             functionsOnStack: ["innerFunction", "handleClick", ""],
       
   926             lineNumber: 9,
       
   927             lineText: "    debugger;"
       
   928         },
       
   929         showConsole);
       
   930 
       
   931     function showConsole() {
       
   932         if (WebInspector.currentFocusElement === WebInspector.console.promptElement)
       
   933             testLocalsCompletion();
       
   934         else {
       
   935             test.addSniffer(WebInspector.console, "afterShow", testLocalsCompletion);
       
   936             WebInspector.showConsole();
       
   937         }
       
   938     }
       
   939 
       
   940     function testLocalsCompletion() {
       
   941         checkCompletions("th", ["parameter1", "closureLocal", "p", "createClosureLocal"], testThisCompletion);
       
   942     }
       
   943 
       
   944     function testThisCompletion() {
       
   945         checkCompletions("this.", ["field1", "field2", "m"], testFieldCompletion);
       
   946     }
       
   947 
       
   948     function testFieldCompletion() {
       
   949         checkCompletions("this.field1.", ["id", "name"], function() { test.releaseControl(); });
       
   950     }
       
   951 
       
   952     function checkCompletions(expression, expectedProperties, callback) {
       
   953         test.addSniffer(WebInspector.console, "_reportCompletions",
       
   954             function(bestMatchOnly, completionsReadyCallback, dotNotation, bracketNotation, prefix, result, isException) {
       
   955                 test.assertTrue(!isException, "Exception while collecting completions");
       
   956                 for (var i = 0; i < expectedProperties.length; i++) {
       
   957                     var name = expectedProperties[i];
       
   958                     test.assertTrue(result[name], "Name " + name + " not found among the completions: " + JSON.stringify(result));
       
   959                 }
       
   960                 setTimeout(callback, 0);
       
   961             });
       
   962       WebInspector.console.prompt.text = expression;
       
   963       WebInspector.console.prompt.autoCompleteSoon();
       
   964     }
       
   965 
       
   966     this.takeControl();
       
   967 };
       
   968 
       
   969 
       
   970 /**
       
   971  * Tests that inspected page doesn't hang on reload if it contains a syntax
       
   972  * error and DevTools window is open.
       
   973  */
       
   974 TestSuite.prototype.testAutoContinueOnSyntaxError = function()
       
   975 {
       
   976     // TODO(yurys): provide an implementation that works with ScriptDebugServer.
       
   977 };
       
   978 
       
   979 
       
   980 /**
       
   981  * Checks current execution line against expectations.
       
   982  * @param {WebInspector.SourceFrame} sourceFrame
       
   983  * @param {number} lineNumber Expected line number
       
   984  * @param {string} lineContent Expected line text
       
   985  */
       
   986 TestSuite.prototype._checkExecutionLine = function(sourceFrame, lineNumber, lineContent)
       
   987 {
       
   988     this.assertEquals(lineNumber, sourceFrame.executionLine, "Unexpected execution line number.");
       
   989     this.assertEquals(lineContent, sourceFrame._textModel.line(lineNumber - 1), "Unexpected execution line text.");
       
   990 }
       
   991 
       
   992 
       
   993 /**
       
   994  * Checks that all expected scripts are present in the scripts list
       
   995  * in the Scripts panel.
       
   996  * @param {Array.<string>} expected Regular expressions describing
       
   997  *     expected script names.
       
   998  * @return {boolean} Whether all the scripts are in "scripts-files" select
       
   999  *     box
       
  1000  */
       
  1001 TestSuite.prototype._scriptsAreParsed = function(expected)
       
  1002 {
       
  1003     var scriptSelect = document.getElementById("scripts-files");
       
  1004     var options = scriptSelect.options;
       
  1005 
       
  1006     // Check that at least all the expected scripts are present.
       
  1007     var missing = expected.slice(0);
       
  1008     for (var i = 0 ; i < options.length; i++) {
       
  1009         for (var j = 0; j < missing.length; j++) {
       
  1010             if (options[i].text.search(missing[j]) !== -1) {
       
  1011                 missing.splice(j, 1);
       
  1012                 break;
       
  1013             }
       
  1014         }
       
  1015     }
       
  1016     return missing.length === 0;
       
  1017 };
       
  1018 
       
  1019 
       
  1020 /**
       
  1021  * Waits for script pause, checks expectations, and invokes the callback.
       
  1022  * @param {Object} expectations  Dictionary of expectations
       
  1023  * @param {function():void} callback
       
  1024  */
       
  1025 TestSuite.prototype._waitForScriptPause = function(expectations, callback)
       
  1026 {
       
  1027     var test = this;
       
  1028     // Wait until script is paused.
       
  1029     test.addSniffer(
       
  1030         WebInspector,
       
  1031         "pausedScript",
       
  1032         function(callFrames) {
       
  1033             var functionsOnStack = [];
       
  1034             for (var i = 0; i < callFrames.length; i++)
       
  1035                 functionsOnStack.push(callFrames[i].functionName);
       
  1036 
       
  1037             test.assertEquals(expectations.functionsOnStack.join(","), functionsOnStack.join(","), "Unexpected stack.");
       
  1038 
       
  1039             // Check that execution line where the script is paused is
       
  1040             // expected one.
       
  1041             test._checkSourceFrameWhenLoaded(expectations, callback);
       
  1042         });
       
  1043 };
       
  1044 
       
  1045 
       
  1046 /**
       
  1047  * Waits for current source frame to load, checks expectations, and invokes
       
  1048  * the callback.
       
  1049  * @param {Object} expectations  Dictionary of expectations
       
  1050  * @param {function():void} callback
       
  1051  */
       
  1052 TestSuite.prototype._checkSourceFrameWhenLoaded = function(expectations, callback)
       
  1053 {
       
  1054     var test = this;
       
  1055 
       
  1056     var frame = WebInspector.currentPanel.visibleView.sourceFrame;
       
  1057     if (frame._loaded)
       
  1058         checkExecLine();
       
  1059     else {
       
  1060         setTimeout(function() {
       
  1061             test._checkSourceFrameWhenLoaded(expectations, callback);
       
  1062         }, 100);
       
  1063     }
       
  1064     function checkExecLine() {
       
  1065         test._checkExecutionLine(frame, expectations.lineNumber, expectations.lineText);
       
  1066         callback();
       
  1067     }
       
  1068 };
       
  1069 
       
  1070 
       
  1071 /**
       
  1072  * Performs sequence of steps.
       
  1073  * @param {Array.<Object|Function>} Array [expectations1,action1,expectations2,
       
  1074  *     action2,...,actionN].
       
  1075  */
       
  1076 TestSuite.prototype._performSteps = function(actions)
       
  1077 {
       
  1078     var test = this;
       
  1079     var i = 0;
       
  1080     function doNextAction() {
       
  1081         if (i > 0)
       
  1082             actions[i++]();
       
  1083         if (i < actions.length - 1)
       
  1084             test._waitForScriptPause(actions[i++], doNextAction);
       
  1085     }
       
  1086     doNextAction();
       
  1087 };
       
  1088 
       
  1089 
       
  1090 /**
       
  1091  * Waits until all the scripts are parsed and asynchronously executes the code
       
  1092  * in the inspected page.
       
  1093  */
       
  1094 TestSuite.prototype._executeCodeWhenScriptsAreParsed = function(code, expectedScripts)
       
  1095 {
       
  1096     var test = this;
       
  1097 
       
  1098     function executeFunctionInInspectedPage() {
       
  1099         // Since breakpoints are ignored in evals' calculate() function is
       
  1100         // execute after zero-timeout so that the breakpoint is hit.
       
  1101         test.evaluateInConsole_(
       
  1102             'setTimeout("' + code + '" , 0)',
       
  1103             function(resultText) {
       
  1104                 test.assertTrue(!isNaN(resultText), "Failed to get timer id: " + resultText + ". Code: " + code);
       
  1105             });
       
  1106     }
       
  1107 
       
  1108     test._waitUntilScriptsAreParsed(expectedScripts, executeFunctionInInspectedPage);
       
  1109 };
       
  1110 
       
  1111 
       
  1112 /**
       
  1113  * Waits until all the scripts are parsed and invokes the callback.
       
  1114  */
       
  1115 TestSuite.prototype._waitUntilScriptsAreParsed = function(expectedScripts, callback)
       
  1116 {
       
  1117     var test = this;
       
  1118 
       
  1119     function waitForAllScripts() {
       
  1120         if (test._scriptsAreParsed(expectedScripts))
       
  1121             callback();
       
  1122         else
       
  1123             test.addSniffer(WebInspector, "parsedScriptSource", waitForAllScripts);
       
  1124     }
       
  1125 
       
  1126     waitForAllScripts();
       
  1127 };
       
  1128 
       
  1129 
       
  1130 /**
       
  1131  * Waits until all debugger scripts are parsed and executes "a()" in the
       
  1132  * inspected page.
       
  1133  */
       
  1134 TestSuite.prototype._executeFunctionForStepTest = function()
       
  1135 {
       
  1136     this._executeCodeWhenScriptsAreParsed("a()", ["debugger_step.html", "debugger_step.js"]);
       
  1137 };
       
  1138 
       
  1139 
       
  1140 /**
       
  1141  * Tests step over in the debugger.
       
  1142  */
       
  1143 TestSuite.prototype.testStepOver = function()
       
  1144 {
       
  1145     this.showPanel("scripts");
       
  1146     var test = this;
       
  1147 
       
  1148     this._executeFunctionForStepTest();
       
  1149 
       
  1150     this._performSteps([
       
  1151         {
       
  1152             functionsOnStack: ["d","a",""],
       
  1153             lineNumber: 3,
       
  1154             lineText: "    debugger;"
       
  1155         },
       
  1156         function() {
       
  1157             document.getElementById("scripts-step-over").click();
       
  1158         },
       
  1159         {
       
  1160             functionsOnStack: ["d","a",""],
       
  1161             lineNumber: 5,
       
  1162             lineText: "  var y = fact(10);"
       
  1163         },
       
  1164         function() {
       
  1165             document.getElementById("scripts-step-over").click();
       
  1166         },
       
  1167         {
       
  1168             functionsOnStack: ["d","a",""],
       
  1169             lineNumber: 6,
       
  1170             lineText: "  return y;"
       
  1171         },
       
  1172         function() {
       
  1173             test.releaseControl();
       
  1174         }
       
  1175     ]);
       
  1176 
       
  1177     test.takeControl();
       
  1178 };
       
  1179 
       
  1180 
       
  1181 /**
       
  1182  * Tests step out in the debugger.
       
  1183  */
       
  1184 TestSuite.prototype.testStepOut = function()
       
  1185 {
       
  1186     this.showPanel("scripts");
       
  1187     var test = this;
       
  1188 
       
  1189     this._executeFunctionForStepTest();
       
  1190 
       
  1191     this._performSteps([
       
  1192         {
       
  1193             functionsOnStack: ["d","a",""],
       
  1194             lineNumber: 3,
       
  1195             lineText: "    debugger;"
       
  1196         },
       
  1197         function() {
       
  1198             document.getElementById("scripts-step-out").click();
       
  1199         },
       
  1200         {
       
  1201             functionsOnStack: ["a",""],
       
  1202             lineNumber: 8,
       
  1203             lineText: "  printResult(result);"
       
  1204         },
       
  1205         function() {
       
  1206             test.releaseControl();
       
  1207         }
       
  1208     ]);
       
  1209 
       
  1210     test.takeControl();
       
  1211 };
       
  1212 
       
  1213 
       
  1214 /**
       
  1215  * Tests step in in the debugger.
       
  1216  */
       
  1217 TestSuite.prototype.testStepIn = function()
       
  1218 {
       
  1219     this.showPanel("scripts");
       
  1220     var test = this;
       
  1221 
       
  1222     this._executeFunctionForStepTest();
       
  1223 
       
  1224     this._performSteps([
       
  1225         {
       
  1226             functionsOnStack: ["d","a",""],
       
  1227             lineNumber: 3,
       
  1228             lineText: "    debugger;"
       
  1229         },
       
  1230         function() {
       
  1231             document.getElementById("scripts-step-over").click();
       
  1232         },
       
  1233         {
       
  1234             functionsOnStack: ["d","a",""],
       
  1235             lineNumber: 5,
       
  1236             lineText: "  var y = fact(10);"
       
  1237         },
       
  1238         function() {
       
  1239             document.getElementById("scripts-step-into").click();
       
  1240         },
       
  1241         {
       
  1242             functionsOnStack: ["fact","d","a",""],
       
  1243             lineNumber: 10,
       
  1244             lineText: "  var r = 1;"
       
  1245         },
       
  1246         function() {
       
  1247             test.releaseControl();
       
  1248         }
       
  1249     ]);
       
  1250 
       
  1251     test.takeControl();
       
  1252 };
       
  1253 
       
  1254 
       
  1255 /**
       
  1256  * Gets a XPathResult matching given xpath.
       
  1257  * @param {string} xpath
       
  1258  * @param {number} resultType
       
  1259  * @param {Node} opt_ancestor Context node. If not specified documentElement
       
  1260  *     will be used
       
  1261  * @return {XPathResult} Type of returned value is determined by "resultType" parameter
       
  1262  */
       
  1263 
       
  1264 TestSuite.prototype._evaluateXpath = function(xpath, resultType, opt_ancestor)
       
  1265 {
       
  1266     if (!opt_ancestor)
       
  1267         opt_ancestor = document.documentElement;
       
  1268     try {
       
  1269         return document.evaluate(xpath, opt_ancestor, null, resultType, null);
       
  1270     } catch(e) {
       
  1271         this.fail('Error in expression: "' + xpath + '".' + e);
       
  1272     }
       
  1273 };
       
  1274 
       
  1275 
       
  1276 /**
       
  1277  * Gets first Node matching given xpath.
       
  1278  * @param {string} xpath
       
  1279  * @param {Node} opt_ancestor Context node. If not specified documentElement
       
  1280  *     will be used
       
  1281  * @return {?Node}
       
  1282  */
       
  1283 TestSuite.prototype._findNode = function(xpath, opt_ancestor)
       
  1284 {
       
  1285     var result = this._evaluateXpath(xpath, XPathResult.FIRST_ORDERED_NODE_TYPE, opt_ancestor).singleNodeValue;
       
  1286     this.assertTrue(!!result, "Cannot find node on path: " + xpath);
       
  1287     return result;
       
  1288 };
       
  1289 
       
  1290 
       
  1291 /**
       
  1292  * Gets a text matching given xpath.
       
  1293  * @param {string} xpath
       
  1294  * @param {Node} opt_ancestor Context node. If not specified documentElement
       
  1295  *     will be used
       
  1296  * @return {?string}
       
  1297  */
       
  1298 TestSuite.prototype._findText = function(xpath, opt_ancestor)
       
  1299 {
       
  1300     var result = this._evaluateXpath(xpath, XPathResult.STRING_TYPE, opt_ancestor).stringValue;
       
  1301     this.assertTrue(!!result, "Cannot find text on path: " + xpath);
       
  1302     return result;
       
  1303 };
       
  1304 
       
  1305 
       
  1306 /**
       
  1307  * Gets an iterator over nodes matching given xpath.
       
  1308  * @param {string} xpath
       
  1309  * @param {Node} opt_ancestor Context node. If not specified, documentElement
       
  1310  *     will be used
       
  1311  * @return {XPathResult} Iterator over the nodes
       
  1312  */
       
  1313 TestSuite.prototype._nodeIterator = function(xpath, opt_ancestor)
       
  1314 {
       
  1315     return this._evaluateXpath(xpath, XPathResult.ORDERED_NODE_ITERATOR_TYPE, opt_ancestor);
       
  1316 };
       
  1317 
       
  1318 
       
  1319 /**
       
  1320  * Checks the scopeSectionDiv against the expectations.
       
  1321  * @param {Node} scopeSectionDiv The section div
       
  1322  * @param {Object} expectations Expectations dictionary
       
  1323  */
       
  1324 TestSuite.prototype._checkScopeSectionDiv = function(scopeSectionDiv, expectations)
       
  1325 {
       
  1326     var scopeTitle = this._findText('./div[@class="header"]/div[@class="title"]/text()', scopeSectionDiv);
       
  1327     this.assertEquals(expectations.title, scopeTitle, "Unexpected scope section title.");
       
  1328     if (!expectations.properties)
       
  1329         return;
       
  1330     this.assertTrue(scopeSectionDiv.hasStyleClass("expanded"), 'Section "' + scopeTitle + '" is collapsed.');
       
  1331 
       
  1332     var propertyIt = this._nodeIterator("./ol/li", scopeSectionDiv);
       
  1333     var propertyLi;
       
  1334     var foundProps = [];
       
  1335     while (propertyLi = propertyIt.iterateNext()) {
       
  1336         var name = this._findText('./span[@class="name"]/text()', propertyLi);
       
  1337         var value = this._findText('./span[@class="value"]/text()', propertyLi);
       
  1338         this.assertTrue(!!name, 'Invalid variable name: "' + name + '"');
       
  1339         this.assertTrue(name in expectations.properties, "Unexpected property: " + name);
       
  1340         this.assertEquals(expectations.properties[name], value, 'Unexpected "' + name + '" property value.');
       
  1341         delete expectations.properties[name];
       
  1342         foundProps.push(name + " = " + value);
       
  1343     }
       
  1344 
       
  1345     // Check that all expected properties were found.
       
  1346     for (var p in expectations.properties)
       
  1347         this.fail('Property "' + p + '" was not found in scope "' + scopeTitle + '". Found properties: "' + foundProps.join(",") + '"');
       
  1348 };
       
  1349 
       
  1350 
       
  1351 /**
       
  1352  * Expands scope sections matching the filter and invokes the callback on
       
  1353  * success.
       
  1354  * @param {function(WebInspector.ObjectPropertiesSection, number):boolean}
       
  1355  *     filter
       
  1356  * @param {Function} callback
       
  1357  */
       
  1358 TestSuite.prototype._expandScopeSections = function(filter, callback)
       
  1359 {
       
  1360     var sections = WebInspector.currentPanel.sidebarPanes.scopechain.sections;
       
  1361 
       
  1362     var toBeUpdatedCount = 0;
       
  1363     function updateListener() {
       
  1364         --toBeUpdatedCount;
       
  1365         if (toBeUpdatedCount === 0) {
       
  1366             // Report when all scopes are expanded and populated.
       
  1367             callback();
       
  1368         }
       
  1369     }
       
  1370 
       
  1371     // Global scope is always the last one.
       
  1372     for (var i = 0; i < sections.length - 1; i++) {
       
  1373         var section = sections[i];
       
  1374         if (!filter(sections, i))
       
  1375             continue;
       
  1376         ++toBeUpdatedCount;
       
  1377         var populated = section.populated;
       
  1378 
       
  1379         this._hookGetPropertiesCallback(updateListener,
       
  1380             function() {
       
  1381                 section.expand();
       
  1382                 if (populated) {
       
  1383                     // Make sure "updateProperties" callback will be called at least once
       
  1384                     // after it was overridden.
       
  1385                     section.update();
       
  1386                 }
       
  1387             });
       
  1388     }
       
  1389 };
       
  1390 
       
  1391 
       
  1392 /**
       
  1393  * Tests that scopes can be expanded and contain expected data.
       
  1394  */
       
  1395 TestSuite.prototype.testExpandScope = function()
       
  1396 {
       
  1397     this.showPanel("scripts");
       
  1398     var test = this;
       
  1399 
       
  1400     this._executeCodeWhenScriptsAreParsed("handleClick()", ["debugger_closure.html"]);
       
  1401 
       
  1402     this._waitForScriptPause(
       
  1403         {
       
  1404             functionsOnStack: ["innerFunction", "handleClick", ""],
       
  1405             lineNumber: 8,
       
  1406             lineText: "    debugger;"
       
  1407         },
       
  1408         expandAllSectionsExceptGlobal);
       
  1409 
       
  1410     // Expanding Global scope takes for too long so we skeep it.
       
  1411     function expandAllSectionsExceptGlobal() {
       
  1412         test._expandScopeSections(function(sections, i) {
       
  1413             return i < sections.length - 1;
       
  1414         },
       
  1415         examineScopes /* When all scopes are expanded and populated check them. */);
       
  1416     }
       
  1417 
       
  1418     // Check scope sections contents.
       
  1419     function examineScopes() {
       
  1420         var scopeVariablesSection = test._findNode('//div[@id="scripts-sidebar"]/div[div[@class="title"]/text()="Scope Variables"]');
       
  1421         var expectedScopes = [
       
  1422             {
       
  1423                 title: "Local",
       
  1424                 properties: {
       
  1425                     x:"2009",
       
  1426                     innerFunctionLocalVar:"2011",
       
  1427                     "this": "DOMWindow",
       
  1428                 }
       
  1429             },
       
  1430             {
       
  1431                 title: "Closure",
       
  1432                 properties: {
       
  1433                     n: '"TextParam"',
       
  1434                     makeClosureLocalVar: '"local.TextParam"',
       
  1435                 }
       
  1436             },
       
  1437             {
       
  1438                 title: "Global",
       
  1439             },
       
  1440         ];
       
  1441         var it = test._nodeIterator('./div[@class="body"]/div', scopeVariablesSection);
       
  1442         var scopeIndex = 0;
       
  1443         var scopeDiv;
       
  1444         while (scopeDiv = it.iterateNext()) {
       
  1445             test.assertTrue(scopeIndex < expectedScopes.length, "Too many scopes.");
       
  1446             test._checkScopeSectionDiv(scopeDiv, expectedScopes[scopeIndex]);
       
  1447             ++scopeIndex;
       
  1448         }
       
  1449         test.assertEquals(expectedScopes.length, scopeIndex, "Unexpected number of scopes.");
       
  1450 
       
  1451         test.releaseControl();
       
  1452     }
       
  1453 
       
  1454     test.takeControl();
       
  1455 };
       
  1456 
       
  1457 
       
  1458 /**
       
  1459  * Returns child tree element for a property with given name.
       
  1460  * @param {TreeElement} parent Parent tree element.
       
  1461  * @param {string} childName
       
  1462  * @param {string} objectPath Path to the object. Will be printed in the case
       
  1463  *     of failure.
       
  1464  * @return {TreeElement}
       
  1465  */
       
  1466 TestSuite.prototype._findChildProperty = function(parent, childName, objectPath)
       
  1467 {
       
  1468     var children = parent.children;
       
  1469     for (var i = 0; i < children.length; i++) {
       
  1470         var treeElement = children[i];
       
  1471         var property = treeElement.property;
       
  1472         if (property.name === childName)
       
  1473             return treeElement;
       
  1474     }
       
  1475     this.fail('Cannot find property "' + childName + '" in ' + objectPath);
       
  1476 };
       
  1477 
       
  1478 
       
  1479 /**
       
  1480  * Executes the 'code' with InjectedScriptAccess.getProperties overriden
       
  1481  * so that all callbacks passed to InjectedScriptAccess.getProperties are
       
  1482  * extended with the "hook".
       
  1483  * @param {Function} hook The hook function.
       
  1484  * @param {Function} code A code snippet to be executed.
       
  1485  */
       
  1486 TestSuite.prototype._hookGetPropertiesCallback = function(hook, code)
       
  1487 {
       
  1488     var accessor = InjectedScriptAccess.prototype;
       
  1489     var orig = accessor.getProperties;
       
  1490     accessor.getProperties = function(objectProxy, ignoreHasOwnProperty, abbreviate, callback) {
       
  1491         orig.call(this, objectProxy, ignoreHasOwnProperty, abbreviate,
       
  1492             function() {
       
  1493               callback.apply(this, arguments);
       
  1494               hook();
       
  1495             });
       
  1496     };
       
  1497     try {
       
  1498         code();
       
  1499     } finally {
       
  1500         accessor.getProperties = orig;
       
  1501     }
       
  1502 };
       
  1503 
       
  1504 
       
  1505 /**
       
  1506  * Tests that all elements in prototype chain of an object have expected
       
  1507  * intrinic proprties(__proto__, constructor, prototype).
       
  1508  */
       
  1509 TestSuite.prototype.testDebugIntrinsicProperties = function()
       
  1510 {
       
  1511     this.showPanel("scripts");
       
  1512     var test = this;
       
  1513 
       
  1514     this._executeCodeWhenScriptsAreParsed("handleClick()", ["debugger_intrinsic_properties.html"]);
       
  1515 
       
  1516     this._waitForScriptPause(
       
  1517         {
       
  1518             functionsOnStack: ["callDebugger", "handleClick", ""],
       
  1519             lineNumber: 29,
       
  1520             lineText: "  debugger;"
       
  1521         },
       
  1522         expandLocalScope);
       
  1523 
       
  1524     var localScopeSection = null;
       
  1525     function expandLocalScope() {
       
  1526       test._expandScopeSections(function(sections, i) {
       
  1527               if (i === 0) {
       
  1528                   test.assertTrue(sections[i].object.isLocal, "Scope #0 is not Local.");
       
  1529                   localScopeSection = sections[i];
       
  1530                   return true;
       
  1531               }
       
  1532               return false;
       
  1533           },
       
  1534           examineLocalScope);
       
  1535     }
       
  1536 
       
  1537     function examineLocalScope() {
       
  1538       var scopeExpectations = [
       
  1539           "a", "Child", [
       
  1540             "__proto__", "Child", [
       
  1541                   "__proto__", "Parent", [
       
  1542                       "__proto__", "Object", null,
       
  1543                     "constructor", "function Parent(n) {", [
       
  1544                         "name", '"Parent"', null,
       
  1545                         "prototype", 'Parent', [
       
  1546                             "parentProtoField", "11", null,
       
  1547                         ]
       
  1548                     ],
       
  1549                     "parentProtoField", "11", null,
       
  1550                 ],
       
  1551                 "constructor", "function Child(n) {", null,
       
  1552                 "childProtoField", "21", null,
       
  1553             ],
       
  1554 
       
  1555             "parentField", "10", null,
       
  1556             "childField", "20", null,
       
  1557           ]
       
  1558       ];
       
  1559       checkProperty(localScopeSection.propertiesTreeOutline, "<Local Scope>", scopeExpectations);
       
  1560     }
       
  1561 
       
  1562     var propQueue = [];
       
  1563     var index = 0;
       
  1564     var expectedFinalIndex = 5;
       
  1565 
       
  1566     function expandAndCheckNextProperty() {
       
  1567         if (index === propQueue.length) {
       
  1568             test.assertEquals(expectedFinalIndex, index, "Unexpected number of expanded objects.");
       
  1569             test.releaseControl();
       
  1570             return;
       
  1571         }
       
  1572 
       
  1573         // Read next property data from the queue.
       
  1574         var treeElement = propQueue[index].treeElement;
       
  1575         var path = propQueue[index].path;
       
  1576         var expectations = propQueue[index].expectations;
       
  1577         index++;
       
  1578 
       
  1579         // Expand the property.
       
  1580         test._hookGetPropertiesCallback(function() {
       
  1581                 checkProperty(treeElement, path, expectations);
       
  1582             },
       
  1583             function() {
       
  1584                 treeElement.expand();
       
  1585             });
       
  1586     }
       
  1587 
       
  1588     function checkProperty(treeElement, path, expectations) {
       
  1589         for (var i = 0; i < expectations.length; i += 3) {
       
  1590             var name = expectations[i];
       
  1591             var description = expectations[i+1];
       
  1592             var value = expectations[i+2];
       
  1593 
       
  1594             var propertyPath = path + "." + name;
       
  1595             var propertyTreeElement = test._findChildProperty(treeElement, name, path);
       
  1596             test.assertTrue(propertyTreeElement, 'Property "' + propertyPath + '" not found.');
       
  1597             test.assertEquals(description, propertyTreeElement.property.value.description, 'Unexpected "' + propertyPath + '" description.');
       
  1598             if (value) {
       
  1599                 // Schedule property content check.
       
  1600                 propQueue.push({
       
  1601                     treeElement: propertyTreeElement,
       
  1602                     path: propertyPath,
       
  1603                     expectations: value,
       
  1604                 });
       
  1605             }
       
  1606         }
       
  1607         // Check next property in the queue.
       
  1608         expandAndCheckNextProperty();
       
  1609     }
       
  1610 
       
  1611     test.takeControl();
       
  1612 };
       
  1613 
       
  1614 
       
  1615 /**
       
  1616  * Tests "Pause" button will pause debugger when a snippet is evaluated.
       
  1617  */
       
  1618 TestSuite.prototype.testPauseInEval = function()
       
  1619 {
       
  1620     this.showPanel("scripts");
       
  1621 
       
  1622     var test = this;
       
  1623 
       
  1624     var pauseButton = document.getElementById("scripts-pause");
       
  1625     pauseButton.click();
       
  1626 
       
  1627     devtools.tools.evaluateJavaScript("fib(10)");
       
  1628 
       
  1629     this.addSniffer(WebInspector, "pausedScript",
       
  1630         function() {
       
  1631             test.releaseControl();
       
  1632         });
       
  1633 
       
  1634     test.takeControl();
       
  1635 };
       
  1636 
       
  1637 
       
  1638 /**
       
  1639  * Key event with given key identifier.
       
  1640  */
       
  1641 TestSuite.createKeyEvent = function(keyIdentifier)
       
  1642 {
       
  1643     var evt = document.createEvent("KeyboardEvent");
       
  1644     evt.initKeyboardEvent("keydown", true /* can bubble */, true /* can cancel */, null /* view */, keyIdentifier, "");
       
  1645     return evt;
       
  1646 };
       
  1647 
       
  1648 
       
  1649 /**
       
  1650  * Tests console eval.
       
  1651  */
       
  1652 TestSuite.prototype.testConsoleEval = function()
       
  1653 {
       
  1654     var test = this;
       
  1655     this.evaluateInConsole_("123",
       
  1656         function(resultText) {
       
  1657             test.assertEquals("123", resultText);
       
  1658             test.releaseControl();
       
  1659         });
       
  1660 
       
  1661     this.takeControl();
       
  1662 };
       
  1663 
       
  1664 
       
  1665 /**
       
  1666  * Tests console log.
       
  1667  */
       
  1668 TestSuite.prototype.testConsoleLog = function()
       
  1669 {
       
  1670     WebInspector.showConsole();
       
  1671     var messages = WebInspector.console.messages;
       
  1672     var index = 0;
       
  1673 
       
  1674     var test = this;
       
  1675     var assertNext = function(line, message, opt_class, opt_count, opt_substr) {
       
  1676         var elem = messages[index++].toMessageElement();
       
  1677         var clazz = elem.getAttribute("class");
       
  1678         var expectation = (opt_count || '') + 'console_test_page.html:' + line + message;
       
  1679         if (opt_substr)
       
  1680             test.assertContains(elem.textContent, expectation);
       
  1681         else
       
  1682             test.assertEquals(expectation, elem.textContent);
       
  1683         if (opt_class)
       
  1684             test.assertContains(clazz, "console-" + opt_class);
       
  1685     };
       
  1686 
       
  1687     assertNext("5", "log", "log-level");
       
  1688     assertNext("7", "debug", "log-level");
       
  1689     assertNext("9", "info", "log-level");
       
  1690     assertNext("11", "warn", "warning-level");
       
  1691     assertNext("13", "error", "error-level");
       
  1692     assertNext("15", "Message format number 1, 2 and 3.5");
       
  1693     assertNext("17", "Message format for string");
       
  1694     assertNext("19", "Object Object");
       
  1695     assertNext("22", "repeated", "log-level", 5);
       
  1696     assertNext("26", "count: 1");
       
  1697     assertNext("26", "count: 2");
       
  1698     assertNext("29", "group", "group-title");
       
  1699     index++;
       
  1700     assertNext("33", "timer:", "log-level", "", true);
       
  1701     assertNext("35", "1 2 3", "log-level");
       
  1702     assertNext("37", "HTMLDocument", "log-level");
       
  1703     assertNext("39", "<html>", "log-level", "", true);
       
  1704 };
       
  1705 
       
  1706 
       
  1707 /**
       
  1708  * Tests eval of global objects.
       
  1709  */
       
  1710 TestSuite.prototype.testEvalGlobal = function()
       
  1711 {
       
  1712     WebInspector.showConsole();
       
  1713 
       
  1714     var inputs = ["foo", "foobar"];
       
  1715     var expectations = ["foo", "fooValue", "foobar", "ReferenceError: foobar is not defined"];
       
  1716 
       
  1717     // Do not change code below - simply add inputs and expectations above.
       
  1718     var initEval = function(input) {
       
  1719         WebInspector.console.prompt.text = input;
       
  1720         WebInspector.console.promptElement.dispatchEvent( TestSuite.createKeyEvent("Enter"));
       
  1721     };
       
  1722     var test = this;
       
  1723     var messagesCount = 0;
       
  1724     var inputIndex = 0;
       
  1725     this.addSniffer(WebInspector.ConsoleView.prototype, "addMessage",
       
  1726         function(commandResult) {
       
  1727             messagesCount++;
       
  1728             if (messagesCount === expectations.length) {
       
  1729                 var messages = WebInspector.console.messages;
       
  1730                 for (var i = 0; i < expectations; ++i) {
       
  1731                     var elem = messages[i++].toMessageElement();
       
  1732                     test.assertEquals(elem.textContent, expectations[i]);
       
  1733                 }
       
  1734                 test.releaseControl();
       
  1735             } else if (messagesCount % 2 === 0)
       
  1736                 initEval(inputs[inputIndex++]);
       
  1737         }, true);
       
  1738 
       
  1739     initEval(inputs[inputIndex++]);
       
  1740     this.takeControl();
       
  1741 };
       
  1742 
       
  1743 
       
  1744 /**
       
  1745  * Tests the message loop re-entrancy.
       
  1746  */
       
  1747 TestSuite.prototype.testMessageLoopReentrant = function()
       
  1748 {
       
  1749     var test = this;
       
  1750     this.showPanel("scripts");
       
  1751 
       
  1752     var breakpointLine = 16;
       
  1753 
       
  1754     WebInspector.showConsole();
       
  1755 
       
  1756     this._waitUntilScriptsAreParsed(["debugger_test_page.html"],
       
  1757         function() {
       
  1758           test.showMainPageScriptSource_(
       
  1759               "debugger_test_page.html",
       
  1760               function(view, url) {
       
  1761                 view._addBreakpoint(breakpointLine);
       
  1762 
       
  1763                 test.evaluateInConsole_(
       
  1764                     'setTimeout("calculate()", 0)',
       
  1765                     function(resultText) {
       
  1766                       test.assertTrue(!isNaN(resultText), "Failed to get timer id: " + resultText);
       
  1767                     });
       
  1768 
       
  1769               });
       
  1770         });
       
  1771 
       
  1772     // Wait until script is paused.
       
  1773     this.addSniffer(
       
  1774         WebInspector,
       
  1775         "pausedScript",
       
  1776         function(callFrames) {
       
  1777             test.evaluateInConsole_(
       
  1778                 'document.cookie',
       
  1779                 test.releaseControl.bind(test)); // This callback will be invoked only if the test succeeds (i.e. no crash).
       
  1780         });
       
  1781 
       
  1782     this.takeControl();
       
  1783 };
       
  1784 
       
  1785 
       
  1786 /**
       
  1787  * Tests that Storage panel can be open and that local DOM storage is added
       
  1788  * to the panel.
       
  1789  */
       
  1790 TestSuite.prototype.testShowStoragePanel = function()
       
  1791 {
       
  1792     var test = this;
       
  1793     this.addSniffer(WebInspector.panels.storage, "addDOMStorage",
       
  1794         function(storage) {
       
  1795             var orig = storage.getEntries;
       
  1796             storage.getEntries = function(callback) {
       
  1797                 orig.call(this, function(entries) {
       
  1798                     callback(entries);
       
  1799                     test.releaseControl();
       
  1800                 });
       
  1801             };
       
  1802             try {
       
  1803                 WebInspector.currentPanel.selectDOMStorage(storage.id);
       
  1804                 storage.getEntries = orig;
       
  1805             } catch (e) {
       
  1806                 test.fail("Exception in selectDOMStorage: " + e);
       
  1807             }
       
  1808         });
       
  1809     this.showPanel("storage");
       
  1810 
       
  1811     // Access localStorage so that it's pushed to the frontend.
       
  1812     this.evaluateInConsole_(
       
  1813         'setTimeout("localStorage.x = 10" , 0)',
       
  1814         function(resultText) {
       
  1815             test.assertTrue(!isNaN(resultText), "Failed to get timer id: " + resultText);
       
  1816         });
       
  1817 
       
  1818     // Wait until DOM storage is added to the panel.
       
  1819     this.takeControl();
       
  1820 };
       
  1821 
       
  1822 
       
  1823 /**
       
  1824  * Test runner for the test suite.
       
  1825  */
       
  1826 var uiTests = {};
       
  1827 
       
  1828 
       
  1829 /**
       
  1830  * Run each test from the test suit on a fresh instance of the suite.
       
  1831  */
       
  1832 uiTests.runAllTests = function()
       
  1833 {
       
  1834     // For debugging purposes.
       
  1835     for (var name in TestSuite.prototype) {
       
  1836         if (name.substring(0, 4) === "test" && typeof TestSuite.prototype[name] === "function")
       
  1837             uiTests.runTest(name);
       
  1838     }
       
  1839 };
       
  1840 
       
  1841 
       
  1842 /**
       
  1843  * Run specified test on a fresh instance of the test suite.
       
  1844  * @param {string} name Name of a test method from TestSuite class.
       
  1845  */
       
  1846 uiTests.runTest = function(name)
       
  1847 {
       
  1848     new TestSuite().runTest(name);
       
  1849 };
       
  1850 
       
  1851 
       
  1852 }