addon-sdk/source/lib/sdk/test.js

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/addon-sdk/source/lib/sdk/test.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,126 @@
     1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.7 +
     1.8 +"use strict";
     1.9 +
    1.10 +module.metadata = {
    1.11 +  "stability": "unstable"
    1.12 +};
    1.13 +
    1.14 +const { Cu } = require("chrome");
    1.15 +const { Task } = Cu.import("resource://gre/modules/Task.jsm", {});
    1.16 +const { defer } = require("sdk/core/promise");
    1.17 +const BaseAssert = require("sdk/test/assert").Assert;
    1.18 +const { isFunction, isObject } = require("sdk/lang/type");
    1.19 +
    1.20 +exports.Assert = BaseAssert;
    1.21 +
    1.22 +function extend(target) {
    1.23 +  let descriptor = {}
    1.24 +  Array.slice(arguments, 1).forEach(function(source) {
    1.25 +    Object.getOwnPropertyNames(source).forEach(function onEach(name) {
    1.26 +      descriptor[name] = Object.getOwnPropertyDescriptor(source, name);
    1.27 +    });
    1.28 +  });
    1.29 +  return Object.create(target, descriptor);
    1.30 +}
    1.31 +
    1.32 +/**
    1.33 + * Function takes test `suite` object in CommonJS format and defines all of the
    1.34 + * tests from that suite and nested suites in a jetpack format on a given
    1.35 + * `target` object. Optionally third argument `prefix` can be passed to prefix
    1.36 + * all the test names.
    1.37 + */
    1.38 +function defineTestSuite(target, suite, prefix) {
    1.39 +  prefix = prefix || "";
    1.40 +  // If suite defines `Assert` that's what `assert` object have to be created
    1.41 +  // from and passed to a test function (This allows custom assertion functions)
    1.42 +  // See for details: http://wiki.commonjs.org/wiki/Unit_Testing/1.1
    1.43 +  let Assert = suite.Assert || BaseAssert;
    1.44 +  // Going through each item in the test suite and wrapping it into a
    1.45 +  // Jetpack test format.
    1.46 +  Object.keys(suite).forEach(function(key) {
    1.47 +     // If name starts with test then it's a test function or suite.
    1.48 +    if (key.indexOf("test") === 0) {
    1.49 +      let test = suite[key];
    1.50 +
    1.51 +      // For each test function so we create a wrapper test function in a
    1.52 +      // jetpack format and copy that to a `target` exports.
    1.53 +      if (isFunction(test)) {
    1.54 +
    1.55 +        // Since names of the test may match across suites we use full object
    1.56 +        // path as a name to avoid overriding same function.
    1.57 +        target[prefix + key] = function(options) {
    1.58 +
    1.59 +          // Creating `assert` functions for this test.
    1.60 +          let assert = Assert(options);
    1.61 +          assert.end = () => options.done();
    1.62 +
    1.63 +          // If test function is a generator use a task JS to allow yield-ing
    1.64 +          // style test runs.
    1.65 +          if (test.isGenerator && test.isGenerator()) {
    1.66 +            options.waitUntilDone();
    1.67 +            Task.spawn(test.bind(null, assert)).
    1.68 +                then(null, assert.fail).
    1.69 +                then(assert.end);
    1.70 +          }
    1.71 +
    1.72 +          // If CommonJS test function expects more than one argument
    1.73 +          // it means that test is async and second argument is a callback
    1.74 +          // to notify that test is finished.
    1.75 +          else if (1 < test.length) {
    1.76 +
    1.77 +            // Letting test runner know that test is executed async and
    1.78 +            // creating a callback function that CommonJS tests will call
    1.79 +            // once it's done.
    1.80 +            options.waitUntilDone();
    1.81 +            test(assert, function() {
    1.82 +              options.done();
    1.83 +            });
    1.84 +          }
    1.85 +
    1.86 +          // Otherwise CommonJS test is synchronous so we call it only with
    1.87 +          // one argument.
    1.88 +          else {
    1.89 +            test(assert);
    1.90 +          }
    1.91 +        }
    1.92 +      }
    1.93 +
    1.94 +      // If it's an object then it's a test suite containing test function
    1.95 +      // and / or nested test suites. In that case we just extend prefix used
    1.96 +      // and call this function to copy and wrap tests from nested suite.
    1.97 +      else if (isObject(test)) {
    1.98 +        // We need to clone `tests` instead of modifying it, since it's very
    1.99 +        // likely that it is frozen (usually test suites imported modules).
   1.100 +        test = extend(Object.prototype, test, {
   1.101 +          Assert: test.Assert || Assert
   1.102 +        });
   1.103 +        defineTestSuite(target, test, prefix + key + ".");
   1.104 +      }
   1.105 +    }
   1.106 +  });
   1.107 +}
   1.108 +
   1.109 +/**
   1.110 + * This function is a CommonJS test runner function, but since Jetpack test
   1.111 + * runner and test format is different from CommonJS this function shims given
   1.112 + * `exports` with all its tests into a Jetpack test format so that the built-in
   1.113 + * test runner will be able to run CommonJS test without manual changes.
   1.114 + */
   1.115 +exports.run = function run(exports) {
   1.116 +
   1.117 +  // We can't leave old properties on exports since those are test in a CommonJS
   1.118 +  // format that why we move everything to a new `suite` object.
   1.119 +  let suite = {};
   1.120 +  Object.keys(exports).forEach(function(key) {
   1.121 +    suite[key] = exports[key];
   1.122 +    delete exports[key];
   1.123 +  });
   1.124 +
   1.125 +  // Now we wrap all the CommonJS tests to a Jetpack format and define
   1.126 +  // those to a given `exports` object since that where jetpack test runner
   1.127 +  // will look for them.
   1.128 +  defineTestSuite(exports, suite);
   1.129 +};

mercurial