assert.js 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  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 { isFunction, isNull, isObject, isString,
  9. isRegExp, isArray, isDate, isPrimitive,
  10. isUndefined, instanceOf, source } = require("../lang/type");
  11. /**
  12. * The `AssertionError` is defined in assert.
  13. * @extends Error
  14. * @example
  15. * new assert.AssertionError({
  16. * message: message,
  17. * actual: actual,
  18. * expected: expected
  19. * })
  20. */
  21. function AssertionError(options) {
  22. let assertionError = Object.create(AssertionError.prototype);
  23. if (isString(options))
  24. options = { message: options };
  25. if ("actual" in options)
  26. assertionError.actual = options.actual;
  27. if ("expected" in options)
  28. assertionError.expected = options.expected;
  29. if ("operator" in options)
  30. assertionError.operator = options.operator;
  31. assertionError.message = options.message;
  32. assertionError.stack = new Error().stack;
  33. return assertionError;
  34. }
  35. AssertionError.prototype = Object.create(Error.prototype, {
  36. constructor: { value: AssertionError },
  37. name: { value: "AssertionError", enumerable: true },
  38. toString: { value: function toString() {
  39. let value;
  40. if (this.message) {
  41. value = this.name + " : " + this.message;
  42. }
  43. else {
  44. value = [
  45. this.name + " : ",
  46. source(this.expected),
  47. this.operator,
  48. source(this.actual)
  49. ].join(" ");
  50. }
  51. return value;
  52. }}
  53. });
  54. exports.AssertionError = AssertionError;
  55. function Assert(logger) {
  56. let assert = Object.create(Assert.prototype, { _log: { value: logger }});
  57. assert.fail = assert.fail.bind(assert);
  58. assert.pass = assert.pass.bind(assert);
  59. return assert;
  60. }
  61. Assert.prototype = {
  62. fail: function fail(e) {
  63. if (!e || typeof(e) !== 'object') {
  64. this._log.fail(e);
  65. return;
  66. }
  67. let message = e.message;
  68. try {
  69. if ('operator' in e) {
  70. message += [
  71. " -",
  72. source(e.expected),
  73. e.operator,
  74. source(e.actual)
  75. ].join(" ");
  76. }
  77. }
  78. catch(e) {}
  79. this._log.fail(message);
  80. },
  81. pass: function pass(message) {
  82. this._log.pass(message);
  83. },
  84. error: function error(e) {
  85. this._log.exception(e);
  86. },
  87. ok: function ok(value, message) {
  88. if (!!!value) {
  89. this.fail({
  90. actual: value,
  91. expected: true,
  92. message: message,
  93. operator: "=="
  94. });
  95. }
  96. else {
  97. this.pass(message);
  98. }
  99. },
  100. /**
  101. * The equality assertion tests shallow, coercive equality with `==`.
  102. * @example
  103. * assert.equal(1, 1, "one is one");
  104. */
  105. equal: function equal(actual, expected, message) {
  106. if (actual == expected) {
  107. this.pass(message);
  108. }
  109. else {
  110. this.fail({
  111. actual: actual,
  112. expected: expected,
  113. message: message,
  114. operator: "=="
  115. });
  116. }
  117. },
  118. /**
  119. * The non-equality assertion tests for whether two objects are not equal
  120. * with `!=`.
  121. * @example
  122. * assert.notEqual(1, 2, "one is not two");
  123. */
  124. notEqual: function notEqual(actual, expected, message) {
  125. if (actual != expected) {
  126. this.pass(message);
  127. }
  128. else {
  129. this.fail({
  130. actual: actual,
  131. expected: expected,
  132. message: message,
  133. operator: "!=",
  134. });
  135. }
  136. },
  137. /**
  138. * The equivalence assertion tests a deep (with `===`) equality relation.
  139. * @example
  140. * assert.deepEqual({ a: "foo" }, { a: "foo" }, "equivalent objects")
  141. */
  142. deepEqual: function deepEqual(actual, expected, message) {
  143. if (isDeepEqual(actual, expected)) {
  144. this.pass(message);
  145. }
  146. else {
  147. this.fail({
  148. actual: actual,
  149. expected: expected,
  150. message: message,
  151. operator: "deepEqual"
  152. });
  153. }
  154. },
  155. /**
  156. * The non-equivalence assertion tests for any deep (with `===`) inequality.
  157. * @example
  158. * assert.notDeepEqual({ a: "foo" }, Object.create({ a: "foo" }),
  159. * "object's inherit from different prototypes");
  160. */
  161. notDeepEqual: function notDeepEqual(actual, expected, message) {
  162. if (!isDeepEqual(actual, expected)) {
  163. this.pass(message);
  164. }
  165. else {
  166. this.fail({
  167. actual: actual,
  168. expected: expected,
  169. message: message,
  170. operator: "notDeepEqual"
  171. });
  172. }
  173. },
  174. /**
  175. * The strict equality assertion tests strict equality, as determined by
  176. * `===`.
  177. * @example
  178. * assert.strictEqual(null, null, "`null` is `null`")
  179. */
  180. strictEqual: function strictEqual(actual, expected, message) {
  181. if (actual === expected) {
  182. this.pass(message);
  183. }
  184. else {
  185. this.fail({
  186. actual: actual,
  187. expected: expected,
  188. message: message,
  189. operator: "==="
  190. });
  191. }
  192. },
  193. /**
  194. * The strict non-equality assertion tests for strict inequality, as
  195. * determined by `!==`.
  196. * @example
  197. * assert.notStrictEqual(null, undefined, "`null` is not `undefined`");
  198. */
  199. notStrictEqual: function notStrictEqual(actual, expected, message) {
  200. if (actual !== expected) {
  201. this.pass(message);
  202. }
  203. else {
  204. this.fail({
  205. actual: actual,
  206. expected: expected,
  207. message: message,
  208. operator: "!=="
  209. })
  210. }
  211. },
  212. /**
  213. * The assertion whether or not given `block` throws an exception. If optional
  214. * `Error` argument is provided and it's type of function thrown error is
  215. * asserted to be an instance of it, if type of `Error` is string then message
  216. * of throw exception is asserted to contain it.
  217. * @param {Function} block
  218. * Function that is expected to throw.
  219. * @param {Error|RegExp} [Error]
  220. * Error constructor that is expected to be thrown or a string that
  221. * must be contained by a message of the thrown exception, or a RegExp
  222. * matching a message of the thrown exception.
  223. * @param {String} message
  224. * Description message
  225. *
  226. * @examples
  227. *
  228. * assert.throws(function block() {
  229. * doSomething(4)
  230. * }, "Object is expected", "Incorrect argument is passed");
  231. *
  232. * assert.throws(function block() {
  233. * Object.create(5)
  234. * }, TypeError, "TypeError is thrown");
  235. */
  236. throws: function throws(block, Error, message) {
  237. let threw = false;
  238. let exception = null;
  239. // If third argument is not provided and second argument is a string it
  240. // means that optional `Error` argument was not passed, so we shift
  241. // arguments.
  242. if (isString(Error) && isUndefined(message)) {
  243. message = Error;
  244. Error = undefined;
  245. }
  246. // Executing given `block`.
  247. try {
  248. block();
  249. }
  250. catch (e) {
  251. threw = true;
  252. exception = e;
  253. }
  254. // If exception was thrown and `Error` argument was not passed assert is
  255. // passed.
  256. if (threw && (isUndefined(Error) ||
  257. // If passed `Error` is RegExp using it's test method to
  258. // assert thrown exception message.
  259. (isRegExp(Error) && Error.test(exception.message)) ||
  260. // If passed `Error` is a constructor function testing if
  261. // thrown exception is an instance of it.
  262. (isFunction(Error) && instanceOf(exception, Error))))
  263. {
  264. this.pass(message);
  265. }
  266. // Otherwise we report assertion failure.
  267. else {
  268. let failure = {
  269. message: message,
  270. operator: "throws"
  271. };
  272. if (exception)
  273. failure.actual = exception;
  274. if (Error)
  275. failure.expected = Error;
  276. this.fail(failure);
  277. }
  278. }
  279. };
  280. exports.Assert = Assert;
  281. function isDeepEqual(actual, expected) {
  282. // 7.1. All identical values are equivalent, as determined by ===.
  283. if (actual === expected) {
  284. return true;
  285. }
  286. // 7.2. If the expected value is a Date object, the actual value is
  287. // equivalent if it is also a Date object that refers to the same time.
  288. else if (isDate(actual) && isDate(expected)) {
  289. return actual.getTime() === expected.getTime();
  290. }
  291. // XXX specification bug: this should be specified
  292. else if (isPrimitive(actual) || isPrimitive(expected)) {
  293. return expected === actual;
  294. }
  295. // 7.3. Other pairs that do not both pass typeof value == "object",
  296. // equivalence is determined by ==.
  297. else if (!isObject(actual) && !isObject(expected)) {
  298. return actual == expected;
  299. }
  300. // 7.4. For all other Object pairs, including Array objects, equivalence is
  301. // determined by having the same number of owned properties (as verified
  302. // with Object.prototype.hasOwnProperty.call), the same set of keys
  303. // (although not necessarily the same order), equivalent values for every
  304. // corresponding key, and an identical "prototype" property. Note: this
  305. // accounts for both named and indexed properties on Arrays.
  306. else {
  307. return actual.prototype === expected.prototype &&
  308. isEquivalent(actual, expected);
  309. }
  310. }
  311. function isEquivalent(a, b, stack) {
  312. let aKeys = Object.keys(a);
  313. let bKeys = Object.keys(b);
  314. return aKeys.length === bKeys.length &&
  315. isArrayEquivalent(aKeys.sort(), bKeys.sort()) &&
  316. aKeys.every(function(key) {
  317. return isDeepEqual(a[key], b[key], stack)
  318. });
  319. }
  320. function isArrayEquivalent(a, b, stack) {
  321. return isArray(a) && isArray(b) &&
  322. a.every(function(value, index) {
  323. return isDeepEqual(value, b[index]);
  324. });
  325. }