| // Copyright 2008 The Closure Library Authors. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS-IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| /** |
| * @fileoverview Helper class to allow for expected unit test failures. |
| * |
| * @author robbyw@google.com (Robby Walker) |
| */ |
| |
| goog.provide('goog.testing.ExpectedFailures'); |
| |
| goog.require('goog.debug.DivConsole'); |
| goog.require('goog.dom'); |
| goog.require('goog.dom.TagName'); |
| goog.require('goog.events'); |
| goog.require('goog.events.EventType'); |
| goog.require('goog.log'); |
| goog.require('goog.style'); |
| goog.require('goog.testing.JsUnitException'); |
| goog.require('goog.testing.TestCase'); |
| goog.require('goog.testing.asserts'); |
| |
| |
| |
| /** |
| * Helper class for allowing some unit tests to fail, particularly designed to |
| * mark tests that should be fixed on a given browser. |
| * |
| * <pre> |
| * var expectedFailures = new goog.testing.ExpectedFailures(); |
| * |
| * function tearDown() { |
| * expectedFailures.handleTearDown(); |
| * } |
| * |
| * function testSomethingThatBreaksInWebKit() { |
| * expectedFailures.expectFailureFor(goog.userAgent.WEBKIT); |
| * |
| * try { |
| * ... |
| * assert(somethingThatFailsInWebKit); |
| * ... |
| * } catch (e) { |
| * expectedFailures.handleException(e); |
| * } |
| * } |
| * </pre> |
| * |
| * @constructor |
| * @final |
| */ |
| goog.testing.ExpectedFailures = function() { |
| goog.testing.ExpectedFailures.setUpConsole_(); |
| this.reset_(); |
| }; |
| |
| |
| /** |
| * The lazily created debugging console. |
| * @type {goog.debug.DivConsole?} |
| * @private |
| */ |
| goog.testing.ExpectedFailures.console_ = null; |
| |
| |
| /** |
| * Logger for the expected failures. |
| * @type {goog.log.Logger} |
| * @private |
| */ |
| goog.testing.ExpectedFailures.prototype.logger_ = |
| goog.log.getLogger('goog.testing.ExpectedFailures'); |
| |
| |
| /** |
| * Whether or not we are expecting failure. |
| * @type {boolean} |
| * @private |
| */ |
| goog.testing.ExpectedFailures.prototype.expectingFailure_; |
| |
| |
| /** |
| * The string to emit upon an expected failure. |
| * @type {string} |
| * @private |
| */ |
| goog.testing.ExpectedFailures.prototype.failureMessage_; |
| |
| |
| /** |
| * An array of suppressed failures. |
| * @type {Array<!Error>} |
| * @private |
| */ |
| goog.testing.ExpectedFailures.prototype.suppressedFailures_; |
| |
| |
| /** |
| * Sets up the debug console, if it isn't already set up. |
| * @private |
| */ |
| goog.testing.ExpectedFailures.setUpConsole_ = function() { |
| if (!goog.testing.ExpectedFailures.console_) { |
| var xButton = goog.dom.createDom(goog.dom.TagName.DIV, { |
| 'style': 'position: absolute; border-left:1px solid #333;' + |
| 'border-bottom:1px solid #333; right: 0; top: 0; width: 1em;' + |
| 'height: 1em; cursor: pointer; background-color: #cde;' + |
| 'text-align: center; color: black' |
| }, 'X'); |
| var div = goog.dom.createDom(goog.dom.TagName.DIV, { |
| 'style': 'position: absolute; border: 1px solid #333; right: 10px;' + |
| 'top : 10px; width: 400px; display: none' |
| }, xButton); |
| document.body.appendChild(div); |
| goog.events.listen(xButton, goog.events.EventType.CLICK, function() { |
| goog.style.setElementShown(div, false); |
| }); |
| |
| goog.testing.ExpectedFailures.console_ = new goog.debug.DivConsole(div); |
| goog.log.addHandler(goog.testing.ExpectedFailures.prototype.logger_, |
| goog.bind(goog.style.setElementShown, null, div, true)); |
| goog.log.addHandler(goog.testing.ExpectedFailures.prototype.logger_, |
| goog.bind(goog.testing.ExpectedFailures.console_.addLogRecord, |
| goog.testing.ExpectedFailures.console_)); |
| } |
| }; |
| |
| |
| /** |
| * Register to expect failure for the given condition. Multiple calls to this |
| * function act as a boolean OR. The first applicable message will be used. |
| * @param {boolean} condition Whether to expect failure. |
| * @param {string=} opt_message Descriptive message of this expected failure. |
| */ |
| goog.testing.ExpectedFailures.prototype.expectFailureFor = function( |
| condition, opt_message) { |
| this.expectingFailure_ = this.expectingFailure_ || condition; |
| if (condition) { |
| this.failureMessage_ = this.failureMessage_ || opt_message || ''; |
| } |
| }; |
| |
| |
| /** |
| * Determines if the given exception was expected. |
| * @param {Object} ex The exception to check. |
| * @return {boolean} Whether the exception was expected. |
| */ |
| goog.testing.ExpectedFailures.prototype.isExceptionExpected = function(ex) { |
| return this.expectingFailure_ && ex instanceof goog.testing.JsUnitException; |
| }; |
| |
| |
| /** |
| * Handle an exception, suppressing it if it is a unit test failure that we |
| * expected. |
| * @param {Error} ex The exception to handle. |
| */ |
| goog.testing.ExpectedFailures.prototype.handleException = function(ex) { |
| if (this.isExceptionExpected(ex)) { |
| goog.log.info(this.logger_, 'Suppressing test failure in ' + |
| goog.testing.TestCase.currentTestName + ':' + |
| (this.failureMessage_ ? '\n(' + this.failureMessage_ + ')' : ''), |
| ex); |
| this.suppressedFailures_.push(ex); |
| return; |
| } |
| |
| // Rethrow the exception if we weren't expecting it or if it is a normal |
| // exception. |
| throw ex; |
| }; |
| |
| |
| /** |
| * Run the given function, catching any expected failures. |
| * @param {Function} func The function to run. |
| * @param {boolean=} opt_lenient Whether to ignore if the expected failures |
| * didn't occur. In this case a warning will be logged in handleTearDown. |
| */ |
| goog.testing.ExpectedFailures.prototype.run = function(func, opt_lenient) { |
| try { |
| func(); |
| } catch (ex) { |
| this.handleException(ex); |
| } |
| |
| if (!opt_lenient && this.expectingFailure_ && |
| !this.suppressedFailures_.length) { |
| fail(this.getExpectationMessage_()); |
| } |
| }; |
| |
| |
| /** |
| * @return {string} A warning describing an expected failure that didn't occur. |
| * @private |
| */ |
| goog.testing.ExpectedFailures.prototype.getExpectationMessage_ = function() { |
| return 'Expected a test failure in \'' + |
| goog.testing.TestCase.currentTestName + '\' but the test passed.'; |
| }; |
| |
| |
| /** |
| * Handle the tearDown phase of a test, alerting the user if an expected test |
| * was not suppressed. |
| */ |
| goog.testing.ExpectedFailures.prototype.handleTearDown = function() { |
| if (this.expectingFailure_ && !this.suppressedFailures_.length) { |
| goog.log.warning(this.logger_, this.getExpectationMessage_()); |
| } |
| this.reset_(); |
| }; |
| |
| |
| /** |
| * Reset internal state. |
| * @private |
| */ |
| goog.testing.ExpectedFailures.prototype.reset_ = function() { |
| this.expectingFailure_ = false; |
| this.failureMessage_ = ''; |
| this.suppressedFailures_ = []; |
| }; |