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 +};