2b264eebfb63cbb35fee870f1b66b2dbd50b23dd
[WebKit-https.git] / WebKit / chromium / src / js / Tests.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 /**
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 resources tab is enabled when corresponding item is selected.
223  */
224 TestSuite.prototype.testEnableResourcesTab = function()
225 {
226     this.showPanel("resources");
227
228     var test = this;
229     this.addSniffer(WebInspector, "updateResource",
230         function(payload) {
231             test.assertEquals("simple_page.html", payload.lastPathComponent);
232             WebInspector.panels.resources.refresh();
233             WebInspector.panels.resources.revealAndSelectItem(WebInspector.resources[payload.id]);
234
235             test.releaseControl();
236         });
237
238     // Following call should lead to reload that we capture in the
239     // addResource override.
240     WebInspector.panels.resources._enableResourceTracking();
241
242     // We now have some time to report results to controller.
243     this.takeControl();
244 };
245
246
247 /**
248  * Tests that profiler works.
249  */
250 TestSuite.prototype.testProfilerTab = function()
251 {
252     this.showPanel("profiles");
253
254     var panel = WebInspector.panels.profiles;
255     var test = this;
256
257     function findDisplayedNode() {
258         var node = panel.visibleView.profileDataGridTree.children[0];
259         if (!node) {
260             // Profile hadn't been queried yet, re-schedule.
261             window.setTimeout(findDisplayedNode, 100);
262             return;
263         }
264
265         // Iterate over displayed functions and search for a function
266         // that is called "fib" or "eternal_fib". If found, this will mean
267         // that we actually have profiled page's code.
268         while (node) {
269             if (node.functionName.indexOf("fib") !== -1)
270                 test.releaseControl();
271             node = node.traverseNextNode(true, null, true);
272         }
273
274         test.fail();
275     }
276
277     function findVisibleView() {
278         if (!panel.visibleView) {
279             setTimeout(findVisibleView, 0);
280             return;
281         }
282         setTimeout(findDisplayedNode, 0);
283     }
284
285     findVisibleView();
286     this.takeControl();
287 };
288
289
290 /**
291  * Tests that heap profiler works.
292  */
293 TestSuite.prototype.testHeapProfiler = function()
294 {
295     this.showPanel("profiles");
296
297     var panel = WebInspector.panels.profiles;
298     var test = this;
299
300     function findDisplayedNode() {
301         var node = panel.visibleView.dataGrid.children[0];
302         if (!node) {
303             // Profile hadn't been queried yet, re-schedule.
304             window.setTimeout(findDisplayedNode, 100);
305             return;
306         }
307
308         // Iterate over displayed functions and find node called "A"
309         // If found, this will mean that we actually have taken heap snapshot.
310         while (node) {
311             if (node.constructorName.indexOf("A") !== -1) {
312                 test.releaseControl();
313                 return;
314             }
315             node = node.traverseNextNode(false, null, true);
316         }
317
318         test.fail();
319     }
320
321     function findVisibleView() {
322         if (!panel.visibleView) {
323             setTimeout(findVisibleView, 0);
324             return;
325         }
326         setTimeout(findDisplayedNode, 0);
327     }
328
329     WebInspector.HeapSnapshotProfileType.prototype.buttonClicked();
330     findVisibleView();
331     this.takeControl();
332 };
333
334
335 /**
336  * Tests that scripts tab can be open and populated with inspected scripts.
337  */
338 TestSuite.prototype.testShowScriptsTab = function()
339 {
340     this.showPanel("scripts");
341     var test = this;
342     // There should be at least main page script.
343     this._waitUntilScriptsAreParsed(["debugger_test_page.html"],
344         function() {
345             test.releaseControl();
346         });
347     // Wait until all scripts are added to the debugger.
348     this.takeControl();
349 };
350
351
352 /**
353  * Tests that scripts tab is populated with inspected scripts even if it
354  * hadn't been shown by the moment inspected paged refreshed.
355  * @see http://crbug.com/26312
356  */
357 TestSuite.prototype.testScriptsTabIsPopulatedOnInspectedPageRefresh = function()
358 {
359     var test = this;
360     this.assertEquals(WebInspector.panels.elements, WebInspector.currentPanel, "Elements panel should be current one.");
361
362     this.addSniffer(WebInspector.panels.scripts, "reset", waitUntilScriptIsParsed);
363
364     // Reload inspected page. It will reset the debugger agent.
365     test.evaluateInConsole_(
366         "window.location.reload(true);",
367         function(resultText) {});
368
369     function waitUntilScriptIsParsed() {
370         test.showPanel("scripts");
371         test._waitUntilScriptsAreParsed(["debugger_test_page.html"],
372             function() {
373                 test.releaseControl();
374             });
375     }
376
377     // Wait until all scripts are added to the debugger.
378     this.takeControl();
379 };
380
381
382 /**
383  * Tests that scripts list contains content scripts.
384  */
385 TestSuite.prototype.testContentScriptIsPresent = function()
386 {
387     this.showPanel("scripts");
388     var test = this;
389
390     test._waitUntilScriptsAreParsed(
391         ["page_with_content_script.html", "simple_content_script.js"],
392         function() {
393           test.releaseControl();
394         });
395
396     // Wait until all scripts are added to the debugger.
397     this.takeControl();
398 };
399
400
401 /**
402  * Tests that scripts are not duplicaed on Scripts tab switch.
403  */
404 TestSuite.prototype.testNoScriptDuplicatesOnPanelSwitch = function()
405 {
406     var test = this;
407
408     // There should be two scripts: one for the main page and another
409     // one which is source of console API(see
410     // InjectedScript._ensureCommandLineAPIInstalled).
411     var expectedScriptsCount = 2;
412     var parsedScripts = [];
413
414     this.showPanel("scripts");
415
416
417     function switchToElementsTab() {
418         test.showPanel("elements");
419         setTimeout(switchToScriptsTab, 0);
420     }
421
422     function switchToScriptsTab() {
423         test.showPanel("scripts");
424         setTimeout(checkScriptsPanel, 0);
425     }
426
427     function checkScriptsPanel() {
428         test.assertTrue(!!WebInspector.panels.scripts.visibleView, "No visible script view.");
429         test.assertTrue(test._scriptsAreParsed(["debugger_test_page.html"]), "Some scripts are missing.");
430         checkNoDuplicates();
431         test.releaseControl();
432     }
433
434     function checkNoDuplicates() {
435         var scriptSelect = document.getElementById("scripts-files");
436         var options = scriptSelect.options;
437         for (var i = 0; i < options.length; i++) {
438             var scriptName = options[i].text;
439             for (var j = i + 1; j < options.length; j++)
440                 test.assertTrue(scriptName !== options[j].text, "Found script duplicates: " + test.optionsToString_(options));
441         }
442     }
443
444     test._waitUntilScriptsAreParsed(
445         ["debugger_test_page.html"],
446         function() {
447             checkNoDuplicates();
448             setTimeout(switchToElementsTab, 0);
449         });
450
451
452     // Wait until all scripts are added to the debugger.
453     this.takeControl();
454 };
455
456
457 // Tests that debugger works correctly if pause event occurs when DevTools
458 // frontend is being loaded.
459 TestSuite.prototype.testPauseWhenLoadingDevTools = function()
460 {
461     this.showPanel("scripts");
462     var test = this;
463
464     var expectations = {
465             functionsOnStack: ["callDebugger"],
466             lineNumber: 8,
467             lineText: "  debugger;"
468         };
469
470
471     // Script execution can already be paused.
472     if (WebInspector.currentPanel.paused) {
473         var callFrame = WebInspector.currentPanel.sidebarPanes.callstack.selectedCallFrame;
474         this.assertEquals(expectations.functionsOnStack[0], callFrame.functionName);
475         var callbackInvoked = false;
476         this._checkSourceFrameWhenLoaded(expectations, function() {
477                 callbackInvoked = true;
478                 if (test.controlTaken_)
479                     test.releaseControl();
480             });
481         if (!callbackInvoked) {
482             test.takeControl();
483         }
484         return;
485     }
486
487     this._waitForScriptPause(
488         {
489             functionsOnStack: ["callDebugger"],
490             lineNumber: 8,
491             lineText: "  debugger;"
492         },
493         function() {
494             test.releaseControl();
495         });
496     this.takeControl();
497 };
498
499
500 // Tests that pressing "Pause" will pause script execution if the script
501 // is already running.
502 TestSuite.prototype.testPauseWhenScriptIsRunning = function()
503 {
504     this.showPanel("scripts");
505     var test = this;
506
507     test.evaluateInConsole_(
508         'setTimeout("handleClick()" , 0)',
509         function(resultText) {
510           test.assertTrue(!isNaN(resultText), "Failed to get timer id: " + resultText);
511           testScriptPauseAfterDelay();
512         });
513
514     // Wait for some time to make sure that inspected page is running the
515     // infinite loop.
516     function testScriptPauseAfterDelay() {
517         setTimeout(testScriptPause, 300);
518     }
519
520     function testScriptPause() {
521         // The script should be in infinite loop. Click "Pause" button to
522         // pause it and wait for the result.
523         WebInspector.panels.scripts.pauseButton.click();
524
525         test._waitForScriptPause(
526             {
527                 functionsOnStack: ["handleClick", ""],
528                 lineNumber: 5,
529                 lineText: "  while(true) {"
530             },
531             function() {
532                 test.releaseControl();
533             });
534     }
535
536     this.takeControl();
537 };
538
539
540 /**
541  * Serializes options collection to string.
542  * @param {HTMLOptionsCollection} options
543  * @return {string}
544  */
545 TestSuite.prototype.optionsToString_ = function(options)
546 {
547     var names = [];
548     for (var i = 0; i < options.length; i++)
549         names.push('"' + options[i].text + '"');
550     return names.join(",");
551 };
552
553
554 /**
555  * Ensures that main HTML resource is selected in Scripts panel and that its
556  * source frame is setup. Invokes the callback when the condition is satisfied.
557  * @param {HTMLOptionsCollection} options
558  * @param {function(WebInspector.SourceView,string)} callback
559  */
560 TestSuite.prototype.showMainPageScriptSource_ = function(scriptName, callback)
561 {
562     var test = this;
563
564     var scriptSelect = document.getElementById("scripts-files");
565     var options = scriptSelect.options;
566
567     test.assertTrue(options.length, "Scripts list is empty");
568
569     // Select page's script if it's not current option.
570     var scriptResource;
571     if (options[scriptSelect.selectedIndex].text === scriptName)
572         scriptResource = options[scriptSelect.selectedIndex].representedObject;
573     else {
574         var pageScriptIndex = -1;
575         for (var i = 0; i < options.length; i++) {
576             if (options[i].text === scriptName) {
577                 pageScriptIndex = i;
578                 break;
579             }
580         }
581         test.assertTrue(-1 !== pageScriptIndex, "Script with url " + scriptName + " not found among " + test.optionsToString_(options));
582         scriptResource = options[pageScriptIndex].representedObject;
583
584         // Current panel is "Scripts".
585         WebInspector.currentPanel._showScriptOrResource(scriptResource);
586         test.assertEquals(pageScriptIndex, scriptSelect.selectedIndex, "Unexpected selected option index.");
587     }
588
589     test.assertTrue(scriptResource instanceof WebInspector.Resource,
590                     "Unexpected resource class.");
591     test.assertTrue(!!scriptResource.url, "Resource URL is null.");
592     test.assertTrue(scriptResource.url.search(scriptName + "$") !== -1, "Main HTML resource should be selected.");
593
594     var scriptsPanel = WebInspector.panels.scripts;
595
596     var view = scriptsPanel.visibleView;
597     test.assertTrue(view instanceof WebInspector.SourceView);
598
599     if (!view.sourceFrame._loaded) {
600         test.addSniffer(view, "_sourceFrameSetupFinished", function(event) {
601             callback(view, scriptResource.url);
602         });
603     } else
604         callback(view, scriptResource.url);
605 };
606
607
608 /*
609  * Evaluates the code in the console as if user typed it manually and invokes
610  * the callback when the result message is received and added to the console.
611  * @param {string} code
612  * @param {function(string)} callback
613  */
614 TestSuite.prototype.evaluateInConsole_ = function(code, callback)
615 {
616     WebInspector.showConsole();
617     WebInspector.console.prompt.text = code;
618     WebInspector.console.promptElement.dispatchEvent( TestSuite.createKeyEvent("Enter"));
619
620     this.addSniffer(WebInspector.ConsoleView.prototype, "addMessage",
621         function(commandResult) {
622             callback(commandResult.toMessageElement().textContent);
623         });
624 };
625
626
627 /**
628  * Tests that console auto completion works when script execution is paused.
629  */
630 TestSuite.prototype.testCompletionOnPause = function()
631 {
632     this.showPanel("scripts");
633     var test = this;
634     this._executeCodeWhenScriptsAreParsed("handleClick()", ["completion_on_pause.html"]);
635
636     this._waitForScriptPause(
637         {
638             functionsOnStack: ["innerFunction", "handleClick", ""],
639             lineNumber: 9,
640             lineText: "    debugger;"
641         },
642         showConsole);
643
644     function showConsole() {
645         if (WebInspector.currentFocusElement === WebInspector.console.promptElement)
646             testLocalsCompletion();
647         else {
648             test.addSniffer(WebInspector.console, "afterShow", testLocalsCompletion);
649             WebInspector.showConsole();
650         }
651     }
652
653     function testLocalsCompletion() {
654         checkCompletions("th", ["parameter1", "closureLocal", "p", "createClosureLocal"], testThisCompletion);
655     }
656
657     function testThisCompletion() {
658         checkCompletions("this.", ["field1", "field2", "m"], testFieldCompletion);
659     }
660
661     function testFieldCompletion() {
662         checkCompletions("this.field1.", ["id", "name"], function() { test.releaseControl(); });
663     }
664
665     function checkCompletions(expression, expectedProperties, callback) {
666         test.addSniffer(WebInspector.console, "_reportCompletions",
667             function(bestMatchOnly, completionsReadyCallback, dotNotation, bracketNotation, prefix, result, isException) {
668                 test.assertTrue(!isException, "Exception while collecting completions");
669                 for (var i = 0; i < expectedProperties.length; i++) {
670                     var name = expectedProperties[i];
671                     test.assertTrue(result[name], "Name " + name + " not found among the completions: " + JSON.stringify(result));
672                 }
673                 setTimeout(callback, 0);
674             });
675       WebInspector.console.prompt.text = expression;
676       WebInspector.console.prompt.autoCompleteSoon();
677     }
678
679     this.takeControl();
680 };
681
682
683 /**
684  * Checks current execution line against expectations.
685  * @param {WebInspector.SourceFrame} sourceFrame
686  * @param {number} lineNumber Expected line number
687  * @param {string} lineContent Expected line text
688  */
689 TestSuite.prototype._checkExecutionLine = function(sourceFrame, lineNumber, lineContent)
690 {
691     this.assertEquals(lineNumber, sourceFrame.executionLine, "Unexpected execution line number.");
692     this.assertEquals(lineContent, sourceFrame._textModel.line(lineNumber - 1), "Unexpected execution line text.");
693 }
694
695
696 /**
697  * Checks that all expected scripts are present in the scripts list
698  * in the Scripts panel.
699  * @param {Array.<string>} expected Regular expressions describing
700  *     expected script names.
701  * @return {boolean} Whether all the scripts are in "scripts-files" select
702  *     box
703  */
704 TestSuite.prototype._scriptsAreParsed = function(expected)
705 {
706     var scriptSelect = document.getElementById("scripts-files");
707     var options = scriptSelect.options;
708
709     // Check that at least all the expected scripts are present.
710     var missing = expected.slice(0);
711     for (var i = 0 ; i < options.length; i++) {
712         for (var j = 0; j < missing.length; j++) {
713             if (options[i].text.search(missing[j]) !== -1) {
714                 missing.splice(j, 1);
715                 break;
716             }
717         }
718     }
719     return missing.length === 0;
720 };
721
722
723 /**
724  * Waits for script pause, checks expectations, and invokes the callback.
725  * @param {Object} expectations  Dictionary of expectations
726  * @param {function():void} callback
727  */
728 TestSuite.prototype._waitForScriptPause = function(expectations, callback)
729 {
730     var test = this;
731     // Wait until script is paused.
732     test.addSniffer(
733         WebInspector,
734         "pausedScript",
735         function(details) {
736             var callFrames = details.callFrames;
737             var functionsOnStack = [];
738             for (var i = 0; i < callFrames.length; i++)
739                 functionsOnStack.push(callFrames[i].functionName);
740
741             test.assertEquals(expectations.functionsOnStack.join(","), functionsOnStack.join(","), "Unexpected stack.");
742
743             // Check that execution line where the script is paused is
744             // expected one.
745             test._checkSourceFrameWhenLoaded(expectations, callback);
746         });
747 };
748
749
750 /**
751  * Waits for current source frame to load, checks expectations, and invokes
752  * the callback.
753  * @param {Object} expectations  Dictionary of expectations
754  * @param {function():void} callback
755  */
756 TestSuite.prototype._checkSourceFrameWhenLoaded = function(expectations, callback)
757 {
758     var test = this;
759
760     var frame = WebInspector.currentPanel.visibleView.sourceFrame;
761     if (frame._loaded)
762         checkExecLine();
763     else {
764         setTimeout(function() {
765             test._checkSourceFrameWhenLoaded(expectations, callback);
766         }, 100);
767     }
768     function checkExecLine() {
769         test._checkExecutionLine(frame, expectations.lineNumber, expectations.lineText);
770         callback();
771     }
772 };
773
774
775 /**
776  * Waits until all the scripts are parsed and asynchronously executes the code
777  * in the inspected page.
778  */
779 TestSuite.prototype._executeCodeWhenScriptsAreParsed = function(code, expectedScripts)
780 {
781     var test = this;
782
783     function executeFunctionInInspectedPage() {
784         // Since breakpoints are ignored in evals' calculate() function is
785         // execute after zero-timeout so that the breakpoint is hit.
786         test.evaluateInConsole_(
787             'setTimeout("' + code + '" , 0)',
788             function(resultText) {
789                 test.assertTrue(!isNaN(resultText), "Failed to get timer id: " + resultText + ". Code: " + code);
790             });
791     }
792
793     test._waitUntilScriptsAreParsed(expectedScripts, executeFunctionInInspectedPage);
794 };
795
796
797 /**
798  * Waits until all the scripts are parsed and invokes the callback.
799  */
800 TestSuite.prototype._waitUntilScriptsAreParsed = function(expectedScripts, callback)
801 {
802     var test = this;
803
804     function waitForAllScripts() {
805         if (test._scriptsAreParsed(expectedScripts))
806             callback();
807         else
808             test.addSniffer(WebInspector, "parsedScriptSource", waitForAllScripts);
809     }
810
811     waitForAllScripts();
812 };
813
814
815 /**
816  * Executes the 'code' with InjectedScriptAccess.getProperties overriden
817  * so that all callbacks passed to InjectedScriptAccess.getProperties are
818  * extended with the "hook".
819  * @param {Function} hook The hook function.
820  * @param {Function} code A code snippet to be executed.
821  */
822 TestSuite.prototype._hookGetPropertiesCallback = function(hook, code)
823 {
824     var accessor = InjectedScriptAccess.prototype;
825     var orig = accessor.getProperties;
826     accessor.getProperties = function(objectProxy, ignoreHasOwnProperty, abbreviate, callback) {
827         orig.call(this, objectProxy, ignoreHasOwnProperty, abbreviate,
828             function() {
829               callback.apply(this, arguments);
830               hook();
831             });
832     };
833     try {
834         code();
835     } finally {
836         accessor.getProperties = orig;
837     }
838 };
839
840
841 /**
842  * Tests "Pause" button will pause debugger when a snippet is evaluated.
843  */
844 TestSuite.prototype.testPauseInEval = function()
845 {
846     this.showPanel("scripts");
847
848     var test = this;
849
850     var pauseButton = document.getElementById("scripts-pause");
851     pauseButton.click();
852
853     devtools.tools.evaluateJavaScript("fib(10)");
854
855     this.addSniffer(WebInspector, "pausedScript",
856         function() {
857             test.releaseControl();
858         });
859
860     test.takeControl();
861 };
862
863
864 /**
865  * Key event with given key identifier.
866  */
867 TestSuite.createKeyEvent = function(keyIdentifier)
868 {
869     var evt = document.createEvent("KeyboardEvent");
870     evt.initKeyboardEvent("keydown", true /* can bubble */, true /* can cancel */, null /* view */, keyIdentifier, "");
871     return evt;
872 };
873
874
875 /**
876  * Test runner for the test suite.
877  */
878 var uiTests = {};
879
880
881 /**
882  * Run each test from the test suit on a fresh instance of the suite.
883  */
884 uiTests.runAllTests = function()
885 {
886     // For debugging purposes.
887     for (var name in TestSuite.prototype) {
888         if (name.substring(0, 4) === "test" && typeof TestSuite.prototype[name] === "function")
889             uiTests.runTest(name);
890     }
891 };
892
893
894 /**
895  * Run specified test on a fresh instance of the test suite.
896  * @param {string} name Name of a test method from TestSuite class.
897  */
898 uiTests.runTest = function(name)
899 {
900     if (uiTests._populatedInterface)
901         new TestSuite().runTest(name);
902     else
903         uiTests._pendingTestName = name;
904 };
905
906 (function() {
907
908 function runTests()
909 {
910     uiTests._populatedInterface = true;
911     var name = uiTests._pendingTestName;
912     delete uiTests._pendingTestName;
913     if (name)
914         new TestSuite().runTest(name);
915 }
916
917 var oldShowElementsPanel = WebInspector.showElementsPanel;
918 WebInspector.showElementsPanel = function()
919 {
920     oldShowElementsPanel.call(this);
921     runTests();
922 }
923
924 var oldShowPanel = WebInspector.showPanel;
925 WebInspector.showPanel = function(name)
926 {
927     oldShowPanel.call(this, name);
928     runTests();
929 }
930
931 })();
932
933 }