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