michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: 'use strict'; michael@0: michael@0: module.metadata = { michael@0: 'stability': 'unstable' michael@0: }; michael@0: michael@0: const { defer } = require('../core/promise'); michael@0: const { setInterval, clearInterval } = require('../timers'); michael@0: michael@0: function getTestNames (exports) michael@0: Object.keys(exports).filter(name => /^test/.test(name)) michael@0: michael@0: function isTestAsync (fn) fn.length > 1 michael@0: function isHelperAsync (fn) fn.length > 2 michael@0: michael@0: /* michael@0: * Takes an `exports` object of a test file and a function `beforeFn` michael@0: * to be run before each test. `beforeFn` is called with a `name` string michael@0: * as the first argument of the test name, and may specify a second michael@0: * argument function `done` to indicate that this function should michael@0: * resolve asynchronously michael@0: */ michael@0: function before (exports, beforeFn) { michael@0: getTestNames(exports).map(name => { michael@0: let testFn = exports[name]; michael@0: if (!isTestAsync(testFn) && !isHelperAsync(beforeFn)) { michael@0: exports[name] = function (assert) { michael@0: beforeFn(name, assert); michael@0: testFn(assert); michael@0: }; michael@0: } michael@0: else if (isTestAsync(testFn) && !isHelperAsync(beforeFn)) { michael@0: exports[name] = function (assert, done) { michael@0: beforeFn(name, assert); michael@0: testFn(assert, done); michael@0: }; michael@0: } michael@0: else if (!isTestAsync(testFn) && isHelperAsync(beforeFn)) { michael@0: exports[name] = function (assert, done) { michael@0: beforeFn(name, assert, () => { michael@0: testFn(assert); michael@0: done(); michael@0: }); michael@0: }; michael@0: } else if (isTestAsync(testFn) && isHelperAsync(beforeFn)) { michael@0: exports[name] = function (assert, done) { michael@0: beforeFn(name, assert, () => { michael@0: testFn(assert, done); michael@0: }); michael@0: }; michael@0: } michael@0: }); michael@0: } michael@0: exports.before = before; michael@0: michael@0: /* michael@0: * Takes an `exports` object of a test file and a function `afterFn` michael@0: * to be run after each test. `afterFn` is called with a `name` string michael@0: * as the first argument of the test name, and may specify a second michael@0: * argument function `done` to indicate that this function should michael@0: * resolve asynchronously michael@0: */ michael@0: function after (exports, afterFn) { michael@0: getTestNames(exports).map(name => { michael@0: let testFn = exports[name]; michael@0: if (!isTestAsync(testFn) && !isHelperAsync(afterFn)) { michael@0: exports[name] = function (assert) { michael@0: testFn(assert); michael@0: afterFn(name, assert); michael@0: }; michael@0: } michael@0: else if (isTestAsync(testFn) && !isHelperAsync(afterFn)) { michael@0: exports[name] = function (assert, done) { michael@0: testFn(assert, () => { michael@0: afterFn(name, assert); michael@0: done(); michael@0: }); michael@0: }; michael@0: } michael@0: else if (!isTestAsync(testFn) && isHelperAsync(afterFn)) { michael@0: exports[name] = function (assert, done) { michael@0: testFn(assert); michael@0: afterFn(name, assert, done); michael@0: }; michael@0: } else if (isTestAsync(testFn) && isHelperAsync(afterFn)) { michael@0: exports[name] = function (assert, done) { michael@0: testFn(assert, () => { michael@0: afterFn(name, assert, done); michael@0: }); michael@0: }; michael@0: } michael@0: }); michael@0: } michael@0: exports.after = after; michael@0: michael@0: function waitUntil (predicate, delay) { michael@0: let { promise, resolve } = defer(); michael@0: let interval = setInterval(() => { michael@0: if (!predicate()) return; michael@0: clearInterval(interval); michael@0: resolve(); michael@0: }, delay || 10); michael@0: return promise; michael@0: } michael@0: exports.waitUntil = waitUntil;