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