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