test.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. /* This Source Code Form is subject to the terms of the Mozilla Public
  2. * License, v. 2.0. If a copy of the MPL was not distributed with this
  3. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  4. "use strict";
  5. module.metadata = {
  6. "stability": "unstable"
  7. };
  8. const { Cu } = require("chrome");
  9. const { Task } = Cu.import("resource://gre/modules/Task.jsm", {});
  10. const { defer } = require("sdk/core/promise");
  11. const BaseAssert = require("sdk/test/assert").Assert;
  12. const { isFunction, isObject } = require("sdk/lang/type");
  13. exports.Assert = BaseAssert;
  14. function extend(target) {
  15. let descriptor = {}
  16. Array.slice(arguments, 1).forEach(function(source) {
  17. Object.getOwnPropertyNames(source).forEach(function onEach(name) {
  18. descriptor[name] = Object.getOwnPropertyDescriptor(source, name);
  19. });
  20. });
  21. return Object.create(target, descriptor);
  22. }
  23. /**
  24. * Function takes test `suite` object in CommonJS format and defines all of the
  25. * tests from that suite and nested suites in a jetpack format on a given
  26. * `target` object. Optionally third argument `prefix` can be passed to prefix
  27. * all the test names.
  28. */
  29. function defineTestSuite(target, suite, prefix) {
  30. prefix = prefix || "";
  31. // If suite defines `Assert` that's what `assert` object have to be created
  32. // from and passed to a test function (This allows custom assertion functions)
  33. // See for details: http://wiki.commonjs.org/wiki/Unit_Testing/1.1
  34. let Assert = suite.Assert || BaseAssert;
  35. // Going through each item in the test suite and wrapping it into a
  36. // Jetpack test format.
  37. Object.keys(suite).forEach(function(key) {
  38. // If name starts with test then it's a test function or suite.
  39. if (key.indexOf("test") === 0) {
  40. let test = suite[key];
  41. // For each test function so we create a wrapper test function in a
  42. // jetpack format and copy that to a `target` exports.
  43. if (isFunction(test)) {
  44. // Since names of the test may match across suites we use full object
  45. // path as a name to avoid overriding same function.
  46. target[prefix + key] = function(options) {
  47. // Creating `assert` functions for this test.
  48. let assert = Assert(options);
  49. assert.end = () => options.done();
  50. // If test function is a generator use a task JS to allow yield-ing
  51. // style test runs.
  52. if (test.isGenerator && test.isGenerator()) {
  53. options.waitUntilDone();
  54. Task.spawn(test.bind(null, assert)).
  55. then(null, assert.fail).
  56. then(assert.end);
  57. }
  58. // If CommonJS test function expects more than one argument
  59. // it means that test is async and second argument is a callback
  60. // to notify that test is finished.
  61. else if (1 < test.length) {
  62. // Letting test runner know that test is executed async and
  63. // creating a callback function that CommonJS tests will call
  64. // once it's done.
  65. options.waitUntilDone();
  66. test(assert, function() {
  67. options.done();
  68. });
  69. }
  70. // Otherwise CommonJS test is synchronous so we call it only with
  71. // one argument.
  72. else {
  73. test(assert);
  74. }
  75. }
  76. }
  77. // If it's an object then it's a test suite containing test function
  78. // and / or nested test suites. In that case we just extend prefix used
  79. // and call this function to copy and wrap tests from nested suite.
  80. else if (isObject(test)) {
  81. // We need to clone `tests` instead of modifying it, since it's very
  82. // likely that it is frozen (usually test suites imported modules).
  83. test = extend(Object.prototype, test, {
  84. Assert: test.Assert || Assert
  85. });
  86. defineTestSuite(target, test, prefix + key + ".");
  87. }
  88. }
  89. });
  90. }
  91. /**
  92. * This function is a CommonJS test runner function, but since Jetpack test
  93. * runner and test format is different from CommonJS this function shims given
  94. * `exports` with all its tests into a Jetpack test format so that the built-in
  95. * test runner will be able to run CommonJS test without manual changes.
  96. */
  97. exports.run = function run(exports) {
  98. // We can't leave old properties on exports since those are test in a CommonJS
  99. // format that why we move everything to a new `suite` object.
  100. let suite = {};
  101. Object.keys(exports).forEach(function(key) {
  102. suite[key] = exports[key];
  103. delete exports[key];
  104. });
  105. // Now we wrap all the CommonJS tests to a Jetpack format and define
  106. // those to a given `exports` object since that where jetpack test runner
  107. // will look for them.
  108. defineTestSuite(exports, suite);
  109. };