# testing/index.js

    1// Copyright 2013 Selenium committers2// Copyright 2013 Software Freedom Conservancy3//4// Licensed under the Apache License, Version 2.0 (the "License");5// you may not use this file except in compliance with the License.6//     You may obtain a copy of the License at7//8// http://www.apache.org/licenses/LICENSE-2.09//10// Unless required by applicable law or agreed to in writing, software11// distributed under the License is distributed on an "AS IS" BASIS,12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.13// See the License for the specific language governing permissions and14// limitations under the License.1516/**17 * @fileoverview Provides wrappers around the following global functions from18 * <a href="http://visionmedia.github.io/mocha/">Mocha's BDD interface</a>:19 * <ul>20 *   <li>after21 *   <li>afterEach22 *   <li>before23 *   <li>beforeEach24 *   <li>it25 *   <li>it.only26 *   <li>it.skip27 *   <li>xit28 * </ul>29 *30 * <p>The provided wrappers leverage the webdriver.promise.ControlFlow to31 * simplify writing asynchronous tests:32 * <pre><code>33 * var webdriver = require('selenium-webdriver'),34 *     remote = require('selenium-webdriver/remote'),35 *     test = require('selenium-webdriver/testing');36 *37 * test.describe('Google Search', function() {38 *   var driver, server;39 *40 *   test.before(function() {41 *     server = new remote.SeleniumServer({42 *       jar: 'path/to/selenium-server-standalone.jar'43 *     });44 *     server.start();45 *46 *     driver = new webdriver.Builder().47 *         withCapabilities({'browserName': 'firefox'}).48 *         usingServer(server.address()).49 *         build();50 *   });51 *52 *   test.after(function() {53 *     driver.quit();54 *     server.stop();55 *   });56 *57 *   test.it('should append query to title', function() {58 *     driver.get('http://www.google.com');59 *     driver.findElement(webdriver.By.name('q')).sendKeys('webdriver');60 *     driver.findElement(webdriver.By.name('btnG')).click();61 *     driver.wait(function() {62 *       return driver.getTitle().then(function(title) {63 *         return 'webdriver - Google Search' === title;64 *       });65 *     }, 1000, 'Waiting for title to update');66 *   });67 * });68 * </code></pre>69 *70 * <p>You may conditionally suppress a test function using the exported71 * "ignore" function. If the provided predicate returns true, the attached72 * test case will be skipped:73 * <pre><code>74 *   test.ignore(maybe()).it('is flaky', function() {75 *     if (Math.random() < 0.5) throw Error();76 *   });77 *78 *   function maybe() { return Math.random() < 0.5; }79 * </code></pre>80 */8182var flow = require('..').promise.controlFlow();838485/**86 * Wraps a function so that all passed arguments are ignored.87 * @param {!Function} fn The function to wrap.88 * @return {!Function} The wrapped function.89 */90function seal(fn) {91  return function() {92    fn();93  };94}959697/**98 * Wraps a function on Mocha's BDD interface so it runs inside a99 * webdriver.promise.ControlFlow and waits for the flow to complete before100 * continuing.101 * @param {!Function} globalFn The function to wrap.102 * @return {!Function} The new function.103 */104function wrapped(globalFn) {105  return function() {106    switch (arguments.length) {107      case 1:108        globalFn(asyncTestFn(arguments[0]));109        break;110111      case 2:112        globalFn(arguments[0], asyncTestFn(arguments[1]));113        break;114115      default:116        throw Error('Invalid # arguments: ' + arguments.length);117    }118  };119120  function asyncTestFn(fn) {121    return function(done) {122      this.timeout(0);123      flow.execute(fn).then(seal(done), done);124    };125  }126}127128129/**130 * Ignores the test chained to this function if the provided predicate returns131 * true.132 * @param {function(): boolean} predicateFn A predicate to call to determine133 *     if the test should be suppressed. This function MUST be synchronous.134 * @return {!Object} An object with wrapped versions of {@link #it()} and135 *     {@link #describe()} that ignore tests as indicated by the predicate.136 */137function ignore(predicateFn) {138  var describe = wrap(exports.xdescribe, exports.describe);139  describe.only = wrap(exports.xdescribe, exports.describe.only);140141  var it = wrap(exports.xit, exports.it);142  it.only = wrap(exports.xit, exports.it.only);143144  return {145    describe: describe,146    it: it147  };148149  function wrap(onSkip, onRun) {150    return function(title, fn) {151      if (predicateFn()) {152        onSkip(title, fn);153      } else {154        onRun(title, fn);155      }156    };157  }158}159160161// PUBLIC API162163/**164 * Registers a new test suite.165 * @param {string} name The suite name.166 * @param {function()=} fn The suite function, or {@code undefined} to define167 *     a pending test suite.168 */169exports.describe = global.describe;170171/**172 * Defines a suppressed test suite.173 * @param {string} name The suite name.174 * @param {function()=} fn The suite function, or {@code undefined} to define175 *     a pending test suite.176 */177exports.xdescribe = global.xdescribe;178exports.describe.skip = global.describe.skip;179180/**181 * Register a function to call after the current suite finishes.182 * @param {function()} fn .183 */184exports.after = wrapped(global.after);185186/**187 * Register a function to call after each test in a suite.188 * @param {function()} fn .189 */190exports.afterEach = wrapped(global.afterEach);191192/**193 * Register a function to call before the current suite starts.194 * @param {function()} fn .195 */196exports.before = wrapped(global.before);197198/**199 * Register a function to call before each test in a suite.200 * @param {function()} fn .201 */202exports.beforeEach = wrapped(global.beforeEach);203204/**205 * Add a test to the current suite.206 * @param {string} name The test name.207 * @param {function()=} fn The test function, or {@code undefined} to define208 *     a pending test case.209 */210exports.it = wrapped(global.it);211212/**213 * An alias for {@link #it()} that flags the test as the only one that should214 * be run within the current suite.215 * @param {string} name The test name.216 * @param {function()=} fn The test function, or {@code undefined} to define217 *     a pending test case.218 */219exports.iit = exports.it.only = wrapped(global.it.only);220221/**222 * Adds a test to the current suite while suppressing it so it is not run.223 * @param {string} name The test name.224 * @param {function()=} fn The test function, or {@code undefined} to define225 *     a pending test case.226 */227exports.xit = exports.it.skip = wrapped(global.xit);228229exports.ignore = ignore;