UNPKG

@power-doctest/tester

Version:
168 lines (165 loc) 6.62 kB
import * as vm from "node:vm"; import { convertCode } from "@power-doctest/core"; // @ts-expect-error: no types import assert from "power-assert"; const CALLBACK_FUNCTION_NAME = "__power_doctest_runner_callback__"; export function test(parsedCode, oprions) { if (parsedCode.state === "disabled") { return Promise.resolve(); } if (oprions?.disableRunning && parsedCode.state === "none") { return Promise.resolve(); } return run(parsedCode.code, { ...(oprions?.defaultDoctestRunnerOptions ? oprions.defaultDoctestRunnerOptions : {}), ...parsedCode.doctestOptions, }).catch((error) => { // if it is expected error, resolve it if (parsedCode.expectedError && error.name === parsedCode.expectedError) { return Promise.resolve(); } const doctestOptions = parsedCode.doctestOptions; error.fileName = doctestOptions?.filePath ? doctestOptions.filePath : error.fileName; error.lineNumber = parsedCode.location.start.line; error.columnNumber = parsedCode.location.start.column; const metadata = parsedCode.metadata; if (metadata) { error.meta = metadata; } return Promise.reject(error); }); } export function run(code, options = {}) { const filePath = options.filePath || "default.js"; const runMode = options.runMode || "all"; const timeout = options.timeout !== undefined ? options.timeout : 2000; const postCallbackName = options.powerDoctestCallbackFunctionName || CALLBACK_FUNCTION_NAME; const context = options.context || {}; // total count of assert let totalAssertionCount = 0; // current count of assert let countOfExecutedAssertion = 0; return new Promise((resolve, reject) => { let isSettled = false; const timeoutId = setTimeout(() => { if (isSettled) { return; } restoreListener(); reject(new Error(`Timeout error ${runMode === "all" ? `If you use { "runMode": "all" }, you should check all condition flow is passed. Total Assertion: ${totalAssertionCount} Executed Assertion: ${countOfExecutedAssertion} Also, you should consider to use { "runMode": "any" }` : ""}`)); }, timeout); // Test Runner like mocha listen unhandledRejection and uncaughtException // Disable these listener before running code const originalUnhandledRejection = process.listeners("unhandledRejection"); const originalUncaughtException = process.listeners("uncaughtException"); process.removeAllListeners("uncaughtException"); process.removeAllListeners("unhandledRejection"); const unhandledRejection = (reason, _promise) => { restoreListener(); reject(reason); }; const uncaughtException = (error) => { restoreListener(); reject(error); }; const restoreListener = () => { isSettled = true; process.removeListener("uncaughtException", uncaughtException); process.removeListener("unhandledRejection", unhandledRejection); // restore const currentUncaughtException = process.listeners("uncaughtException"); const currentUnhandledRejection = process.listeners("unhandledRejection"); originalUncaughtException .filter((listener) => { // remove duplicated return !currentUncaughtException.includes(listener); }) .forEach((listener) => { process.addListener("uncaughtException", listener); }); originalUnhandledRejection .filter((listener) => { // remove duplicated return !currentUnhandledRejection.includes(listener); }) .forEach((listener) => { process.addListener("unhandledRejection", listener); }); // clearTimeout clearTimeout(timeoutId); }; process.on("uncaughtException", uncaughtException); process.on("unhandledRejection", unhandledRejection); const poweredCode = convertCode(code, { assertAfterCallbackName: postCallbackName, filePath: filePath, }); // total count of assert totalAssertionCount = poweredCode.split(CALLBACK_FUNCTION_NAME).length - 1; // current count of assert countOfExecutedAssertion = 0; try { const HostBuildIns = Object.freeze({ __proto__: null, version: parseInt(process.versions.node.split(".")[0]), process, console, setTimeout, setInterval, setImmediate, clearTimeout, clearInterval, clearImmediate, }); const script = new vm.Script(poweredCode, { filename: options.filePath, }); const vmContext = vm.createContext({ require: (moduleName) => { if (moduleName === "power-assert") { return assert; } // options.requireMock if (options.requireMock && Object.hasOwn(options.requireMock, moduleName)) { return options.requireMock[moduleName]; } return require(moduleName); }, [postCallbackName]: (_id) => { countOfExecutedAssertion++; if (runMode === "all" && countOfExecutedAssertion === totalAssertionCount) { // when all finish restoreListener(); resolve(); } else if (runMode === "any") { // when anyone finish restoreListener(); resolve(); } }, ...HostBuildIns, ...context, }); script.runInNewContext(vmContext, { timeout, }); } catch (error) { restoreListener(); reject(error); } // No assertion code if (totalAssertionCount === 0) { restoreListener(); resolve(); } }); } //# sourceMappingURL=index.js.map