Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / devtools / front_end / 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  * HasKey assertion tests that object has given key.
97  * @param {!Object} object
98  * @param {string} key
99  */
100 TestSuite.prototype.assertHasKey = function(object, key)
101 {
102     if (!object.hasOwnProperty(key))
103         this.fail("Expected object to contain key '" + key + "'");
104 };
105
106
107 /**
108  * Contains assertion tests that string contains substring.
109  * @param {string} string Outer.
110  * @param {string} substring Inner.
111  */
112 TestSuite.prototype.assertContains = function(string, substring)
113 {
114     if (string.indexOf(substring) === -1)
115         this.fail("Expected to: '" + string + "' to contain '" + substring + "'");
116 };
117
118
119 /**
120  * Takes control over execution.
121  */
122 TestSuite.prototype.takeControl = function()
123 {
124     this.controlTaken_ = true;
125     // Set up guard timer.
126     var self = this;
127     this.timerId_ = setTimeout(function() {
128         self.reportFailure_("Timeout exceeded: 20 sec");
129     }, 20000);
130 };
131
132
133 /**
134  * Releases control over execution.
135  */
136 TestSuite.prototype.releaseControl = function()
137 {
138     if (this.timerId_ !== -1) {
139         clearTimeout(this.timerId_);
140         this.timerId_ = -1;
141     }
142     this.reportOk_();
143 };
144
145
146 /**
147  * Async tests use this one to report that they are completed.
148  */
149 TestSuite.prototype.reportOk_ = function()
150 {
151     window.domAutomationController.send("[OK]");
152 };
153
154
155 /**
156  * Async tests use this one to report failures.
157  */
158 TestSuite.prototype.reportFailure_ = function(error)
159 {
160     if (this.timerId_ !== -1) {
161         clearTimeout(this.timerId_);
162         this.timerId_ = -1;
163     }
164     window.domAutomationController.send("[FAILED] " + error);
165 };
166
167
168 /**
169  * Runs all global functions starting with "test" as unit tests.
170  */
171 TestSuite.prototype.runTest = function(testName)
172 {
173     try {
174         this[testName]();
175         if (!this.controlTaken_)
176             this.reportOk_();
177     } catch (e) {
178         this.reportFailure_(e);
179     }
180 };
181
182
183 /**
184  * @param {string} panelName Name of the panel to show.
185  */
186 TestSuite.prototype.showPanel = function(panelName)
187 {
188     // Open Scripts panel.
189     var button = document.getElementById("tab-" + panelName);
190     button.selectTabForTest();
191     this.assertEquals(WebInspector.panels[panelName], WebInspector.inspectorView.currentPanel());
192 };
193
194
195 /**
196  * Overrides the method with specified name until it's called first time.
197  * @param {!Object} receiver An object whose method to override.
198  * @param {string} methodName Name of the method to override.
199  * @param {!Function} override A function that should be called right after the
200  *     overridden method returns.
201  * @param {boolean} opt_sticky Whether restore original method after first run
202  *     or not.
203  */
204 TestSuite.prototype.addSniffer = function(receiver, methodName, override, opt_sticky)
205 {
206     var orig = receiver[methodName];
207     if (typeof orig !== "function")
208         this.fail("Cannot find method to override: " + methodName);
209     var test = this;
210     receiver[methodName] = function(var_args) {
211         try {
212             var result = orig.apply(this, arguments);
213         } finally {
214             if (!opt_sticky)
215                 receiver[methodName] = orig;
216         }
217         // In case of exception the override won't be called.
218         try {
219             override.apply(this, arguments);
220         } catch (e) {
221             test.fail("Exception in overriden method '" + methodName + "': " + e);
222         }
223         return result;
224     };
225 };
226
227
228 // UI Tests
229
230
231 /**
232  * Tests that scripts tab can be open and populated with inspected scripts.
233  */
234 TestSuite.prototype.testShowScriptsTab = function()
235 {
236     this.showPanel("sources");
237     var test = this;
238     // There should be at least main page script.
239     this._waitUntilScriptsAreParsed(["debugger_test_page.html"],
240         function() {
241             test.releaseControl();
242         });
243     // Wait until all scripts are added to the debugger.
244     this.takeControl();
245 };
246
247
248 /**
249  * Tests that scripts tab is populated with inspected scripts even if it
250  * hadn't been shown by the moment inspected paged refreshed.
251  * @see http://crbug.com/26312
252  */
253 TestSuite.prototype.testScriptsTabIsPopulatedOnInspectedPageRefresh = function()
254 {
255     var test = this;
256     this.assertEquals(WebInspector.panels.elements, WebInspector.inspectorView.currentPanel(), "Elements panel should be current one.");
257
258     WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.GlobalObjectCleared, waitUntilScriptIsParsed);
259
260     // Reload inspected page. It will reset the debugger agent.
261     test.evaluateInConsole_("window.location.reload(true);", function(resultText) {});
262
263     function waitUntilScriptIsParsed()
264     {
265         WebInspector.debuggerModel.removeEventListener(WebInspector.DebuggerModel.Events.GlobalObjectCleared, waitUntilScriptIsParsed);
266         test.showPanel("sources");
267         test._waitUntilScriptsAreParsed(["debugger_test_page.html"],
268             function() {
269                 test.releaseControl();
270             });
271     }
272
273     // Wait until all scripts are added to the debugger.
274     this.takeControl();
275 };
276
277
278 /**
279  * Tests that scripts list contains content scripts.
280  */
281 TestSuite.prototype.testContentScriptIsPresent = function()
282 {
283     this.showPanel("sources");
284     var test = this;
285
286     test._waitUntilScriptsAreParsed(
287         ["page_with_content_script.html", "simple_content_script.js"],
288         function() {
289           test.releaseControl();
290         });
291
292     // Wait until all scripts are added to the debugger.
293     this.takeControl();
294 };
295
296
297 /**
298  * Tests that scripts are not duplicaed on Scripts tab switch.
299  */
300 TestSuite.prototype.testNoScriptDuplicatesOnPanelSwitch = function()
301 {
302     var test = this;
303
304     // There should be two scripts: one for the main page and another
305     // one which is source of console API(see
306     // InjectedScript._ensureCommandLineAPIInstalled).
307     var expectedScriptsCount = 2;
308     var parsedScripts = [];
309
310     this.showPanel("sources");
311
312     function switchToElementsTab() {
313         test.showPanel("elements");
314         setTimeout(switchToScriptsTab, 0);
315     }
316
317     function switchToScriptsTab() {
318         test.showPanel("sources");
319         setTimeout(checkScriptsPanel, 0);
320     }
321
322     function checkScriptsPanel() {
323         test.assertTrue(test._scriptsAreParsed(["debugger_test_page.html"]), "Some scripts are missing.");
324         checkNoDuplicates();
325         test.releaseControl();
326     }
327
328     function checkNoDuplicates() {
329         var uiSourceCodes = test.nonAnonymousUISourceCodes_();
330         for (var i = 0; i < uiSourceCodes.length; i++) {
331             var scriptName = uiSourceCodes[i].url;
332             for (var j = i + 1; j < uiSourceCodes.length; j++)
333                 test.assertTrue(scriptName !== uiSourceCodes[j].url, "Found script duplicates: " + test.uiSourceCodesToString_(uiSourceCodes));
334         }
335     }
336
337     test._waitUntilScriptsAreParsed(
338         ["debugger_test_page.html"],
339         function() {
340             checkNoDuplicates();
341             setTimeout(switchToElementsTab, 0);
342         });
343
344
345     // Wait until all scripts are added to the debugger.
346     this.takeControl();
347 };
348
349
350 // Tests that debugger works correctly if pause event occurs when DevTools
351 // frontend is being loaded.
352 TestSuite.prototype.testPauseWhenLoadingDevTools = function()
353 {
354     this.showPanel("sources");
355
356     // Script execution can already be paused.
357     if (WebInspector.debuggerModel.debuggerPausedDetails)
358         return;
359
360     this._waitForScriptPause(this.releaseControl.bind(this));
361     this.takeControl();
362 };
363
364
365 // Tests that pressing "Pause" will pause script execution if the script
366 // is already running.
367 TestSuite.prototype.testPauseWhenScriptIsRunning = function()
368 {
369     this.showPanel("sources");
370
371     this.evaluateInConsole_(
372         'setTimeout("handleClick()" , 0)',
373         didEvaluateInConsole.bind(this));
374
375     function didEvaluateInConsole(resultText) {
376         this.assertTrue(!isNaN(resultText), "Failed to get timer id: " + resultText);
377         // Wait for some time to make sure that inspected page is running the
378         // infinite loop.
379         setTimeout(testScriptPause.bind(this), 300);
380     }
381
382     function testScriptPause() {
383         // The script should be in infinite loop. Click "Pause" button to
384         // pause it and wait for the result.
385         WebInspector.panels.sources._pauseButton.element.click();
386
387         this._waitForScriptPause(this.releaseControl.bind(this));
388     }
389
390     this.takeControl();
391 };
392
393
394 /**
395  * Tests network size.
396  */
397 TestSuite.prototype.testNetworkSize = function()
398 {
399     var test = this;
400
401     function finishResource(resource, finishTime)
402     {
403         test.assertEquals(219, resource.transferSize, "Incorrect total encoded data length");
404         test.assertEquals(25, resource.resourceSize, "Incorrect total data length");
405         test.releaseControl();
406     }
407
408     this.addSniffer(WebInspector.NetworkDispatcher.prototype, "_finishNetworkRequest", finishResource);
409
410     // Reload inspected page to sniff network events
411     test.evaluateInConsole_("window.location.reload(true);", function(resultText) {});
412
413     this.takeControl();
414 };
415
416
417 /**
418  * Tests network sync size.
419  */
420 TestSuite.prototype.testNetworkSyncSize = function()
421 {
422     var test = this;
423
424     function finishResource(resource, finishTime)
425     {
426         test.assertEquals(219, resource.transferSize, "Incorrect total encoded data length");
427         test.assertEquals(25, resource.resourceSize, "Incorrect total data length");
428         test.releaseControl();
429     }
430
431     this.addSniffer(WebInspector.NetworkDispatcher.prototype, "_finishNetworkRequest", finishResource);
432
433     // Send synchronous XHR to sniff network events
434     test.evaluateInConsole_("var xhr = new XMLHttpRequest(); xhr.open(\"GET\", \"chunked\", false); xhr.send(null);", function() {});
435
436     this.takeControl();
437 };
438
439
440 /**
441  * Tests network raw headers text.
442  */
443 TestSuite.prototype.testNetworkRawHeadersText = function()
444 {
445     var test = this;
446
447     function finishResource(resource, finishTime)
448     {
449         if (!resource.responseHeadersText)
450             test.fail("Failure: resource does not have response headers text");
451         test.assertEquals(164, resource.responseHeadersText.length, "Incorrect response headers text length");
452         test.releaseControl();
453     }
454
455     this.addSniffer(WebInspector.NetworkDispatcher.prototype, "_finishNetworkRequest", finishResource);
456
457     // Reload inspected page to sniff network events
458     test.evaluateInConsole_("window.location.reload(true);", function(resultText) {});
459
460     this.takeControl();
461 };
462
463
464 /**
465  * Tests network timing.
466  */
467 TestSuite.prototype.testNetworkTiming = function()
468 {
469     var test = this;
470
471     function finishResource(resource, finishTime)
472     {
473         // Setting relaxed expectations to reduce flakiness.
474         // Server sends headers after 100ms, then sends data during another 100ms.
475         // We expect these times to be measured at least as 70ms.
476         test.assertTrue(resource.timing.receiveHeadersEnd - resource.timing.connectStart >= 70,
477                         "Time between receiveHeadersEnd and connectStart should be >=70ms, but was " +
478                         "receiveHeadersEnd=" + resource.timing.receiveHeadersEnd + ", connectStart=" + resource.timing.connectStart + ".");
479         test.assertTrue(resource.responseReceivedTime - resource.startTime >= 0.07,
480                 "Time between responseReceivedTime and startTime should be >=0.07s, but was " +
481                 "responseReceivedTime=" + resource.responseReceivedTime + ", startTime=" + resource.startTime + ".");
482         test.assertTrue(resource.endTime - resource.startTime >= 0.14,
483                 "Time between endTime and startTime should be >=0.14s, but was " +
484                 "endtime=" + resource.endTime + ", startTime=" + resource.startTime + ".");
485
486         test.releaseControl();
487     }
488
489     this.addSniffer(WebInspector.NetworkDispatcher.prototype, "_finishNetworkRequest", finishResource);
490
491     // Reload inspected page to sniff network events
492     test.evaluateInConsole_("window.location.reload(true);", function(resultText) {});
493
494     this.takeControl();
495 };
496
497
498 TestSuite.prototype.testConsoleOnNavigateBack = function()
499 {
500     if (WebInspector.multitargetConsoleModel.messages().length === 1)
501         firstConsoleMessageReceived.call(this);
502     else
503         WebInspector.multitargetConsoleModel.addEventListener(WebInspector.ConsoleModel.Events.MessageAdded, firstConsoleMessageReceived, this);
504
505     function firstConsoleMessageReceived() {
506         WebInspector.multitargetConsoleModel.removeEventListener(WebInspector.ConsoleModel.Events.MessageAdded, firstConsoleMessageReceived, this);
507         this.evaluateInConsole_("clickLink();", didClickLink.bind(this));
508     }
509
510     function didClickLink() {
511         // Check that there are no new messages(command is not a message).
512         this.assertEquals(3, WebInspector.multitargetConsoleModel.messages().length);
513         this.evaluateInConsole_("history.back();", didNavigateBack.bind(this));
514     }
515
516     function didNavigateBack()
517     {
518         // Make sure navigation completed and possible console messages were pushed.
519         this.evaluateInConsole_("void 0;", didCompleteNavigation.bind(this));
520     }
521
522     function didCompleteNavigation() {
523         this.assertEquals(7, WebInspector.multitargetConsoleModel.messages().length);
524         this.releaseControl();
525     }
526
527     this.takeControl();
528 };
529
530 TestSuite.prototype.testReattachAfterCrash = function()
531 {
532     PageAgent.navigate("about:crash");
533     PageAgent.navigate("about:blank");
534     WebInspector.runtimeModel.addEventListener(WebInspector.RuntimeModel.Events.ExecutionContextCreated, this.releaseControl, this);
535 };
536
537
538 TestSuite.prototype.testSharedWorker = function()
539 {
540     function didEvaluateInConsole(resultText) {
541         this.assertEquals("2011", resultText);
542         this.releaseControl();
543     }
544     this.evaluateInConsole_("globalVar", didEvaluateInConsole.bind(this));
545     this.takeControl();
546 };
547
548
549 TestSuite.prototype.testPauseInSharedWorkerInitialization = function()
550 {
551     if (WebInspector.debuggerModel.debuggerPausedDetails)
552         return;
553     this._waitForScriptPause(this.releaseControl.bind(this));
554     this.takeControl();
555 };
556
557 TestSuite.prototype.enableTouchEmulation = function()
558 {
559     WebInspector.targetManager.mainTarget().domModel.emulateTouchEventObjects(true);
560 };
561
562
563 // Regression test for crbug.com/370035.
564 TestSuite.prototype.testDeviceMetricsOverrides = function()
565 {
566     const dumpPageMetrics = function()
567     {
568         return JSON.stringify({
569             width: window.innerWidth,
570             height: window.innerHeight,
571             deviceScaleFactor: window.devicePixelRatio
572         });
573     };
574
575     var test = this;
576
577     function testOverrides(params, metrics, callback)
578     {
579         PageAgent.invoke_setDeviceMetricsOverride(params, getMetrics);
580
581         function getMetrics()
582         {
583             test.evaluateInConsole_("(" + dumpPageMetrics.toString() + ")()", checkMetrics);
584         }
585
586         function checkMetrics(consoleResult)
587         {
588             test.assertEquals('"' + JSON.stringify(metrics) + '"', consoleResult, "Wrong metrics for params: " + JSON.stringify(params));
589             callback();
590         }
591     }
592
593     function step1()
594     {
595         testOverrides({width: 1200, height: 1000, deviceScaleFactor: 1, mobile: false, fitWindow: true}, {width: 1200, height: 1000, deviceScaleFactor: 1}, step2);
596     }
597
598     function step2()
599     {
600         testOverrides({width: 1200, height: 1000, deviceScaleFactor: 1, mobile: false, fitWindow: false}, {width: 1200, height: 1000, deviceScaleFactor: 1}, step3);
601     }
602
603     function step3()
604     {
605         testOverrides({width: 1200, height: 1000, deviceScaleFactor: 3, mobile: false, fitWindow: true}, {width: 1200, height: 1000, deviceScaleFactor: 3}, step4);
606     }
607
608     function step4()
609     {
610         testOverrides({width: 1200, height: 1000, deviceScaleFactor: 3, mobile: false, fitWindow: false}, {width: 1200, height: 1000, deviceScaleFactor: 3}, finish);
611     }
612
613     function finish()
614     {
615         test.releaseControl();
616     }
617
618     step1();
619     test.takeControl();
620 };
621
622 TestSuite.prototype.waitForTestResultsInConsole = function()
623 {
624     var messages = WebInspector.multitargetConsoleModel.messages();
625     for (var i = 0; i < messages.length; ++i) {
626         var text = messages[i].messageText;
627         if (text === "PASS")
628             return;
629         else if (/^FAIL/.test(text))
630             this.fail(text); // This will throw.
631     }
632     // Neither PASS nor FAIL, so wait for more messages.
633     function onConsoleMessage(event)
634     {
635         var text = event.data.messageText;
636         if (text === "PASS")
637             this.releaseControl();
638         else if (/^FAIL/.test(text))
639             this.fail(text);
640     }
641
642     WebInspector.multitargetConsoleModel.addEventListener(WebInspector.ConsoleModel.Events.MessageAdded, onConsoleMessage, this);
643     this.takeControl();
644 };
645
646 TestSuite.prototype.checkLogAndErrorMessages = function()
647 {
648     var messages = WebInspector.multitargetConsoleModel.messages();
649
650     var matchesCount = 0;
651     function validMessage(message)
652     {
653         if (message.text === "log" && message.level === WebInspector.ConsoleMessage.MessageLevel.Log) {
654             ++matchesCount;
655             return true;
656         }
657
658         if (message.text === "error" && message.level === WebInspector.ConsoleMessage.MessageLevel.Error) {
659             ++matchesCount;
660             return true;
661         }
662         return false;
663     }
664
665     for (var i = 0; i < messages.length; ++i) {
666         if (validMessage(messages[i]))
667             continue;
668         this.fail(messages[i].text + ":" + messages[i].level); // This will throw.
669     }
670
671     if (matchesCount === 2)
672         return;
673
674     // Wait for more messages.
675     function onConsoleMessage(event)
676     {
677         var message = event.data;
678         if (validMessage(message)) {
679             if (matchesCount === 2) {
680                 this.releaseControl();
681                 return;
682             }
683         } else
684             this.fail(message.text + ":" + messages[i].level);
685     }
686
687     WebInspector.multitargetConsoleModel.addEventListener(WebInspector.ConsoleModel.Events.MessageAdded, onConsoleMessage, this);
688     this.takeControl();
689 };
690
691 /**
692  * Serializes array of uiSourceCodes to string.
693  * @param {!Array.<!WebInspectorUISourceCode>} uiSourceCodes
694  * @return {string}
695  */
696 TestSuite.prototype.uiSourceCodesToString_ = function(uiSourceCodes)
697 {
698     var names = [];
699     for (var i = 0; i < uiSourceCodes.length; i++)
700         names.push('"' + uiSourceCodes[i].url + '"');
701     return names.join(",");
702 };
703
704
705 /**
706  * Returns all loaded non anonymous uiSourceCodes.
707  * @return {!Array.<!WebInspectorUISourceCode>}
708  */
709 TestSuite.prototype.nonAnonymousUISourceCodes_ = function()
710 {
711     function filterOutAnonymous(uiSourceCode)
712     {
713         return !!uiSourceCode.url;
714     }
715
716     function filterOutService(uiSourceCode)
717     {
718         return !uiSourceCode.project().isServiceProject();
719     }
720
721     var uiSourceCodes = WebInspector.workspace.uiSourceCodes();
722     uiSourceCodes = uiSourceCodes.filter(filterOutService);
723     return uiSourceCodes.filter(filterOutAnonymous);
724 };
725
726
727 /*
728  * Evaluates the code in the console as if user typed it manually and invokes
729  * the callback when the result message is received and added to the console.
730  * @param {string} code
731  * @param {function(string)} callback
732  */
733 TestSuite.prototype.evaluateInConsole_ = function(code, callback)
734 {
735     function innerEvaluate()
736     {
737         WebInspector.console.show();
738         var consoleView = WebInspector.ConsolePanel._view();
739         consoleView._prompt.text = code;
740         consoleView._promptElement.dispatchEvent(TestSuite.createKeyEvent("Enter"));
741
742         this.addSniffer(WebInspector.ConsoleView.prototype, "_showConsoleMessage",
743             function(viewMessage) {
744                 callback(viewMessage.toMessageElement().textContent);
745             }.bind(this));
746     }
747
748     if (!WebInspector.context.flavor(WebInspector.ExecutionContext)) {
749         WebInspector.context.addFlavorChangeListener(WebInspector.ExecutionContext, innerEvaluate, this);
750         return;
751     }
752
753     innerEvaluate.call(this);
754 };
755
756 /**
757  * Checks that all expected scripts are present in the scripts list
758  * in the Scripts panel.
759  * @param {!Array.<string>} expected Regular expressions describing
760  *     expected script names.
761  * @return {boolean} Whether all the scripts are in "scripts-files" select
762  *     box
763  */
764 TestSuite.prototype._scriptsAreParsed = function(expected)
765 {
766     var uiSourceCodes = this.nonAnonymousUISourceCodes_();
767     // Check that at least all the expected scripts are present.
768     var missing = expected.slice(0);
769     for (var i = 0; i < uiSourceCodes.length; ++i) {
770         for (var j = 0; j < missing.length; ++j) {
771             if (uiSourceCodes[i].name().search(missing[j]) !== -1) {
772                 missing.splice(j, 1);
773                 break;
774             }
775         }
776     }
777     return missing.length === 0;
778 };
779
780
781 /**
782  * Waits for script pause, checks expectations, and invokes the callback.
783  * @param {function():void} callback
784  */
785 TestSuite.prototype._waitForScriptPause = function(callback)
786 {
787     function pauseListener(event) {
788         WebInspector.debuggerModel.removeEventListener(WebInspector.DebuggerModel.Events.DebuggerPaused, pauseListener, this);
789         callback();
790     }
791     WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.DebuggerPaused, pauseListener, this);
792 };
793
794
795 /**
796  * Waits until all the scripts are parsed and asynchronously executes the code
797  * in the inspected page.
798  */
799 TestSuite.prototype._executeCodeWhenScriptsAreParsed = function(code, expectedScripts)
800 {
801     var test = this;
802
803     function executeFunctionInInspectedPage() {
804         // Since breakpoints are ignored in evals' calculate() function is
805         // execute after zero-timeout so that the breakpoint is hit.
806         test.evaluateInConsole_(
807             'setTimeout("' + code + '" , 0)',
808             function(resultText) {
809                 test.assertTrue(!isNaN(resultText), "Failed to get timer id: " + resultText + ". Code: " + code);
810             });
811     }
812
813     test._waitUntilScriptsAreParsed(expectedScripts, executeFunctionInInspectedPage);
814 };
815
816
817 /**
818  * Waits until all the scripts are parsed and invokes the callback.
819  */
820 TestSuite.prototype._waitUntilScriptsAreParsed = function(expectedScripts, callback)
821 {
822     var test = this;
823
824     function waitForAllScripts() {
825         if (test._scriptsAreParsed(expectedScripts))
826             callback();
827         else
828             test.addSniffer(WebInspector.panels.sources.sourcesView(), "_addUISourceCode", waitForAllScripts);
829     }
830
831     waitForAllScripts();
832 };
833
834
835 /**
836  * Key event with given key identifier.
837  */
838 TestSuite.createKeyEvent = function(keyIdentifier)
839 {
840     var evt = document.createEvent("KeyboardEvent");
841     evt.initKeyboardEvent("keydown", true /* can bubble */, true /* can cancel */, null /* view */, keyIdentifier, "");
842     return evt;
843 };
844
845
846 /**
847  * Test runner for the test suite.
848  */
849 var uiTests = {};
850
851
852 /**
853  * Run each test from the test suit on a fresh instance of the suite.
854  */
855 uiTests.runAllTests = function()
856 {
857     // For debugging purposes.
858     for (var name in TestSuite.prototype) {
859         if (name.substring(0, 4) === "test" && typeof TestSuite.prototype[name] === "function")
860             uiTests.runTest(name);
861     }
862 };
863
864
865 /**
866  * Run specified test on a fresh instance of the test suite.
867  * @param {string} name Name of a test method from TestSuite class.
868  */
869 uiTests.runTest = function(name)
870 {
871     if (uiTests._populatedInterface)
872         new TestSuite().runTest(name);
873     else
874         uiTests._pendingTestName = name;
875 };
876
877 (function() {
878
879 function runTests()
880 {
881     uiTests._populatedInterface = true;
882     var name = uiTests._pendingTestName;
883     delete uiTests._pendingTestName;
884     if (name)
885         new TestSuite().runTest(name);
886 }
887
888 WebInspector.notifications.addEventListener(WebInspector.NotificationService.Events.InspectorUILoadedForTests, runTests);
889
890 })();
891
892 }