OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 |
| 6 /** |
| 7 * @fileoverview This file contains small testing framework along with the |
| 8 * test suite for the frontend. These tests are a part of the continues build |
| 9 * and are executed by the devtools_sanity_unittest.cc as a part of the |
| 10 * Interactive UI Test suite. |
| 11 */ |
| 12 |
| 13 if (window.domAutomationController) { |
| 14 |
| 15 var ___interactiveUiTestsMode = true; |
| 16 |
| 17 /** |
| 18 * Test suite for interactive UI tests. |
| 19 * @constructor |
| 20 */ |
| 21 TestSuite = function() { |
| 22 this.controlTaken_ = false; |
| 23 this.timerId_ = -1; |
| 24 }; |
| 25 |
| 26 |
| 27 /** |
| 28 * Reports test failure. |
| 29 * @param {string} message Failure description. |
| 30 */ |
| 31 TestSuite.prototype.fail = function(message) { |
| 32 if (this.controlTaken_) { |
| 33 this.reportFailure_(message); |
| 34 } else { |
| 35 throw message; |
| 36 } |
| 37 }; |
| 38 |
| 39 |
| 40 /** |
| 41 * Equals assertion tests that expected == actual. |
| 42 * @param {Object} expected Expected object. |
| 43 * @param {Object} actual Actual object. |
| 44 * @param {string} opt_message User message to print if the test fails. |
| 45 */ |
| 46 TestSuite.prototype.assertEquals = function(expected, actual, opt_message) { |
| 47 if (expected != actual) { |
| 48 var message = 'Expected: "' + expected + '", but was "' + actual + '"'; |
| 49 if (opt_message) { |
| 50 message = opt_message + '(' + message + ')'; |
| 51 } |
| 52 this.fail(message); |
| 53 } |
| 54 }; |
| 55 |
| 56 |
| 57 /** |
| 58 * True assertion tests that value == true. |
| 59 * @param {Object} value Actual object. |
| 60 * @param {string} opt_message User message to print if the test fails. |
| 61 */ |
| 62 TestSuite.prototype.assertTrue = function(value, opt_message) { |
| 63 this.assertEquals(true, value, opt_message); |
| 64 }; |
| 65 |
| 66 |
| 67 /** |
| 68 * Contains assertion tests that string contains substring. |
| 69 * @param {string} string Outer. |
| 70 * @param {string} substring Inner. |
| 71 */ |
| 72 TestSuite.prototype.assertContains = function(string, substring) { |
| 73 if (string.indexOf(substring) == -1) { |
| 74 this.fail('Expected to: "' + string + '" to contain "' + substring + '"'); |
| 75 } |
| 76 }; |
| 77 |
| 78 |
| 79 /** |
| 80 * Takes control over execution. |
| 81 */ |
| 82 TestSuite.prototype.takeControl = function() { |
| 83 this.controlTaken_ = true; |
| 84 // Set up guard timer. |
| 85 var self = this; |
| 86 this.timerId_ = setTimeout(function() { |
| 87 self.reportFailure_('Timeout exceeded: 20 sec'); |
| 88 }, 20000); |
| 89 }; |
| 90 |
| 91 |
| 92 /** |
| 93 * Releases control over execution. |
| 94 */ |
| 95 TestSuite.prototype.releaseControl = function() { |
| 96 if (this.timerId_ != -1) { |
| 97 clearTimeout(this.timerId_); |
| 98 this.timerId_ = -1; |
| 99 } |
| 100 this.reportOk_(); |
| 101 }; |
| 102 |
| 103 |
| 104 /** |
| 105 * Async tests use this one to report that they are completed. |
| 106 */ |
| 107 TestSuite.prototype.reportOk_ = function() { |
| 108 window.domAutomationController.send('[OK]'); |
| 109 }; |
| 110 |
| 111 |
| 112 /** |
| 113 * Async tests use this one to report failures. |
| 114 */ |
| 115 TestSuite.prototype.reportFailure_ = function(error) { |
| 116 if (this.timerId_ != -1) { |
| 117 clearTimeout(this.timerId_); |
| 118 this.timerId_ = -1; |
| 119 } |
| 120 window.domAutomationController.send('[FAILED] ' + error); |
| 121 }; |
| 122 |
| 123 |
| 124 /** |
| 125 * Runs all global functions starting with 'test' as unit tests. |
| 126 */ |
| 127 TestSuite.prototype.runTest = function(testName) { |
| 128 try { |
| 129 this[testName](); |
| 130 if (!this.controlTaken_) { |
| 131 this.reportOk_(); |
| 132 } |
| 133 } catch (e) { |
| 134 this.reportFailure_(e); |
| 135 } |
| 136 }; |
| 137 |
| 138 |
| 139 /** |
| 140 * @param {string} panelName Name of the panel to show. |
| 141 */ |
| 142 TestSuite.prototype.showPanel = function(panelName) { |
| 143 // Open Scripts panel. |
| 144 var toolbar = document.getElementById('toolbar'); |
| 145 var button = toolbar.getElementsByClassName(panelName)[0]; |
| 146 button.click(); |
| 147 this.assertEquals(WebInspector.panels[panelName], |
| 148 WebInspector.currentPanel); |
| 149 }; |
| 150 |
| 151 |
| 152 /** |
| 153 * Overrides the method with specified name until it's called first time. |
| 154 * @param {Object} receiver An object whose method to override. |
| 155 * @param {string} methodName Name of the method to override. |
| 156 * @param {Function} override A function that should be called right after the |
| 157 * overriden method returns. |
| 158 * @param {boolean} opt_sticky Whether restore original method after first run |
| 159 * or not. |
| 160 */ |
| 161 TestSuite.prototype.addSniffer = function(receiver, methodName, override, |
| 162 opt_sticky) { |
| 163 var orig = receiver[methodName]; |
| 164 if (typeof orig != 'function') { |
| 165 this.fail('Cannot find method to override: ' + methodName); |
| 166 } |
| 167 var test = this; |
| 168 receiver[methodName] = function(var_args) { |
| 169 try { |
| 170 var result = orig.apply(this, arguments); |
| 171 } finally { |
| 172 if (!opt_sticky) { |
| 173 receiver[methodName] = orig; |
| 174 } |
| 175 } |
| 176 // In case of exception the override won't be called. |
| 177 try { |
| 178 override.apply(this, arguments); |
| 179 } catch (e) { |
| 180 test.fail('Exception in overriden method "' + methodName + '": ' + e); |
| 181 } |
| 182 return result; |
| 183 }; |
| 184 }; |
| 185 |
| 186 |
| 187 // UI Tests |
| 188 |
| 189 |
| 190 /** |
| 191 * Tests that the real injected host is present in the context. |
| 192 */ |
| 193 TestSuite.prototype.testHostIsPresent = function() { |
| 194 this.assertTrue(typeof DevToolsHost == 'object' && !DevToolsHost.isStub); |
| 195 }; |
| 196 |
| 197 |
| 198 /** |
| 199 * Tests elements tree has an 'HTML' root. |
| 200 */ |
| 201 TestSuite.prototype.testElementsTreeRoot = function() { |
| 202 var doc = WebInspector.domAgent.document; |
| 203 this.assertEquals('HTML', doc.documentElement.nodeName); |
| 204 this.assertTrue(doc.documentElement.hasChildNodes()); |
| 205 }; |
| 206 |
| 207 |
| 208 /** |
| 209 * Tests that main resource is present in the system and that it is |
| 210 * the only resource. |
| 211 */ |
| 212 TestSuite.prototype.testMainResource = function() { |
| 213 var tokens = []; |
| 214 var resources = WebInspector.resources; |
| 215 for (var id in resources) { |
| 216 tokens.push(resources[id].lastPathComponent); |
| 217 } |
| 218 this.assertEquals('simple_page.html', tokens.join(',')); |
| 219 }; |
| 220 |
| 221 |
| 222 /** |
| 223 * Tests that resources tab is enabled when corresponding item is selected. |
| 224 */ |
| 225 TestSuite.prototype.testEnableResourcesTab = function() { |
| 226 this.showPanel('resources'); |
| 227 |
| 228 var test = this; |
| 229 this.addSniffer(WebInspector, 'addResource', |
| 230 function(identifier, payload) { |
| 231 test.assertEquals('simple_page.html', payload.lastPathComponent); |
| 232 WebInspector.panels.resources.refresh(); |
| 233 WebInspector.resources[identifier]._resourcesTreeElement.select(); |
| 234 |
| 235 test.releaseControl(); |
| 236 }); |
| 237 |
| 238 // Following call should lead to reload that we capture in the |
| 239 // addResource override. |
| 240 WebInspector.panels.resources._enableResourceTracking(); |
| 241 |
| 242 // We now have some time to report results to controller. |
| 243 this.takeControl(); |
| 244 }; |
| 245 |
| 246 |
| 247 /** |
| 248 * Tests resource headers. |
| 249 */ |
| 250 TestSuite.prototype.testResourceHeaders = function() { |
| 251 this.showPanel('resources'); |
| 252 |
| 253 var test = this; |
| 254 |
| 255 var requestOk = false; |
| 256 var responseOk = false; |
| 257 var timingOk = false; |
| 258 |
| 259 this.addSniffer(WebInspector, 'addResource', |
| 260 function(identifier, payload) { |
| 261 var resource = this.resources[identifier]; |
| 262 if (resource.mainResource) { |
| 263 // We are only interested in secondary resources in this test. |
| 264 return; |
| 265 } |
| 266 |
| 267 var requestHeaders = JSON.stringify(resource.requestHeaders); |
| 268 test.assertContains(requestHeaders, 'Accept'); |
| 269 requestOk = true; |
| 270 }, true); |
| 271 |
| 272 this.addSniffer(WebInspector, 'updateResource', |
| 273 function(identifier, payload) { |
| 274 var resource = this.resources[identifier]; |
| 275 if (resource.mainResource) { |
| 276 // We are only interested in secondary resources in this test. |
| 277 return; |
| 278 } |
| 279 |
| 280 if (payload.didResponseChange) { |
| 281 var responseHeaders = JSON.stringify(resource.responseHeaders); |
| 282 test.assertContains(responseHeaders, 'Content-type'); |
| 283 test.assertContains(responseHeaders, 'Content-Length'); |
| 284 test.assertTrue(typeof resource.responseReceivedTime != 'undefnied'); |
| 285 responseOk = true; |
| 286 } |
| 287 |
| 288 if (payload.didTimingChange) { |
| 289 test.assertTrue(typeof resource.startTime != 'undefnied'); |
| 290 timingOk = true; |
| 291 } |
| 292 |
| 293 if (payload.didCompletionChange) { |
| 294 test.assertTrue(requestOk); |
| 295 test.assertTrue(responseOk); |
| 296 test.assertTrue(timingOk); |
| 297 test.assertTrue(typeof resource.endTime != 'undefnied'); |
| 298 test.releaseControl(); |
| 299 } |
| 300 }, true); |
| 301 |
| 302 WebInspector.panels.resources._enableResourceTracking(); |
| 303 this.takeControl(); |
| 304 }; |
| 305 |
| 306 |
| 307 /** |
| 308 * Test that profiler works. |
| 309 */ |
| 310 TestSuite.prototype.testProfilerTab = function() { |
| 311 this.showPanel('profiles'); |
| 312 |
| 313 var test = this; |
| 314 this.addSniffer(WebInspector, 'addProfile', |
| 315 function(profile) { |
| 316 var panel = WebInspector.panels.profiles; |
| 317 panel.showProfile(profile); |
| 318 var node = panel.visibleView.profileDataGridTree.children[0]; |
| 319 // Iterate over displayed functions and search for a function |
| 320 // that is called 'fib' or 'eternal_fib'. If found, it will mean |
| 321 // that we actually have profiled page's code. |
| 322 while (node) { |
| 323 if (node.functionName.indexOf("fib") != -1) { |
| 324 test.releaseControl(); |
| 325 } |
| 326 node = node.traverseNextNode(true, null, true); |
| 327 } |
| 328 |
| 329 test.fail(); |
| 330 }); |
| 331 |
| 332 InspectorController.startProfiling(); |
| 333 window.setTimeout('InspectorController.stopProfiling();', 1000); |
| 334 this.takeControl(); |
| 335 }; |
| 336 |
| 337 |
| 338 /** |
| 339 * Tests that scripts tab can be open and populated with inspected scripts. |
| 340 */ |
| 341 TestSuite.prototype.testShowScriptsTab = function() { |
| 342 var parsedDebuggerTestPageHtml = false; |
| 343 |
| 344 // Intercept parsedScriptSource calls to check that all expected scripts are |
| 345 // added to the debugger. |
| 346 var test = this; |
| 347 this.addSniffer(WebInspector, 'parsedScriptSource', |
| 348 function(sourceID, sourceURL, source, startingLine) { |
| 349 if (sourceURL.search(/debugger_test_page.html$/) != -1) { |
| 350 if (parsedDebuggerTestPageHtml) { |
| 351 test.fail('Unexpected parse event: ' + sourceURL); |
| 352 } |
| 353 parsedDebuggerTestPageHtml = true; |
| 354 } else { |
| 355 test.fail('Unexpected script URL: ' + sourceURL); |
| 356 } |
| 357 |
| 358 if (!WebInspector.panels.scripts.visibleView) { |
| 359 test.fail('No visible script view: ' + sourceURL); |
| 360 } |
| 361 |
| 362 if (parsedDebuggerTestPageHtml) { |
| 363 test.releaseControl(); |
| 364 } |
| 365 }, true /* sticky */); |
| 366 |
| 367 this.showPanel('scripts'); |
| 368 |
| 369 // Wait until all scripts are added to the debugger. |
| 370 this.takeControl(); |
| 371 }; |
| 372 |
| 373 |
| 374 /** |
| 375 * Tests that a breakpoint can be set. |
| 376 */ |
| 377 TestSuite.prototype.testSetBreakpoint = function() { |
| 378 var parsedDebuggerTestPageHtml = false; |
| 379 var parsedDebuggerTestJs = false; |
| 380 |
| 381 this.showPanel('scripts'); |
| 382 |
| 383 var scriptUrl = null; |
| 384 var breakpointLine = 12; |
| 385 |
| 386 var test = this; |
| 387 var orig = devtools.DebuggerAgent.prototype.handleScriptsResponse_; |
| 388 this.addSniffer(devtools.DebuggerAgent.prototype, 'handleScriptsResponse_', |
| 389 function(msg) { |
| 390 var scriptSelect = document.getElementById('scripts-files'); |
| 391 var scriptResource = |
| 392 scriptSelect.options[scriptSelect.selectedIndex].representedObject; |
| 393 |
| 394 test.assertTrue(scriptResource instanceof WebInspector.Resource); |
| 395 test.assertTrue(!!scriptResource.url); |
| 396 test.assertTrue( |
| 397 scriptResource.url.search(/debugger_test_page.html$/) != -1, |
| 398 'Main HTML resource should be selected.'); |
| 399 |
| 400 // Store for access from setbreakpoint handler. |
| 401 scriptUrl = scriptResource.url; |
| 402 |
| 403 var scriptsPanel = WebInspector.panels.scripts; |
| 404 |
| 405 var view = scriptsPanel.visibleView; |
| 406 test.assertTrue(view instanceof WebInspector.SourceView); |
| 407 |
| 408 if (!view.sourceFrame._isContentLoaded()) { |
| 409 test.addSniffer(view, '_sourceFrameSetupFinished', function(event) { |
| 410 view._addBreakpoint(breakpointLine); |
| 411 // Force v8 execution. |
| 412 devtools.tools.evaluateJavaScript('javascript:void(0)'); |
| 413 }); |
| 414 } else { |
| 415 view._addBreakpoint(breakpointLine); |
| 416 // Force v8 execution. |
| 417 devtools.tools.evaluateJavaScript('javascript:void(0)'); |
| 418 } |
| 419 }); |
| 420 |
| 421 this.addSniffer( |
| 422 devtools.DebuggerAgent.prototype, |
| 423 'handleSetBreakpointResponse_', |
| 424 function(msg) { |
| 425 var bps = this.urlToBreakpoints_[scriptUrl]; |
| 426 test.assertTrue(!!bps, 'No breakpoints for line ' + breakpointLine); |
| 427 var line = devtools.DebuggerAgent.webkitToV8LineNumber_(breakpointLine); |
| 428 test.assertTrue(!!bps[line].getV8Id(), |
| 429 'Breakpoint id was not assigned.'); |
| 430 test.releaseControl(); |
| 431 }); |
| 432 |
| 433 this.takeControl(); |
| 434 }; |
| 435 |
| 436 |
| 437 /** |
| 438 * Tests 'Pause' button will pause debugger when a snippet is evaluated. |
| 439 */ |
| 440 TestSuite.prototype.testPauseInEval = function() { |
| 441 this.showPanel('scripts'); |
| 442 |
| 443 var test = this; |
| 444 |
| 445 var pauseButton = document.getElementById('scripts-pause'); |
| 446 pauseButton.click(); |
| 447 |
| 448 devtools.tools.evaluateJavaScript('fib(10)'); |
| 449 |
| 450 this.addSniffer(WebInspector, 'pausedScript', |
| 451 function() { |
| 452 test.releaseControl(); |
| 453 }); |
| 454 |
| 455 test.takeControl(); |
| 456 }; |
| 457 |
| 458 |
| 459 /** |
| 460 * Key event with given key identifier. |
| 461 */ |
| 462 TestSuite.KeyEvent = function(key) { |
| 463 this.keyIdentifier = key; |
| 464 }; |
| 465 TestSuite.KeyEvent.prototype.preventDefault = function() {}; |
| 466 TestSuite.KeyEvent.prototype.stopPropagation = function() {}; |
| 467 |
| 468 |
| 469 /** |
| 470 * Tests console eval. |
| 471 */ |
| 472 TestSuite.prototype.testConsoleEval = function() { |
| 473 WebInspector.console.visible = true; |
| 474 WebInspector.console.prompt.text = '123'; |
| 475 WebInspector.console.promptElement.handleKeyEvent( |
| 476 new TestSuite.KeyEvent('Enter')); |
| 477 |
| 478 var test = this; |
| 479 this.addSniffer(WebInspector.ConsoleView.prototype, 'addMessage', |
| 480 function(commandResult) { |
| 481 test.assertEquals('123', commandResult.toMessageElement().textContent); |
| 482 test.releaseControl(); |
| 483 }); |
| 484 |
| 485 this.takeControl(); |
| 486 }; |
| 487 |
| 488 |
| 489 /** |
| 490 * Tests console log. |
| 491 */ |
| 492 TestSuite.prototype.testConsoleLog = function() { |
| 493 WebInspector.console.visible = true; |
| 494 var messages = WebInspector.console.messages; |
| 495 var index = 0; |
| 496 |
| 497 var test = this; |
| 498 var assertNext = function(line, message, opt_class, opt_count, opt_substr) { |
| 499 var elem = messages[index++].toMessageElement(); |
| 500 var clazz = elem.getAttribute('class'); |
| 501 var expectation = (opt_count || '') + 'console_test_page.html:' + |
| 502 line + message; |
| 503 if (opt_substr) { |
| 504 test.assertContains(elem.textContent, expectation); |
| 505 } else { |
| 506 test.assertEquals(expectation, elem.textContent); |
| 507 } |
| 508 if (opt_class) { |
| 509 test.assertContains(clazz, 'console-' + opt_class); |
| 510 } |
| 511 }; |
| 512 |
| 513 assertNext('5', 'log', 'log-level'); |
| 514 assertNext('7', 'debug', 'log-level'); |
| 515 assertNext('9', 'info', 'log-level'); |
| 516 assertNext('11', 'warn', 'warning-level'); |
| 517 assertNext('13', 'error', 'error-level'); |
| 518 assertNext('15', 'Message format number 1, 2 and 3.5'); |
| 519 assertNext('17', 'Message format for string'); |
| 520 assertNext('19', 'Object Object'); |
| 521 assertNext('22', 'repeated', 'log-level', 5); |
| 522 assertNext('26', 'count: 1'); |
| 523 assertNext('26', 'count: 2'); |
| 524 assertNext('29', 'group', 'group-title'); |
| 525 index++; |
| 526 assertNext('33', 'timer:', 'log-level', '', true); |
| 527 }; |
| 528 |
| 529 |
| 530 /** |
| 531 * Tests eval of global objects. |
| 532 */ |
| 533 TestSuite.prototype.testEvalGlobal = function() { |
| 534 WebInspector.console.visible = true; |
| 535 WebInspector.console.prompt.text = 'foo'; |
| 536 WebInspector.console.promptElement.handleKeyEvent( |
| 537 new TestSuite.KeyEvent('Enter')); |
| 538 |
| 539 var test = this; |
| 540 this.addSniffer(WebInspector.ConsoleView.prototype, 'addMessage', |
| 541 function(commandResult) { |
| 542 test.assertEquals('fooValue', |
| 543 commandResult.toMessageElement().textContent); |
| 544 test.releaseControl(); |
| 545 }); |
| 546 |
| 547 this.takeControl(); |
| 548 }; |
| 549 |
| 550 |
| 551 /** |
| 552 * Tests eval on call frame. |
| 553 */ |
| 554 TestSuite.prototype.testEvalCallFrame = function() { |
| 555 }; |
| 556 |
| 557 |
| 558 /** |
| 559 * Test runner for the test suite. |
| 560 */ |
| 561 var uiTests = {}; |
| 562 |
| 563 |
| 564 /** |
| 565 * Run each test from the test suit on a fresh instance of the suite. |
| 566 */ |
| 567 uiTests.runAllTests = function() { |
| 568 // For debugging purposes. |
| 569 for (var name in TestSuite.prototype) { |
| 570 if (name.substring(0, 4) == 'test' && |
| 571 typeof TestSuite.prototype[name] == 'function') { |
| 572 uiTests.runTest(name); |
| 573 } |
| 574 } |
| 575 }; |
| 576 |
| 577 |
| 578 /** |
| 579 * Run specified test on a fresh instance of the test suite. |
| 580 * @param {string} name Name of a test method from TestSuite class. |
| 581 */ |
| 582 uiTests.runTest = function(name) { |
| 583 new TestSuite().runTest(name); |
| 584 }; |
| 585 |
| 586 |
| 587 } |
OLD | NEW |