PageRenderTime 25ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/examples/cujo/bower_components/curl/src/curl/tdd/runner.js

https://gitlab.com/x33n/todomvc
JavaScript | 319 lines | 106 code | 41 blank | 172 comment | 18 complexity | da6588de64171dc39ab17aafb29d5066 MD5 | raw file
  1. /** MIT License (c) copyright 2010-2013 B Cavalier & J Hann */
  2. /**
  3. * curl createContext module
  4. *
  5. * Licensed under the MIT License at:
  6. * http://www.opensource.org/licenses/mit-license.php
  7. *
  8. */
  9. define(['curl', 'curl/_privileged', './undefine'], function (curl, priv, undefine) {
  10. "use strict";
  11. var Promise, runQueue, undef;
  12. Promise = priv['Promise'];
  13. /**
  14. * Creates an objet with a "run" function and an "undefine" function.
  15. * The run function accepts a "testing" function that can be
  16. * used to test asynchronous modules. (Note: curl.js doesn't
  17. * supply or advocate any particular testing framework.) If provided,
  18. * the "setup" and "teardown" functions will execute immediately before
  19. * and after the testing function.
  20. *
  21. * The setup function is provided a `require` function as its first
  22. * parameter. The setup function may be used to
  23. * `define` or `require` any mock or stub modules that the developer
  24. * deems necessary to isolate the functionality of the module being
  25. * tested. Mocks or stubs may also be created in the testing function.
  26. * However, sync `require` behavior (R-value require) is not supported
  27. * inside the testing function. (Note: async require *is* supported
  28. * inside the module being tested!)
  29. *
  30. * If the setup function needs to perform async operations, it must
  31. * supply a second `done` parameter and call it when it is done
  32. * performaing async tasks. This callback also has a promise-like
  33. * interface for developers who prefer to work with promises. If this
  34. * parameter is not supplied by the developer, the setup function is
  35. * assumed to be synchronous.
  36. *
  37. * Note: if any modules (or plugin-based resources) are fetched using
  38. * the `require` supplied to the setup, testing, or teardown functions,
  39. * the functions will wait for the required modules/resources to
  40. * resolve before proceeding. In summary: the developer does *not*
  41. * need to provide the `done` parameter if using the provided `require`.
  42. *
  43. * There can be several testing functions. Each one should be passed
  44. * individually to the run function.
  45. *
  46. * The teardown function can be used to clean up any resources used
  47. * in the setup or testing functions. Any modules/resources created
  48. * by the supplied `require` or the standard `define` are cleaned up
  49. * automatically. So, in general, you should only need to supply a
  50. * teardown function if your code creates non-AMD resources.
  51. *
  52. * The testing functions and the teardown function take the same
  53. * parameters as the setup function. Be sure to supply and call the
  54. * second `done` parameter if these functions perform async tasks.
  55. *
  56. * All functions may run async. Your testing must be able to handle
  57. * async tests or it wil likely fail.
  58. *
  59. * Each of the testing functions is run in isolation from the others
  60. * (and in isolation from any other testing functions created by other
  61. * invocations of the runner function). Actually, the functions are
  62. * sequenced temporally so they don't execute at the same time. In
  63. * addition, at the end of each function's execution, it will restore
  64. * curl.js's cache back to the state it was before execution, so each
  65. * function can be assured it has a clean environment.
  66. *
  67. * The undefine function can be used to explicitly undefine a
  68. * module or resource inside a testing function (or anywhere else).
  69. *
  70. * Promises returned by curl.js or this runner module are *not*
  71. * compliant to the CommonJS Promises/A standard. Use a library
  72. * such as when.js (http://github.com/cujojs/when) to create compliant
  73. * promises. See example 2.
  74. *
  75. * @param [require] {Function}
  76. * @param [setup] {isolatedFunction} if defined, this
  77. * function will be run immediately before the returned function.
  78. * @param [teardown] {isolatedFunction} if defined, this
  79. * function will be run immediately after the returned function.
  80. * @returns {Object}
  81. * @returns {Object.run} function run (testFunc, callback) {}
  82. * @returns {Object.undefine} function run (idOrArray) {}
  83. *
  84. * @example 1
  85. *
  86. * define(['curl/tdd/runner', 'require'], function (runner, require) {
  87. * var r1;
  88. *
  89. * // no need for `done` callback if using supplied `require`.
  90. * function setup (require) {
  91. * var xhrResult = [{ foo: 'bar' }];
  92. * // load and configure some mocks
  93. * require(['mocks/xhr', 'mocks/rpc'], function (xhr, rpc) {
  94. * define('my/xhr', xhr.config(xhrResult));
  95. * define('my/rpc', rpc.config(xhr));
  96. * });
  97. * // define any other resources
  98. * define('my/transform', function () {
  99. * return function (val) { return val; };
  100. * });
  101. * }
  102. *
  103. * // configure runner.
  104. * // (no need for teardown since mocks were created using supplied
  105. * // `require` and standard `define`)
  106. * r1 = runner(require, setup);
  107. *
  108. * // queue a testing function to be run.
  109. * // setup will be run beforehand and all modules will be
  110. * // cleaned up automatically.
  111. * r1.run(function (require) {
  112. * require(['my/module1-to-test'], function (m1) {
  113. * // perform assertions here
  114. * });
  115. * });
  116. *
  117. * // queue another testing function to be run.
  118. * // setup will be run beforehand and all modules will be
  119. * // cleaned up automatically.
  120. * r1.run(function (require) {
  121. * define('my/other-data-to-test-with', { name: 'Fred' });
  122. * require(['my/module2-to-test'], function (m2) {
  123. * // perform assertions here
  124. * });
  125. * });
  126. * });
  127. *
  128. * @example 2
  129. *
  130. * define(['curl/tdd/runner', 'require', 'when'], function (runner, require, when) {
  131. * // convert a returned promise to a Promises/A promise using when.js
  132. * var cjsPromise = when(r1.run(null, function () {
  133. * // do tests here
  134. * });
  135. * });
  136. *
  137. */
  138. return function runner (require, setup, teardown) {
  139. var promise;
  140. if (!require) require = curl;
  141. function run (testFunc) {
  142. var cacheSnapshot, callback;
  143. callback = arguments[1];
  144. // enqueue cache snapshot
  145. enqueue(function _copyCache () {
  146. cacheSnapshot = copyCache(priv['cache']);
  147. });
  148. // enqueue setup
  149. if (setup) enqueue(waitForAsyncTasks(setup, require));
  150. // enqueue testFunc
  151. enqueue(waitForAsyncTasks(testFunc, require));
  152. // enqueue teardown
  153. if (teardown) enqueue(waitForAsyncTasks(teardown, require));
  154. promise = new Promise();
  155. // enqueue cache restore and a hook for outside code
  156. enqueue(function _restoreCache () {
  157. restoreCache(priv['cache'], cacheSnapshot);
  158. if (callback) callback();
  159. promise.resolve();
  160. });
  161. return promise;
  162. }
  163. return {
  164. run: run,
  165. undefine: undefine
  166. };
  167. };
  168. /**
  169. * The signature of the functions supplied to runner.
  170. * @param require {Function} standard AMD `require`
  171. * @param [done] {Function|Promise} if included in the function
  172. * parameters, this callback must be called when all async tasks
  173. * are completed. This function has a promise-like interface,
  174. * including `done.resolve(val)` and `done.reject(ex)` for
  175. * those who would rather use promises.
  176. */
  177. function isolatedFunction (require) {}
  178. /**
  179. * Enqueues a function that provides a promise.
  180. * @private
  181. * @param promiseProvider {Function} must return a promise if it
  182. * has async tasks.
  183. */
  184. function enqueue (promiseProvider) {
  185. var next;
  186. function dequeue () {
  187. when(promiseProvider(), next.resolve, next.reject);
  188. }
  189. next = new Promise();
  190. when(runQueue, dequeue, runQueue && runQueue.reject);
  191. runQueue = next;
  192. }
  193. /**
  194. * Waits for async require calls and/or done.
  195. * @private
  196. * @param func {Function}
  197. * standard signature is `function (require, [done]) {}`
  198. */
  199. function waitForAsyncTasks (func, require) {
  200. var promise, otherAsyncDone, requiresDone, trackedRequire;
  201. promise = new Promise();
  202. otherAsyncDone = new Promise();
  203. requiresDone = new Promise();
  204. trackedRequire = createTrackedRequire(require, requiresDone.resolve);
  205. // last param is `done`
  206. if (func.length > 1) {
  207. // turn otherAsyncDone into a dual callback/promise thingy
  208. otherAsyncDone = (function (promise) {
  209. var dual = promise.resolve;
  210. dual.resolve = promise.resolve;
  211. dual.reject = promise.reject;
  212. dual.then = promise.then;
  213. return dual;
  214. }(otherAsyncDone));
  215. }
  216. else {
  217. // pre-resolve
  218. otherAsyncDone.resolve();
  219. }
  220. // wait for promises
  221. requiresDone.then(function _otherAsyncDone () {
  222. otherAsyncDone.then(promise.resolve, promise.reject);
  223. });
  224. // return a queueable function
  225. return function _waitForAsyncTasks () {
  226. // call function
  227. func(trackedRequire, otherAsyncDone);
  228. // check if there were no async `require` calls
  229. if (trackedRequire.notAsync()) {
  230. requiresDone.resolve();
  231. }
  232. // return promise
  233. return promise;
  234. };
  235. }
  236. function createTrackedRequire (require, modulesAllFetched) {
  237. var callCount = 0;
  238. function trackedRequire (idOrArray, callback) {
  239. var cb;
  240. callCount++;
  241. cb = function () {
  242. callback.apply(this, arguments);
  243. // if this is the last require
  244. if (--callCount == 0) modulesAllFetched();
  245. };
  246. return require(idOrArray, cb);
  247. }
  248. // preserve AMD API
  249. trackedRequire.toUrl = require.toUrl;
  250. // helpful
  251. trackedRequire.notAsync = function () { return callCount == 0; };
  252. return trackedRequire;
  253. }
  254. function copyCache (cache) {
  255. var copy = {};
  256. for (var p in cache) {
  257. copy[p] = cache[p];
  258. }
  259. return copy;
  260. }
  261. function restoreCache (cache, copy) {
  262. for (var p in cache) {
  263. if (!(p in copy)) {
  264. undefine(p);
  265. }
  266. }
  267. }
  268. function when (promiseOrValue, callback, errback, progback) {
  269. // we can't just sniff for then(). if we do, resources that have a
  270. // then() method will make dependencies wait!
  271. if (promiseOrValue && typeof promiseOrValue.then == 'function') {
  272. return promiseOrValue.then(callback, errback, progback);
  273. }
  274. else {
  275. return callback(promiseOrValue);
  276. }
  277. }
  278. });