UNPKG

uvu-jest

Version:

Run the existing jest code with uvu

375 lines (374 loc) 15.7 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.exec = exports.test = exports.suite = exports.Test = void 0; process.env.NODE_ENV = 'test'; const kleur_1 = __importDefault(require("kleur")); const diff_1 = require("../diff"); let isCLI = false, isNode = false; let hrtime = (now = Date.now()) => () => (Date.now() - now).toFixed(2) + 'ms'; let write = console.log; class Test { constructor(name, handler) { this.name = name; this.handler = handler; } } exports.Test = Test; const into = (ctx, key) => (name, handler) => { ctx[key].push(new Test(name, handler)); }; const intoSuite = (ctx, key) => (suite) => { ctx[key].push(suite); }; const context = (state) => ({ tests: [], before: [], after: [], bEach: [], aEach: [], only: [], skips: 0, state, filename: 'not set' }); const milli = (arr) => (arr[0] * 1e3 + arr[1] / 1e6).toFixed(2) + 'ms'; const hook = (ctx, key) => (handler) => { ctx[key].push(handler); }; if (isNode = typeof process < 'u' && typeof process.stdout < 'u') { // globalThis polyfill; Node < 12 if (typeof globalThis !== 'object') { Object.defineProperty(global, 'globalThis', { get: function () { return this; } }); } /** * bin.js * .bin/ts-uvu * @type {RegExp} */ let rgx = /(ts-bin\.ts|bin\.js|\.bin[\\+\/]uj|uvu-jest[\\+\/]bin\.js)/i; isCLI = process.argv.some(x => rgx.test(x)); // attach node-specific utils write = x => process.stdout.write(x); // @ts-ignore hrtime = (now = process.hrtime()) => () => milli(process.hrtime(now)); } else if (typeof performance < 'u') { hrtime = (now = performance.now()) => () => (performance.now() - now).toFixed(2) + 'ms'; } globalThis.UVU_QUEUE = globalThis.UVU_QUEUE || []; isCLI || globalThis.UVU_QUEUE.push([null]); const QUOTE = kleur_1.default.dim('"'), GUTTER = '\n '; const IGNORE = /^\s*at.*(?:\(|\s)(?:node|(internal\/[\w/]*))/; const FAILURE = kleur_1.default.bold().bgRed(' FAIL '); const SUCCESS = kleur_1.default.bold().bgGreen(' SUCCESS '); const FILE = kleur_1.default.bold().underline().white; const SUITE = kleur_1.default.bgWhite().bold; const TestResultIcon = (result) => result ? '✔' : '✘'; function stack(stack, idx) { let i = 0, line, out = ''; let arr = stack.substring(idx).replace(/\\/g, '/').split('\n'); for (; i < arr.length; i++) { line = arr[i].trim(); if (line.length && !IGNORE.test(line)) { out += '\n ' + line; } } return kleur_1.default.grey(out) + '\n'; } const isAssertion = (err) => { return err.name.startsWith('AssertionError'); }; function format(name, err, suite = '') { let { details, operator = '' } = err; let idx = err.stack && err.stack.indexOf('\n').toString(); if (isAssertion(err) && !operator.includes('not')) { // @ts-ignore details = (0, diff_1.compare)(err.actual, err.expected); } // TODO? let str = ' ' + FAILURE + (suite ? kleur_1.default.red(SUITE(` ${suite} `)) : '') + ' ' + QUOTE + kleur_1.default.red().bold(name) + QUOTE; str += '\n ' + err.message + (operator ? kleur_1.default.italic().dim(` (${operator})`) : '') + '\n'; if (details) str += GUTTER + details.split('\n').join(GUTTER); // @ts-ignore if (!!~idx) str += stack(err.stack, idx); return str + '\n'; } const executeWithErrorReporting = (fn, where) => __awaiter(void 0, void 0, void 0, function* () { try { return yield fn(); } catch (err) { const { filename, nameStack } = where || { filename: '', nameStack: [] }; if (filename) console.log(FILE(filename)); const whereString = nameStack ? FAILURE + SUITE(` ${nameStack.join(' > ')} `) : undefined; console.error(...[whereString, err].filter(Boolean)); } }); function runner(ctx, thisSuiteName, filename, suiteNameStack) { return __awaiter(this, void 0, void 0, function* () { let { only, tests, before, after, bEach, aEach, state } = ctx; let arr = only.length ? only : tests; const res = { errorMessages: true, count: { test: { passed: 0, skipped: (only.length ? tests.length : 0) + ctx.skips, errored: 0 }, suite: { passed: 0, errored: 0 } }, }; // let passed=0, errors='', errorCounts=0, let errorCountOfThis = 0; const testResults = []; const childSuiteResults = []; try { for (const hook of before) { yield executeWithErrorReporting(hook.bind(0, state), { filename, nameStack: [thisSuiteName, ...suiteNameStack || [], 'before'], }); } for (const test of arr) { if (!(test instanceof Test)) { const suite = test; // nested suite const result = yield suite.runSync(filename, [...suiteNameStack || [], thisSuiteName]); if (result.errorMessages !== true) { if (res.errorMessages === true) res.errorMessages = ''; res.errorMessages += result.errorMessages; } res.count.test.passed += result.count.test.passed; res.count.test.skipped += result.count.test.skipped; res.count.test.errored += result.count.test.errored; res.count.suite.passed += result.count.suite.passed; res.count.suite.errored += result.count.suite.errored; childSuiteResults.push(result.result); } else { // test state.__test__ = test.name; globalThis.CURRENT_TEST_NAME = test.name; globalThis.CURRENT_SUITE_NAMES = [...suiteNameStack || [], thisSuiteName]; try { for (const beHook of bEach) { yield executeWithErrorReporting(beHook.bind(0, state), { filename, nameStack: [thisSuiteName, ...suiteNameStack || [], 'beforeEach'], }); } yield test.handler(state); for (const aeHook of aEach) { yield executeWithErrorReporting(aeHook.bind(0, state), { filename, nameStack: [thisSuiteName, ...suiteNameStack || [], 'afterEach'], }); } res.count.test.passed++; testResults.push({ name: test.name, result: true, }); } catch (err) { for (const aeHook of aEach) { yield executeWithErrorReporting(aeHook.bind(0, state), { filename, nameStack: [thisSuiteName, ...suiteNameStack || [], 'afterEach'], }); } if (typeof res.errorMessages === 'string' && res.errorMessages.length) res.errorMessages += '\n'; const errorMessage = format(test.name, err, thisSuiteName); res.errorMessages += errorMessage; res.count.test.errored++; errorCountOfThis++; testResults.push({ name: test.name, result: false, errorMessage, }); } } } } finally { state.__test__ = ''; for (const afterHook of after) { yield executeWithErrorReporting(afterHook.bind(0, state), { filename, nameStack: [thisSuiteName, ...suiteNameStack || [], 'after'], }); } if (errorCountOfThis) { res.count.suite.errored++; } else { res.count.suite.passed++; } return Object.assign(Object.assign({}, res), { result: { filename, suiteName: ctx.state.__suite__ || 'unknown', testResults, suiteResult: res.count.test.errored === 0, thisSuiteResult: errorCountOfThis === 0, childResults: childSuiteResults, } }); } }); } let timer; function defer() { clearTimeout(timer); timer = setTimeout(exec); } function suiteFactory(ctx, name = '') { ctx.state.__test__ = ''; ctx.state.__suite__ = name; const props = { before: Object.assign(hook(ctx, 'before'), { each: hook(ctx, 'bEach'), }), after: Object.assign(hook(ctx, 'after'), { each: hook(ctx, 'aEach'), }), only: into(ctx, 'only'), nest: intoSuite(ctx, 'tests'), skip: () => { ctx.skips++; }, runSync: (filename, suiteNames) => { const copy = Object.assign({}, ctx); Object.assign(ctx, context(copy.state)); return runner.bind(0)(copy, name, filename, suiteNames); }, run: () => { const copy = Object.assign({}, ctx); Object.assign(ctx, context(copy.state)); globalThis.UVU_QUEUE[globalThis.UVU_INDEX || 0].push(runner.bind(0, copy, name)); isCLI || defer(); } }; return Object.assign(into(ctx, 'tests'), props); } const suite = (name = '', state = {}) => suiteFactory(context(state), name); exports.suite = suite; exports.test = (0, exports.suite)(); const reportResults = (result, depth = 1) => { const { filename, suiteName, thisSuiteResult, testResults, childResults } = result; if (depth === 1 && filename) { console.group('👾 Each Suite Result: '); console.log(FILE(filename)); } console.group(SUITE(` ${suiteName} `)); testResults.forEach(({ name, result }) => { console.log((result ? kleur_1.default.green : kleur_1.default.red)(`${TestResultIcon(result)} ${name}`)); }); if (childResults) { childResults.forEach(cr => reportResults(cr, depth + 1)); } console.groupEnd(); if (depth === 1) { console.groupEnd(); } }; const results = []; const reportErrorSuites = (_result = results, resultStack = []) => { if (Array.isArray(_result)) { results.forEach(r => reportErrorSuites(r, resultStack)); return; } if (!_result.thisSuiteResult) { if (_result.filename) { console.group(FILE(_result.filename)); } // report const message = [ ...resultStack .map(r => (r.thisSuiteResult ? '' : FAILURE) + (SUITE(r.suiteName))), FAILURE + SUITE(_result.suiteName) ].join(' > '); console.group(message); _result.testResults.forEach(({ name, result }) => { console.log((result ? kleur_1.default.green : kleur_1.default.red)(`${TestResultIcon(result)} ${name}`)); }); console.groupEnd(); if (_result.filename) { console.groupEnd(); } } if (_result.childResults) { _result.childResults.forEach(r => reportErrorSuites(r, [...resultStack, _result])); } }; function exec(bail) { return __awaiter(this, void 0, void 0, function* () { let timer = hrtime(); let exitCode = 0; const counts = { tests: { total: 0, passed: 0, skipped: 0, errored: 0, }, suite: { passed: 0, errored: 0, } }; const files = globalThis.UVU_QUEUE.length; for (let group of globalThis.UVU_QUEUE) { console.log(); const [name, ...testRunners] = group; if (name === null || name === void 0 ? void 0 : name.file) { globalThis.CURRENT_FILE_PATH = name.file; } for (let _runner of testRunners) { let result = yield _runner((name === null || name === void 0 ? void 0 : name.name) || undefined); counts.tests.skipped += result.count.test.skipped; counts.suite.passed += result.count.suite.passed; counts.suite.errored += result.count.suite.errored; counts.tests.passed += result.count.test.passed; counts.tests.errored += result.count.test.errored; counts.tests.total += result.count.test.passed + result.count.test.errored; results.push(result.result); reportResults(result.result); if (result.errorMessages.toString() !== 'true' && result.errorMessages) { write('\n' + result.errorMessages + '\n'); exitCode = 1; if (bail) return isNode && process.exit(1); } } } console.log(); console.group(`💎 ${counts.tests.errored ? FAILURE : SUCCESS} Total results by here`); console.group(kleur_1.default.bold().underline().blue('Total:')); console.log(`Files: ${files}`); console.log(`Suites: ${counts.suite.passed + counts.suite.errored}`); console.log(`Tests: ${counts.tests.passed + counts.tests.errored}`); console.groupEnd(); if (counts.tests.errored > 0) { console.group(kleur_1.default.bold().underline().red('Errors:')); console.log(kleur_1.default.red('Errored Suites: ' + counts.suite.errored)); console.log(kleur_1.default.red('Errored Tests: ' + counts.tests.errored)); console.groupEnd(); } console.group(kleur_1.default.bold().underline().blue('Details:')); console.log('Skipped: ' + (counts.tests.skipped ? kleur_1.default.yellow(counts.tests.skipped) : counts.tests.skipped)); console.log('Duration: ' + timer() + '\n\n'); console.groupEnd(); console.groupEnd(); if (counts.suite.errored > 0) { console.group('💥 Errored Tests'); reportErrorSuites(); console.groupEnd(); } if (isNode) process.exitCode = exitCode; }); } exports.exec = exec;