UNPKG

@magic/test

Version:

simple yet powerful unit testing library

175 lines (157 loc) 4.21 kB
import is from '@magic/types' import log from '@magic/log' import { cleanError, cleanFunctionString, getTestKey, stats } from '../lib/index.js' import { isolation } from './isolation.js' import { runSuite } from './suite.js' /** * Run a test or delegate to a suite. * * - If `test.fn` exists → executes the test and returns a `TestResult`. * - If only `test.tests` exists → delegates to {@link runSuite}, which returns a `Suite`. * * @param {Test} test - The test definition. * @returns {Promise<TestResult | Suite | undefined | void>} The result object or undefined on error. */ export const runTest = async test => { try { // expect can be undefined, we set expect to true to provide a default for tests if (!is.ownProp(test, 'expect')) { // alternative name for expect if (is.ownProp(test, 'is')) { test.expect = test.is } else { test.expect = true } } const { fn, name, pkg, before, parent, expect, runs = 1, tests, info } = test if (!is.ownProp(test, 'fn')) { if (is.object(test) && is.object(tests)) { return await runSuite({ pkg, parent: name, name, tests, }) } log.error('test.fn is not a function', test.key, test.info || '') return } /** @type {(() => (void | Promise<void>)) | void | undefined} */ let after if (is.function(before)) { try { const result = await before(test) if (is.function(result)) { after = /** @type {() => (void | Promise<void>)} */ (result) } } catch (e) { log.error('test.before', test.before, e) } } let result let exp let expString const msg = cleanFunctionString(fn) const key = getTestKey(pkg, parent, name) let pass = false let res for (let i = 0; i < runs; i++) { try { if (is.function(fn) || is.promise(fn)) { await isolation.executeIsolated(key, async () => { if (is.function(fn)) { res = await fn() } else if (is.promise(fn)) { res = await fn } }) } else { res = fn } } catch (e) { log.error('test.fn', key, cleanError(/** @type {Error} */ (e))) } try { if (is.function(expect)) { /** @type {unknown[]} */ const combinedRes = [].concat(/** @type {any} */ (res)) if (combinedRes.length > 1) { res = combinedRes } exp = await expect(res) expString = cleanFunctionString(expect) if (res !== true) { pass = exp === res || exp === true } } else if (is.promise(expect)) { exp = await expect expString = expect } else { exp = expect expString = expect } if (!pass) { if (is.undefined(exp)) { pass = exp === res } else if (is.sameType(exp, res)) { pass = is.deep.equal(exp, res) } } if (!pass) { result = res // abort the run loop if the first iteration fails break } } catch (e) { log.error('E_TEST_EXPECT', key, e) } if (i >= runs - 1) { pass = true result = res } } if (is.function(after)) { try { await after() } catch (e) { log.error('test.after', key, e) } } if (is.function(test.after)) { try { await test.after() } catch (e) { log.error('test.after', key, e) } } if (!pass) { let testName = name if (parent && parent !== name) { testName = `${parent}.${name}` } if (pkg !== parent && pkg !== name) { testName = `${pkg}.${testName}` } log.error('FAIL', testName, info) } stats.test({ parent, name, pass, pkg, }) return { result, msg, pass, parent: parent || '', name, expect: exp, expString, key, info, } } catch (e) { log.error('test error', e) } }