# lib/webdriver/testing/testcase.js

    1// Copyright 2011 Software Freedom Conservancy. All Rights Reserved.2//3// Licensed under the Apache License, Version 2.0 (the "License");4// you may not use this file except in compliance with the License.5// You may obtain a copy of the License at6//7//      http://www.apache.org/licenses/LICENSE-2.08//9// Unless required by applicable law or agreed to in writing, software10// distributed under the License is distributed on an "AS IS" BASIS,11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.12// See the License for the specific language governing permissions and13// limitations under the License.1415/**16 * @fileoverview Defines a special test case that runs each test inside of a17 * {@code webdriver.Application}. This allows each phase to schedule18 * asynchronous actions that run to completion before the next phase of the19 * test.20 *21 * This file requires the global {@code G_testRunner} to be initialized before22 * use. This can be accomplished by also importing23 * {@link webdriver.testing.jsunit}. This namespace is not required by default24 * to improve interoperability with other namespaces that may initialize25 * G_testRunner.26 */2728goog.provide('webdriver.testing.TestCase');2930goog.require('goog.testing.TestCase');31goog.require('webdriver.promise.ControlFlow');32/** @suppress {extraRequire} Imported for user convenience. */33goog.require('webdriver.testing.asserts');34353637/**38 * Constructs a test case that synchronizes each test case with the singleton39 * {@code webdriver.promise.ControlFlow}.40 *41 * @param {!webdriver.testing.Client} client The test client to use for42 *     reporting test results.43 * @param {string=} opt_name The name of the test case, defaults to44 *     'Untitled Test Case'.45 * @constructor46 * @extends {goog.testing.TestCase}47 */48webdriver.testing.TestCase = function(client, opt_name) {49  goog.base(this, opt_name);5051  /** @private {!webdriver.testing.Client} */52  this.client_ = client;53};54goog.inherits(webdriver.testing.TestCase, goog.testing.TestCase);555657/**58 * Executes the next test inside its own {@code webdriver.Application}.59 * @override60 */61webdriver.testing.TestCase.prototype.cycleTests = function() {62  var test = this.next();63  if (!test) {64    this.finalize();65    return;66  }6768  goog.testing.TestCase.currentTestName = test.name;69  this.result_.runCount++;70  this.log('Running test: ' + test.name);71  this.client_.sendTestStartedEvent(test.name);7273  var self = this;74  var hadError = false;75  var app = webdriver.promise.controlFlow();7677  this.runSingleTest_(test, onError).then(function() {78    hadError || self.doSuccess(test);79    self.timeout(function() {80      self.cycleTests();81    }, 100);82  });8384  function onError(e) {85    hadError = true;86    self.doError(test, app.annotateError(e));87    // Note: result_ is a @protected field but still uses the trailing88    // underscore.89    var err = self.result_.errors[self.result_.errors.length - 1];90    self.client_.sendErrorEvent(err.toString());91  }92};939495/** @override */96webdriver.testing.TestCase.prototype.logError = function(name, opt_e) {97  var errMsg = null;98  var stack = null;99  if (opt_e) {100    this.log(opt_e);101    if (goog.isString(opt_e)) {102      errMsg = opt_e;103    } else {104      // In case someone calls this function directly, make sure we have a105      // properly annotated error.106      webdriver.promise.controlFlow().annotateError(opt_e);107      errMsg = opt_e.toString();108      stack = opt_e.stack.substring(errMsg.length + 1);109    }110  } else {111    errMsg = 'An unknown error occurred';112  }113  var err = new goog.testing.TestCase.Error(name, errMsg, stack);114115  // Avoid double logging.116  if (!opt_e || !opt_e['isJsUnitException'] ||117      !opt_e['loggedJsUnitException']) {118    this.saveMessage(err.toString());119  }120121  if (opt_e && opt_e['isJsUnitException']) {122    opt_e['loggedJsUnitException'] = true;123  }124125  return err;126};127128129/**130 * Executes a single test, scheduling each phase with the global application.131 * Each phase will wait for the application to go idle before moving on to the132 * next test phase.  This function models the follow basic test flow:133 *134 *   try {135 *     this.setUp.call(test.scope);136 *     test.ref.call(test.scope);137 *   } catch (ex) {138 *     onError(ex);139 *   } finally {140 *     try {141 *       this.tearDown.call(test.scope);142 *     } catch (e) {143 *       onError(e);144 *     }145 *   }146 *147 * @param {!goog.testing.TestCase.Test} test The test to run.148 * @param {function(*)} onError The function to call each time an error is149 *     detected.150 * @return {!webdriver.promise.Promise} A promise that will be resolved when the151 *     test has finished running.152 * @private153 */154webdriver.testing.TestCase.prototype.runSingleTest_ = function(test, onError) {155  var flow = webdriver.promise.controlFlow();156  flow.clearHistory();157158  return execute(test.name + '.setUp()', this.setUp)().159      then(execute(test.name + '()', test.ref)).160      thenCatch(onError).161      then(execute(test.name + '.tearDown()', this.tearDown)).162      thenCatch(onError);163164  function execute(description, fn) {165    return function() {166      return flow.execute(goog.bind(fn, test.scope), description);167    }168  }169};