manten
Version:
満点 - Lightweight testing library for Node.js
13 lines (11 loc) • 13.4 kB
JavaScript
var ue=Object.defineProperty;var i=(e,t)=>ue(e,"name",{value:t,configurable:!0});import{setMaxListeners as fe}from"node:events";import{inspect as j}from"node:util";import N from"node:path";import I from"node:fs";import q from"node:os";import{expect as Qe}from"expect";let b=!0;const S=typeof self<"u"?self:typeof window<"u"?window:typeof global<"u"?global:{};let T=0;if(S.process&&S.process.env&&S.process.stdout){const{FORCE_COLOR:e,NODE_DISABLE_COLORS:t,NO_COLOR:s,TERM:n,COLORTERM:o}=S.process.env;t||s||e==="0"?b=!1:e==="1"||e==="2"||e==="3"?b=!0:n==="dumb"?b=!1:"CI"in S.process.env&&["TRAVIS","CIRCLECI","APPVEYOR","GITLAB_CI","GITHUB_ACTIONS","BUILDKITE","DRONE"].some(c=>c in S.process.env)?b=!0:b=process.stdout.isTTY,b&&(process.platform==="win32"||o&&(o==="truecolor"||o==="24bit")?T=3:n&&(n.endsWith("-256color")||n.endsWith("256"))?T=2:T=1)}let z={enabled:b,supportLevel:T};function v(e,t,s=1){const n=`\x1B[${e}m`,o=`\x1B[${t}m`,c=new RegExp(`\\x1b\\[${t}m`,"g");return l=>z.enabled&&z.supportLevel>=s?n+(""+l).replace(c,n)+o:""+l}i(v,"kolorist");const g=v(2,22),F=v(31,39),J=v(32,39),V=v(33,39),Y=i(e=>Number.isFinite(e)?e:0,"toZeroIfInfinity");function de(e){return{days:Math.trunc(e/864e5),hours:Math.trunc(e/36e5%24),minutes:Math.trunc(e/6e4%60),seconds:Math.trunc(e/1e3%60),milliseconds:Math.trunc(e%1e3),microseconds:Math.trunc(Y(e*1e3)%1e3),nanoseconds:Math.trunc(Y(e*1e6)%1e3)}}i(de,"parseNumber");function he(e){return{days:e/86400000n,hours:e/3600000n%24n,minutes:e/60000n%60n,seconds:e/1000n%60n,milliseconds:e%1000n,microseconds:0n,nanoseconds:0n}}i(he,"parseBigint");function me(e){switch(typeof e){case"number":{if(Number.isFinite(e))return de(e);break}case"bigint":return he(e)}throw new TypeError("Expected a finite number or bigint")}i(me,"parseMilliseconds");const pe=i(e=>e===0||e===0n,"isZero"),ge=i((e,t)=>t===1||t===1n?e:`${e}s`,"pluralize"),ye=1e-7,be=24n*60n*60n*1000n;function P(e,t){const s=typeof e=="bigint";if(!s&&!Number.isFinite(e))throw new TypeError("Expected a finite number or bigint");t={...t};const n=e<0?"-":"";e=e<0?-e:e,t.colonNotation&&(t.compact=!1,t.formatSubMilliseconds=!1,t.separateMilliseconds=!1,t.verbose=!1),t.compact&&(t.unitCount=1,t.secondsDecimalDigits=0,t.millisecondsDecimalDigits=0);let o=[];const c=i((f,d)=>{const p=Math.floor(f*10**d+ye);return(Math.round(p)/10**d).toFixed(d)},"floorDecimals"),l=i((f,d,p,h)=>{if(!((o.length===0||!t.colonNotation)&&pe(f)&&!(t.colonNotation&&p==="m"))){if(h??=String(f),t.colonNotation){const w=h.includes(".")?h.split(".")[0].length:h.length,$=o.length>0?2:1;h="0".repeat(Math.max(0,$-w))+h}else h+=t.verbose?" "+ge(d,f):p;o.push(h)}},"add"),a=me(e),r=BigInt(a.days);if(t.hideYearAndDays?l(BigInt(r)*24n+BigInt(a.hours),"hour","h"):(t.hideYear?l(r,"day","d"):(l(r/365n,"year","y"),l(r%365n,"day","d")),l(Number(a.hours),"hour","h")),l(Number(a.minutes),"minute","m"),!t.hideSeconds)if(t.separateMilliseconds||t.formatSubMilliseconds||!t.colonNotation&&e<1e3&&!t.subSecondsAsDecimals){const f=Number(a.seconds),d=Number(a.milliseconds),p=Number(a.microseconds),h=Number(a.nanoseconds);if(l(f,"second","s"),t.formatSubMilliseconds)l(d,"millisecond","ms"),l(p,"microsecond","\xB5s"),l(h,"nanosecond","ns");else{const w=d+p/1e3+h/1e6,$=typeof t.millisecondsDecimalDigits=="number"?t.millisecondsDecimalDigits:0,le=w>=1?Math.round(w):Math.ceil(w),B=$?w.toFixed($):le;l(Number.parseFloat(B),"millisecond","ms",B)}}else{const f=(s?Number(e%be):e)/1e3%60,d=typeof t.secondsDecimalDigits=="number"?t.secondsDecimalDigits:1,p=c(f,d),h=t.keepDecimalsOnWholeSeconds?p:p.replace(/\.0+$/,"");l(Number.parseFloat(h),"second","s",h)}if(o.length===0)return n+"0"+(t.verbose?" milliseconds":"ms");const u=t.colonNotation?":":" ";return typeof t.unitCount=="number"&&(o=o.slice(0,Math.max(t.unitCount,1))),n+o.join(u)}i(P,"prettyMilliseconds");const y=`
`,we=" ",{log:A,error:k}=console,Se=J("\u2714"),Te=F("\u2716"),$e=V("\u2022"),ve=g("\u25CB"),R=i(()=>{const e=new Date,t=String(e.getHours()).padStart(2,"0"),s=String(e.getMinutes()).padStart(2,"0"),n=String(e.getSeconds()).padStart(2,"0");return g(`${t}:${s}:${n}`)},"formatTimestamp"),U=i(({startTime:e,timeout:t,endTime:s})=>{const n=(s||Date.now())-e;let o=P(n);return t&&(o+=` / ${P(t)}`),n<50?"":` ${g(`(${o})`)}`},"prettyDuration"),ke=i(e=>e.replaceAll(/^/gm,we),"indentMultiline"),M=i((e,t=!0)=>{const{title:s,attempt:n,retry:o}=e;let c=`${s+U(e)}`;return t&&o>1&&(c+=g(` (${n}/${o})`)),c},"getTestTitle"),O=i((e,t,s)=>{let n=`${R()} ${Te} ${M(e)}`;s&&(n+=` [${s}]`),k(n),k(`${ke(j(t))}
`)},"logTestFail"),Ee=i(e=>{A(`${R()} ${Se} ${M(e)}`)},"logTestSuccess"),W=i(e=>{A(`${R()} ${ve} ${M(e,!1)}`)},"logTestSkip"),Le=i(e=>{if(e.length===0)return;const t=[];let s=0,n=0,o=0,c,l;for(const r of e)r.startTime&&(!c||c>r.startTime)&&(c=r.startTime),r.endTime===void 0?t.push(r):((!l||l<r.endTime)&&(l=r.endTime),r.error?n+=1:r.skip?o+=1:s+=1);let a="";if(t.length>0){for(const r of t)a+=`${y}${$e} ${r.title+U(r)}`;a+=y}a+=`${y}${g(P((l??Date.now())-c))}`,a+=y+(s>0?J:g)(`${s.toLocaleString()} passed`),n>0&&(a+=y+F(`${n.toLocaleString()} failed`)),o>0&&(a+=y+g(`${o.toLocaleString()} skipped`)),t.length>0&&(a+=y+V(`${t.length.toLocaleString()} pending`)),a+=y,A(a)},"logReport"),De=i(()=>{let e,t;const s=new Promise((n,o)=>{e=n,t=o});return Object.assign(s,{resolve:e,reject:t})},"createDeferred"),Ne=i((e,t)=>{const s=De(),n=setTimeout(()=>{const o=new Error(`Timeout: ${e}ms`);t?.abort(o),s.reject(o)},e);return Object.assign(s,{timeoutId:n})},"setTimer"),G=i((e,t,s)=>{if(!e||!("then"in e)||t===void 0||t===0)return e;const n=Ne(t,s);return Promise.race([e,n]).finally(()=>{clearTimeout(n.timeoutId)})},"timeLimitFunction"),K=i(e=>{const t=[];return{addHook:i(o=>{t.push(o)},"addHook"),runHooks:i(async(...o)=>{for(const c of t)try{await c(...o)}catch(l){await e(l)}},"runHooks")}},"createHook"),Ie=i(async(e,t)=>{for(let s=1;s<=t;s+=1)try{await e(s);return}catch(n){if(s===t)throw n}},"retry"),Fe=i(e=>{const t=Object.keys(e).sort(),s={};for(const n of t)s[n]=e[n];return JSON.stringify(s,null,2)},"stableJsonStringify"),Pe=i(e=>{const t=[];return e.new>0&&t.push(`\u{1F4F8} ${e.new} new`),e.updated>0&&t.push(`\u270F\uFE0F ${e.updated} updated`),t.length>0?`
Snapshots: ${t.join(", ")}`:""},"formatSnapshotSummary"),Ae=i(e=>j(e,{depth:null,sorted:!0,breakLength:80,maxArrayLength:1/0,maxStringLength:1/0}),"serialize");let m;const E=new Set,L=new Set,C=new Map;let Z=!1,_=process.env.MANTEN_SNAPSHOT_PATH||".manten.snap";const Re=process.env.MANTEN_UPDATE_SNAPSHOTS==="1"||process.env.MANTEN_UPDATE_SNAPSHOTS==="true",Me=i(()=>{if(m===void 0)try{const e=N.resolve(_),t=I.readFileSync(e,"utf8");m=JSON.parse(t)}catch{m={}}},"loadSnapshots"),Oe=i((e,t)=>{m===void 0&&Me();const s=C.get(e);if(s!==void 0&&s!==t)throw new Error(`Duplicate test title detected: "${e}". Test titles must be unique across all files when using global snapshots.`);C.set(e,t);let n=0;return{expectSnapshot:i((l,a)=>{let r;a?r=a:(n+=1,r=`${e} ${n}`);const u=Ae(l);if(E.has(r)||L.has(r)){const d=m[r];if(u===d)return;throw new Error(`Duplicate snapshot key: "${r}". Test names must be unique across all test files.`)}if(!Object.hasOwn(m,r)){m[r]=u,E.add(r);return}const f=m[r];if(u!==f)if(Re)m[r]=u,L.add(r);else throw new Error(`Snapshot mismatch for "${r}"
Expected:
${f}
Received:
${u}`)},"expectSnapshot"),reset:i(()=>{n=0},"reset")}},"createSnapshotContext"),_e=i(()=>{if(E.size===0&&L.size===0||!m)return;const e=N.resolve(_),t=N.dirname(e);Z||(I.mkdirSync(t,{recursive:!0}),Z=!0);const s=Fe(m);I.writeFileSync(e,s,"utf8")},"saveSnapshots"),xe=i(()=>({new:E.size,updated:L.size}),"getSnapshotSummary"),He=i(e=>{if(e.snapshotPath!==void 0&&m!==void 0)throw new Error("configure() must be called before any snapshot tests are run. Snapshots have already been loaded from the default location. Please ensure configure() is called at the beginning of your test suite.");e.snapshotPath!==void 0&&(_=e.snapshotPath)},"configure");class Q extends Error{static{i(this,"SkipError")}constructor(t){super(t||"Test skipped"),this.name="SkipError"}}const X=i(e=>(e&&typeof e=="object"&&"matcherResult"in e&&e.constructor.name==="JestAssertionError"&&delete e.matcherResult,e),"patchJestAssertionError"),ee=i(async(e,t)=>{const{testFunction:s,timeout:n}=e,o=K(r=>{O(e,r,"onTestFail")}),c=i(async r=>{await o.runHooks(r)},"handleError"),l=K(async r=>{O(e,X(r),"onTestFinish")}),a=Oe(e.title,e);try{await Ie(async r=>{e.attempt=r,e.startTime=Date.now(),r>1&&a.reset();const u=new AbortController;let f;t&&(f=i(()=>{u.signal.aborted||u.abort(t.abortController.signal.reason)},"handleParentAbort"),t.abortController.signal.addEventListener("abort",f),t.abortController.signal.aborted&&f());try{await G(s({signal:u.signal,onTestFail:o.addHook,onTestFinish:l.addHook,skip:i(d=>{throw new Q(d)},"skip"),expectSnapshot:a.expectSnapshot}),n,u),Ee(e)}catch(d){if(d instanceof Q)e.skip=!0,W(e);else throw O(e,X(d)),await c(d),d}finally{t&&f&&t.abortController.signal.removeEventListener("abort",f),u.signal.aborted||u.abort(),await l.runHooks(),e.endTime=Date.now()}},e.retry)}catch(r){e.error=r,process.exitCode=1}},"runTest"),te=[];process.on("exit",()=>{try{_e()}catch(t){process.stderr.write(`Failed to save snapshots: ${t}
`)}Le(te);const e=xe();if(e.new>0||e.updated>0){const t=Pe(e);process.stdout.write(`${t}
`)}});const D=process.env.TESTONLY;D&&console.log(g(`Only running tests that match: ${JSON.stringify(D)}
`));const se=i((e,t)=>((s,n,o)=>{if(e&&(s=`${e} ${s}`),t&&(t.testsStarted=!0),D&&!s.includes(D))return Promise.resolve();const c={title:s,testFunction:n,retry:1};if(o!==void 0&&(typeof o=="number"?c.timeout=o:(c.timeout=o?.timeout,o?.retry&&(c.retry=o?.retry))),te.push(c),t?.skipped){c.skip=!0,c.skipReason=t.skipReason;const l=Date.now();return c.startTime=l,c.endTime=l,W(c),Promise.resolve()}return(async()=>{const a=i(async()=>{if(t?.concurrencyLimiter){const r=await t.concurrencyLimiter.acquire();try{await ee(c,t)}finally{r()}}else await ee(c,t)},"executeTest")();t&&t.pendingTests.push(a),await a})()}),"createTest"),Be=i(async e=>{for(;e.length>0;){const t=e.splice(0);await Promise.all(t)}},"waitAllPromises"),je=i(e=>("default"in e&&(e=e.default),"default"in e&&(e=e.default),e),"unwrapModule"),x=i(e=>{let t=typeof e=="number"?e:ne(),s=0;const n=[];let o;return e==="auto"&&(o=setInterval(()=>{const r=ne();if(r!==t)for(t=r;n.length>0&&s<t;){const u=n.shift();u&&(s+=1,u())}},5e3),o.unref()),{acquire:i(()=>s<t?(s+=1,Promise.resolve(()=>{s-=1;const r=n.shift();r&&(s+=1,r())})):new Promise(r=>{n.push(()=>{r(()=>{s-=1;const u=n.shift();u&&(s+=1,u())})})}),"acquire"),setLimit:i(r=>{for(t=r;n.length>0&&s<t;){const u=n.shift();u&&(s+=1,u())}},"setLimit"),cleanup:i(()=>{o&&clearInterval(o)},"cleanup")}},"createSemaphore"),ne=i(()=>{const e=q.cpus().length,t=q.loadavg()[0];return Math.max(1,Math.min(Math.floor(e*(1-Math.min(t/e,.8))),e))},"calculateAutoLimit"),oe=i((e,t)=>((s,n,o)=>(e&&(s=`${e} ${s}`),t&&(t.testsStarted=!0),(async()=>{const c=H(s,o?.parallel,o?.timeout);await i(async()=>{if(t?.concurrencyLimiter){const a=await t.concurrencyLimiter.acquire();try{await c.run(n,t)}finally{a()}}else await c.run(n,t)},"executeDescribe")()})())),"createDescribe"),re=i((e,t)=>((s,...n)=>{t&&(t.testsStarted=!0);const o=i(()=>{const c=H(e);return c.run(async()=>je(await s).apply(c,n),t)},"executeTestSuite");return t?.concurrencyLimiter?t.concurrencyLimiter.acquire().then(c=>o().finally(c)):o()}),"createRunTestSuite"),ie=se(),ce=oe(),ae=re(),H=i((e,t,s)=>{let n;t!==void 0&&(t===!0?n=void 0:t===!1?n=x(1):typeof t=="number"?n=x(t):t==="auto"&&(n=x("auto")));const o=new AbortController;fe(0,o.signal);const c={pendingTests:[],callbacks:{onFinish:[]},concurrencyLimiter:n,abortController:o,timeout:s,skipped:!1,skipReason:void 0,testsStarted:!1,run:i(async(l,a)=>{a?.skipped&&(c.skipped=!0,c.skipReason=a.skipReason);let r;a&&(r=i(()=>{o.signal.aborted||o.abort(a.abortController.signal.reason)},"handleParentAbort"),a.abortController.signal.addEventListener("abort",r),a.abortController.signal.aborted&&r());try{const u=(async()=>{await l(c.api),await Be(c.pendingTests)})();a&&a.pendingTests.push(u),await G(u,s,o)}catch(u){k(u),process.exitCode=1}finally{a&&r&&a.abortController.signal.removeEventListener("abort",r),o.signal.aborted||o.abort(),n&&n.cleanup();for(const u of c.callbacks.onFinish)try{await u()}catch(f){k(f),process.exitCode=1}}},"run")};return c.api={signal:o.signal,test:e?se(`${e} \u203A`,c):ie,describe:e?oe(`${e} \u203A`,c):ce,runTestSuite:e?re(e,c):ae,onFinish:i(l=>{c.callbacks.onFinish.push(l)},"onFinish"),skip:i(l=>{if(c.testsStarted)throw new Error("skip() must be called before any tests or nested describes run. Move skip() to the beginning of the describe callback.");c.skipped=!0,c.skipReason=l},"skip")},c},"createContext"),qe=H(),ze=i((e,t,s)=>{const n=typeof e=="string"?e:void 0,o=typeof e=="string"?t:e,c=typeof e=="string"?s:void 0;return i(async function(...a){const r=this||qe;n?await r.api.describe(n,u=>o(u,...a),c):await o(r.api,...a)},"testSuiteWrapper")},"testSuite"),Je=i(e=>{setTimeout(()=>{process.exitCode=1,console.error(F(`\u2716 Process timed out after ${e}ms`)),setImmediate(()=>process.exit(1))},e).unref()},"setProcessTimeout");export{He as configure,ce as describe,Qe as expect,ae as runTestSuite,Je as setProcessTimeout,ie as test,ze as testSuite};