Index: chrome/resources/inspector/tests.js |
=================================================================== |
--- chrome/resources/inspector/tests.js (revision 0) |
+++ chrome/resources/inspector/tests.js (revision 0) |
@@ -0,0 +1,1657 @@ |
+// Copyright (c) 2009 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+ |
+/** |
+ * @fileoverview This file contains small testing framework along with the |
+ * test suite for the frontend. These tests are a part of the continues build |
+ * and are executed by the devtools_sanity_unittest.cc as a part of the |
+ * Interactive UI Test suite. |
+ */ |
+ |
+if (window.domAutomationController) { |
+ |
+var ___interactiveUiTestsMode = true; |
+ |
+/** |
+ * Test suite for interactive UI tests. |
+ * @constructor |
+ */ |
+TestSuite = function() { |
+ this.controlTaken_ = false; |
+ this.timerId_ = -1; |
+}; |
+ |
+ |
+/** |
+ * Reports test failure. |
+ * @param {string} message Failure description. |
+ */ |
+TestSuite.prototype.fail = function(message) { |
+ if (this.controlTaken_) { |
+ this.reportFailure_(message); |
+ } else { |
+ throw message; |
+ } |
+}; |
+ |
+ |
+/** |
+ * Equals assertion tests that expected == actual. |
+ * @param {Object} expected Expected object. |
+ * @param {Object} actual Actual object. |
+ * @param {string} opt_message User message to print if the test fails. |
+ */ |
+TestSuite.prototype.assertEquals = function(expected, actual, opt_message) { |
+ if (expected != actual) { |
+ var message = 'Expected: "' + expected + '", but was "' + actual + '"'; |
+ if (opt_message) { |
+ message = opt_message + '(' + message + ')'; |
+ } |
+ this.fail(message); |
+ } |
+}; |
+ |
+ |
+/** |
+ * True assertion tests that value == true. |
+ * @param {Object} value Actual object. |
+ * @param {string} opt_message User message to print if the test fails. |
+ */ |
+TestSuite.prototype.assertTrue = function(value, opt_message) { |
+ this.assertEquals(true, !!value, opt_message); |
+}; |
+ |
+ |
+/** |
+ * Contains assertion tests that string contains substring. |
+ * @param {string} string Outer. |
+ * @param {string} substring Inner. |
+ */ |
+TestSuite.prototype.assertContains = function(string, substring) { |
+ if (string.indexOf(substring) == -1) { |
+ this.fail('Expected to: "' + string + '" to contain "' + substring + '"'); |
+ } |
+}; |
+ |
+ |
+/** |
+ * Takes control over execution. |
+ */ |
+TestSuite.prototype.takeControl = function() { |
+ this.controlTaken_ = true; |
+ // Set up guard timer. |
+ var self = this; |
+ this.timerId_ = setTimeout(function() { |
+ self.reportFailure_('Timeout exceeded: 20 sec'); |
+ }, 20000); |
+}; |
+ |
+ |
+/** |
+ * Releases control over execution. |
+ */ |
+TestSuite.prototype.releaseControl = function() { |
+ if (this.timerId_ != -1) { |
+ clearTimeout(this.timerId_); |
+ this.timerId_ = -1; |
+ } |
+ this.reportOk_(); |
+}; |
+ |
+ |
+/** |
+ * Async tests use this one to report that they are completed. |
+ */ |
+TestSuite.prototype.reportOk_ = function() { |
+ window.domAutomationController.send('[OK]'); |
+}; |
+ |
+ |
+/** |
+ * Async tests use this one to report failures. |
+ */ |
+TestSuite.prototype.reportFailure_ = function(error) { |
+ if (this.timerId_ != -1) { |
+ clearTimeout(this.timerId_); |
+ this.timerId_ = -1; |
+ } |
+ window.domAutomationController.send('[FAILED] ' + error); |
+}; |
+ |
+ |
+/** |
+ * Runs all global functions starting with 'test' as unit tests. |
+ */ |
+TestSuite.prototype.runTest = function(testName) { |
+ try { |
+ this[testName](); |
+ if (!this.controlTaken_) { |
+ this.reportOk_(); |
+ } |
+ } catch (e) { |
+ this.reportFailure_(e); |
+ } |
+}; |
+ |
+ |
+/** |
+ * @param {string} panelName Name of the panel to show. |
+ */ |
+TestSuite.prototype.showPanel = function(panelName) { |
+ // Open Scripts panel. |
+ var toolbar = document.getElementById('toolbar'); |
+ var button = toolbar.getElementsByClassName(panelName)[0]; |
+ button.click(); |
+ this.assertEquals(WebInspector.panels[panelName], |
+ WebInspector.currentPanel); |
+}; |
+ |
+ |
+/** |
+ * Overrides the method with specified name until it's called first time. |
+ * @param {Object} receiver An object whose method to override. |
+ * @param {string} methodName Name of the method to override. |
+ * @param {Function} override A function that should be called right after the |
+ * overriden method returns. |
+ * @param {boolean} opt_sticky Whether restore original method after first run |
+ * or not. |
+ */ |
+TestSuite.prototype.addSniffer = function(receiver, methodName, override, |
+ opt_sticky) { |
+ var orig = receiver[methodName]; |
+ if (typeof orig != 'function') { |
+ this.fail('Cannot find method to override: ' + methodName); |
+ } |
+ var test = this; |
+ receiver[methodName] = function(var_args) { |
+ try { |
+ var result = orig.apply(this, arguments); |
+ } finally { |
+ if (!opt_sticky) { |
+ receiver[methodName] = orig; |
+ } |
+ } |
+ // In case of exception the override won't be called. |
+ try { |
+ override.apply(this, arguments); |
+ } catch (e) { |
+ test.fail('Exception in overriden method "' + methodName + '": ' + e); |
+ } |
+ return result; |
+ }; |
+}; |
+ |
+ |
+// UI Tests |
+ |
+ |
+/** |
+ * Tests that the real injected host is present in the context. |
+ */ |
+TestSuite.prototype.testHostIsPresent = function() { |
+ this.assertTrue(typeof DevToolsHost == 'object' && !DevToolsHost.isStub); |
+}; |
+ |
+ |
+/** |
+ * Tests elements tree has an 'HTML' root. |
+ */ |
+TestSuite.prototype.testElementsTreeRoot = function() { |
+ var doc = WebInspector.domAgent.document; |
+ this.assertEquals('HTML', doc.documentElement.nodeName); |
+ this.assertTrue(doc.documentElement.hasChildNodes()); |
+}; |
+ |
+ |
+/** |
+ * Tests that main resource is present in the system and that it is |
+ * the only resource. |
+ */ |
+TestSuite.prototype.testMainResource = function() { |
+ var tokens = []; |
+ var resources = WebInspector.resources; |
+ for (var id in resources) { |
+ tokens.push(resources[id].lastPathComponent); |
+ } |
+ this.assertEquals('simple_page.html', tokens.join(',')); |
+}; |
+ |
+ |
+/** |
+ * Tests that resources tab is enabled when corresponding item is selected. |
+ */ |
+TestSuite.prototype.testEnableResourcesTab = function() { |
+ this.showPanel('resources'); |
+ |
+ var test = this; |
+ this.addSniffer(WebInspector, 'addResource', |
+ function(identifier, payload) { |
+ test.assertEquals('simple_page.html', payload.lastPathComponent); |
+ WebInspector.panels.resources.refresh(); |
+ WebInspector.resources[identifier]._resourcesTreeElement.select(); |
+ |
+ test.releaseControl(); |
+ }); |
+ |
+ // Following call should lead to reload that we capture in the |
+ // addResource override. |
+ WebInspector.panels.resources._enableResourceTracking(); |
+ |
+ // We now have some time to report results to controller. |
+ this.takeControl(); |
+}; |
+ |
+ |
+/** |
+ * Tests resource headers. |
+ */ |
+TestSuite.prototype.testResourceHeaders = function() { |
+ this.showPanel('resources'); |
+ |
+ var test = this; |
+ |
+ var requestOk = false; |
+ var responseOk = false; |
+ var timingOk = false; |
+ |
+ this.addSniffer(WebInspector, 'addResource', |
+ function(identifier, payload) { |
+ var resource = this.resources[identifier]; |
+ if (resource.mainResource) { |
+ // We are only interested in secondary resources in this test. |
+ return; |
+ } |
+ |
+ var requestHeaders = JSON.stringify(resource.requestHeaders); |
+ test.assertContains(requestHeaders, 'Accept'); |
+ requestOk = true; |
+ }, true); |
+ |
+ this.addSniffer(WebInspector, 'updateResource', |
+ function(identifier, payload) { |
+ var resource = this.resources[identifier]; |
+ if (resource.mainResource) { |
+ // We are only interested in secondary resources in this test. |
+ return; |
+ } |
+ |
+ if (payload.didResponseChange) { |
+ var responseHeaders = JSON.stringify(resource.responseHeaders); |
+ test.assertContains(responseHeaders, 'Content-type'); |
+ test.assertContains(responseHeaders, 'Content-Length'); |
+ test.assertTrue(typeof resource.responseReceivedTime != 'undefnied'); |
+ responseOk = true; |
+ } |
+ |
+ if (payload.didTimingChange) { |
+ test.assertTrue(typeof resource.startTime != 'undefnied'); |
+ timingOk = true; |
+ } |
+ |
+ if (payload.didCompletionChange) { |
+ test.assertTrue(requestOk); |
+ test.assertTrue(responseOk); |
+ test.assertTrue(timingOk); |
+ test.assertTrue(typeof resource.endTime != 'undefnied'); |
+ test.releaseControl(); |
+ } |
+ }, true); |
+ |
+ WebInspector.panels.resources._enableResourceTracking(); |
+ this.takeControl(); |
+}; |
+ |
+ |
+/** |
+ * Test that profiler works. |
+ */ |
+TestSuite.prototype.testProfilerTab = function() { |
+ this.showPanel('profiles'); |
+ |
+ var test = this; |
+ this.addSniffer(WebInspector, 'addProfileHeader', |
+ function(type, profile) { |
+ var panel = WebInspector.panels.profiles; |
+ panel.showProfile(profile); |
+ var node = panel.visibleView.profileDataGridTree.children[0]; |
+ // Iterate over displayed functions and search for a function |
+ // that is called 'fib' or 'eternal_fib'. If found, it will mean |
+ // that we actually have profiled page's code. |
+ while (node) { |
+ if (node.functionName.indexOf('fib') != -1) { |
+ test.releaseControl(); |
+ } |
+ node = node.traverseNextNode(true, null, true); |
+ } |
+ |
+ test.fail(); |
+ }); |
+ var ticksCount = 0; |
+ var tickRecord = '\nt,'; |
+ this.addSniffer(RemoteDebuggerAgent, 'DidGetNextLogLines', |
+ function(log) { |
+ var pos = 0; |
+ while ((pos = log.indexOf(tickRecord, pos)) != -1) { |
+ pos += tickRecord.length; |
+ ticksCount++; |
+ } |
+ if (ticksCount > 100) { |
+ InspectorController.stopProfiling(); |
+ } |
+ }, true); |
+ |
+ InspectorController.startProfiling(); |
+ this.takeControl(); |
+}; |
+ |
+ |
+/** |
+ * Tests that scripts tab can be open and populated with inspected scripts. |
+ */ |
+TestSuite.prototype.testShowScriptsTab = function() { |
+ var parsedDebuggerTestPageHtml = false; |
+ |
+ // Intercept parsedScriptSource calls to check that all expected scripts are |
+ // added to the debugger. |
+ var test = this; |
+ var receivedConsoleApiSource = false; |
+ this.addSniffer(WebInspector, 'parsedScriptSource', |
+ function(sourceID, sourceURL, source, startingLine) { |
+ if (sourceURL == undefined) { |
+ if (receivedConsoleApiSource) { |
+ test.fail('Unexpected script without URL'); |
+ } else { |
+ receivedConsoleApiSource = true; |
+ } |
+ } else if (sourceURL.search(/debugger_test_page.html$/) != -1) { |
+ if (parsedDebuggerTestPageHtml) { |
+ test.fail('Unexpected parse event: ' + sourceURL); |
+ } |
+ parsedDebuggerTestPageHtml = true; |
+ } else { |
+ test.fail('Unexpected script URL: ' + sourceURL); |
+ } |
+ |
+ if (!WebInspector.panels.scripts.visibleView) { |
+ test.fail('No visible script view: ' + sourceURL); |
+ } |
+ |
+ // There should be two scripts: one for the main page and another |
+ // one which is source of console API(see |
+ // InjectedScript._ensureCommandLineAPIInstalled). |
+ if (parsedDebuggerTestPageHtml && receivedConsoleApiSource) { |
+ test.releaseControl(); |
+ } |
+ }, true /* sticky */); |
+ |
+ this.showPanel('scripts'); |
+ |
+ // Wait until all scripts are added to the debugger. |
+ this.takeControl(); |
+}; |
+ |
+ |
+/** |
+ * Tests that scripts list contains content scripts. |
+ */ |
+TestSuite.prototype.testContentScriptIsPresent = function() { |
+ this.showPanel('scripts'); |
+ var test = this; |
+ |
+ test._waitUntilScriptsAreParsed( |
+ ['page_with_content_script.html$', 'simple_content_script.js$'], |
+ function() { |
+ test.releaseControl(); |
+ }); |
+ |
+ // Wait until all scripts are added to the debugger. |
+ this.takeControl(); |
+}; |
+ |
+ |
+/** |
+ * Tests that scripts are not duplicaed on Scripts tab switch. |
+ */ |
+TestSuite.prototype.testNoScriptDuplicatesOnPanelSwitch = function() { |
+ var test = this; |
+ |
+ // There should be two scripts: one for the main page and another |
+ // one which is source of console API(see |
+ // InjectedScript._ensureCommandLineAPIInstalled). |
+ var expectedScriptsCount = 2; |
+ var parsedScripts = []; |
+ |
+ |
+ function switchToElementsTab() { |
+ test.showPanel('elements'); |
+ setTimeout(switchToScriptsTab, 0); |
+ } |
+ |
+ function switchToScriptsTab() { |
+ test.showPanel('scripts'); |
+ setTimeout(checkScriptsPanel, 0); |
+ } |
+ |
+ function checkScriptsPanel() { |
+ test.assertTrue(!!WebInspector.panels.scripts.visibleView, |
+ 'No visible script view.'); |
+ var select = WebInspector.panels.scripts.filesSelectElement; |
+ test.assertEquals(expectedScriptsCount, select.options.length, |
+ 'Unexpected options count'); |
+ test.releaseControl(); |
+ } |
+ |
+ this.addSniffer(WebInspector, 'parsedScriptSource', |
+ function(sourceID, sourceURL, source, startingLine) { |
+ test.assertTrue( |
+ parsedScripts.indexOf(sourceURL) == -1, |
+ 'Duplicated script: ' + sourceURL); |
+ test.assertTrue( |
+ parsedScripts.length < expectedScriptsCount, |
+ 'Too many scripts: ' + sourceURL); |
+ parsedScripts.push(sourceURL); |
+ |
+ if (parsedScripts.length == expectedScriptsCount) { |
+ setTimeout(switchToElementsTab, 0); |
+ } |
+ }, true /* sticky */); |
+ |
+ this.showPanel('scripts'); |
+ |
+ // Wait until all scripts are added to the debugger. |
+ this.takeControl(); |
+}; |
+ |
+ |
+/** |
+ * Tests that a breakpoint can be set. |
+ */ |
+TestSuite.prototype.testSetBreakpoint = function() { |
+ var parsedDebuggerTestPageHtml = false; |
+ var parsedDebuggerTestJs = false; |
+ |
+ this.showPanel('scripts'); |
+ |
+ var scriptUrl = null; |
+ var breakpointLine = 12; |
+ |
+ var test = this; |
+ this.addSniffer(devtools.DebuggerAgent.prototype, 'handleScriptsResponse_', |
+ function(msg) { |
+ var scriptSelect = document.getElementById('scripts-files'); |
+ var options = scriptSelect.options; |
+ |
+ // There should be console API source (see |
+ // InjectedScript._ensureCommandLineAPIInstalled) and the page script. |
+ test.assertEquals(2, options.length, 'Unexpected number of scripts(' + |
+ test.optionsToString_(options) + ')'); |
+ |
+ test.showMainPageScriptSource_( |
+ 'debugger_test_page.html', |
+ function(view, url) { |
+ view._addBreakpoint(breakpointLine); |
+ // Force v8 execution. |
+ RemoteToolsAgent.ExecuteVoidJavaScript(); |
+ test.waitForSetBreakpointResponse_(url, breakpointLine, |
+ function() { |
+ test.releaseControl(); |
+ }); |
+ }); |
+ }); |
+ |
+ |
+ this.takeControl(); |
+}; |
+ |
+ |
+/** |
+ * Serializes options collection to string. |
+ * @param {HTMLOptionsCollection} options |
+ * @return {string} |
+ */ |
+TestSuite.prototype.optionsToString_ = function(options) { |
+ var names = []; |
+ for (var i = 0; i < options.length; i++) { |
+ names.push('"' + options[i].text + '"'); |
+ } |
+ return names.join(','); |
+}; |
+ |
+ |
+/** |
+ * Ensures that main HTML resource is selected in Scripts panel and that its |
+ * source frame is setup. Invokes the callback when the condition is satisfied. |
+ * @param {HTMLOptionsCollection} options |
+ * @param {function(WebInspector.SourceView,string)} callback |
+ */ |
+TestSuite.prototype.showMainPageScriptSource_ = function(scriptName, callback) { |
+ var test = this; |
+ |
+ var scriptSelect = document.getElementById('scripts-files'); |
+ var options = scriptSelect.options; |
+ |
+ // There should be console API source (see |
+ // InjectedScript._ensureCommandLineAPIInstalled) and the page script. |
+ test.assertEquals(2, options.length, |
+ 'Unexpected number of scripts(' + test.optionsToString_(options) + ')'); |
+ |
+ // Select page's script if it's not current option. |
+ var scriptResource; |
+ if (options[scriptSelect.selectedIndex].text === scriptName) { |
+ scriptResource = options[scriptSelect.selectedIndex].representedObject; |
+ } else { |
+ var pageScriptIndex = -1; |
+ for (var i = 0; i < options.length; i++) { |
+ if (options[i].text === scriptName) { |
+ pageScriptIndex = i; |
+ break; |
+ } |
+ } |
+ test.assertTrue(-1 !== pageScriptIndex, |
+ 'Script with url ' + scriptName + ' not found among ' + |
+ test.optionsToString_(options)); |
+ scriptResource = options[pageScriptIndex].representedObject; |
+ |
+ // Current panel is 'Scripts'. |
+ WebInspector.currentPanel._showScriptOrResource(scriptResource); |
+ test.assertEquals(pageScriptIndex, scriptSelect.selectedIndex, |
+ 'Unexpected selected option index.'); |
+ } |
+ |
+ test.assertTrue(scriptResource instanceof WebInspector.Resource, |
+ 'Unexpected resource class.'); |
+ test.assertTrue(!!scriptResource.url, 'Resource URL is null.'); |
+ test.assertTrue( |
+ scriptResource.url.search(scriptName + '$') != -1, |
+ 'Main HTML resource should be selected.'); |
+ |
+ var scriptsPanel = WebInspector.panels.scripts; |
+ |
+ var view = scriptsPanel.visibleView; |
+ test.assertTrue(view instanceof WebInspector.SourceView); |
+ |
+ if (!view.sourceFrame._isContentLoaded()) { |
+ test.addSniffer(view, '_sourceFrameSetupFinished', function(event) { |
+ callback(view, scriptResource.url); |
+ }); |
+ } else { |
+ callback(view, scriptResource.url); |
+ } |
+}; |
+ |
+ |
+/* |
+ * Evaluates the code in the console as if user typed it manually and invokes |
+ * the callback when the result message is received and added to the console. |
+ * @param {string} code |
+ * @param {function(string)} callback |
+ */ |
+TestSuite.prototype.evaluateInConsole_ = function(code, callback) { |
+ WebInspector.console.visible = true; |
+ WebInspector.console.prompt.text = code; |
+ WebInspector.console.promptElement.handleKeyEvent( |
+ new TestSuite.KeyEvent('Enter')); |
+ |
+ this.addSniffer(WebInspector.ConsoleView.prototype, 'addMessage', |
+ function(commandResult) { |
+ callback(commandResult.toMessageElement().textContent); |
+ }); |
+}; |
+ |
+ |
+/* |
+ * Waits for 'setbreakpoint' response, checks that corresponding breakpoint |
+ * was successfully set and invokes the callback if it was. |
+ * @param {string} scriptUrl |
+ * @param {number} breakpointLine |
+ * @param {function()} callback |
+ */ |
+TestSuite.prototype.waitForSetBreakpointResponse_ = function(scriptUrl, |
+ breakpointLine, |
+ callback) { |
+ var test = this; |
+ test.addSniffer( |
+ devtools.DebuggerAgent.prototype, |
+ 'handleSetBreakpointResponse_', |
+ function(msg) { |
+ var bps = this.urlToBreakpoints_[scriptUrl]; |
+ test.assertTrue(!!bps, 'No breakpoints for line ' + breakpointLine); |
+ var line = devtools.DebuggerAgent.webkitToV8LineNumber_(breakpointLine); |
+ test.assertTrue(!!bps[line].getV8Id(), |
+ 'Breakpoint id was not assigned.'); |
+ callback(); |
+ }); |
+}; |
+ |
+ |
+/** |
+ * Tests eval on call frame. |
+ */ |
+TestSuite.prototype.testEvalOnCallFrame = function() { |
+ this.showPanel('scripts'); |
+ |
+ var breakpointLine = 16; |
+ |
+ var test = this; |
+ this.addSniffer(devtools.DebuggerAgent.prototype, 'handleScriptsResponse_', |
+ function(msg) { |
+ test.showMainPageScriptSource_( |
+ 'debugger_test_page.html', |
+ function(view, url) { |
+ view._addBreakpoint(breakpointLine); |
+ // Force v8 execution. |
+ RemoteToolsAgent.ExecuteVoidJavaScript(); |
+ test.waitForSetBreakpointResponse_(url, breakpointLine, |
+ setBreakpointCallback); |
+ }); |
+ }); |
+ |
+ function setBreakpointCallback() { |
+ // Since breakpoints are ignored in evals' calculate() function is |
+ // execute after zero-timeout so that the breakpoint is hit. |
+ test.evaluateInConsole_( |
+ 'setTimeout("calculate(123)" , 0)', |
+ function(resultText) { |
+ test.assertTrue(!isNaN(resultText), |
+ 'Failed to get timer id: ' + resultText); |
+ waitForBreakpointHit(); |
+ }); |
+ } |
+ |
+ function waitForBreakpointHit() { |
+ test.addSniffer( |
+ devtools.DebuggerAgent.prototype, |
+ 'handleBacktraceResponse_', |
+ function(msg) { |
+ test.assertEquals(2, this.callFrames_.length, |
+ 'Unexpected stack depth on the breakpoint. ' + |
+ JSON.stringify(msg)); |
+ test.assertEquals('calculate', this.callFrames_[0].functionName, |
+ 'Unexpected top frame function.'); |
+ // Evaluate 'e+1' where 'e' is an argument of 'calculate' function. |
+ test.evaluateInConsole_( |
+ 'e+1', |
+ function(resultText) { |
+ test.assertEquals('124', resultText, 'Unexpected "e+1" value.'); |
+ test.releaseControl(); |
+ }); |
+ }); |
+ } |
+ |
+ this.takeControl(); |
+}; |
+ |
+ |
+/** |
+ * Tests that console auto completion works when script execution is paused. |
+ */ |
+TestSuite.prototype.testCompletionOnPause = function() { |
+ this.showPanel('scripts'); |
+ var test = this; |
+ this._executeCodeWhenScriptsAreParsed( |
+ 'handleClick()', |
+ ['completion_on_pause.html$']); |
+ |
+ this._waitForScriptPause( |
+ { |
+ functionsOnStack: ['innerFunction', 'handleClick', |
+ '(anonymous function)'], |
+ lineNumber: 9, |
+ lineText: ' debugger;' |
+ }, |
+ showConsole); |
+ |
+ function showConsole() { |
+ test.addSniffer(WebInspector.console, 'afterShow', testLocalsCompletion); |
+ WebInspector.showConsole(); |
+ } |
+ |
+ function testLocalsCompletion() { |
+ checkCompletions( |
+ 'th', |
+ ['parameter1', 'closureLocal', 'p', 'createClosureLocal'], |
+ testThisCompletion); |
+ } |
+ |
+ function testThisCompletion() { |
+ checkCompletions( |
+ 'this.', |
+ ['field1', 'field2', 'm'], |
+ testFieldCompletion); |
+ } |
+ |
+ function testFieldCompletion() { |
+ checkCompletions( |
+ 'this.field1.', |
+ ['id', 'name'], |
+ function() { |
+ test.releaseControl(); |
+ }); |
+ } |
+ |
+ function checkCompletions(expression, expectedProperties, callback) { |
+ test.addSniffer(WebInspector.console, '_reportCompletions', |
+ function(bestMatchOnly, completionsReadyCallback, dotNotation, |
+ bracketNotation, prefix, result, isException) { |
+ test.assertTrue(!isException, |
+ 'Exception while collecting completions'); |
+ for (var i = 0; i < expectedProperties.length; i++) { |
+ var name = expectedProperties[i]; |
+ test.assertTrue(result[name], 'Name ' + name + |
+ ' not found among the completions: ' + |
+ JSON.stringify(result)); |
+ } |
+ setTimeout(callback, 0); |
+ }); |
+ WebInspector.console.prompt.text = expression; |
+ WebInspector.console.prompt.autoCompleteSoon(); |
+ } |
+ |
+ this.takeControl(); |
+}; |
+ |
+ |
+/** |
+ * Tests that inspected page doesn't hang on reload if it contains a syntax |
+ * error and DevTools window is open. |
+ */ |
+TestSuite.prototype.testAutoContinueOnSyntaxError = function() { |
+ this.showPanel('scripts'); |
+ var test = this; |
+ |
+ function checkScriptsList() { |
+ var scriptSelect = document.getElementById('scripts-files'); |
+ var options = scriptSelect.options; |
+ // There should be only console API source (see |
+ // InjectedScript._ensureCommandLineAPIInstalled) since the page script |
+ // contains a syntax error. |
+ for (var i = 0 ; i < options.length; i++) { |
+ if (options[i].text.search('script_syntax_error.html$') != -1) { |
+ test.fail('Script with syntax error should not be in the list of ' + |
+ 'parsed scripts.'); |
+ } |
+ } |
+ } |
+ |
+ this.addSniffer(devtools.DebuggerAgent.prototype, 'handleScriptsResponse_', |
+ function(msg) { |
+ checkScriptsList(); |
+ |
+ // Reload inspected page. |
+ test.evaluateInConsole_( |
+ 'window.location.reload(true);', |
+ function(resultText) { |
+ test.assertEquals('undefined', resultText, |
+ 'Unexpected result of reload().'); |
+ waitForExceptionEvent(); |
+ }); |
+ }); |
+ |
+ function waitForExceptionEvent() { |
+ var exceptionCount = 0; |
+ test.addSniffer( |
+ devtools.DebuggerAgent.prototype, |
+ 'handleExceptionEvent_', |
+ function(msg) { |
+ exceptionCount++; |
+ test.assertEquals(1, exceptionCount, 'Too many exceptions.'); |
+ test.assertEquals(undefined, msg.getBody().script, |
+ 'Unexpected exception: ' + JSON.stringify(msg)); |
+ test.releaseControl(); |
+ }); |
+ |
+ // Check that the script is not paused on parse error. |
+ test.addSniffer( |
+ WebInspector, |
+ 'pausedScript', |
+ function(callFrames) { |
+ test.fail('Script execution should not pause on syntax error.'); |
+ }); |
+ } |
+ |
+ this.takeControl(); |
+}; |
+ |
+ |
+/** |
+ * Checks current execution line against expectations. |
+ * @param {WebInspector.SourceFrame} sourceFrame |
+ * @param {number} lineNumber Expected line number |
+ * @param {string} lineContent Expected line text |
+ */ |
+TestSuite.prototype._checkExecutionLine = function(sourceFrame, lineNumber, |
+ lineContent) { |
+ var sourceRow = sourceFrame.sourceRow(lineNumber); |
+ |
+ var line = sourceRow.getElementsByClassName('webkit-line-content')[0]; |
+ this.assertEquals(lineNumber, sourceFrame.executionLine, |
+ 'Unexpected execution line number.'); |
+ this.assertEquals(lineContent, line.textContent, |
+ 'Unexpected execution line text.'); |
+ |
+ this.assertTrue(!!sourceRow, 'Execution line not found'); |
+ this.assertTrue(sourceRow.hasStyleClass('webkit-execution-line'), |
+ 'Execution line ' + lineNumber + ' is not highlighted. Class: ' + |
+ sourceRow.className); |
+} |
+ |
+ |
+/** |
+ * Checks that all expected scripts are present in the scripts list |
+ * in the Scripts panel. |
+ * @param {Array.<string>} expected Regular expressions describing |
+ * expected script names. |
+ * @return {boolean} Whether all the scripts are in 'scripts-files' select |
+ * box |
+ */ |
+TestSuite.prototype._scriptsAreParsed = function(expected) { |
+ var scriptSelect = document.getElementById('scripts-files'); |
+ var options = scriptSelect.options; |
+ |
+ // Check that at least all the expected scripts are present. |
+ var missing = expected.slice(0); |
+ for (var i = 0 ; i < options.length; i++) { |
+ for (var j = 0; j < missing.length; j++) { |
+ if (options[i].text.search(missing[j]) != -1) { |
+ missing.splice(j, 1); |
+ break; |
+ } |
+ } |
+ } |
+ return missing.length == 0; |
+}; |
+ |
+ |
+/** |
+ * Waits for script pause, checks expectations, and invokes the callback. |
+ * @param {Object} expectations Dictionary of expectations |
+ * @param {function():void} callback |
+ */ |
+TestSuite.prototype._waitForScriptPause = function(expectations, callback) { |
+ var test = this; |
+ // Wait until script is paused. |
+ test.addSniffer( |
+ WebInspector, |
+ 'pausedScript', |
+ function(callFrames) { |
+ test.assertEquals(expectations.functionsOnStack.length, |
+ callFrames.length, |
+ 'Unexpected stack depth'); |
+ |
+ var functionsOnStack = []; |
+ for (var i = 0; i < callFrames.length; i++) { |
+ functionsOnStack.push(callFrames[i].functionName); |
+ } |
+ test.assertEquals( |
+ expectations.functionsOnStack.join(','), |
+ functionsOnStack.join(','), 'Unexpected stack.'); |
+ |
+ checkSourceFrameWhenLoaded(); |
+ }); |
+ |
+ // Check that execution line where the script is paused is expected one. |
+ function checkSourceFrameWhenLoaded() { |
+ var frame = WebInspector.currentPanel.visibleView.sourceFrame; |
+ if (frame._isContentLoaded()) { |
+ checkExecLine(); |
+ } else { |
+ frame.addEventListener('content loaded', checkExecLine); |
+ } |
+ function checkExecLine() { |
+ test._checkExecutionLine(frame, expectations.lineNumber, |
+ expectations.lineText); |
+ // Make sure we don't listen anymore. |
+ frame.removeEventListener('content loaded', checkExecLine); |
+ callback(); |
+ } |
+ } |
+}; |
+ |
+ |
+/** |
+ * Performs sequence of steps. |
+ * @param {Array.<Object|Function>} Array [expectations1,action1,expectations2, |
+ * action2,...,actionN]. |
+ */ |
+TestSuite.prototype._performSteps = function(actions) { |
+ var test = this; |
+ var i = 0; |
+ function doNextAction() { |
+ if (i > 0) { |
+ actions[i++](); |
+ } |
+ if (i < actions.length - 1) { |
+ test._waitForScriptPause(actions[i++], doNextAction); |
+ } |
+ } |
+ doNextAction(); |
+}; |
+ |
+ |
+/** |
+ * Waits until all the scripts are parsed and asynchronously executes the code |
+ * in the inspected page. |
+ */ |
+TestSuite.prototype._executeCodeWhenScriptsAreParsed = function( |
+ code, expectedScripts) { |
+ var test = this; |
+ |
+ function executeFunctionInInspectedPage() { |
+ // Since breakpoints are ignored in evals' calculate() function is |
+ // execute after zero-timeout so that the breakpoint is hit. |
+ test.evaluateInConsole_( |
+ 'setTimeout("' + code + '" , 0)', |
+ function(resultText) { |
+ test.assertTrue(!isNaN(resultText), |
+ 'Failed to get timer id: ' + resultText); |
+ }); |
+ } |
+ |
+ test._waitUntilScriptsAreParsed( |
+ expectedScripts, executeFunctionInInspectedPage); |
+}; |
+ |
+ |
+/** |
+ * Waits until all the scripts are parsed and invokes the callback. |
+ */ |
+TestSuite.prototype._waitUntilScriptsAreParsed = function( |
+ expectedScripts, callback) { |
+ var test = this; |
+ |
+ function waitForAllScripts() { |
+ if (test._scriptsAreParsed(expectedScripts)) { |
+ callback(); |
+ } else { |
+ test.addSniffer(WebInspector, 'parsedScriptSource', waitForAllScripts); |
+ } |
+ } |
+ |
+ waitForAllScripts(); |
+}; |
+ |
+ |
+/** |
+ * Waits until all debugger scripts are parsed and executes 'a()' in the |
+ * inspected page. |
+ */ |
+TestSuite.prototype._executeFunctionForStepTest = function() { |
+ this._executeCodeWhenScriptsAreParsed( |
+ 'a()', |
+ ['debugger_step.html$', 'debugger_step.js$']); |
+}; |
+ |
+ |
+/** |
+ * Tests step over in the debugger. |
+ */ |
+TestSuite.prototype.testStepOver = function() { |
+ this.showPanel('scripts'); |
+ var test = this; |
+ |
+ this._executeFunctionForStepTest(); |
+ |
+ this._performSteps([ |
+ { |
+ functionsOnStack: ['d','a','(anonymous function)'], |
+ lineNumber: 3, |
+ lineText: ' debugger;' |
+ }, |
+ function() { |
+ document.getElementById('scripts-step-over').click(); |
+ }, |
+ { |
+ functionsOnStack: ['d','a','(anonymous function)'], |
+ lineNumber: 5, |
+ lineText: ' var y = fact(10);' |
+ }, |
+ function() { |
+ document.getElementById('scripts-step-over').click(); |
+ }, |
+ { |
+ functionsOnStack: ['d','a','(anonymous function)'], |
+ lineNumber: 6, |
+ lineText: ' return y;' |
+ }, |
+ function() { |
+ test.releaseControl(); |
+ } |
+ ]); |
+ |
+ test.takeControl(); |
+}; |
+ |
+ |
+/** |
+ * Tests step out in the debugger. |
+ */ |
+TestSuite.prototype.testStepOut = function() { |
+ this.showPanel('scripts'); |
+ var test = this; |
+ |
+ this._executeFunctionForStepTest(); |
+ |
+ this._performSteps([ |
+ { |
+ functionsOnStack: ['d','a','(anonymous function)'], |
+ lineNumber: 3, |
+ lineText: ' debugger;' |
+ }, |
+ function() { |
+ document.getElementById('scripts-step-out').click(); |
+ }, |
+ { |
+ functionsOnStack: ['a','(anonymous function)'], |
+ lineNumber: 8, |
+ lineText: ' printResult(result);' |
+ }, |
+ function() { |
+ test.releaseControl(); |
+ } |
+ ]); |
+ |
+ test.takeControl(); |
+}; |
+ |
+ |
+/** |
+ * Tests step in in the debugger. |
+ */ |
+TestSuite.prototype.testStepIn = function() { |
+ this.showPanel('scripts'); |
+ var test = this; |
+ |
+ this._executeFunctionForStepTest(); |
+ |
+ this._performSteps([ |
+ { |
+ functionsOnStack: ['d','a','(anonymous function)'], |
+ lineNumber: 3, |
+ lineText: ' debugger;' |
+ }, |
+ function() { |
+ document.getElementById('scripts-step-over').click(); |
+ }, |
+ { |
+ functionsOnStack: ['d','a','(anonymous function)'], |
+ lineNumber: 5, |
+ lineText: ' var y = fact(10);' |
+ }, |
+ function() { |
+ document.getElementById('scripts-step-into').click(); |
+ }, |
+ { |
+ functionsOnStack: ['fact','d','a','(anonymous function)'], |
+ lineNumber: 15, |
+ lineText: ' return r;' |
+ }, |
+ function() { |
+ test.releaseControl(); |
+ } |
+ ]); |
+ |
+ test.takeControl(); |
+}; |
+ |
+ |
+/** |
+ * Gets a XPathResult matching given xpath. |
+ * @param {string} xpath |
+ * @param {number} resultType |
+ * @param {Node} opt_ancestor Context node. If not specified documentElement |
+ * will be used |
+ * @return {XPathResult} Type of returned value is determined by 'resultType' parameter |
+ */ |
+ |
+TestSuite.prototype._evaluateXpath = function( |
+ xpath, resultType, opt_ancestor) { |
+ if (!opt_ancestor) { |
+ opt_ancestor = document.documentElement; |
+ } |
+ try { |
+ return document.evaluate(xpath, opt_ancestor, null, resultType, null); |
+ } catch(e) { |
+ this.fail('Error in expression: "' + xpath + '".' + e); |
+ } |
+}; |
+ |
+ |
+/** |
+ * Gets first Node matching given xpath. |
+ * @param {string} xpath |
+ * @param {Node} opt_ancestor Context node. If not specified documentElement |
+ * will be used |
+ * @return {?Node} |
+ */ |
+TestSuite.prototype._findNode = function(xpath, opt_ancestor) { |
+ var result = this._evaluateXpath(xpath, XPathResult.FIRST_ORDERED_NODE_TYPE, |
+ opt_ancestor).singleNodeValue; |
+ this.assertTrue(!!result, 'Cannot find node on path: ' + xpath); |
+ return result; |
+}; |
+ |
+ |
+/** |
+ * Gets a text matching given xpath. |
+ * @param {string} xpath |
+ * @param {Node} opt_ancestor Context node. If not specified documentElement |
+ * will be used |
+ * @return {?string} |
+ */ |
+TestSuite.prototype._findText = function(xpath, opt_ancestor) { |
+ var result = this._evaluateXpath(xpath, XPathResult.STRING_TYPE, |
+ opt_ancestor).stringValue; |
+ this.assertTrue(!!result, 'Cannot find text on path: ' + xpath); |
+ return result; |
+}; |
+ |
+ |
+/** |
+ * Gets an iterator over nodes matching given xpath. |
+ * @param {string} xpath |
+ * @param {Node} opt_ancestor Context node. If not specified, documentElement |
+ * will be used |
+ * @return {XPathResult} Iterator over the nodes |
+ */ |
+TestSuite.prototype._nodeIterator = function(xpath, opt_ancestor) { |
+ return this._evaluateXpath(xpath, XPathResult.ORDERED_NODE_ITERATOR_TYPE, |
+ opt_ancestor); |
+}; |
+ |
+ |
+/** |
+ * Checks the scopeSectionDiv against the expectations. |
+ * @param {Node} scopeSectionDiv The section div |
+ * @param {Object} expectations Expectations dictionary |
+ */ |
+TestSuite.prototype._checkScopeSectionDiv = function( |
+ scopeSectionDiv, expectations) { |
+ var scopeTitle = this._findText( |
+ './div[@class="header"]/div[@class="title"]/text()', scopeSectionDiv); |
+ this.assertEquals(expectations.title, scopeTitle, |
+ 'Unexpected scope section title.'); |
+ if (!expectations.properties) { |
+ return; |
+ } |
+ this.assertTrue(scopeSectionDiv.hasStyleClass('expanded'), 'Section "' + |
+ scopeTitle + '" is collapsed.'); |
+ |
+ var propertyIt = this._nodeIterator('./ol[@class="properties"]/li', |
+ scopeSectionDiv); |
+ var propertyLi; |
+ var foundProps = []; |
+ while (propertyLi = propertyIt.iterateNext()) { |
+ var name = this._findText('./span[@class="name"]/text()', propertyLi); |
+ var value = this._findText('./span[@class="value"]/text()', propertyLi); |
+ this.assertTrue(!!name, 'Invalid variable name: "' + name + '"'); |
+ this.assertTrue(name in expectations.properties, |
+ 'Unexpected property: ' + name); |
+ this.assertEquals(expectations.properties[name], value, |
+ 'Unexpected "' + name + '" property value.'); |
+ delete expectations.properties[name]; |
+ foundProps.push(name + ' = ' + value); |
+ } |
+ |
+ // Check that all expected properties were found. |
+ for (var p in expectations.properties) { |
+ this.fail('Property "' + p + '" was not found in scope "' + scopeTitle + |
+ '". Found properties: "' + foundProps.join(',') + '"'); |
+ } |
+}; |
+ |
+ |
+/** |
+ * Expands scope sections matching the filter and invokes the callback on |
+ * success. |
+ * @param {function(WebInspector.ObjectPropertiesSection, number):boolean} |
+ * filter |
+ * @param {Function} callback |
+ */ |
+TestSuite.prototype._expandScopeSections = function(filter, callback) { |
+ var sections = WebInspector.currentPanel.sidebarPanes.scopechain.sections; |
+ |
+ var toBeUpdatedCount = 0; |
+ function updateListener() { |
+ --toBeUpdatedCount; |
+ if (toBeUpdatedCount == 0) { |
+ // Report when all scopes are expanded and populated. |
+ callback(); |
+ } |
+ } |
+ |
+ // Global scope is always the last one. |
+ for (var i = 0; i < sections.length - 1; i++) { |
+ var section = sections[i]; |
+ if (!filter(sections, i)) { |
+ continue; |
+ } |
+ ++toBeUpdatedCount; |
+ var populated = section.populated; |
+ |
+ this._hookGetPropertiesCallback(updateListener, |
+ function() { |
+ section.expand(); |
+ if (populated) { |
+ // Make sure 'updateProperties' callback will be called at least once |
+ // after it was overridden. |
+ section.update(); |
+ } |
+ }); |
+ } |
+}; |
+ |
+ |
+/** |
+ * Tests that scopes can be expanded and contain expected data. |
+ */ |
+TestSuite.prototype.testExpandScope = function() { |
+ this.showPanel('scripts'); |
+ var test = this; |
+ |
+ this._executeCodeWhenScriptsAreParsed( |
+ 'handleClick()', |
+ ['debugger_closure.html$']); |
+ |
+ this._waitForScriptPause( |
+ { |
+ functionsOnStack: ['innerFunction', 'handleClick', |
+ '(anonymous function)'], |
+ lineNumber: 8, |
+ lineText: ' debugger;' |
+ }, |
+ expandAllSectionsExceptGlobal); |
+ |
+ // Expanding Global scope takes for too long so we skeep it. |
+ function expandAllSectionsExceptGlobal() { |
+ test._expandScopeSections(function(sections, i) { |
+ return i < sections.length - 1; |
+ }, |
+ examineScopes /* When all scopes are expanded and populated check |
+ them. */); |
+ } |
+ |
+ // Check scope sections contents. |
+ function examineScopes() { |
+ var scopeVariablesSection = test._findNode('//div[@id="scripts-sidebar"]/' + |
+ 'div[div[@class="title"]/text()="Scope Variables"]'); |
+ var expectedScopes = [ |
+ { |
+ title: 'Local', |
+ properties: { |
+ x:2009, |
+ innerFunctionLocalVar:2011, |
+ 'this': 'global', |
+ } |
+ }, |
+ { |
+ title: 'Closure', |
+ properties: { |
+ n:'TextParam', |
+ makeClosureLocalVar:'local.TextParam', |
+ } |
+ }, |
+ { |
+ title: 'Global', |
+ }, |
+ ]; |
+ var it = test._nodeIterator('./div[@class="body"]/div', |
+ scopeVariablesSection); |
+ var scopeIndex = 0; |
+ var scopeDiv; |
+ while (scopeDiv = it.iterateNext()) { |
+ test.assertTrue(scopeIndex < expectedScopes.length, |
+ 'Too many scopes.'); |
+ test._checkScopeSectionDiv(scopeDiv, expectedScopes[scopeIndex]); |
+ ++scopeIndex; |
+ } |
+ test.assertEquals(expectedScopes.length, scopeIndex, |
+ 'Unexpected number of scopes.'); |
+ |
+ test.releaseControl(); |
+ } |
+ |
+ test.takeControl(); |
+}; |
+ |
+ |
+/** |
+ * Returns child tree element for a property with given name. |
+ * @param {TreeElement} parent Parent tree element. |
+ * @param {string} childName |
+ * @param {string} objectPath Path to the object. Will be printed in the case |
+ * of failure. |
+ * @return {TreeElement} |
+ */ |
+TestSuite.prototype._findChildProperty = function( |
+ parent, childName, objectPath) { |
+ var children = parent.children; |
+ for (var i = 0; i < children.length; i++) { |
+ var treeElement = children[i]; |
+ var property = treeElement.property; |
+ if (property.name == childName) { |
+ return treeElement; |
+ } |
+ } |
+ this.fail('Cannot find property "' + childName + '" in ' + objectPath); |
+}; |
+ |
+ |
+/** |
+ * Executes the 'code' with InjectedScriptAccess.getProperties overriden |
+ * so that all callbacks passed to InjectedScriptAccess.getProperties are |
+ * extended with the 'hook'. |
+ * @param {Function} hook The hook function. |
+ * @param {Function} code A code snippet to be executed. |
+ */ |
+TestSuite.prototype._hookGetPropertiesCallback = function(hook, code) { |
+ var orig = InjectedScriptAccess.getProperties; |
+ InjectedScriptAccess.getProperties = function(objectProxy, |
+ ignoreHasOwnProperty, callback) { |
+ orig.call(InjectedScriptAccess, objectProxy, ignoreHasOwnProperty, |
+ function() { |
+ callback.apply(this, arguments); |
+ hook(); |
+ }); |
+ }; |
+ try { |
+ code(); |
+ } finally { |
+ InjectedScriptAccess.getProperties = orig; |
+ } |
+}; |
+ |
+ |
+/** |
+ * Tests that all elements in prototype chain of an object have expected |
+ * intrinic proprties(__proto__, constructor, prototype). |
+ */ |
+TestSuite.prototype.testDebugIntrinsicProperties = function() { |
+ this.showPanel('scripts'); |
+ var test = this; |
+ |
+ this._executeCodeWhenScriptsAreParsed( |
+ 'handleClick()', |
+ ['debugger_intrinsic_properties.html$']); |
+ |
+ this._waitForScriptPause( |
+ { |
+ functionsOnStack: ['callDebugger', 'handleClick', |
+ '(anonymous function)'], |
+ lineNumber: 29, |
+ lineText: ' debugger;' |
+ }, |
+ expandLocalScope); |
+ |
+ var localScopeSection = null; |
+ function expandLocalScope() { |
+ test._expandScopeSections(function(sections, i) { |
+ if (i == 0) { |
+ test.assertTrue(sections[i].object.isLocal, 'Scope #0 is not Local.'); |
+ localScopeSection = sections[i]; |
+ return true; |
+ } |
+ return false; |
+ }, |
+ examineLocalScope); |
+ } |
+ |
+ function examineLocalScope() { |
+ var scopeExpectations = [ |
+ 'a', 'Object', [ |
+ 'constructor', 'function Child()', [ |
+ 'constructor', 'function Function()', null, |
+ 'name', 'Child', null, |
+ 'prototype', 'Object', [ |
+ 'childProtoField', '21', null |
+ ] |
+ ], |
+ |
+ '__proto__', 'Object', [ |
+ '__proto__', 'Object', [ |
+ '__proto__', 'Object', [ |
+ '__proto__', 'null', null, |
+ 'constructor', 'function Object()', null, |
+ ], |
+ 'constructor', 'function Parent()', [ |
+ 'name', 'Parent', null, |
+ 'prototype', 'Object', [ |
+ 'parentProtoField', '11', null, |
+ ] |
+ ], |
+ 'parentProtoField', '11', null, |
+ ], |
+ 'constructor', 'function Child()', null, |
+ 'childProtoField', '21', null, |
+ ], |
+ |
+ 'parentField', '10', null, |
+ 'childField', '20', null, |
+ ] |
+ ]; |
+ |
+ checkProperty( |
+ localScopeSection.propertiesTreeOutline, |
+ '<Local Scope>', |
+ scopeExpectations); |
+ } |
+ |
+ var propQueue = []; |
+ var index = 0; |
+ var expectedFinalIndex = 8; |
+ |
+ function expandAndCheckNextProperty() { |
+ if (index == propQueue.length) { |
+ test.assertEquals(expectedFinalIndex, index, |
+ 'Unexpected number of expanded objects.'); |
+ test.releaseControl(); |
+ return; |
+ } |
+ |
+ // Read next property data from the queue. |
+ var treeElement = propQueue[index].treeElement; |
+ var path = propQueue[index].path; |
+ var expectations = propQueue[index].expectations; |
+ index++; |
+ |
+ // Expand the property. |
+ test._hookGetPropertiesCallback(function() { |
+ checkProperty(treeElement, path, expectations); |
+ }, |
+ function() { |
+ treeElement.expand(); |
+ }); |
+ } |
+ |
+ function checkProperty(treeElement, path, expectations) { |
+ for (var i = 0; i < expectations.length; i += 3) { |
+ var name = expectations[i]; |
+ var description = expectations[i+1]; |
+ var value = expectations[i+2]; |
+ |
+ var propertyPath = path + '.' + name; |
+ var propertyTreeElement = test._findChildProperty( |
+ treeElement, name, path); |
+ test.assertTrue(propertyTreeElement, |
+ 'Property "' + propertyPath + '" not found.'); |
+ test.assertEquals(description, |
+ propertyTreeElement.property.value.description, |
+ 'Unexpected "' + propertyPath + '" description.'); |
+ if (value) { |
+ // Schedule property content check. |
+ propQueue.push({ |
+ treeElement: propertyTreeElement, |
+ path: propertyPath, |
+ expectations: value, |
+ }); |
+ } |
+ } |
+ // Check next property in the queue. |
+ expandAndCheckNextProperty(); |
+ } |
+ |
+ test.takeControl(); |
+}; |
+ |
+ |
+/** |
+ * Tests 'Pause' button will pause debugger when a snippet is evaluated. |
+ */ |
+TestSuite.prototype.testPauseInEval = function() { |
+ this.showPanel('scripts'); |
+ |
+ var test = this; |
+ |
+ var pauseButton = document.getElementById('scripts-pause'); |
+ pauseButton.click(); |
+ |
+ devtools.tools.evaluateJavaScript('fib(10)'); |
+ |
+ this.addSniffer(WebInspector, 'pausedScript', |
+ function() { |
+ test.releaseControl(); |
+ }); |
+ |
+ test.takeControl(); |
+}; |
+ |
+ |
+/** |
+ * Key event with given key identifier. |
+ */ |
+TestSuite.KeyEvent = function(key) { |
+ this.keyIdentifier = key; |
+}; |
+TestSuite.KeyEvent.prototype.preventDefault = function() {}; |
+TestSuite.KeyEvent.prototype.stopPropagation = function() {}; |
+ |
+ |
+/** |
+ * Tests console eval. |
+ */ |
+TestSuite.prototype.testConsoleEval = function() { |
+ var test = this; |
+ this.evaluateInConsole_('123', |
+ function(resultText) { |
+ test.assertEquals('123', resultText); |
+ test.releaseControl(); |
+ }); |
+ |
+ this.takeControl(); |
+}; |
+ |
+ |
+/** |
+ * Tests console log. |
+ */ |
+TestSuite.prototype.testConsoleLog = function() { |
+ WebInspector.console.visible = true; |
+ var messages = WebInspector.console.messages; |
+ var index = 0; |
+ |
+ var test = this; |
+ var assertNext = function(line, message, opt_class, opt_count, opt_substr) { |
+ var elem = messages[index++].toMessageElement(); |
+ var clazz = elem.getAttribute('class'); |
+ var expectation = (opt_count || '') + 'console_test_page.html:' + |
+ line + message; |
+ if (opt_substr) { |
+ test.assertContains(elem.textContent, expectation); |
+ } else { |
+ test.assertEquals(expectation, elem.textContent); |
+ } |
+ if (opt_class) { |
+ test.assertContains(clazz, 'console-' + opt_class); |
+ } |
+ }; |
+ |
+ assertNext('5', 'log', 'log-level'); |
+ assertNext('7', 'debug', 'log-level'); |
+ assertNext('9', 'info', 'log-level'); |
+ assertNext('11', 'warn', 'warning-level'); |
+ assertNext('13', 'error', 'error-level'); |
+ assertNext('15', 'Message format number 1, 2 and 3.5'); |
+ assertNext('17', 'Message format for string'); |
+ assertNext('19', 'Object Object'); |
+ assertNext('22', 'repeated', 'log-level', 5); |
+ assertNext('26', 'count: 1'); |
+ assertNext('26', 'count: 2'); |
+ assertNext('29', 'group', 'group-title'); |
+ index++; |
+ assertNext('33', 'timer:', 'log-level', '', true); |
+ assertNext('35', '1 2 3', 'log-level'); |
+ assertNext('37', 'HTMLDocument', 'log-level'); |
+ assertNext('39', '<html>', 'log-level', '', true); |
+}; |
+ |
+ |
+/** |
+ * Tests eval of global objects. |
+ */ |
+TestSuite.prototype.testEvalGlobal = function() { |
+ WebInspector.console.visible = true; |
+ |
+ var inputs = ['foo', 'foobar']; |
+ var expectations = ['foo', 'fooValue', |
+ 'foobar', 'ReferenceError: foobar is not defined']; |
+ |
+ // Do not change code below - simply add inputs and expectations above. |
+ var initEval = function(input) { |
+ WebInspector.console.prompt.text = input; |
+ WebInspector.console.promptElement.handleKeyEvent( |
+ new TestSuite.KeyEvent('Enter')); |
+ }; |
+ var test = this; |
+ var messagesCount = 0; |
+ var inputIndex = 0; |
+ this.addSniffer(WebInspector.ConsoleView.prototype, 'addMessage', |
+ function(commandResult) { |
+ messagesCount++; |
+ if (messagesCount == expectations.length) { |
+ var messages = WebInspector.console.messages; |
+ for (var i = 0; i < expectations; ++i) { |
+ var elem = messages[i++].toMessageElement(); |
+ test.assertEquals(elem.textContent, expectations[i]); |
+ } |
+ test.releaseControl(); |
+ } else if (messagesCount % 2 == 0) { |
+ initEval(inputs[inputIndex++]); |
+ } |
+ }, true); |
+ |
+ initEval(inputs[inputIndex++]); |
+ this.takeControl(); |
+}; |
+ |
+ |
+/** |
+ * Test runner for the test suite. |
+ */ |
+var uiTests = {}; |
+ |
+ |
+/** |
+ * Run each test from the test suit on a fresh instance of the suite. |
+ */ |
+uiTests.runAllTests = function() { |
+ // For debugging purposes. |
+ for (var name in TestSuite.prototype) { |
+ if (name.substring(0, 4) == 'test' && |
+ typeof TestSuite.prototype[name] == 'function') { |
+ uiTests.runTest(name); |
+ } |
+ } |
+}; |
+ |
+ |
+/** |
+ * Run specified test on a fresh instance of the test suite. |
+ * @param {string} name Name of a test method from TestSuite class. |
+ */ |
+uiTests.runTest = function(name) { |
+ new TestSuite().runTest(name); |
+}; |
+ |
+ |
+} |
Property changes on: chrome/resources/inspector/tests.js |
___________________________________________________________________ |
Added: svn:executable |
+ * |