UNPKG

under-the-sun

Version:

A nothing-new-under-the-sun testing framework.

19 lines (16 loc) 5.64 kB
#!/usr/bin/env node "use strict";var I=Object.create;var y=Object.defineProperty;var j=Object.getOwnPropertyDescriptor;var O=Object.getOwnPropertyNames;var B=Object.getPrototypeOf,C=Object.prototype.hasOwnProperty;var W=(e,t,r,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of O(t))!C.call(e,s)&&s!==r&&y(e,s,{get:()=>t[s],enumerable:!(o=j(t,s))||o.enumerable});return e};var u=(e,t,r)=>(r=e!=null?I(B(e)):{},W(t||!e||!e.__esModule?y(r,"default",{value:e,enumerable:!0}):r,e));var f=u(require("node:path"));var T=require("node:perf_hooks");var c=require("perf_hooks");function b(){let e=c.performance.now(),t=0,r=0;return{reportStart(){e=c.performance.now()},reportResult(o,s){if(!s.error){t++;return}r++,S("FAIL: "+o),console.error(s.error.stack??s.error.message),console.error()},reportFinish(){let o=t+r,s=c.performance.now()-e,n=`${t}/${o} tests passed (${Math.ceil(s)}ms)`;(r>0?S:z)(n)}}}function S(e){console.error("\x1B[31m%s\x1B[0m",e)}function z(e){console.info("\x1B[32m%s\x1B[0m",e)}var E=u(require("node:assert"));async function P(e){d("runTests() should not be called while another test run is in progress");let t=a();try{return t.reporter.reportStart?.(),t.testsRunning=[],await e(),!(await Promise.allSettled(t.testsRunning)).some(o=>o.status==="rejected"||o.value.error!==null)}finally{t.testsRunning=null,t.reporter.reportFinish?.()}}function k(e){let t=a();t.testsRunning!==null&&t.filterByDescription.test(e.description)&&t.testsRunning.push(t.scheduler.schedule(e))}function d(e){E.default.strictEqual(a().testsRunning,null,e)}function v(){return{schedule(e){return e.run()}}}function D(e){d("Scheduler should not be set in the middle of a test run"),a().scheduler=e}var F=v;var g=global??window??{},h=Symbol.for("__UNDER_THE_SUN_STATE__"),p;function a(){return p||(g[h]||(g[h]={testsRunning:null,reporter:b(),scheduler:F(),filterByDescription:/.*/}),p=g[h],p)}function R(e){return e instanceof Error?e:new Error(JSON.stringify(e))}async function G(e){try{return await e(),null}catch(t){return R(t)}}function w(e,t){k({description:e,run:async()=>{let o=T.performance.now(),s=await G(t),i={runtimeMs:T.performance.now()-o,error:s};return await a().reporter.reportResult(e,i),i}})}function L(e){return(t,r)=>w(e+t,r)}function M(){let e=Promise.resolve({runtimeMs:0,error:null});return{schedule(t){return e=e.then(async()=>t.run()),e}}}var _=u(require("node:fs/promises")),q=u(require("node:path"));async function x({dir:e,ignorePattern:t},r){let o=await _.default.readdir(e,{withFileTypes:!0});await Promise.allSettled(o.map(s=>{let n=q.default.posix.join(e,s.name);if(!t.test(n))return s.isDirectory()?x({dir:n,ignorePattern:t},r):r(n)}))}var J=e=>new Function(`return import(${JSON.stringify(e)})`).call(0);async function m(e){try{require(e)}catch(t){if(t instanceof Error&&t.code==="ERR_REQUIRE_ESM")await J(e);else throw t}}function A(e){try{return H(e)}catch(t){throw console.error($),t}}function H(e){let t={dir:".",ignoreRegex:/(^|\/)(node_modules($|\/)|\.)/,testFilePathRegex1:/\.(spec|test)\.[mc]?[jt]sx?$/,testFilePathRegex2:/.*/,testDescriptionRegex:/.*/,magicGlobal:!1,modulesToLoad:[],serial:!1},r=e.slice(2),o=0,s=()=>{let n=r.shift();switch(n){case"-h":case"--help":console.log($),process.exit(2);return;case"-i":case"--ignore":{let i=r.shift();if(!i)throw new Error("-i/--ignore requires an argument");t.ignoreRegex=new RegExp(i);return}case"-m":case"--magic":t.magicGlobal=!0;return;case"-p":case"--pattern":{let i=r.shift();if(!i)throw new Error("-p/--pattern requires an argument");t.testFilePathRegex1=new RegExp(i);return}case"-r":case"--require":{let i=r.shift();if(!i)throw new Error("-r requires an argument");t.modulesToLoad.push(i);return}case"-s":case"--serial":t.serial=!0;return}if(n.startsWith("-"))throw n.includes("=")?new Error(`Unrecognized option ${n}. (This tool does not support \`--some-arg=some-value\`-style options. Use \`--some-arg some-value\` instead.)`):new Error(`Unrecognized option ${n}`);switch(o++,o){case 1:t.dir=n;return;case 2:t.testFilePathRegex2=new RegExp(n);return;case 3:t.testDescriptionRegex=new RegExp(n);return}throw new Error(`Too many arguments, starting with ${n}`)};for(;r.length>0;)s();return t}var $=` Usage: under-the-sun [-r <module>] [-m] [-s] [-p <file pattern>] [<dir> [<file filter> [<description filter>]]] Parameters: <dir> Directory in which to find tests <file filter> Regex pattern for matching test file names, applied *in addition to* -p/--pattern <description filter> Regex pattern for matching test descriptions Options: --magic, -m Make "test" available as a global [boolean] --pattern, -p Regex pattern for matching test files [string] --require, -r Preload the specified module at startup [string] --serial, -s Run tests sequentially, not in parallel [boolean] `;function N(e){e().then(()=>{process.exit(0)},t=>{console.error(t),process.exit(1)})}var Q=a(),l=A(process.argv),U=f.default.relative(__dirname,process.cwd()).replace(/\\/g,"/");async function K(){for(let e of l.modulesToLoad)/^\.{1,2}\//.test(e)?await m(f.default.posix.join(U,e)):await m(e)}function V(){if(Q.filterByDescription=l.testDescriptionRegex,l.serial&&D(M()),l.magicGlobal){let e=global;e.test=w,e.defineTestGroup=L}}function X(e){return l.testFilePathRegex1.test(e)&&l.testFilePathRegex2.test(e)}N(async()=>{await K(),V();let e=await P(()=>x({dir:l.dir,ignorePattern:l.ignoreRegex},async t=>{if(!!X(t))try{await m(f.default.posix.join(U,t))}catch(r){await a().reporter.reportResult(t,{runtimeMs:0,error:R(r)})}}));process.exit(e?0:1)});