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 var ticksCount = 0; |
| 332 var tickRecord = '\nt,'; |
| 333 this.addSniffer(RemoteDebuggerAgent, 'DidGetNextLogLines', |
| 334 function(log) { |
| 335 var pos = 0; |
| 336 while ((pos = log.indexOf(tickRecord, pos)) != -1) { |
| 337 pos += tickRecord.length; |
| 338 ticksCount++; |
| 339 } |
| 340 if (ticksCount > 100) { |
| 341 InspectorController.stopProfiling(); |
| 342 } |
| 343 }, true); |
| 344 |
| 345 InspectorController.startProfiling(); |
| 346 this.takeControl(); |
| 347 }; |
| 348 |
| 349 |
| 350 /** |
| 351 * Tests that scripts tab can be open and populated with inspected scripts. |
| 352 */ |
| 353 TestSuite.prototype.testShowScriptsTab = function() { |
| 354 var parsedDebuggerTestPageHtml = false; |
| 355 |
| 356 // Intercept parsedScriptSource calls to check that all expected scripts are |
| 357 // added to the debugger. |
| 358 var test = this; |
| 359 var receivedConsoleApiSource = false; |
| 360 this.addSniffer(WebInspector, 'parsedScriptSource', |
| 361 function(sourceID, sourceURL, source, startingLine) { |
| 362 if (sourceURL == undefined) { |
| 363 if (receivedConsoleApiSource) { |
| 364 test.fail('Unexpected script without URL'); |
| 365 } else { |
| 366 receivedConsoleApiSource = true; |
| 367 } |
| 368 } else if (sourceURL.search(/debugger_test_page.html$/) != -1) { |
| 369 if (parsedDebuggerTestPageHtml) { |
| 370 test.fail('Unexpected parse event: ' + sourceURL); |
| 371 } |
| 372 parsedDebuggerTestPageHtml = true; |
| 373 } else { |
| 374 test.fail('Unexpected script URL: ' + sourceURL); |
| 375 } |
| 376 |
| 377 if (!WebInspector.panels.scripts.visibleView) { |
| 378 test.fail('No visible script view: ' + sourceURL); |
| 379 } |
| 380 |
| 381 // There should be two scripts: one for the main page and another |
| 382 // one which is source of console API(see |
| 383 // InjectedScript._ensureCommandLineAPIInstalled). |
| 384 if (parsedDebuggerTestPageHtml && receivedConsoleApiSource) { |
| 385 test.releaseControl(); |
| 386 } |
| 387 }, true /* sticky */); |
| 388 |
| 389 this.showPanel('scripts'); |
| 390 |
| 391 // Wait until all scripts are added to the debugger. |
| 392 this.takeControl(); |
| 393 }; |
| 394 |
| 395 |
| 396 /** |
| 397 * Tests that a breakpoint can be set. |
| 398 */ |
| 399 TestSuite.prototype.testSetBreakpoint = function() { |
| 400 var parsedDebuggerTestPageHtml = false; |
| 401 var parsedDebuggerTestJs = false; |
| 402 |
| 403 this.showPanel('scripts'); |
| 404 |
| 405 var scriptUrl = null; |
| 406 var breakpointLine = 12; |
| 407 |
| 408 var test = this; |
| 409 var orig = devtools.DebuggerAgent.prototype.handleScriptsResponse_; |
| 410 this.addSniffer(devtools.DebuggerAgent.prototype, 'handleScriptsResponse_', |
| 411 function(msg) { |
| 412 var scriptSelect = document.getElementById('scripts-files'); |
| 413 var options = scriptSelect.options; |
| 414 |
| 415 // There should be console API source (see |
| 416 // InjectedScript._ensureCommandLineAPIInstalled) and the page script. |
| 417 test.assertEquals(2, options.length, 'Unexpected number of scripts.'); |
| 418 |
| 419 // Select page's script if it's not current option. |
| 420 var scriptResource; |
| 421 if (options[scriptSelect.selectedIndex].text == |
| 422 'debugger_test_page.html') { |
| 423 scriptResource = |
| 424 options[scriptSelect.selectedIndex].representedObject; |
| 425 } else { |
| 426 var pageScriptIndex = (1 - scriptSelect.selectedIndex); |
| 427 test.assertEquals('debugger_test_page.html', |
| 428 options[pageScriptIndex].text); |
| 429 scriptResource = options[pageScriptIndex].representedObject; |
| 430 // Current panel is 'Scripts'. |
| 431 WebInspector.currentPanel._showScriptOrResource(scriptResource); |
| 432 } |
| 433 |
| 434 test.assertTrue(scriptResource instanceof WebInspector.Resource, |
| 435 'Unexpected resource class.'); |
| 436 test.assertTrue(!!scriptResource.url, 'Resource URL is null.'); |
| 437 test.assertTrue( |
| 438 scriptResource.url.search(/debugger_test_page.html$/) != -1, |
| 439 'Main HTML resource should be selected.'); |
| 440 |
| 441 // Store for access from setbreakpoint handler. |
| 442 scriptUrl = scriptResource.url; |
| 443 |
| 444 var scriptsPanel = WebInspector.panels.scripts; |
| 445 |
| 446 var view = scriptsPanel.visibleView; |
| 447 test.assertTrue(view instanceof WebInspector.SourceView); |
| 448 |
| 449 if (!view.sourceFrame._isContentLoaded()) { |
| 450 test.addSniffer(view, '_sourceFrameSetupFinished', function(event) { |
| 451 view._addBreakpoint(breakpointLine); |
| 452 // Force v8 execution. |
| 453 RemoteToolsAgent.ExecuteVoidJavaScript(); |
| 454 }); |
| 455 } else { |
| 456 view._addBreakpoint(breakpointLine); |
| 457 // Force v8 execution. |
| 458 RemoteToolsAgent.ExecuteVoidJavaScript(); |
| 459 } |
| 460 }); |
| 461 |
| 462 this.addSniffer( |
| 463 devtools.DebuggerAgent.prototype, |
| 464 'handleSetBreakpointResponse_', |
| 465 function(msg) { |
| 466 var bps = this.urlToBreakpoints_[scriptUrl]; |
| 467 test.assertTrue(!!bps, 'No breakpoints for line ' + breakpointLine); |
| 468 var line = devtools.DebuggerAgent.webkitToV8LineNumber_(breakpointLine); |
| 469 test.assertTrue(!!bps[line].getV8Id(), |
| 470 'Breakpoint id was not assigned.'); |
| 471 test.releaseControl(); |
| 472 }); |
| 473 |
| 474 this.takeControl(); |
| 475 }; |
| 476 |
| 477 |
| 478 /** |
| 479 * Tests 'Pause' button will pause debugger when a snippet is evaluated. |
| 480 */ |
| 481 TestSuite.prototype.testPauseInEval = function() { |
| 482 this.showPanel('scripts'); |
| 483 |
| 484 var test = this; |
| 485 |
| 486 var pauseButton = document.getElementById('scripts-pause'); |
| 487 pauseButton.click(); |
| 488 |
| 489 devtools.tools.evaluateJavaScript('fib(10)'); |
| 490 |
| 491 this.addSniffer(WebInspector, 'pausedScript', |
| 492 function() { |
| 493 test.releaseControl(); |
| 494 }); |
| 495 |
| 496 test.takeControl(); |
| 497 }; |
| 498 |
| 499 |
| 500 /** |
| 501 * Key event with given key identifier. |
| 502 */ |
| 503 TestSuite.KeyEvent = function(key) { |
| 504 this.keyIdentifier = key; |
| 505 }; |
| 506 TestSuite.KeyEvent.prototype.preventDefault = function() {}; |
| 507 TestSuite.KeyEvent.prototype.stopPropagation = function() {}; |
| 508 |
| 509 |
| 510 /** |
| 511 * Tests console eval. |
| 512 */ |
| 513 TestSuite.prototype.testConsoleEval = function() { |
| 514 WebInspector.console.visible = true; |
| 515 WebInspector.console.prompt.text = '123'; |
| 516 WebInspector.console.promptElement.handleKeyEvent( |
| 517 new TestSuite.KeyEvent('Enter')); |
| 518 |
| 519 var test = this; |
| 520 this.addSniffer(WebInspector.ConsoleView.prototype, 'addMessage', |
| 521 function(commandResult) { |
| 522 test.assertEquals('123', commandResult.toMessageElement().textContent); |
| 523 test.releaseControl(); |
| 524 }); |
| 525 |
| 526 this.takeControl(); |
| 527 }; |
| 528 |
| 529 |
| 530 /** |
| 531 * Tests console log. |
| 532 */ |
| 533 TestSuite.prototype.testConsoleLog = function() { |
| 534 WebInspector.console.visible = true; |
| 535 var messages = WebInspector.console.messages; |
| 536 var index = 0; |
| 537 |
| 538 var test = this; |
| 539 var assertNext = function(line, message, opt_class, opt_count, opt_substr) { |
| 540 var elem = messages[index++].toMessageElement(); |
| 541 var clazz = elem.getAttribute('class'); |
| 542 var expectation = (opt_count || '') + 'console_test_page.html:' + |
| 543 line + message; |
| 544 if (opt_substr) { |
| 545 test.assertContains(elem.textContent, expectation); |
| 546 } else { |
| 547 test.assertEquals(expectation, elem.textContent); |
| 548 } |
| 549 if (opt_class) { |
| 550 test.assertContains(clazz, 'console-' + opt_class); |
| 551 } |
| 552 }; |
| 553 |
| 554 assertNext('5', 'log', 'log-level'); |
| 555 assertNext('7', 'debug', 'log-level'); |
| 556 assertNext('9', 'info', 'log-level'); |
| 557 assertNext('11', 'warn', 'warning-level'); |
| 558 assertNext('13', 'error', 'error-level'); |
| 559 assertNext('15', 'Message format number 1, 2 and 3.5'); |
| 560 assertNext('17', 'Message format for string'); |
| 561 assertNext('19', 'Object Object'); |
| 562 assertNext('22', 'repeated', 'log-level', 5); |
| 563 assertNext('26', 'count: 1'); |
| 564 assertNext('26', 'count: 2'); |
| 565 assertNext('29', 'group', 'group-title'); |
| 566 index++; |
| 567 assertNext('33', 'timer:', 'log-level', '', true); |
| 568 }; |
| 569 |
| 570 |
| 571 /** |
| 572 * Tests eval of global objects. |
| 573 */ |
| 574 TestSuite.prototype.testEvalGlobal = function() { |
| 575 WebInspector.console.visible = true; |
| 576 WebInspector.console.prompt.text = 'foo'; |
| 577 WebInspector.console.promptElement.handleKeyEvent( |
| 578 new TestSuite.KeyEvent('Enter')); |
| 579 |
| 580 var test = this; |
| 581 this.addSniffer(WebInspector.ConsoleView.prototype, 'addMessage', |
| 582 function(commandResult) { |
| 583 test.assertEquals('fooValue', |
| 584 commandResult.toMessageElement().textContent); |
| 585 test.releaseControl(); |
| 586 }); |
| 587 |
| 588 this.takeControl(); |
| 589 }; |
| 590 |
| 591 |
| 592 /** |
| 593 * Tests eval on call frame. |
| 594 */ |
| 595 TestSuite.prototype.testEvalCallFrame = function() { |
| 596 }; |
| 597 |
| 598 |
| 599 /** |
| 600 * Test runner for the test suite. |
| 601 */ |
| 602 var uiTests = {}; |
| 603 |
| 604 |
| 605 /** |
| 606 * Run each test from the test suit on a fresh instance of the suite. |
| 607 */ |
| 608 uiTests.runAllTests = function() { |
| 609 // For debugging purposes. |
| 610 for (var name in TestSuite.prototype) { |
| 611 if (name.substring(0, 4) == 'test' && |
| 612 typeof TestSuite.prototype[name] == 'function') { |
| 613 uiTests.runTest(name); |
| 614 } |
| 615 } |
| 616 }; |
| 617 |
| 618 |
| 619 /** |
| 620 * Run specified test on a fresh instance of the test suite. |
| 621 * @param {string} name Name of a test method from TestSuite class. |
| 622 */ |
| 623 uiTests.runTest = function(name) { |
| 624 new TestSuite().runTest(name); |
| 625 }; |
| 626 |
| 627 |
| 628 } |
OLD | NEW |