@checksum-ai/runtime 4.16.1 → 4.16.2-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env +1 -1
- package/cli.js +1 -1
- package/index.js +1 -1
- package/package.json +2 -2
- package/scripts/playwright_patches/1.62.1.js +562 -0
package/.env
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
CHECKSUM_RUNTIME_BUILD_TIME=2026-08-
|
|
1
|
+
CHECKSUM_RUNTIME_BUILD_TIME=2026-08-16T12:55:44.617Z
|
package/cli.js
CHANGED
|
@@ -438,7 +438,7 @@ export default {
|
|
|
438
438
|
import { RunMode, getChecksumConfig } from "@checksum-ai/runtime";
|
|
439
439
|
|
|
440
440
|
export default getChecksumConfig(${JSON.stringify(this.config,null,2)});
|
|
441
|
-
`;(0,at.writeFileSync)(n,a)}async cleanup(){this.runtimeAnalytics&&(re("Flushing events..."),await Ye(this.runtimeAnalytics.close(),2e4)),this.deleteVolatileConfig(),this.deleteWrappedPlaywrightConfig(),this.childProcesses.forEach(n=>{n.kill("SIGTERM")})}async getEnvInfo(){let n=this.config?Vae(this.config):void 0,
|
|
441
|
+
`;(0,at.writeFileSync)(n,a)}async cleanup(){this.runtimeAnalytics&&(re("Flushing events..."),await Ye(this.runtimeAnalytics.close(),2e4)),this.deleteVolatileConfig(),this.deleteWrappedPlaywrightConfig(),this.childProcesses.forEach(n=>{n.kill("SIGTERM")})}async getEnvInfo(){let n=this.getExplicitRunTitle(),a=this.config?Vae(this.config):void 0,l={commitHash:"",branch:"branch",environment:process.env.CI?"CI":"local",...a?{testingEnvironment:a}:{},name:"name",startedAt:Date.now(),runtimeVersion:this.checksumRuntimeVersion,request:{...de.testRun.request,...n?{title:n}:{}},platform:de.testRun.platform,runType:de.testRun.runType};try{l.commitHash=await this.getCmdOutput("git rev-parse HEAD")}catch(i){console.log("Error getting git hash",i.message)}try{l.branch=await this.getCmdOutput("git rev-parse --abbrev-ref HEAD")}catch(i){console.log("Error getting branch",i.message)}return l.name=await this.getEnvName(n),l}getExplicitRunTitle(){let n=this.getChecksumArg("title");return typeof n=="string"&&n.length>0?n:void 0}async getEnvName(n){if(n)return n;let a;try{a=await this.getCmdOutput("git log -1 --pretty=%B")}catch(l){console.log("Error getting name",l.message)}return a}getVolatileConfigPath(){return(0,yt.join)(this.checksumRoot??"","checksum.config.tmp.ts")}deleteVolatileConfig(){let n=this.getVolatileConfigPath();(0,at.existsSync)(n)&&(0,at.rmSync)(n)}loadChecksumConfig(n={}){let a={...this.readChecksumConfigFile(n),...this.volatileChecksumConfig??{}};if(this.debugMode){if(!a.options)throw new Error("Checksum config options are not initialized");a.options.printLogs=!0}this.config=a,this.traceChecksumConfig()}readChecksumConfigFile(n={}){let a=(0,yt.join)(this.checksumRoot,"checksum.config.ts");if(!(0,at.existsSync)(a))return n.muteChecksumConfigMissingError||console.warn(`No checksum.config.ts found at ${a}. Continuing with default configuration.`),{};try{return zO(a)}catch(l){return console.log(`Failed to load checksum.config.ts at ${a}:
|
|
442
442
|
${l.message||l}`),process.exit(1)}}traceChecksumConfig(){!this.runtimeAnalytics||!this.config||this.runtimeAnalytics.checksumConfig(this.config)}processConfigArg(){this.deleteVolatileConfig();let n=this.getChecksumArg("config");if(n)try{this.volatileChecksumConfig=JSON.parse(n)}catch(a){this.volatileChecksumConfig=void 0,console.log("Error parsing cksm config",a.message)}}getNonChecksumArgs(){return this.args.filter(n=>!this.isChecksumArg(n))}getArg(n){let a=this.args.find(i=>i.startsWith(`${n}`)),l=a?.split("=")[1]?.trim();return l||!!a}getChecksumArg(n){return this.getArg(`--cksm-${n}`)}getChecksumRootOrigin(){return(0,yt.join)(this.projectRootDirectory,"node_modules","@checksum-ai","runtime","checksum-root")}locateChecksumLibs(){try{this.findProjectRoot(),this.findChecksumRoot()}catch(n){console.log(n.message),process.exit(1)}}findProjectRoot(){let n=process.cwd(),a=n;for(let l=0;l<10;l++){if((0,at.existsSync)((0,yt.join)(a,".git"))){if(this.absoluteProjectRootDirectory)break;this.absoluteProjectRootDirectory=a,re("Project root directory found at (git root)",this.absoluteProjectRootDirectory)}if((0,yt.parse)(a).root===a)break;a=(0,yt.join)(a,"..")}n=process.cwd();for(let l=0;l<6;l++){let i=(0,yt.join)(n,"node_modules","@checksum-ai","runtime");if((0,at.existsSync)(i)){this.projectRootDirectory=n,this.absoluteProjectRootDirectory||(this.absoluteProjectRootDirectory=n),re("Project root directory found at (node_modules fallback)",this.projectRootDirectory);return}if((0,yt.parse)(n).root===n)break;n=(0,yt.join)(n,"..")}if(this.absoluteProjectRootDirectory){this.projectRootDirectory=this.absoluteProjectRootDirectory;return}throw new Error(process.env.CHECKSUM_RUNTIME_DEV_MODE?"Could not resolve checksum root origins, tried to look in .git and node modules. please build the runtime":"Could not resolve checksum root origins")}setChecksumRoot(n){this.checksumRoot=n,re("Checksum root directory found at",this.checksumRoot),this.createInternalFilesDir()}findChecksumRoot(){if(de.checksumRootFolderBypass){let a=(0,yt.isAbsolute)(de.checksumRootFolderBypass)?de.checksumRootFolderBypass:(0,yt.join)(this.projectRootDirectory,de.checksumRootFolderBypass);this.setChecksumRoot(a);return}let n=[this.projectRootDirectory];for(;n.length;){let a=n.pop();if(a===void 0)break;let l=(0,at.readdirSync)(a,{withFileTypes:!0});for(let i of l){let f=(0,yt.join)(a,i.name),d=f;if(i.isSymbolicLink())try{let p=(0,at.readlinkSync)(f);if(!(0,at.lstatSync)(p).isDirectory())continue;d=p}catch{continue}else if(!i.isDirectory())continue;if(i.name===Sv){if(wWe(d)){this.setChecksumRoot(d);return}}else n.push(f)}}throw new Error("Could not find checksum root folder. Run `npx checksumai init` to create one.")}log(n,a=!1){a?this.printError(n):console.log(n)}runtimeTrace(n,a){this.runtimeAnalytics?.eventWithMessage(a,n)}isChecksumArg(n){return n.startsWith("--cksm-")}};function $c(r,n){for(let a of r){if(a===n)return"";if(a.startsWith(`${n}=`))return a.slice(n.length+1).trim()}}u($c,"findFlagValue");function eS(r,n){return $c(r,n)!==void 0}u(eS,"findFlag");var KA="--cksm-auto-heal",Mi={enable:`${KA}`,createPr:`${KA}-create-pr`,repoName:`${KA}-repo-name`,prNumber:`${KA}-pr-number`,branch:`${KA}-branch`};function oce(r){if(!eS(r.args,Mi.enable))return{kind:"disabled"};let n=$c(r.args,Mi.createPr),a=$c(r.args,Mi.repoName),l=$c(r.args,Mi.prNumber),i=$c(r.args,Mi.branch),f=CWe(n,!0);if(f===void 0)return{kind:"error",message:`${Mi.createPr}: expected "true" or "false" (or omit a value), got "${n}"`};let d;if(l!==void 0&&l!==""){let t=Number(l);if(!Number.isInteger(t)||t<=0)return{kind:"error",message:`${Mi.prNumber}: expected a positive integer, got "${l}"`};d=t}let p=SWe(r.env),h=typeof a=="string"&&a!==""?a:p.repoName,o=typeof i=="string"&&i!==""?i:p.branch,c=d??p.prNumber;if(f&&!h)return{kind:"error",message:`${Mi.repoName} is required when auto-create-pr is enabled \u2014 pass --cksm-auto-heal-repo-name=owner/repo or set GITHUB_REPOSITORY / CI_PROJECT_PATH`};if(c!==void 0&&!h)return{kind:"error",message:`${Mi.repoName} is required when ${Mi.prNumber} is set`};let e={autoCreatePR:f};return o&&(e.branch=o),c!==void 0&&(e.prNumber=c),h&&(e.repoName=h),{kind:"enabled",options:e}}u(oce,"resolveAutoHealOptions");function SWe(r){if(r.GITHUB_ACTIONS==="true"){let n=r.GITHUB_REPOSITORY||void 0,a=r.GITHUB_HEAD_REF||r.GITHUB_REF_NAME||void 0,l;if(r.GITHUB_EVENT_NAME==="pull_request"){let f=r.GITHUB_REF?.match(/^refs\/pull\/(\d+)\//);if(f){let d=Number(f[1]);Number.isInteger(d)&&d>0&&(l=d)}}let i={};return n&&(i.repoName=n),a&&(i.branch=a),l!==void 0&&(i.prNumber=l),i}if(r.GITLAB_CI==="true"){let n=r.CI_PROJECT_PATH||void 0,a=r.CI_MERGE_REQUEST_SOURCE_BRANCH_NAME||r.CI_COMMIT_REF_NAME||void 0,l;if(r.CI_MERGE_REQUEST_IID){let f=Number(r.CI_MERGE_REQUEST_IID);Number.isInteger(f)&&f>0&&(l=f)}let i={};return n&&(i.repoName=n),a&&(i.branch=a),l!==void 0&&(i.prNumber=l),i}return{}}u(SWe,"detectCiContext");function CWe(r,n){if(r===void 0)return n;if(r===""||r==="true")return!0;if(r==="false")return!1}u(CWe,"parseBoolFlag");function rce(r){let n=r.body&&typeof r.body=="object"?r.body:{};if(!r.ok){let a=kWe(n.message);return{kind:"fatal",message:`Checksum could not create the test run (HTTP ${r.status})`+(a?`: ${a}`:".")}}return n.uuid?{kind:"success",uuid:n.uuid,...n.appName!==void 0?{appName:n.appName}:{}}:{kind:"fatal",message:`Checksum test-run creation (HTTP ${r.status}) returned no session id; the report cannot be hosted.`}}u(rce,"decideSessionCreateOutcome");function kWe(r){return typeof r=="string"?r:Array.isArray(r)?r.filter(n=>typeof n=="string").join("; "):""}u(kWe,"extractMessage");var TWe="--cksm-affected",PWe="--cksm-affected-dry-run";function ice(r){let n=$c(r.args,TWe),a=eS(r.args,PWe);return n===void 0&&!a?{kind:"disabled"}:{kind:"enabled",baseRef:n?.trim()||xWe(r.env),dryRun:a}}u(ice,"resolveAffectedTestsOptions");function xWe(r){let n=r.GITHUB_BASE_REF?.trim();if(n)return`origin/${n}`;let a=r.CI_MERGE_REQUEST_TARGET_BRANCH_NAME?.trim();if(a)return`origin/${a}`}u(xWe,"detectCiBaseRef");var _We=/^[A-Za-z0-9._/~^@-]+$/,RWe=/^[0-9a-fA-F]{4,40}$/;function ace(r){if(!_We.test(r))throw new Error(`Unsafe git ref for --cksm-affected: "${r}". Only letters, digits and . _ / - are allowed.`);return r}u(ace,"assertSafeRef");async function BWe(r,n){let a=r?.trim();if(a)return ace(a);try{let l=(await n("git symbolic-ref --short refs/remotes/origin/HEAD")).trim();if(l)return ace(l)}catch{}for(let l of["origin/main","origin/master"])try{return await n(`git rev-parse --verify --quiet ${l}`),l}catch{}throw new Error("Could not resolve a base git ref for --cksm-affected. Pass one explicitly, e.g. --cksm-affected=origin/main")}u(BWe,"resolveBaseRef");async function cce(r,n){let a=await BWe(r,n),l="false";try{l=(await n("git rev-parse --is-shallow-repository")).trim()}catch{}if(l==="true")throw new Error("--cksm-affected needs full git history but this is a shallow clone. In GitHub Actions set `fetch-depth: 0` on actions/checkout (or `GIT_DEPTH: 0` / `git fetch --unshallow` in GitLab CI).");let i=(await n(`git merge-base "${a}" HEAD`)).trim();if(!RWe.test(i))throw new Error(`Unexpected merge-base output for --cksm-affected: "${i}"`);return(await n(`git diff --name-only "${i}"`)).split(`
|
|
443
443
|
`).map(d=>d.trim()).filter(Boolean)}u(cce,"computeAffectedChangedFiles");var AUt=require("@playwright/test/reporter"),ip=require("fs"),ap=require("path");var W3=u((r,n,a)=>`v2:${DWe(r)}::${JSON.stringify([...n,a])}`,"computeVariantKey"),DWe=u(r=>(r??"").replace(/\\/g,"/"),"normalizeFilePath");var tS=require("fs"),Kt=ee(require("typescript"));var $3=5,rp={mint(){return fy($3)},parse(r){let n=rp.tryParse(r);if(n===null)throw new TypeError(`Not a usable checksumTestId: ${JSON.stringify(r)}`);return n},tryParse(r){return typeof r!="string"||r.trim()===""?null:r},isSame(r,n){return r===n}};var sce=" - ",lce=/(?:\.fix)?\.checksum\.(?:(?:spec|test)\.(?:ts|js|tsx|jsx)|md)$/i,NWe=/^[A-Za-z0-9_-]+$/,LWe=new RegExp(`^[0-9A-Za-z]{${$3}}$`);function uce(r){return QWe(r).id}u(uce,"readTestIdFromFileName");function QWe(r){let n=MWe(r);if(n===null)return{title:fce(r),id:null};let a=n.lastIndexOf(sce);if(a===-1)return{title:n,id:LWe.test(n)?rp.tryParse(n):null};let l=n.slice(a+sce.length);return NWe.test(l)?{title:n.slice(0,a),id:rp.tryParse(l)}:{title:n,id:null}}u(QWe,"splitFileName");function fce(r){let n=r.replace(/\\/g,"/");return n.slice(n.lastIndexOf("/")+1)}u(fce,"basename");function MWe(r){let n=fce(r);return lce.test(n)?n.replace(lce,""):null}u(MWe,"stemOf");var G3=class{static{u(this,"FileContentReader")}read(n,a){if(!(!n||!(0,tS.existsSync)(n)))try{let l=(0,tS.readFileSync)(n,"utf-8"),i=Kt.createSourceFile(n,l,Kt.ScriptTarget.Latest,!0),f=new Map,d=u(p=>{if(Kt.isCallExpression(p)&&this.isTestCall(p.expression)){let h=p.arguments[0];if(h&&Kt.isCallExpression(h)&&Kt.isIdentifier(h.expression)&&h.expression.text==="defineChecksumTest"){let o=this.extractDefineChecksumTestArgs(h);o&&f.set(o.testTitle,o.checksumTestId)}}Kt.forEachChild(p,d)},"visit");return d(i),a&&f.has(a)?f.get(a):f.size>0?Array.from(f.values())[0]:void 0}catch{return}}isTestCall(n){return Kt.isIdentifier(n)&&n.text==="test"?!0:Kt.isPropertyAccessExpression(n)?Kt.isIdentifier(n.expression)&&n.expression.text==="test":!1}extractDefineChecksumTestArgs(n){let a=n.arguments;if(a.length<2)return;let l=a[0],i=a[1];if(!l||!i)return;let f=this.extractStringLiteral(l),d=rp.tryParse(this.extractStringLiteral(i));if(f&&d)return{testTitle:f,checksumTestId:d}}extractStringLiteral(n){if(Kt.isStringLiteral(n)||Kt.isNoSubstitutionTemplateLiteral(n))return n.text}},FWe=new G3;function dce(r,n){return FWe.read(r,n)??uce(r)??void 0}u(dce,"extractChecksumTestId");function nS(r,n){return n.pwTestIdToChecksumTestId[r.playwrightTestId]||(r.filePath!==void 0?dce(r.filePath,r.title):void 0)}u(nS,"resolveChecksumTestId");var oS=u((r,n)=>Object.values(n[r]??{}).some(a=>a.autoRecoveryMetadata?.data?.autoRecovered),"isAutoRecovered"),J3=u(r=>typeof r=="number"&&!isNaN(r)?r:0,"getStat");function rS(r,n,a){let l=[],i=0,f=0,d=J3(n.expected)+J3(n.flaky),p=J3(n.unexpected);for(let h of r){let o=UWe(h,a);l.push(o),o.recovered&&f++,o.bug&&(i++,o.outcome==="expected"||o.outcome==="flaky"?d--:o.outcome==="unexpected"&&p--)}return{passed:d,failed:p,bug:i,recovered:f,tests:l}}u(rS,"computeReportStats");function UWe(r,n){let a=nS({playwrightTestId:r.playwrightTestId,filePath:r.filePath,title:r.title},n),l=jWe(r),i=HWe(r.annotations),f=oS(r.playwrightTestId,n.playwrightIdToMetadata),d=WWe(a,r,n),p=n.toRelativeFilePath(r.filePath);return{checksumTestId:a,playwrightTestId:r.playwrightTestId,success:r.ok,recovered:f,bug:l,annotations:i,tags:r.tags,outcome:r.outcome,variantKey:W3(r.filePath,r.titlePath,r.title),...r.filePath!==void 0?{filePath:r.filePath}:{},...p!==void 0?{relativeFilePath:p}:{},...r.projectName?{project:r.projectName}:{},...r.errorMessage?{errorMessage:r.errorMessage}:{},...d!==void 0?{usingChecksumTestIdPlaceholder:d}:{}}}u(UWe,"rowToTestWithMetadata");function pce(r,n){let a=r.files,l=u(p=>{let o=nS({playwrightTestId:p.testId,filePath:p.location.file,title:p.title},n)??`${p.location.file}::${p.title}`,c=W3(p.location.file,p.path??[],p.title);return`${o}::${p.projectName}::${c}`},"keyFor"),i=new Map;for(let p of a)for(let h of p.tests)i.set(l(h),h);let f=new Set(i.values()),d={total:0,expected:0,unexpected:0,flaky:0,skipped:0};for(let p of a){p.tests=p.tests.filter(h=>f.has(h));for(let h of p.tests)d.total++,h.outcome==="expected"?d.expected++:h.outcome==="unexpected"?d.unexpected++:h.outcome==="flaky"?d.flaky++:d.skipped++}r.files=a.filter(p=>p.tests.length>0),r.stats={...r.stats,...d}}u(pce,"dedupeReportByChecksumTestId");var jWe=u(r=>r.tags.includes("bug")||r.annotations.some(n=>n.type==="bug")||r.title.includes("@bug"),"detectBug"),HWe=u(r=>r.filter(n=>!!n?.type).map(n=>({type:n.type,description:n.description||""})),"normalizeAnnotations"),WWe=u((r,n,a)=>{if(a.checksumTestIdToUsingPlaceholder)return a.pwTestIdToChecksumTestId[n.playwrightTestId]&&a.checksumTestIdToUsingPlaceholder[r]||!1},"computeUsingPlaceholder");var q3=u((r,n,a)=>{if(!r)return;let l=a||n,i;if((0,ap.isAbsolute)(r))i=r;else{let f=(0,ap.dirname)(n);i=(0,ap.resolve)(f,r)}return i.replace(l+"/","")},"toRelativeFilePath"),hce=u(r=>{let n=[];if(r.specs&&n.push(...r.specs),r.suites)for(let a of r.suites)n.push(...hce(a));return n},"extractSpecsFromSuite"),$We=u(r=>{let n=[];for(let a of r.suites)n.push(...hce(a));return n},"extractFlatSpecsMap"),GWe=u(r=>{let n=[],a=u((l,i)=>{for(let f of l.specs){let d=f.tests[0];n.push({playwrightTestId:f.id,ok:f.ok,outcome:d?.status??"skipped",title:f.title,titlePath:i,filePath:f.file,tags:f.tags,annotations:d?.annotations??[]})}for(let f of l.suites??[])a(f,[...i,f.title])},"walk");for(let l of r.suites)a(l,[]);return n},"jsonReportToRows"),JWe=u((r,n,a,l,i)=>(qWe(r,n),rS(GWe(r),r.stats,{pwTestIdToChecksumTestId:a,playwrightIdToMetadata:n,toRelativeFilePath:u(f=>l?q3(f,l,i):f,"toRelativeFilePath")})),"modifyStatsPerTestFile"),qWe=u((r,n)=>{for(let a of $We(r))oS(a.id,n)&&(a.recovered=!0)},"markRecoveredSpecs"),gce=u((r,n,a,l,i)=>{(0,ip.cpSync)(r,r+".backup");try{let f=JSON.parse((0,ip.readFileSync)(r,"utf-8")),d=JWe(f,n,a,l,i);return f.checksumMetadata=d,(0,ip.writeFileSync)(r,JSON.stringify(f,null,2)),f}catch(f){return console.error("Error processing JSON report",f.message),null}},"processJSONReport");var Pa,ZA=!1,Ace=!1,VWe=u(r=>{if(!Pa||ZA)return;ZA=!0;let n={...Pa,exit_code:r,duration_ms:Date.now()-Pa._startedAtMs};if(delete n._startedAtMs,!!Dv())try{nf({severity:Rv(n),message:`cli ${n.command} exit=${r} ${n.duration_ms}ms`,...n})}catch(a){try{Bv(`[wide-event] cli emit failed: ${String(a)}`)}catch{}}},"emitCliInvocation"),yce=u(r=>{Pa={timestamp:new Date().toISOString(),_startedAtMs:Date.now(),kind:"cli_invocation",command:r,pid:process.pid,node_version:process.version,platform:process.platform,ci:process.env.CI==="true"||process.env.CI==="1"},ZA=!1,Ace||(Ace=!0,process.on("exit",VWe),process.on("uncaughtException",n=>{throw Pa&&!ZA&&(Pa.error={type:n.name,message:n.message,source:"uncaughtException"}),n}),process.on("unhandledRejection",n=>{if(Pa&&!ZA){let a=n instanceof Error?n:new Error(String(n));Pa.error={type:a.name,message:a.message,source:"unhandledRejection"}}}))},"startCliInvocation"),cp=u(()=>Pa,"getCliInvocation");var iS=u(r=>(re("Using report folder",r),(0,bt.join)(r,"index.html")),"makeFilePath"),mce=u(r=>(re("Using JSON report folder",r),(0,bt.join)(r,"report.json")),"makeJsonFilePath"),V3=u(r=>{let n=["B","KB","MB","GB"],a=r,l=0;for(;a>=1024&&l<n.length-1;)a/=1024,l++;return`${l===0?a:a.toFixed(1)} ${n[l]}`},"formatBytes"),XA=class r extends fn{constructor(a,l=!1){super(a,l);this.MAX_COMPLETION_WAIT=jg(process.env).cliHardCapMs;this.testSuiteRunId="";this.didFail=!1;this.reportUploadFailed=!1;this.isolationMode=!1;this.isUploadReportMode=!1;this.dedupeByChecksumTestId=!1;this.reportUploadSucceeded=!1;this.trmMessagesBuffer="";this.completeIndicators={upload:!1,tests:!1,report:!1};this.uploadProgress=0;this.playwrightIdToReportMetadata={};this.pwTestIdToChecksumTestId={};this.logFilter={"upload-progress":{logEveryMS:1e3*30,lastLogTimestamp:0,logOnValueChange:!0,value:""}}}static{u(this,"TestsRunner")}get replMode(){return this.getChecksumArg("repl")??!1}get listMode(){return this.args.includes("--list")}getTestSuiteRunInfo(){if(!this.testSuiteRunInfo)throw new Error("Test suite run info is not initialized");return this.testSuiteRunInfo}getTestRunMonitorProcess(){if(!this.testRunMonitorProcess)throw new Error("Test run monitor process is not initialized");return this.testRunMonitorProcess}getConfig(){if(!this.config)throw new Error("Checksum config is not initialized");return this.config}getConfigOptions(){let a=this.getConfig().options;if(!a)throw new Error("Checksum config options are not initialized");return a}async run(){this.loadTracer(),this.validateAuthExists(),this.buildVolatileChecksumConfig();let a=cp();if(a&&(a.shard_mode=process.env.CHECKSUM_SHARD_MODE==="true",a.list_mode=this.listMode,a.repl_mode=!!this.replMode,a.isolation_mode=!!this.isolationMode,a.runtime_version=this.checksumRuntimeVersion,a.app_name=this.config?.appName??null,a.env_override_name=process.env.CHECKSUM_RUNTIME_ENV_OVERRIDE_NAME??null,a.env_override_role=process.env.CHECKSUM_RUNTIME_ENV_OVERRIDE_ROLE??null),process.env.CHECKSUM_SHARD_MODE==="true"){await this.runShardMode();return}if(this.listMode){await this.runListMode();return}if(!await this.getSession())return;a&&(a.test_suite_run_id=this.testSuiteRunId),this.testSuiteAnalytics=zA.getInstance(this.getTestSuiteRunInfo(),this.isolationMode),this.testSuiteAnalytics.genericEvent(`Project root found at ${this.projectRootDirectory}`,{message:`Checksum root found at ${this.checksumRoot}`});let l={CHECKSUM_ROOT_FOLDER:this.checksumRoot,CHECKSUM_TEST_SUITE_INFO:JSON.stringify(this.getTestSuiteRunInfo())},i;try{i=await Ye(this.startTestRunMonitor(this.testSuiteRunId),1e4,"test run monitor timeout"),l.CHECKSUM_UPLOAD_AGENT_PORT=i}catch{let s="Error starting test run monitor. Test results will not be available on checksum.";this.testSuiteAnalytics.testRunMonitorError({error:s}),this.log(s)}l.PW_TEST_HTML_REPORT_OPEN="never",this.isolationMode&&(l.CHECKSUM_ISOLATED_MODE="true"),this.testSuiteRunId&&(l.CHECKSUM_TEST_SUITE_ID=this.testSuiteRunId),this.replMode&&(l.CHECKSUM_REPL="true"),this.getChecksumArg("tm")&&(l.CHECKSUM_SHOW_TIME_MACHINE="true");let f=this.getNonChecksumArgs(),d=this.getChecksumArg("rerun-failed"),p=ice({args:this.args,env:process.env});if(p.kind==="enabled"&&d){this.printError("--cksm-affected cannot be combined with --cksm-rerun-failed."),await this.shutdown(1);return}if(d)try{let s=await this.fetchFailedTestPaths(typeof d=="string"?d:void 0);if(s.length===0){console.log("No non-passing tests found \u2014 nothing to rerun."),await this.shutdown(0);return}console.log(`Re-running ${s.length} non-passing test file(s)`),f.unshift(...s)}catch(s){this.printError(`Failed to fetch failed tests: ${s.message}`),await this.shutdown(1);return}if(p.kind==="enabled"){if(f.some(g=>g==="--grep"||g==="-g"||g.startsWith("--grep=")||g.startsWith("-g="))){this.printError("--cksm-affected cannot be combined with a manual --grep argument."),await this.shutdown(1);return}try{let g=await cce(p.baseRef,b=>this.getCmdOutput(b));if(g.length===0){console.log("No changed files detected for --cksm-affected \u2014 nothing to run."),await this.shutdown(0);return}let A=await this.fetchAffectedTestIds(g);if(A.length===0){console.log("No tests affected by the current changes \u2014 nothing to run."),await this.shutdown(0);return}let m=`(${A.sort().map(b=>b.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")).join("|")})`;if(p.dryRun){console.log("[--cksm-affected-dry-run] "+JSON.stringify({baseRef:p.baseRef??null,changedFiles:g,affectedTestIds:A,playwrightArg:`--grep=${m}`},null,2)),await this.shutdown(0);return}console.log(`Running ${A.length} affected test(s) from ${g.length} changed file(s)`),f.push(`--grep=${m}`)}catch(g){this.printError(`Failed to determine affected tests: ${g.message}`),await this.shutdown(1);return}}this.replMode&&f.push("--debug");let h=this.isolationMode||this.replMode?this.getPlaywrightConfigFilePath():this.buildRunPlaywrightConfig(),o=f,c=process.env[j3];if(c){let s=nce(f,c);o=s.args,s.jsonOutputName&&(l.PLAYWRIGHT_JSON_OUTPUT_NAME=s.jsonOutputName)}let e=process.env[H3];e&&(o=ece(o),l.PLAYWRIGHT_BLOB_OUTPUT_FILE=(0,bt.join)(e,`blob-${Date.now()}-${(0,aS.randomUUID)().slice(0,8)}.zip`));let t=["test","--config",h,...o];await this.patchPlaywright();try{console.log(`Tests running with @checksum-ai/runtime version ${this.checksumRuntimeVersion}`),this.replMode?(console.log("Running in REPL mode"),this.runPlaywright(t,l),await this.execFile("node",[`${__dirname}/repl.js`],l)):(this.testSuiteAnalytics.testSuiteStart(),await this.runPlaywright(t,l)),console.log(`Tests execution finished. Checksum is finalizing${this.isolationMode?"":" and uploading"} the report, please wait...`)}catch{this.didFail=!0,console.log("Error during test execution: Failed passing test"),this.testSuiteAnalytics.testsFailed()}finally{this.isolationMode||console.log("Waiting for test files to upload to Checksum..."),await this.sendReportUploadRequest(),await this.patchPlaywright(!0),this.completeIndicators.tests=!0,await this.handleCompleteMessage()}}async runShardMode(){console.log(`[shard-mode] Running shard ${process.env.SHARD_INDEX}/${process.env.SHARD_TOTAL} (batch: ${process.env.BATCH_ID})`);let a=this.getNonChecksumArgs(),l=process.env.SHARD_INDEX,i=process.env.SHARD_TOTAL;l&&i&&a.push(`--shard=${l}/${i}`),a.push("--reporter=blob,line");let f={CHECKSUM_ROOT_FOLDER:this.checksumRoot,PW_TEST_HTML_REPORT_OPEN:"never"},d=["test","--config",this.getPlaywrightConfigFilePath(),...a];await this.patchPlaywright();let p=Xi("shard_run",{runtime_version:this.checksumRuntimeVersion,shard_index:process.env.SHARD_INDEX,shard_total:process.env.SHARD_TOTAL,batch_id:process.env.BATCH_ID}),h=0;try{console.log(`[shard-mode] Tests running with @checksum-ai/runtime version ${this.checksumRuntimeVersion}`),await this.runPlaywright(d,f),console.log("[shard-mode] Tests execution finished.")}catch{h=1,console.log("[shard-mode] Tests finished with failures.")}finally{await this.patchPlaywright(!0)}p.emit({outcome:h===0?"success":"error",exit_code:h}),process.exitCode=h}async runListMode(){let a=this.getNonChecksumArgs(),l={CHECKSUM_ROOT_FOLDER:this.checksumRoot,PW_TEST_HTML_REPORT_OPEN:"never"},i=["test","--config",this.getPlaywrightConfigFilePath(),...a];await this.patchPlaywright();let f=0;try{await this.runPlaywright(i,l)}catch{f=1}finally{await this.patchPlaywright(!0)}process.exitCode=f}async uploadReport(){this.loadTracer(),this.validateAuthExists(),this.buildVolatileChecksumConfig(),this.isUploadReportMode=!0,this.getConfigOptions().hostReports=!0,this.tryLoadPlaywrightConfigFromFile(),this.loadPlaywrightConfigOptions();let a=this.resolveUserReportPathArg();a&&this.playwrightConfigOptions&&(this.playwrightConfigOptions.reportPath=a);let l=this.getChecksumArg("merge-blobs");if(l){let f=await this.mergeBlobArchive(l);if(!f){await this.shutdown(1);return}this.playwrightConfigOptions&&(this.playwrightConfigOptions.reportPath=f,this.playwrightConfigOptions.hasJsonReporter=!1),this.dedupeByChecksumTestId=!0}let i=this.playwrightConfigOptions?.reportPath;if(!i||!(0,Qt.existsSync)(i)){this.printError(`Could not find report at ${i??"(unresolved)"}. Pass an explicit path: npx checksumai upload-report <path>`),await this.shutdown(1);return}if(await this.getSession()){this.testSuiteAnalytics=zA.getInstance(this.getTestSuiteRunInfo(),this.isolationMode);try{await Ye(this.startTestRunMonitor(this.testSuiteRunId),1e4,"test run monitor timeout")}catch(f){this.printError(`Failed to start test run monitor: ${f.message}. Report will not be uploaded.`),await this.shutdown(1);return}console.log(`Uploading report from ${i} to Checksum...`),await this.sendReportUploadRequest(),this.completeIndicators.tests=!0,await this.handleCompleteMessage()}}async mergeBlobArchive(a){let l=(0,bt.isAbsolute)(a)?a:(0,bt.resolve)(this.absoluteProjectRootDirectory,a);if(((0,Qt.existsSync)(l)?(0,Qt.readdirSync)(l).filter(p=>p.endsWith(".zip")):[]).length===0)return this.printError(`No blob reports found in ${l}`),null;let f=(0,bt.join)(this.getInternalFilesDirPath(),"merged-report");(0,Qt.mkdirSync)(f,{recursive:!0});try{await this.runMergeReportsPatched(l,f)}catch(p){return this.printError(`merge-reports failed: ${p.message}`),null}let d=(0,bt.join)(f,"index.html");return(0,Qt.existsSync)(d)?d:(this.printError(`Merged report not found at ${d}`),null)}tryLoadPlaywrightConfigFromFile(){if(!this.playwrightConfig)try{let a=this.getPlaywrightConfigFilePath();if((0,Qt.existsSync)(a)){let l=(0,bce.createRequire)(__filename)(a),i=l?.default??l;i&&(this.playwrightConfig=i)}}catch(a){re("Could not load playwright config from file, falling back to defaults: "+a.message)}}resolveUserReportPathArg(){let a=this.args.find(i=>!i.startsWith("-")&&!i.includes("="));if(!a)return;let l=(0,bt.isAbsolute)(a)?a:(0,bt.resolve)(process.cwd(),a);if(!(0,Qt.existsSync)(l))return l;try{if((0,Qt.statSync)(l).isDirectory())return(0,bt.join)(l,"index.html")}catch{}return l}loadPlaywrightConfigOptions(){let a="",l="",i=!1,f="never";process.env.PLAYWRIGHT_HTML_OUTPUT_DIR&&(a=iS(process.env.PLAYWRIGHT_HTML_OUTPUT_DIR));let d=this.playwrightConfig?.reporter;if(d instanceof Array){let p=d.filter(c=>c instanceof Array&&c[0]==="html"),h=d.filter(c=>c instanceof Array&&c[0]==="json");if(!process.env.PLAYWRIGHT_HTML_OUTPUT_DIR){let c=p.find(e=>e[1]?.outputFolder);c&&(a=iS((0,bt.join)(this.checksumRoot,c[1].outputFolder)))}let o=h.find(c=>c[1]?.outputFile);o&&(i=!0,l=(0,bt.join)(this.checksumRoot,o[1].outputFile));for(let c of p){let e=c[1]?.open;if(e==="always"||e==="on-failure"){f=e;break}}}a||(de.isRepoMode?a=iS((0,bt.join)(de.runtimeRoot,"playwright-report")):a=iS((0,bt.join)(this.projectRootDirectory,"playwright-report"))),l||(de.isRepoMode?l=mce((0,bt.join)(de.runtimeRoot,"playwright-report")):l=mce((0,bt.join)(this.projectRootDirectory,"playwright-report"))),this.playwrightConfigOptions={openReportCriteria:f,reportPath:a,jsonReportPath:l,hasJsonReporter:i}}async sendReportUploadRequest(){let a=u(async c=>{this.testSuiteAnalytics?.testSuiteError({error:c}),this.log(c),this.reportUploadFailed=!0,this.completeIndicators.report=!0,this.getTestRunMonitorProcess().stdin.write("cli:report=false"),await this.sendProcessingError(this.buildProcessErrorPayload({stage:"report-upload",reason:"Required Playwright HTML report could not be produced for upload",reportUploaded:!1,evidence:{detail:c}}))},"makeError");if(!this.playwrightConfigOptions){await a("No playwright config options");return}let{reportPath:l,openReportCriteria:i,jsonReportPath:f,hasJsonReporter:d}=this.playwrightConfigOptions;if(d&&f!==void 0&&((0,Qt.existsSync)(f)?gce(f,this.playwrightIdToReportMetadata,this.pwTestIdToChecksumTestId,this.checksumRoot,this.absoluteProjectRootDirectory):console.warn(`Could not find JSON report file at ${f}`)),!(0,Qt.existsSync)(l)){await a(`Could not find report file at ${l}`);return}re("Sending report upload request",l);let p=this.saveMetadataToFile((0,bt.dirname)(l)),h={reportPath:l,pathToChecksumMetadata:p,openReportCriteria:i,didFail:this.didFail,checksumRoot:this.checksumRoot,projectRoot:this.absoluteProjectRootDirectory,isUploadReport:this.isUploadReportMode,dedupeByChecksumTestId:this.dedupeByChecksumTestId},o=Buffer.from(JSON.stringify(h)).toString("base64");this.getTestRunMonitorProcess().stdin.write(`cli:report=${o}`)}saveMetadataToFile(a){try{let l=(0,bt.join)(a,`checksum-metadata-${(0,aS.randomUUID)()}.json`);return(0,Qt.writeFileSync)(l,JSON.stringify(this.playwrightIdToReportMetadata,null,2)),l}catch(l){let i=`Error saving metadata to file - ${l.message}`;this.testSuiteAnalytics?.testSuiteError({error:i}),this.log(i);return}}startTestMonitorProcess(a){let i=[JSON.stringify({sessionId:a,apiURL:this.getConfig().apiURL,apiKey:this.getConfig().apiKey}),...this.isolationMode?["isolated"]:[]],f={command:"node",args:[(0,bt.join)(__dirname,"test-run-monitor.js"),...i]};return de.isRepoMode&&(f.command="yarn",f.args=["test-run-monitor",...i]),vce.spawn(f.command,f.args)}startTestRunMonitor(a){return new Promise(l=>{console.log("Starting test run monitor");let i=this.startTestMonitorProcess(a);this.testRunMonitorProcess=i,i.stdout.on("data",f=>{let d=this.parseTRMMessages(f.toString().trim());if(d.length)for(let p of d){if(p.startsWith("trace")){this.logTRMMessage(p),this.handleTestRunMonitorTrace(p.substring("trace".length+1));continue}if(p.startsWith("wideevent")){this.reEmitMonitorWideEvent(p.substring("wideevent".length+1));continue}if(!p.startsWith("monitor")){this.logTRMMessage(p);continue}let h=u(()=>{let t=p.substring("monitor".length+1),s=t.indexOf("=");if(s===-1)return{key:t,value:""};let[g,A]=[t.substring(0,s),t.substring(s+1)];return{key:g,value:A}},"getKeyAndValue"),{key:o,value:c}=h();if(!Object.values(vx).includes(o)){this.logTRMMessage(`Received unknown monitor message: "${p}"`);continue}let e=o;this.logInterProcessEventMessage(e,c),o==="port"?l(c):this.handleTestRunMonitorMessage(e,c)}}),i.on("exit",(f,d)=>{let p=`test run monitor process exited with code ${f} and signal ${d}`;this.testSuiteAnalytics?.testRunMonitorError({error:p}),this.log(p)}),i.stderr.on("data",f=>{let d=`TRM Error: ${f.toString().substring(0,300)}`;this.testSuiteAnalytics?.testRunMonitorError({error:d}),this.log(d)}),i.on("error",f=>{let d=`Error starting test run monitor: ${f.message}`;this.testSuiteAnalytics?.testRunMonitorError({error:d}),this.log(d)})})}logInterProcessEventMessage(a,l){u(()=>{let f=this.logFilter[a];if(!f)return!0;if(f.logOnValueChange&&f.value!==l)return f.value=l,!0;if(f.logEveryMS){let d=Date.now();if(d-f.lastLogTimestamp>=f.logEveryMS)return f.lastLogTimestamp=d,!0}return!1},"shouldLog")()&&this.logTRMMessage(`Received monitor message: "${a}${l?`=${l}`:""}"`)}parseTRMMessages(a){let l=this.trmMessagesBuffer+a,i=-1,f=-1,d=u(()=>{i=l.indexOf("{trm}"),f=l.indexOf("{/trm}")},"findIndexes");d();let p=[];for(;i!==-1&&f!==-1&&f>i;){if(p.push(l.substring(i+"{trm}".length,f)),l=l.slice(f+"{/trm}".length).trim(),l.length&&!l.startsWith("{trm}"))return console.warn("Buffered data does not start with start delimiter",l),this.trmMessagesBuffer="",p;this.trmMessagesBuffer=l,d()}return p}setChecksumTestMetadata(a){let{retry:l,type:i,pwTestId:f,checksumTestId:d}=a;this.playwrightIdToReportMetadata[f]??={};let p=this.playwrightIdToReportMetadata[f][l]??={};switch(i){case"autoRecoveryMetadata":p.autoRecoveryMetadata=a;break;case"autoRecoverySummaries":p.autoRecoverySummaries=a;break}this.pwTestIdToChecksumTestId[f]=d}handleTestRunMonitorTrace(a){let l=a.indexOf("="),[i,f]=l>-1?[a.substring(0,l),a.substring(l+1)]:[a,""];if(!Object.values(ZO).includes(i)){re("Unknown trace event",i);return}try{let d=f?JSON.parse(f):{};switch(i){case"Upload Start":this.testSuiteAnalytics?.uploadStart({filename:d.filename});break;case"Upload Complete":this.testSuiteAnalytics?.uploadComplete({filename:d.filename});break;case"Upload Failed":this.testSuiteAnalytics?.uploadFailed({filename:d.filename,error:d.error??""});break;case"Trace Processing Error":this.testSuiteAnalytics?.traceProcessingError({error:d.error??""});break}}catch(d){re(`Error parsing trace payload, ${d.message}`)}}logAbandonedAssets(a){let{abandonedAssets:l,finalizeReason:i}=a??{};if(!(!Array.isArray(l)||l.length===0)){(i==="budget"||i==="budget-ceiling")&&this.log("Uploads did not finish within the allotted time. Raise CHECKSUM_RUNTIME_UPLOAD_FINALIZE_BUDGET_MS to allow longer."),this.log(`Artifacts not uploaded (${l.length}):`);for(let f of l){let d=f??{},p=typeof d.file_name=="string"?d.file_name:"unknown",h=typeof d.committed_bytes=="number"&&d.committed_bytes>0?d.committed_bytes:typeof d.loaded_bytes=="number"?d.loaded_bytes:0,o=typeof d.size_bytes=="number"?d.size_bytes:void 0;this.log(o!==void 0&&o>0?` - ${p} (${V3(h)} of ${V3(o)})`:` - ${p} (${V3(h)} sent)`)}}}async handleTestRunMonitorMessage(a,l){switch(a){case"uploads-complete-with-errors":this.log("Error uploading test files to Checksum");try{let i=JSON.parse(l);this.logAbandonedAssets(i),this.testSuiteAnalytics?.uploadsCompleteWithErrors(i);let f=cp();f&&(f.uploads_outcome="complete_with_errors",f.uploads_errored=Array.isArray(i.uploadErrors)?i.uploadErrors.length:void 0)}catch{}this.completeIndicators.upload=!0;break;case"checksumTestMetadata":try{let i=JSON.parse(l);this.setChecksumTestMetadata(i)}catch(i){let f=`Error parsing test metadata event - ${i.message}`;this.testSuiteAnalytics?.testSuiteError({error:f}),this.log(f)}break;case"uploads-complete":this.isolationMode||console.log("Test files uploaded successfully"),this.testSuiteAnalytics?.allUploadsComplete();{let i=cp();i&&(i.uploads_outcome="complete",i.uploads_errored=0)}this.sendUploadsComplete().then(()=>{this.completeIndicators.upload=!0});break;case"report-complete":{let i=l.slice(0,l.indexOf(":"))==="true",f=l.slice(l.indexOf(":")+1),d="{}",p=!1;try{if(f&&(0,Qt.existsSync)(f)){d=(0,Qt.readFileSync)(f,"utf-8"),p=!0;try{(0,Qt.unlinkSync)(f)}catch{console.warn(`Failed to delete temporary stats file: ${f}`)}}else console.warn(`Stats file not found at: ${f}`)}catch(s){console.error(`Error reading stats file: ${s.message}`)}let{stats:h,statsParsed:o}=this.handleStats(d);if(this.skippedTests=Yae(p&&o?h:void 0),this.isolationMode){this.completeIndicators.report=!0;break}this.testSuiteAnalytics?.testSuiteEnd({passed:h.passed,failed:h.failed,bug:h.bug,total:h.tests?.length||0});let c=cp();c&&(c.tests_total=h.tests?.length??0,c.tests_passed=h.passed,c.tests_failed=h.failed,c.tests_bug=h.bug,c.tests_recovered=h.recovered);let e=i?void 0:this.buildReportUploadProcessErrorPayload(h,f),t=await this.sendTestRunEnd(h,i,e);if(this.completeIndicators.report=!0,this.reportUploadSucceeded=i&&t,!t){this.didFail=!0,c&&(c.report_uploaded=!1),this.testSuiteAnalytics?.testSuiteError({error:"Checksum rejected the test run completion update"}),this.log("Checksum rejected the test run completion update",!0),i||await this.handleReportUploadFailure(e);break}if(c&&(c.report_uploaded=i),i){let s=`${this.checksumAppUrl}/#/test-runs/${this.testSuiteRunId}`,g=`Checksum report URL: ${s}`,A="*".repeat(g.length);console.log(`${A}
|
|
444
444
|
${g}
|
package/index.js
CHANGED
|
@@ -416,7 +416,7 @@ ${c}
|
|
|
416
416
|
})`,{filename:i})(s,(0,joe.createRequire)(i),r,i,(0,Hoe.dirname)(i));let p=r.exports,d=p.default;return d===void 0?p:d}u(Joe,"requireChecksumConfig");var kw=require("fs"),Zoe=require("module"),Xoe=require("node:util");var hc={defaultMerge:Symbol("deepmerge-ts: default merge"),skip:Symbol("deepmerge-ts: skip")},Y_t={defaultMerge:hc.defaultMerge};function QFe(i,n){return n}u(QFe,"defaultMetaDataUpdater");function MFe(i,n){return i.filter(c=>c!==void 0)}u(MFe,"defaultFilterValues");var qoe;(function(i){i[i.NOT=0]="NOT",i[i.RECORD=1]="RECORD",i[i.ARRAY=2]="ARRAY",i[i.SET=3]="SET",i[i.MAP=4]="MAP",i[i.OTHER=5]="OTHER"})(qoe||(qoe={}));function $oe(i){return typeof i!="object"||i===null?0:Array.isArray(i)?2:jFe(i)?1:i instanceof Set?3:i instanceof Map?4:5}u($oe,"getObjectType");function FFe(i){let n=new Set;for(let c of i)for(let s of[...Object.keys(c),...Object.getOwnPropertySymbols(c)])n.add(s);return n}u(FFe,"getKeys");function UFe(i,n){return typeof i=="object"&&Object.prototype.propertyIsEnumerable.call(i,n)}u(UFe,"objectHasProperty");function zoe(i){let n=0,c=i[0]?.[Symbol.iterator]();return{[Symbol.iterator](){return{next(){do{if(c===void 0)return{done:!0,value:void 0};let s=c.next();if(s.done===!0){n+=1,c=i[n]?.[Symbol.iterator]();continue}return{done:!1,value:s.value}}while(!0)}}}}}u(zoe,"getIterableOfIterables");var Voe=["[object Object]","[object Module]"];function jFe(i){if(!Voe.includes(Object.prototype.toString.call(i)))return!1;let{constructor:n}=i;if(n===void 0)return!0;let c=n.prototype;return!(c===null||typeof c!="object"||!Voe.includes(Object.prototype.toString.call(c))||!c.hasOwnProperty("isPrototypeOf"))}u(jFe,"isRecord");function HFe(i,n,c){let s={};for(let r of FFe(i)){let f=[];for(let h of i)UFe(h,r)&&f.push(h[r]);if(f.length===0)continue;let p=n.metaDataUpdater(c,{key:r,parents:i}),d=Koe(f,n,p);d!==hc.skip&&(r==="__proto__"?Object.defineProperty(s,r,{value:d,configurable:!0,enumerable:!0,writable:!0}):s[r]=d)}return s}u(HFe,"mergeRecords$1");function WFe(i){return i.flat()}u(WFe,"mergeArrays$1");function GFe(i){return new Set(zoe(i))}u(GFe,"mergeSets$1");function JFe(i){return new Map(zoe(i))}u(JFe,"mergeMaps$1");function qFe(i){return i.at(-1)}u(qFe,"mergeOthers$1");var Cw={mergeRecords:HFe,mergeArrays:WFe,mergeSets:GFe,mergeMaps:JFe,mergeOthers:qFe};function Yoe(...i){return $Fe({})(...i)}u(Yoe,"deepmerge");function $Fe(i,n){let c=VFe(i,s);function s(...r){return Koe(r,c,n)}return u(s,"customizedDeepmerge"),s}u($Fe,"deepmergeCustom");function VFe(i,n){return{defaultMergeFunctions:Cw,mergeFunctions:{...Cw,...Object.fromEntries(Object.entries(i).filter(([c,s])=>Object.hasOwn(Cw,c)).map(([c,s])=>s===!1?[c,Cw.mergeOthers]:[c,s]))},metaDataUpdater:i.metaDataUpdater??QFe,deepmerge:n,useImplicitDefaultMerging:i.enableImplicitDefaultMerging??!1,filterValues:i.filterValues===!1?void 0:i.filterValues??MFe,actions:hc}}u(VFe,"getUtils");function Koe(i,n,c){let s=n.filterValues?.(i,c)??i;if(s.length===0)return;if(s.length===1)return NM(s,n,c);let r=$oe(s[0]);if(r!==0&&r!==5){for(let f=1;f<s.length;f++)if($oe(s[f])!==r)return NM(s,n,c)}switch(r){case 1:return zFe(s,n,c);case 2:return YFe(s,n,c);case 3:return KFe(s,n,c);case 4:return ZFe(s,n,c);default:return NM(s,n,c)}}u(Koe,"mergeUnknowns");function zFe(i,n,c){let s=n.mergeFunctions.mergeRecords(i,n,c);return s===hc.defaultMerge||n.useImplicitDefaultMerging&&s===void 0&&n.mergeFunctions.mergeRecords!==n.defaultMergeFunctions.mergeRecords?n.defaultMergeFunctions.mergeRecords(i,n,c):s}u(zFe,"mergeRecords");function YFe(i,n,c){let s=n.mergeFunctions.mergeArrays(i,n,c);return s===hc.defaultMerge||n.useImplicitDefaultMerging&&s===void 0&&n.mergeFunctions.mergeArrays!==n.defaultMergeFunctions.mergeArrays?n.defaultMergeFunctions.mergeArrays(i):s}u(YFe,"mergeArrays");function KFe(i,n,c){let s=n.mergeFunctions.mergeSets(i,n,c);return s===hc.defaultMerge||n.useImplicitDefaultMerging&&s===void 0&&n.mergeFunctions.mergeSets!==n.defaultMergeFunctions.mergeSets?n.defaultMergeFunctions.mergeSets(i):s}u(KFe,"mergeSets");function ZFe(i,n,c){let s=n.mergeFunctions.mergeMaps(i,n,c);return s===hc.defaultMerge||n.useImplicitDefaultMerging&&s===void 0&&n.mergeFunctions.mergeMaps!==n.defaultMergeFunctions.mergeMaps?n.defaultMergeFunctions.mergeMaps(i):s}u(ZFe,"mergeMaps");function NM(i,n,c){let s=n.mergeFunctions.mergeOthers(i,n,c);return s===hc.defaultMerge||n.useImplicitDefaultMerging&&s===void 0&&n.mergeFunctions.mergeOthers!==n.defaultMergeFunctions.mergeOthers?n.defaultMergeFunctions.mergeOthers(i):s}u(NM,"mergeOthers");var ere=!1;function Foe(i){ere=i}u(Foe,"setLogToConsole");function ye(...i){ere&&process.stdout.write(`${(0,Xoe.format)(...i)}
|
|
417
417
|
`)}u(ye,"log");function XFe(i,n){return i in n}u(XFe,"isLocatorMethod");async function ZX(i,n,c,s=6e4){if(!XFe(n,i))return i;let r=u(()=>{let f=i[n];return typeof f=="function"?Reflect.apply(f,i,c):f},"executor");return s===null?r():dt(r(),s,"Execution timeout")}u(ZX,"executePlaywrightMethodWithTimeout");function LM({envName:i,role:n}={}){let c=process.env.CHECKSUM_RUNTIME_ENV_OVERRIDE_NAME??i,s=process.env.CHECKSUM_RUNTIME_ENV_OVERRIDE_ROLE??n;return{...c!==void 0?{envName:c}:{},...s!==void 0?{role:s}:{}}}u(LM,"applyRuntimeEnvOverrides");function QM(i,{role:n,environment:c}={},s=!1){let r;if(c&&(r=i.environments?.find(({name:d})=>c===d)),r??=i.environments?.find(({default:d})=>d),!r)throw new Error("Environment not found. Please make sure your environments are properly defined in the checksum.config.ts file.");let f=r.users,p;if(f&&(n!==void 0&&(p=f.find(d=>d.role===n)),p??=f.find(d=>d.default)),!p)throw new Error(`User not found. Please make sure the users array for the "${r.name}" environment is properly defined in the checksum.config.ts file.`);if(c&&r.name!==c){if(s)throw new Error(`Could not find environment with name "${c}" in config file`);console.warn(`Could not find environment with name "${c}" in config file, trying to use default environment`)}if(n&&p.role!==n){if(s)throw new Error(`Could not find user with role "${n}" for environment "${c}" in config file`);console.warn(`Could not find user with role "${n}" for environment "${c}" in config file, trying to login with environment's default user`)}return{environment:r,user:p}}u(QM,"resolveEnvironment");function e3e(i){let n=`${i??Po()}/login.ts`;if(!(0,kw.existsSync)(n))throw console.warn(`Could not locate the login.ts file. Please make sure your login file exists at ${n}`),new Error("Could not find login.ts file");try{return(0,Zoe.createRequire)(__filename)(n).default}catch(c){throw console.warn("Error loading login.ts file",c),c}}u(e3e,"requireLogin");async function t3e(i,n,{environment:c,user:s,config:r}){try{let f=u(()=>n(i,{environment:c,user:s,config:r}),"performLogin");"checksumAI"in i?await i.checksumAI(`Log in${s.role?` using ${s.role} role`:""}${c.name?` in ${c.name} environment`:""}`,f,{skipAutoRecovery:!0}):await f()}catch(f){throw console.warn(`The login method threw an exception, please check the login.ts file for the following error:
|
|
418
418
|
`,f),f}}u(t3e,"proceedWithLogin");function ds(i){let{config:n,folder:c,throwIfAuthFailed:s}=i,r=e3e(c);return async(f,{role:p,environment:d}={})=>{let{envName:h,role:o}=LM({...d!==void 0?{envName:d}:{},...p!==void 0?{role:p}:{}}),{environment:a,user:e}=QM(n,{...o!==void 0?{role:o}:{},...h!==void 0?{environment:h}:{}},i.throwIfEnvInfoInvalid);if(s&&!e)throw new Error("Could not find user credentials matching the provided role and/or environment");await t3e(f,r,{environment:a,user:e,config:n})}}u(ds,"makeUnboundLogin");function tre(i,{environmentName:n,userRole:c}){let s=ds(i);return r=>s(r,{environment:n,role:c})}u(tre,"makeBoundLogin");var or;function AI(){if(or)return or;try{let i=(0,kw.existsSync)(`${Po()}/checksum.config.ts`)?"checksum.config.ts":(0,kw.existsSync)(`${Po()}/checksum.config.tmp.ts`)?"checksum.config.tmp.ts":void 0;if(i)return Tw(Joe(`${Po()}/${i}`))}catch{console.warn("Could not find checksum config file, will run with defaults")}return Tw()}u(AI,"loadChecksumConfig");function Tw(i={}){let n={apiKey:"",runMode:"normal",apiURL:"https://api.checksum.ai",options:{useChecksumSelectors:!0,useChecksumAI:{actions:!0,assertions:!1,skipTestsWithKnownBugs:!0},newAssertionsEnabled:!0,useMockData:!1,printLogs:!1,consecutiveActionDelay:100,hideReports:process.env.AUTHORIZE_REPORTS==="true"}};return i.options?.useChecksumAI?.visualComparison===void 0&&i.options?.useChecksumAI?.assertions&&(i.options.useChecksumAI.visualComparison=!0),or=Yoe(n,i),process.env.CHECKSUM_API_URL&&(or.apiURL=process.env.CHECKSUM_API_URL),Fe.testRun.disableAutoRecovery&&or.options&&(or.options.useChecksumAI={actions:!1,assertions:!1,visualComparison:!1}),Fe.testRun.disableHostReports&&or.options&&(or.options.hostReports=!1),or.options&&(Fe.testRun.disableTimeMachine&&(or.options.disableTimeMachine=!0),Fe.testRun.disableRrwebRecording&&(or.options.disableRrwebRecording=!0)),or}u(Tw,"getChecksumConfig");var sc="Checksum failed connecting to the server";var IBt=require("@playwright/test");async function nre(i){await i.body?.cancel().catch(()=>{})}u(nre,"drainBody");var n3e="http://127.0.0.1";async function MM(i,n){let c=n.fetchImpl??globalThis.fetch;try{let s=await c(`${n3e}:${n.port}/`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(i),signal:AbortSignal.timeout(n.timeoutMs)});return await nre(s),s.ok?!0:(n.onError?.(new Error(`monitor rejected message with status ${s.status}`)),!1)}catch(s){return n.onError?.(s),!1}}u(MM,"postToMonitor");async function FM(i,n){return await MM(i,n)?!0:n.attempts<=1?!1:(await o3e(n.retryDelayMs),FM(i,{...n,attempts:n.attempts-1}))}u(FM,"postToMonitorWithRetry");function o3e(i){return new Promise(n=>{setTimeout(n,i)})}u(o3e,"sleep");var xw={uploadTimeoutMs:{env:"CHECKSUM_RUNTIME_UPLOAD_TIMEOUT_MS",fallback:6e5,min:5e3,max:36e5},noProgressAbortMs:{env:"CHECKSUM_RUNTIME_UPLOAD_NO_PROGRESS_MS",fallback:6e4,min:5e3,max:6e5},assetDeadlineMs:{env:"CHECKSUM_RUNTIME_UPLOAD_ASSET_DEADLINE_MS",fallback:12e5,min:3e4,max:54e5},maxAttempts:{env:"CHECKSUM_RUNTIME_UPLOAD_MAX_ATTEMPTS",fallback:6,min:1,max:20},stallTimeoutMs:{env:"CHECKSUM_RUNTIME_UPLOAD_STALL_TIMEOUT_MS",fallback:6e5,min:3e4,max:36e5},minProgressBytes:{env:"CHECKSUM_RUNTIME_UPLOAD_MIN_PROGRESS_BYTES",fallback:65536,min:1,max:67108864},finalizeBudgetMs:{env:"CHECKSUM_RUNTIME_UPLOAD_FINALIZE_BUDGET_MS",fallback:18e5,min:6e4,max:54e5},finalizeCeilingMs:{env:"CHECKSUM_RUNTIME_UPLOAD_FINALIZE_CEILING_MS",fallback:24e5,min:12e4,max:72e5},cliHardCapMs:{env:"CHECKSUM_RUNTIME_UPLOAD_CLI_HARD_CAP_MS",fallback:27e5,min:12e4,max:72e5},watchTimeoutMs:{env:"CHECKSUM_RUNTIME_UPLOAD_WATCH_TIMEOUT_MS",fallback:12e4,min:5e3,max:18e5},ipcTimeoutMs:{env:"CHECKSUM_RUNTIME_UPLOAD_IPC_TIMEOUT_MS",fallback:5e3,min:500,max:6e4},onEndFlushTimeoutMs:{env:"CHECKSUM_RUNTIME_UPLOAD_ONEND_FLUSH_TIMEOUT_MS",fallback:5e3,min:500,max:6e4},resumableChunkBytes:{env:"CHECKSUM_RUNTIME_UPLOAD_RESUMABLE_CHUNK_BYTES",fallback:8388608,min:262144,max:268435456},connectTimeoutMs:{env:"CHECKSUM_RUNTIME_UPLOAD_CONNECT_TIMEOUT_MS",fallback:1e4,min:1e3,max:6e4},headersTimeoutMs:{env:"CHECKSUM_RUNTIME_UPLOAD_HEADERS_TIMEOUT_MS",fallback:3e5,min:5e3,max:18e5},bodyTimeoutMs:{env:"CHECKSUM_RUNTIME_UPLOAD_BODY_TIMEOUT_MS",fallback:3e5,min:5e3,max:18e5},maxConcurrentUploads:{env:"CHECKSUM_RUNTIME_UPLOAD_MAX_CONCURRENT",fallback:15,min:1,max:64}};function r3e(i,n,c,s){if(i===void 0||i.trim()==="")return n;let r=Number(i);return Number.isInteger(r)?Math.min(s,Math.max(c,r)):n}u(r3e,"clampEnvInt");var i3e=[["cliHardCapMs",["finalizeCeilingMs"],.9],["finalizeCeilingMs",["finalizeBudgetMs"],1],["finalizeBudgetMs",["stallTimeoutMs","assetDeadlineMs","watchTimeoutMs"],1],["assetDeadlineMs",["uploadTimeoutMs"],1],["uploadTimeoutMs",["noProgressAbortMs"],.5]];function a3e(i){let n={...i},c=[];for(let[s,r,f]of i3e)for(let p of r){let d=Math.floor(n[s]*f);n[p]>d&&(c.push(`${xw[p].env} (${n[p]}ms) exceeds its bound under ${xw[s].env} (${n[s]}ms); using ${d}ms`),n[p]=d)}return{tunables:n,warnings:c}}u(a3e,"coerceScopeHierarchy");function c3e(i){let n={};for(let c of Object.keys(xw)){let s=xw[c];n[c]=r3e(i[s.env],s.fallback,s.min,s.max)}return n.resumableChunkBytes=Math.max(1,Math.floor(n.resumableChunkBytes/262144))*262144,a3e(n)}u(c3e,"resolveUploadTunablesDetailed");function UM(i){return c3e(i).tunables}u(UM,"resolveUploadTunables");var gBt=UM({});var l3e=4,ore=3,s3e=500,Pw=class i{constructor(n){this.workerInfo=n;this.hasMonitor=!1;i.monitorAgentPort?this.hasMonitor=!0:ye("CHECKSUM_UPLOAD_AGENT_PORT is not defined"),this.sendPlaywrightConfig()}static{u(this,"TestRunMonitorBridge")}static{this.monitorAgentHost="http://localhost"}static{this.monitorAgentPort=process.env.CHECKSUM_UPLOAD_AGENT_PORT}static{this.playwrightConfigSent=!1}static{this.tunables=UM(process.env)}addAsset(n){this.signalUpload(n,["trace","har","rrweb-recording"].includes(n.type))}addChecksumTestMetadata(n){let c={type:"checksumTestMetadata",payload:n};this.sendMessage(c)}addTestInfo(n,c,s=!1){this.sendMessage({type:"testInfo",payload:{checksumTestId:n,pwTestId:c,usingChecksumTestIdPlaceholder:s}})}addSingleTestStats(n,c){this.sendMessage({type:"testStats",payload:{passed:n,healed:c}})}sendPlaywrightConfig(){if(i.playwrightConfigSent)return;let n=this.buildPlaywrightConfig();this.sendMessage({type:"playwrightConfig",payload:n}),i.playwrightConfigSent=!0}buildPlaywrightConfig(){let{use:n,testDir:c,testMatch:s,testIgnore:r,timeout:f,outputDir:p,retries:d}=this.workerInfo.project,{reporter:h,configFile:o,projects:a}=this.workerInfo.config,e=Object.keys(a).filter(t=>[n,c,s,r,f,p,d].includes(t)).reduce((t,l)=>({...t,[l]:a[Number(l)]}),{});return{reporter:h,...o!==void 0?{configFile:o}:{},filteredProjects:e,use:n,testDir:c,testMatch:s,testIgnore:r,timeout:f,outputDir:p,retries:d}}sendMessage(n){return this.hasMonitor&&i.monitorAgentPort?MM(n,{port:i.monitorAgentPort,timeoutMs:i.tunables.ipcTimeoutMs,onError:u(c=>ye("Failed to send message to test run monitor",n,c),"onError")}):void 0}async signalUpload(n,c=!1){let s=i.monitorAgentPort;if(!this.hasMonitor||!s)return;let r={type:"asset",payload:n,watch:c},f;await FM(r,{port:s,timeoutMs:i.tunables.ipcTimeoutMs*l3e,attempts:ore,retryDelayMs:s3e,onError:u(d=>{f=d,ye("Failed to register asset with test run monitor",r,d)},"onError")})||pv("asset_registration_failed",{test_suite_run_id:process.env.CHECKSUM_TEST_SUITE_ID,outcome:"error",asset_type:n.type,asset_path:n.path,attempts:ore,error:vp(f)})}};var rre=require("@playwright/test"),ire=require("puppeteer-extra-plugin"),are=require("module");var _w=(0,are.createRequire)(__filename),Bw=class i{constructor(n){this.options=n;this.importPluginPackages()}static{u(this,"StealthProvider")}importPluginPackages(){i.pw??=_w("playwright-extra"),i.StealthPlugin??=_w("puppeteer-extra-plugin-stealth")}static createFreshInstance(){try{let{addExtra:n}=_w("playwright-extra");return n(i.pw.chromium)}catch(n){throw console.error("[StealthProvider] Error creating fresh instance:",n),n}}buildChromiumWithPlugins(n){let{project:c}=this.options,s=c.use.userAgent,r=c.use.playwrightExtra??[],f=i.StealthPlugin();if(r.push(f),this.isCustomUserAgent(s)){f.enabledEvasions.delete("user-agent-override");let h=_w("puppeteer-extra-plugin-stealth/evasions/user-agent-override")({userAgent:s});r.push(h)}let p=n??i.pw.chromium;return r.forEach((d,h)=>{try{!(d instanceof ire.PuppeteerExtraPlugin)&&d!==f&&console.warn(`Plugin at index ${h} in the project's checksum.config.ts file is not an instance of PuppeteerExtraPlugin.`),p.use(d)}catch(o){console.warn(o)}}),p}isCustomUserAgent(n){return n?!Object.values(rre.devices).some(c=>n===c.userAgent):!1}};function cre(i,n){return{testSuiteRunId:n,appName:"generic-app",commitHash:"generic-commit-hash",envName:"generic-env-name",branch:"generic-branch",checksumRuntimeVersion:i}}u(cre,"createDefaultTestSuiteRunInfo");var ure=require("crypto");var RBt=require("@playwright/test");var UBt=require("@playwright/test");function lre(i,n){return Reflect.get(i,n)}u(lre,"readDynamicProperty");var Rw=class i{constructor(n){this.context=n;this._pages={};this._pagesInProgress=new Map;this._testAssets=null;this.pageGuidMap=new WeakMap;this.instanceId=++i.instanceCounter,this.proxy=this.createContextProxy()}static{u(this,"ChecksumBrowserContext")}static{this.instanceCounter=0}static{this.guidCounter=0}static makeContext(n){return new i(n).proxy}_rawContext(){return this.context}setTestAssets(n){this._testAssets=n,this.setupPageListener()}getTestAssets(){return this._testAssets}cleanup(){this._pagesInProgress.clear(),this._pages={}}async newPage(){let n=await this.context.newPage();return this.makePage(n)}async makePage(n){if(n._isChecksumPage===!0)return n;if(n.isClosed())throw new Error("Cannot initialize closed page");let c=this.getPageGuid(n),s=this.getPageByGuid(c);if(s)return s;let r=this._pagesInProgress.get(c);if(r)return r;let f=this._createPageInstance(n);this._pagesInProgress.set(c,f);try{let p=await f;return this._pagesInProgress.delete(c),this.addPage(p),p}catch(p){throw this._pagesInProgress.delete(c),p}}addPage(n){let c=this.getPageGuid(n);this._pages[c]=n,n.once("close",()=>{this.removePage(c)})}removePage(n){this._pages[n]&&delete this._pages[n]}async _createPageInstance(n){if(n.isClosed())throw new Error("Cannot initialize page: Page has already been closed");if(this._testAssets)try{return await Sw.init(n,this.proxy,this._testAssets.testRunAnalytics,this._testAssets.monitorBridge,this._testAssets.variableStore,this._testAssets.testInfo,this._testAssets.checksumTestId,this._testAssets.test,this._testAssets.config,this._testAssets.isReplMode)}catch(c){throw new Error(`Failed to initialize RuntimePage: ${c instanceof Error?c.message:String(c)}. Test assets must be properly configured.`)}throw new Error("Cannot create page: Test assets not set. Use setTestAssets() before creating pages.")}createContextProxy(){return new Proxy(this,{get:u(function(c,s){return c.getContextMethod(s)},"get")})}setupPageListener(){this.context.on("page",async n=>{try{if(n.isClosed())return;await this.makePage(n)}catch(c){let s=c instanceof Error?c.message:String(c);if(s.includes("Page has already been closed")||s.includes("Target page, context or browser has been closed"))return;throw console.error("[ChecksumBrowserContext] Failed to initialize page created by application:",c),c}})}getPageGuid(n){let c=this.pageGuidMap.get(n);return c||(c=`page@checksumctx_${this.instanceId}_${++i.guidCounter}`,this.pageGuidMap.set(n,c)),c}getPageByGuid(n){return this._pages[n]}getAllPages(){return Object.values(this._pages)}getContextMethod(n){if(n in this){let s=lre(this,n);return typeof s=="function"?s.bind(this):s}let c=lre(this.context,n);return typeof c=="function"?c.bind(this.context):c}pages(){return this.getAllPages()}getPage(n){let c=this.getAllPages(),s=c[n];if(!s)throw new Error(`Page with index ${n} not found. Total pages: ${c.length}`);return s}};var jM={},fre=u((i,n,c={},s=!1)=>{let{environments:r}=n,f=r?.find(d=>d.default),p=i.extend({baseURL:f?.baseURL,bypassCSP:!0,permissions:u(async({},d,h)=>{await d(h.project.use.permissions)},"permissions"),proxy:u(async({proxy:d},h)=>{d?await h({...d,bypass:"127.0.0.1,localhost"}):await h(d)},"proxy"),browser:u(async({browser:d,launchOptions:h},o,a)=>{let e=u(()=>(a.project.use.playwrightExtra||[]).some(g=>g.constructor.name==="StealthPlugin"),"checkIfStealthPluginExists"),t=u(async()=>{let g=await(await new Bw({project:a.project}).buildChromiumWithPlugins()).launch(h);await o(g)},"launchBrowserWithStealthPlugin");Fe.useRemoteDebugging.enabled&&e()?await t():await o(d)},"browser"),launchOptions:u(async({launchOptions:d},h)=>{let o=d.args?[...d.args]:[],a=u(e=>{o.includes(e)||o.push(e)},"pushArg");c.useMockData&&a("--disable-web-security"),Fe.bypassProxyForLocalhost&&a("--proxy-bypass-list=localhost,127.0.0.1"),Fe.useRemoteDebugging.enabled&&a(`--remote-debugging-port=${Fe.useRemoteDebugging.port}`),await h({...d,args:o})},"launchOptions"),variableStore:u(async({},d)=>{let h=new Jf;await d(h)},"variableStore"),vs:u(async({variableStore:d},h)=>{await h(d)},"vs"),variablesStore:u(async({variableStore:d},h)=>{await h(d)},"variablesStore"),testRunAnalytics:u(async({},d,h)=>{let o=(()=>{try{if(process.env.CHECKSUM_TEST_SUITE_INFO)return JSON.parse(process.env.CHECKSUM_TEST_SUITE_INFO);throw new Error("Missing Checksum test suite info")}catch{return console.warn("\u26A0\uFE0F Warning: It seems like you ran the test suite directly via Playwright. This can lead to degraded functionality and unexpected issues. To ensure a fully supported and stable test run, please use: 'npx checksumai test'."),cre("playwright-run-version-unknown",(0,ure.randomUUID)())}})(),a=sre(h)??"",e=Xo.getTestRunInstance({...o,checksumTestId:a},process.env.CHECKSUM_ISOLATED_MODE==="true",h);await d(e)},"testRunAnalytics"),monitorBridge:[async({},d,h)=>{let o=new Pw(h);await d(o)},{scope:"worker"}],context:u(async({context:d,monitorBridge:h,variableStore:o,testRunAnalytics:a},e,t)=>{let l=Rw.makeContext(d);l.setTestAssets({testRunAnalytics:a,monitorBridge:h,variableStore:o,testInfo:t,checksumTestId:sre(t),test:p,config:n,isReplMode:s}),await e(l),l.cleanup()},"context"),page:u(async({page:d,context:h},o,a)=>{let e=await h.makePage(d);Object.defineProperty(e.constructor,"name",{value:"Page"}),await o(e),e&&typeof e.onTestComplete=="function"&&await e.onTestComplete(a.status==="passed"),e&&typeof e.shutdown=="function"&&await e.shutdown()},"page")});return{test:p,defineChecksumTest:u3e}},"createChecksumTestExtension"),u3e=u((i,n,c)=>{let r=(new Error().stack?.split(`
|
|
419
|
-
`)??[]).find(f=>f.includes(".checksum.spec.ts"));if(!r)throw new Error("Could not locate test location. Make sure test is run from valid checksum test file");return jM[r]=n,i},"defineChecksumTest");function sre(i){let{file:n,line:c}=i,s=Object.keys(jM).filter(f=>f.trim().includes(n.trim()));if(!s.length)return;let r=4;for(let f=0;f<r;f++)for(let p of s){let d;try{d=parseInt(p.split(":").reverse()[1]??"")}catch{continue}if(d-f===c)return jM[p]}}u(sre,"getCurrentTestIdFromMapping");var mRt=require("@playwright/test");var cRt=require("@playwright/test");var f3e=["toMatchSnapshot"],d3e=["toBe","toBeCloseTo","toBeDefined","toBeFalsy","toBeGreaterThan","toBeGreaterThanOrEqual","toBeInstanceOf","toBeLessThan","toBeLessThanOrEqual","toBeNaN","toBeNull","toBeTruthy","toBeUndefined","toContain","toContainEqual","toEqual","toHaveLength","toHaveProperty","toMatch","toMatchObject","toStrictEqual","toThrow","toThrowError"],dre=[...d3e,...f3e];var Dw=class{constructor(n,c,s=!1,r=!1,f={}){this.page=n;this.soft=s;this.skipAutoRecovery=r;this.configuration=f;this.fallbackEnabled=!1;this.withChecksumAI=!1;this.withChecksumAIMatcher=u(()=>(this.withChecksumAI=!0,Promise.reject(new Error)),"withChecksumAIMatcher");this.setThought(c)}static{u(this,"ExpectCallProxyHandler")}setThought(n){n&&(this.checksumAIThought=this.page.addNewThought(n,"assertion"),this.useChecksumAIOptions=this.page.getRuntimeOptions().useChecksumAI,this.useChecksumAIOptions.assertions&&(this.page.setAIFallback(n,"assertion"),this.fallbackEnabled=!0))}get(n,c,s){if(typeof c!="string")return Reflect.get(n,c);if(c==="checksumAI")return f=>(this.setThought(f),s);if(c==="withChecksumAI"||typeof Reflect.get(n,c)=="function"){let f=c==="withChecksumAI"?this.withChecksumAIMatcher:Reflect.get(n,c);return(...p)=>dre.includes(c)?f.call(n,...p):this.checksumAIThought?this.executeMatcherWithinStep(f,n,...p):this.executeMatcher(f,n,...p)}let r=Reflect.get(n,c);return new Proxy(r,this)}findNestedErrors(n,c){n.error&&n.infectParentStepsWithError&&c.push(n.error);for(let s of n.steps)this.findNestedErrors(s,c)}async callMatcher(n,c,...s){this.beforeMatcherCall();let r=n.call(c,...s),f=r instanceof Promise?await r:r;if(this.step){let p=[];if(this.step.error?p.push(this.step.error):this.findNestedErrors(this.step,p),p.length)throw p[0]}return f}executeMatcherWithinStep(n,c,...s){let r=this.checksumAIThought;if(!r)throw new Error("ExpectCallProxyHandler: checksumAIThought is not set");return this.page.step(r.thought,()=>this.callMatcher(n,c,...s).catch(this.onMatcherFailure.bind(this)),!1,{obtainStep:u(f=>this.step=f,"obtainStep")}).catch(f=>{if(!this.soft&&!(this.withChecksumAI&&f.message===sc))throw f}).finally(this.afterMatcherExecute.bind(this))}async executeMatcher(n,c,...s){try{return await this.callMatcher(n,c,...s)}catch(r){await this.onMatcherFailure(r);return}finally{this.afterMatcherExecute()}}beforeMatcherCall(){this.page.setExpectConfiguration(this.configuration)}afterMatcherExecute(){this.checksumAIThought=void 0,this.fallbackEnabled=!1,this.withChecksumAI=!1,this.page.resetFallbacks(),this.page.setExpectConfiguration(void 0)}shouldInitiateFallback(n){return!(!this.fallbackEnabled||Reflect.get(n,"matcherResult")?.name==="toHaveScreenshot"&&!this.useChecksumAIOptions?.visualComparison)}async onMatcherFailure(n){if(!this.shouldInitiateFallback(n))throw ye("[onMatcherFailure] fallback disabled, continuing with error."),this.page.testRunAnalytics.testRunAssertionExecutionFailure({failedThought:this.checksumAIThought?.thought??"",error:n.message,matcher:n.matcherResult?.name}),n;if(Bm()){if(ye("[onMatcherFailure] assertion failed inside a locator handler, skipping fallback"),this.soft)return;throw n}if(ye("[onMatcherFailure] initiating fallback sequence with thought:",this.checksumAIThought?.thought),this.skipAutoRecovery){if(this.soft)return;throw n}try{await this.page.initiateFallbackSequence({type:"assertion",error:n,withChecksumAI:this.withChecksumAI,testInfo:this.page.testInfo})}catch(c){if(this.soft)return;throw c}throw this.soft=!0,n}};var Nw=class i{constructor(n,c={}){this.page=n;this.configuration=c}static{u(this,"ExpectProxyHandler")}get(n,c){if(c==="setPage")return r=>{this.page=r};if(c==="extend")return(...r)=>{let f=n.extend(...r);return new Proxy(f,this)};if(c==="configure")return r=>{let f=n.configure(r);return new Proxy(f,new i(this.page,{...this.configuration,...r}))};if(c==="soft")return(...r)=>{let f=this.manipulateArgsAndExtractThought(r),p=n.soft(...r);return new Proxy(p,this.makeExpectCallProxyHandler(f,!0))};if(c==="poll")return(...r)=>{let f=this.manipulateArgsAndExtractThought(r),p=n.poll(...r);return new Proxy(p,this.makeExpectCallProxyHandler(f))};if(c==="skipAutoRecovery")return new Proxy(()=>{},{apply:u((f,p,d)=>{let h=this.manipulateArgsAndExtractThought(d),o=n.apply(p,d);return new Proxy(o,this.makeExpectCallProxyHandler(h,void 0,!0))},"apply"),get:u((f,p)=>{let d=new Proxy(n,new i(this.page,{...this.configuration,skipAutoRecovery:!0}));return Reflect.get(d,p)},"get")});let s=Reflect.get(n,c);return s instanceof Function?s.bind(n):s}apply(n,c,s){let r=this.manipulateArgsAndExtractThought(s),f=n.apply(c,s);return new Proxy(f,this.makeExpectCallProxyHandler(r))}makeExpectCallProxyHandler(n,c,s){if(!this.page)throw new Error("ExpectProxyHandler: page is not set");return new Dw(this.page,n,c===!0?!0:this.configuration.soft,s===!0?!0:this.configuration.skipAutoRecovery,this.configuration)}manipulateArgsAndExtractThought(n){let c,s=n[1];if(typeof s=="string")c=s,n[1]=void 0;else if(s!==null&&typeof s=="object"){let r=s;if(r.checksumAI===!1){r.checksumAI=void 0;return}r.checksumAI&&typeof r.checksumAI=="string"?(c=r.checksumAI,r.checksumAI=void 0):r.message&&(c=r.message,r.message=void 0)}return c}};var Lw=class i{constructor(n){this.expect=n;
|
|
419
|
+
`)??[]).find(f=>f.includes(".checksum.spec.ts"));if(!r)throw new Error("Could not locate test location. Make sure test is run from valid checksum test file");return jM[r]=n,i},"defineChecksumTest");function sre(i){let{file:n,line:c}=i,s=Object.keys(jM).filter(f=>f.trim().includes(n.trim()));if(!s.length)return;let r=4;for(let f=0;f<r;f++)for(let p of s){let d;try{d=parseInt(p.split(":").reverse()[1]??"")}catch{continue}if(d-f===c)return jM[p]}}u(sre,"getCurrentTestIdFromMapping");var mRt=require("@playwright/test");var cRt=require("@playwright/test");var f3e=["toMatchSnapshot"],d3e=["toBe","toBeCloseTo","toBeDefined","toBeFalsy","toBeGreaterThan","toBeGreaterThanOrEqual","toBeInstanceOf","toBeLessThan","toBeLessThanOrEqual","toBeNaN","toBeNull","toBeTruthy","toBeUndefined","toContain","toContainEqual","toEqual","toHaveLength","toHaveProperty","toMatch","toMatchObject","toStrictEqual","toThrow","toThrowError"],dre=[...d3e,...f3e];var Dw=class{constructor(n,c,s=!1,r=!1,f={}){this.page=n;this.soft=s;this.skipAutoRecovery=r;this.configuration=f;this.fallbackEnabled=!1;this.withChecksumAI=!1;this.withChecksumAIMatcher=u(()=>(this.withChecksumAI=!0,Promise.reject(new Error)),"withChecksumAIMatcher");this.setThought(c)}static{u(this,"ExpectCallProxyHandler")}setThought(n){n&&(this.checksumAIThought=this.page.addNewThought(n,"assertion"),this.useChecksumAIOptions=this.page.getRuntimeOptions().useChecksumAI,this.useChecksumAIOptions.assertions&&(this.page.setAIFallback(n,"assertion"),this.fallbackEnabled=!0))}get(n,c,s){if(typeof c!="string")return Reflect.get(n,c);if(c==="checksumAI")return f=>(this.setThought(f),s);if(c==="withChecksumAI"||typeof Reflect.get(n,c)=="function"){let f=c==="withChecksumAI"?this.withChecksumAIMatcher:Reflect.get(n,c);return(...p)=>dre.includes(c)?f.call(n,...p):this.checksumAIThought?this.executeMatcherWithinStep(f,n,...p):this.executeMatcher(f,n,...p)}let r=Reflect.get(n,c);return new Proxy(r,this)}findNestedErrors(n,c){n.error&&n.infectParentStepsWithError&&c.push(n.error);for(let s of n.steps)this.findNestedErrors(s,c)}async callMatcher(n,c,...s){this.beforeMatcherCall();let r=n.call(c,...s),f=r instanceof Promise?await r:r;if(this.step){let p=[];if(this.step.error?p.push(this.step.error):this.findNestedErrors(this.step,p),p.length)throw p[0]}return f}executeMatcherWithinStep(n,c,...s){let r=this.checksumAIThought;if(!r)throw new Error("ExpectCallProxyHandler: checksumAIThought is not set");return this.page.step(r.thought,()=>this.callMatcher(n,c,...s).catch(this.onMatcherFailure.bind(this)),!1,{obtainStep:u(f=>this.step=f,"obtainStep")}).catch(f=>{if(!this.soft&&!(this.withChecksumAI&&f.message===sc))throw f}).finally(this.afterMatcherExecute.bind(this))}async executeMatcher(n,c,...s){try{return await this.callMatcher(n,c,...s)}catch(r){await this.onMatcherFailure(r);return}finally{this.afterMatcherExecute()}}beforeMatcherCall(){this.page.setExpectConfiguration(this.configuration)}afterMatcherExecute(){this.checksumAIThought=void 0,this.fallbackEnabled=!1,this.withChecksumAI=!1,this.page.resetFallbacks(),this.page.setExpectConfiguration(void 0)}shouldInitiateFallback(n){return!(!this.fallbackEnabled||Reflect.get(n,"matcherResult")?.name==="toHaveScreenshot"&&!this.useChecksumAIOptions?.visualComparison)}async onMatcherFailure(n){if(!this.shouldInitiateFallback(n))throw ye("[onMatcherFailure] fallback disabled, continuing with error."),this.page.testRunAnalytics.testRunAssertionExecutionFailure({failedThought:this.checksumAIThought?.thought??"",error:n.message,matcher:n.matcherResult?.name}),n;if(Bm()){if(ye("[onMatcherFailure] assertion failed inside a locator handler, skipping fallback"),this.soft)return;throw n}if(ye("[onMatcherFailure] initiating fallback sequence with thought:",this.checksumAIThought?.thought),this.skipAutoRecovery){if(this.soft)return;throw n}try{await this.page.initiateFallbackSequence({type:"assertion",error:n,withChecksumAI:this.withChecksumAI,testInfo:this.page.testInfo})}catch(c){if(this.soft)return;throw c}throw this.soft=!0,n}};var Nw=class i{constructor(n,c={}){this.page=n;this.configuration=c}static{u(this,"ExpectProxyHandler")}get(n,c){if(c==="setPage")return r=>{this.page=r};if(c==="extend")return(...r)=>{let f=n.extend(...r);return new Proxy(f,this)};if(c==="configure")return r=>{let f=n.configure(r);return new Proxy(f,new i(this.page,{...this.configuration,...r}))};if(c==="soft")return(...r)=>{let f=this.manipulateArgsAndExtractThought(r),p=n.soft(...r);return new Proxy(p,this.makeExpectCallProxyHandler(f,!0))};if(c==="poll")return(...r)=>{let f=this.manipulateArgsAndExtractThought(r),p=n.poll(...r);return new Proxy(p,this.makeExpectCallProxyHandler(f))};if(c==="skipAutoRecovery")return new Proxy(()=>{},{apply:u((f,p,d)=>{let h=this.manipulateArgsAndExtractThought(d),o=n.apply(p,d);return new Proxy(o,this.makeExpectCallProxyHandler(h,void 0,!0))},"apply"),get:u((f,p)=>{let d=new Proxy(n,new i(this.page,{...this.configuration,skipAutoRecovery:!0}));return Reflect.get(d,p)},"get")});let s=Reflect.get(n,c);return s instanceof Function?s.bind(n):s}apply(n,c,s){let r=this.manipulateArgsAndExtractThought(s),f=n.apply(c,s);return new Proxy(f,this.makeExpectCallProxyHandler(r))}makeExpectCallProxyHandler(n,c,s){if(!this.page)throw new Error("ExpectProxyHandler: page is not set");return new Dw(this.page,n,c===!0?!0:this.configuration.soft,s===!0?!0:this.configuration.skipAutoRecovery,this.configuration)}manipulateArgsAndExtractThought(n){let c,s=n[1];if(typeof s=="string")c=s,n[1]=void 0;else if(s!==null&&typeof s=="object"){let r=s;if(r.checksumAI===!1){r.checksumAI=void 0;return}r.checksumAI&&typeof r.checksumAI=="string"?(c=r.checksumAI,r.checksumAI=void 0):r.message&&(c=r.message,r.message=void 0)}return c}};var Lw=class i{constructor(n){this.expect=n;let c=new Proxy(this.expect,new Nw);this.proxy=c}static{u(this,"ChecksumExpect")}static init(n){return new i(n).proxy}};var pre=require("@playwright/test");function p3e(i,n=AI()){let{options:c,apiKey:s,environments:r,defaultEnvironmentName:f}=n,p=r?.find(g=>g.default);if(f&&(p=r?.find(g=>g.name===f)),!p)throw new Error("No default environment found. Please set the default field to true on one of the environments in the checksum.config.ts file.");if(!p.baseURL)throw new Error("A baseURL is required. Please set the baseURL field in the default environment in the checksum.config.ts file.");if(!p.users?.find(g=>g.default))throw new Error("A default user within the default environment is required. Please set one of the default environment users as default in the checksum.config.ts file.");if(!s)throw new Error("An API key is required. Please set the apiKey field in the checksum.config.ts file.");let h=process.env.CHECKSUM_REPL==="true",{test:o,defineChecksumTest:a}=fre(i??pre.test,n,c,h),e=Lw.init(o.expect),t=h3e();o._checksum={expect:e,checksumAI:t},h&&(o.beforeEach(async()=>{o.setTimeout(0)}),o.afterEach(async()=>{for(;;)await ct(1e3)}));let l=u(({name:g,userRole:A})=>{let{envName:y,role:v}=LM({...g!==void 0?{envName:g}:{},...A!==void 0?{role:A}:{}}),{environment:O,user:m}=QM(n,{...y!==void 0?{environment:y}:{},...v!==void 0?{role:v}:{}},!0),k=tre({config:n,throwIfEnvInfoInvalid:!0},{environmentName:O.name,userRole:m.role});return{environment:O,user:m,login:k}},"getEnvironment");return{test:o,expect:e,login:ds({config:n}),defineChecksumTest:a,getEnvironment:l,checksumAI:t}}u(p3e,"init");var HM=u(i=>new Proxy(i,{apply:u(function(c,s,r){let f={...c.options,...r[2]};return c.page.checksumAI(r[0],r[1],f)},"apply"),get:u(function(c,s){return s==="withDialog"?(c.options.withDialog=!0,HM(i)):s==="skipAutoRecovery"?(c.options.skipAutoRecovery=!0,HM(i)):c[s]},"get"),set:u(function(c,s,r){return s!=="page"?!1:(c[s]=r,!!r)},"set")}),"makeChecksumAIProxy"),h3e=u(()=>{let i=u(function(){},"func");return i.page=void 0,i.options={withDialog:!1,skipAutoRecovery:!1},HM(i)},"makeChecksumAI");0&&(module.exports={AutoRecoveryMode,RunMode,getChecksumConfig,getLogin,init});
|
|
420
420
|
/*! Bundled license information:
|
|
421
421
|
|
|
422
422
|
playwright-extra/dist/index.cjs.js:
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"description": "Checksum.ai test runtime",
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"dependencies": {
|
|
6
|
-
"@playwright/test": "1.
|
|
6
|
+
"@playwright/test": "1.62.1",
|
|
7
7
|
"dotenv": "17.4.2",
|
|
8
8
|
"jsdom": "30.0.1",
|
|
9
9
|
"playwright-extra": "4.3.6",
|
|
@@ -45,5 +45,5 @@
|
|
|
45
45
|
"url": "https://github.com/checksum-ai/checksum-ai-monorepo/issues"
|
|
46
46
|
},
|
|
47
47
|
"homepage": "https://github.com/checksum-ai/checksum-ai-monorepo/tree/main/packages/runtime#readme",
|
|
48
|
-
"version": "4.16.1"
|
|
48
|
+
"version": "4.16.2-beta.1"
|
|
49
49
|
}
|
|
@@ -0,0 +1,562 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const crypto = require("crypto");
|
|
3
|
+
const { join } = require("path");
|
|
4
|
+
|
|
5
|
+
// Args
|
|
6
|
+
const on = process.argv[2] !== "off";
|
|
7
|
+
|
|
8
|
+
// -------- [Modifiers] -------- //
|
|
9
|
+
|
|
10
|
+
// Amends the file with the given entry point text and append text
|
|
11
|
+
// When "on" is true, the append text is added to the entry point,
|
|
12
|
+
// otherwise the append text is completely removed from the file
|
|
13
|
+
function amend(filePath, entryPointText, appendText) {
|
|
14
|
+
const data = fs.readFileSync(filePath, "utf8");
|
|
15
|
+
if (!data.includes(entryPointText)) {
|
|
16
|
+
throw new Error("Entry point not found!", entryPointText);
|
|
17
|
+
}
|
|
18
|
+
// Ignore if the append text is already present
|
|
19
|
+
if (on && data.includes(appendText)) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
// Add or clear according to on state
|
|
23
|
+
const result = on
|
|
24
|
+
? data.replace(entryPointText, entryPointText + appendText)
|
|
25
|
+
: data.replace(appendText, "");
|
|
26
|
+
|
|
27
|
+
// Write
|
|
28
|
+
fs.writeFileSync(filePath, result, "utf8");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Bracket each injected block with a per-patch unique marker derived from a
|
|
32
|
+
// hash of `originalContent`. This guarantees OFF mode reverts only the patch
|
|
33
|
+
// that produced the marker, instead of accidentally matching another patch's
|
|
34
|
+
// shared `/* checksumai */ ` prefix (which previously caused
|
|
35
|
+
// `alwaysInjectScripts` OFF — whose newContent collapses to the bare marker —
|
|
36
|
+
// to clobber the FIRST `/* checksumai */ ` it found, corrupting whichever
|
|
37
|
+
// patch happened to live at the lowest line number).
|
|
38
|
+
//
|
|
39
|
+
// Backward-compat for the legacy single-marker format: in OFF mode, if the
|
|
40
|
+
// new bracketed form is not present but the legacy `/* checksumai */ <body>`
|
|
41
|
+
// form is, revert that and exit. New ON installs always write the bracketed
|
|
42
|
+
// form.
|
|
43
|
+
function replaceContent(filePath, originalContent, newContent) {
|
|
44
|
+
const fileContent = fs.readFileSync(filePath, "utf8");
|
|
45
|
+
|
|
46
|
+
const id = crypto
|
|
47
|
+
.createHash("sha1")
|
|
48
|
+
.update(originalContent)
|
|
49
|
+
.digest("hex")
|
|
50
|
+
.slice(0, 8);
|
|
51
|
+
const openMarker = `/*checksumai:${id}*/`;
|
|
52
|
+
const closeMarker = `/*checksumai:end:${id}*/`;
|
|
53
|
+
const taggedNewContent = `${openMarker}${newContent}${closeMarker}`;
|
|
54
|
+
|
|
55
|
+
if (on) {
|
|
56
|
+
if (fileContent.includes(taggedNewContent)) {
|
|
57
|
+
// Already patched.
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (!fileContent.includes(originalContent)) {
|
|
61
|
+
// Nothing to replace. Either already migrated to a different shape or
|
|
62
|
+
// the playwright internals changed and this patch needs updating.
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
fs.writeFileSync(
|
|
66
|
+
filePath,
|
|
67
|
+
fileContent.replace(originalContent, taggedNewContent),
|
|
68
|
+
"utf8"
|
|
69
|
+
);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// OFF: precise revert via the unique bracketed block.
|
|
74
|
+
if (fileContent.includes(taggedNewContent)) {
|
|
75
|
+
fs.writeFileSync(
|
|
76
|
+
filePath,
|
|
77
|
+
fileContent.replace(taggedNewContent, originalContent),
|
|
78
|
+
"utf8"
|
|
79
|
+
);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// OFF (legacy): older patched files used `/* checksumai */ ${newContent}`
|
|
84
|
+
// with a shared marker. Revert that exact body if present so users coming
|
|
85
|
+
// from a previous runtime version aren't stranded with stale injections.
|
|
86
|
+
const legacyTagged = `/* checksumai */ ${newContent}`;
|
|
87
|
+
if (fileContent.includes(legacyTagged)) {
|
|
88
|
+
fs.writeFileSync(
|
|
89
|
+
filePath,
|
|
90
|
+
fileContent.replace(legacyTagged, originalContent),
|
|
91
|
+
"utf8"
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function doesFileExist(filePath) {
|
|
97
|
+
if (!fs.existsSync(filePath)) {
|
|
98
|
+
console.warn("File not found", filePath);
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// -------- [Modifications] -------- //
|
|
105
|
+
|
|
106
|
+
// File targets are unchanged from 1.60 (the big lib/ -> esbuild-bundle
|
|
107
|
+
// collapse landed there):
|
|
108
|
+
// playwright-core/lib/coreBundle.js
|
|
109
|
+
// playwright/lib/worker/workerProcessEntry.js
|
|
110
|
+
// playwright/lib/common/index.js
|
|
111
|
+
// playwright/lib/index.js
|
|
112
|
+
// playwright/lib/runner/index.js
|
|
113
|
+
//
|
|
114
|
+
// 1.60 -> 1.62 moved three anchors:
|
|
115
|
+
// - `_wrapApiCall(func, options2)` -> `_wrapApiCall(func, options)`; the
|
|
116
|
+
// esbuild dedup suffix on `options` disappeared, so every `options2`
|
|
117
|
+
// reference in the channelOwner patch reverts to `options`.
|
|
118
|
+
// - `_failWithError(error)` -> `_failWithError(root)`; the method now walks
|
|
119
|
+
// an error tree via a local `visit()`.
|
|
120
|
+
// - Inside workerProcessEntry the helpers are no longer namespaced imports:
|
|
121
|
+
// `(0, import_util2.testInfoError)` -> `testInfoError` and
|
|
122
|
+
// `(0, import_utils.stringifyStackFrames)` -> `stringifyStackFrames2`.
|
|
123
|
+
// `import_fs5`, `toPosixPath2` and `import_path8` in runner/index.js kept
|
|
124
|
+
// their 1.60 suffixes — but these are build-pipeline artifacts, so re-validate
|
|
125
|
+
// every anchor on each upgrade rather than assuming the previous patch applies.
|
|
126
|
+
|
|
127
|
+
// Remove conditions for injecting Playwright scripts
|
|
128
|
+
function alwaysInjectScripts(projectRoot) {
|
|
129
|
+
const file = join(
|
|
130
|
+
projectRoot,
|
|
131
|
+
"node_modules/playwright-core/lib/coreBundle.js"
|
|
132
|
+
);
|
|
133
|
+
if (!doesFileExist(file)) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const originalContent = 'if (debugMode() === "console")';
|
|
138
|
+
|
|
139
|
+
const newContent = "";
|
|
140
|
+
|
|
141
|
+
replaceContent(file, originalContent, newContent);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Add implementation for generateSelectorAndLocator and inject to Playwright console API
|
|
145
|
+
function addGenerateSelectorAndLocator(projectRoot) {
|
|
146
|
+
const file = join(
|
|
147
|
+
projectRoot,
|
|
148
|
+
"node_modules/playwright-core/lib/coreBundle.js"
|
|
149
|
+
);
|
|
150
|
+
if (!doesFileExist(file)) {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
const entryPointText1 = "this._generateLocator(element, language),\\n ";
|
|
154
|
+
const appendText1 =
|
|
155
|
+
"generateSelectorAndLocator: (element, language) => this._generateSelectorAndLocator(element, language),\\n asLocator,\\n ";
|
|
156
|
+
amend(file, entryPointText1, appendText1);
|
|
157
|
+
|
|
158
|
+
const entryPointText2 = `return asLocator(language || "javascript", selector);\\n }\\n `;
|
|
159
|
+
const appendText2 =
|
|
160
|
+
'_generateSelectorAndLocator(element, language) {\\n if (!(element instanceof Element))\\n throw new Error(`Usage: playwright.locator(element).`);\\n const selector = this._injectedScript.generateSelectorSimple(element);\\n return {selector, locator: asLocator(language || \\"javascript\\", selector)};\\n }\\n ';
|
|
161
|
+
amend(file, entryPointText2, appendText2);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// -------- [Runtime modifications] -------- //
|
|
165
|
+
|
|
166
|
+
function expect(projectRoot) {
|
|
167
|
+
const file = join(
|
|
168
|
+
projectRoot,
|
|
169
|
+
"node_modules/playwright/lib/matchers/expect.js"
|
|
170
|
+
);
|
|
171
|
+
if (!doesFileExist(file)) {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
let originalContent, newContent;
|
|
175
|
+
|
|
176
|
+
// originalContent = `return (...args) => {
|
|
177
|
+
// const testInfo = (0, _globals.currentTestInfo)();`;
|
|
178
|
+
// newContent = `return (...args) => {
|
|
179
|
+
// let noSoft = false;
|
|
180
|
+
// if (args.find(arg=>arg==='no-soft')){
|
|
181
|
+
// noSoft = true;
|
|
182
|
+
// args.pop();
|
|
183
|
+
// }
|
|
184
|
+
// const testInfo = (0, _globals.currentTestInfo)();`;
|
|
185
|
+
// replaceContent(file, originalContent, newContent);
|
|
186
|
+
|
|
187
|
+
// originalContent = `step.complete({
|
|
188
|
+
// error
|
|
189
|
+
// })`;
|
|
190
|
+
// newContent = `step.complete({
|
|
191
|
+
// error,
|
|
192
|
+
// noSoft
|
|
193
|
+
// })`;
|
|
194
|
+
// replaceContent(file, originalContent, newContent);
|
|
195
|
+
|
|
196
|
+
// originalContent = `if (this._info.isSoft) testInfo._failWithError(error);else throw error;`;
|
|
197
|
+
// newContent = `if (this._info.isSoft && !noSoft) testInfo._failWithError(error);else throw error;`;
|
|
198
|
+
// replaceContent(file, originalContent, newContent);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function testInfo(projectRoot) {
|
|
202
|
+
const file = join(
|
|
203
|
+
projectRoot,
|
|
204
|
+
"node_modules/playwright/lib/worker/workerProcessEntry.js"
|
|
205
|
+
);
|
|
206
|
+
if (!doesFileExist(file)) {
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
let originalContent, newContent;
|
|
210
|
+
let entryPointText, appendText;
|
|
211
|
+
|
|
212
|
+
// originalContent = `const filteredStack = (0, _util.filteredStackTrace)((0, _utils.captureRawStack)());`;
|
|
213
|
+
// newContent = `const filteredStack = (0, _util.filteredStackTrace)((0, _utils.captureRawStack)().filter(s=>!s.includes('@checksum-ai/runtime')));`;
|
|
214
|
+
// replaceContent(file, originalContent, newContent);
|
|
215
|
+
|
|
216
|
+
entryPointText = `location = location || filteredStack[0];`;
|
|
217
|
+
appendText = `\nif (this._checksumInternal) {
|
|
218
|
+
location = undefined;
|
|
219
|
+
this._checksumInternal = false;
|
|
220
|
+
}
|
|
221
|
+
if (this._checksumNoLocation){
|
|
222
|
+
location = undefined;
|
|
223
|
+
}`;
|
|
224
|
+
amend(file, entryPointText, appendText);
|
|
225
|
+
|
|
226
|
+
originalContent = `if (childStep.error && childStep.infectParentStepsWithError) {`;
|
|
227
|
+
newContent = `if (childStep.error && childStep.infectParentStepsWithError && !step.preventInfectParentStepsWithError) {`;
|
|
228
|
+
replaceContent(file, originalContent, newContent);
|
|
229
|
+
|
|
230
|
+
originalContent = `_failWithError(root) {`;
|
|
231
|
+
newContent = `addError(error, message) {
|
|
232
|
+
const serialized = testInfoError(error);
|
|
233
|
+
serialized.message = [message, serialized.message].join('\\n\\n');
|
|
234
|
+
serialized.stack = [message, serialized.stack].join('\\n\\n');
|
|
235
|
+
const step = error[stepSymbol];
|
|
236
|
+
if (step && step.boxedStack) serialized.stack = \`\${error.name}: \${error.message}\\n\${stringifyStackFrames2(step.boxedStack).join('\\n')}\`;
|
|
237
|
+
this.errors.push(serialized);
|
|
238
|
+
}
|
|
239
|
+
_failWithError(root) {`;
|
|
240
|
+
replaceContent(file, originalContent, newContent);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function testType(projectRoot) {
|
|
244
|
+
const file = join(projectRoot, "node_modules/playwright/lib/common/index.js");
|
|
245
|
+
if (!doesFileExist(file)) {
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
entryPointText = `return await currentZone().with("stepZone", step).run(async () => {`;
|
|
250
|
+
appendText = `\nif (options.obtainStep){
|
|
251
|
+
options.obtainStep(step);
|
|
252
|
+
}`;
|
|
253
|
+
amend(file, entryPointText, appendText);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function indexContent(projectRoot) {
|
|
257
|
+
const file = join(projectRoot, "node_modules/playwright/lib/index.js");
|
|
258
|
+
if (!doesFileExist(file)) {
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
let originalContent, newContent;
|
|
262
|
+
originalContent = `const browser = await playwright[browserName].launch();`;
|
|
263
|
+
newContent = `
|
|
264
|
+
let browser = playwright[browserName];
|
|
265
|
+
try {
|
|
266
|
+
const { playwrightExtra } = workerInfo?.project?.use || {};
|
|
267
|
+
if (playwrightExtra && playwrightExtra?.length) {
|
|
268
|
+
const pw = require("playwright-extra")
|
|
269
|
+
const PupeteerExtraPlugin = require("puppeteer-extra-plugin").PuppeteerExtraPlugin
|
|
270
|
+
const chromium = pw.chromium;
|
|
271
|
+
|
|
272
|
+
playwrightExtra.forEach((plugin, i) => {
|
|
273
|
+
try {
|
|
274
|
+
if(!(plugin instanceof PupeteerExtraPlugin)){
|
|
275
|
+
console.warn(\`Plugin at index \${i} is not an instance of PupeteerExtraPlugin\`);
|
|
276
|
+
}
|
|
277
|
+
chromium.use(plugin);
|
|
278
|
+
} catch (e) {
|
|
279
|
+
console.warn(e);
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
browser = chromium;
|
|
283
|
+
}
|
|
284
|
+
} catch (e) {
|
|
285
|
+
console.warn(
|
|
286
|
+
"CHECKSUM: Failed to load Playwright Extra, using Playwright instead.",
|
|
287
|
+
e
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
browser = await browser.launch();
|
|
291
|
+
`;
|
|
292
|
+
replaceContent(file, originalContent, newContent);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function channelOwner(projectRoot) {
|
|
296
|
+
const file = join(
|
|
297
|
+
projectRoot,
|
|
298
|
+
"node_modules/playwright-core/lib/coreBundle.js"
|
|
299
|
+
);
|
|
300
|
+
if (!doesFileExist(file)) {
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
let originalContent, newContent;
|
|
304
|
+
let entryPointText, appendText;
|
|
305
|
+
|
|
306
|
+
entryPointText = `async _wrapApiCall(func, options) {`;
|
|
307
|
+
|
|
308
|
+
appendText = `\nif (this._checksumInternal){
|
|
309
|
+
options = options || {};
|
|
310
|
+
options.internal = true;
|
|
311
|
+
}`;
|
|
312
|
+
amend(file, entryPointText, appendText);
|
|
313
|
+
|
|
314
|
+
entryPointText = `const apiZone = { title: options?.title, apiName: stackTrace.apiName, frames: stackTrace.frames, internal: options?.internal ?? false, reported: false, userData: void 0, stepId: void 0 };`;
|
|
315
|
+
|
|
316
|
+
appendText = `\nif (!apiZone.internal && this._checksumTitle){
|
|
317
|
+
apiZone.apiName = this._checksumTitle;
|
|
318
|
+
this._checksumTitle = undefined;
|
|
319
|
+
}
|
|
320
|
+
if (!apiZone.apiName){
|
|
321
|
+
options = options || {};
|
|
322
|
+
options.internal = true;
|
|
323
|
+
apiZone.internal = true;
|
|
324
|
+
apiZone.reported = true;
|
|
325
|
+
}
|
|
326
|
+
if (apiZone.apiName && apiZone.apiName.startsWith('proxy')) {
|
|
327
|
+
apiZone.apiName = apiZone.apiName.replace('proxy', 'page');
|
|
328
|
+
}`;
|
|
329
|
+
amend(file, entryPointText, appendText);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
function stackTrace(projectRoot) {
|
|
333
|
+
const file = join(
|
|
334
|
+
projectRoot,
|
|
335
|
+
"node_modules/playwright-core/lib/coreBundle.js"
|
|
336
|
+
);
|
|
337
|
+
if (!doesFileExist(file)) {
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
let originalContent, newContent;
|
|
342
|
+
|
|
343
|
+
// Create a regex for getting a file for each line in the stacktrace that overrides the original regex
|
|
344
|
+
// This regex is only used for getting files, the original regex is sitll used for the rest of the content
|
|
345
|
+
originalContent = `let file = match[7];`;
|
|
346
|
+
const fileRe = /(\/.*?\.[a-zA-Z0-9]+)(?=:\d+:\d+)/;
|
|
347
|
+
newContent = `
|
|
348
|
+
const fileRe = new RegExp(${JSON.stringify(fileRe.source)}, "${
|
|
349
|
+
fileRe.flags
|
|
350
|
+
}");
|
|
351
|
+
const m = fileRe.exec(match[0] ?? "");
|
|
352
|
+
let file = m ? m[1] : undefined;
|
|
353
|
+
`;
|
|
354
|
+
replaceContent(file, originalContent, newContent);
|
|
355
|
+
|
|
356
|
+
// Filter out checksum-ai/runtime from stack traces
|
|
357
|
+
originalContent = `return stack.split("\\n");`;
|
|
358
|
+
newContent = `return stack.split("\\n").filter(s=>!s.includes('@checksum-ai/runtime'));`;
|
|
359
|
+
replaceContent(file, originalContent, newContent);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
function reportTraceFile(projectRoot) {
|
|
363
|
+
const file = join(projectRoot, "node_modules/playwright/lib/runner/index.js");
|
|
364
|
+
if (!doesFileExist(file)) {
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
let originalContent, newContent;
|
|
369
|
+
|
|
370
|
+
originalContent = `const buffer = import_fs5.default.readFileSync(a.path);`;
|
|
371
|
+
newContent = `let buffer = import_fs5.default.readFileSync(a.path);
|
|
372
|
+
if (a.name === "trace") {
|
|
373
|
+
let retries = 2;
|
|
374
|
+
while (!buffer.slice(0,100).toString().startsWith("checksum-playwright-trace") && retries > 0) {
|
|
375
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 5000);
|
|
376
|
+
buffer = import_fs5.default.readFileSync(a.path)
|
|
377
|
+
retries--;
|
|
378
|
+
}
|
|
379
|
+
}`;
|
|
380
|
+
replaceContent(file, originalContent, newContent);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
function htmlReporter(projectRoot) {
|
|
384
|
+
const file = join(projectRoot, "node_modules/playwright/lib/runner/index.js");
|
|
385
|
+
if (!doesFileExist(file)) {
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
let originalContent, newContent;
|
|
390
|
+
|
|
391
|
+
// Filter out runtime files from snippet generation
|
|
392
|
+
originalContent = `function createSnippets(stepsInFile) {
|
|
393
|
+
for (const file of stepsInFile.keys()) {
|
|
394
|
+
let source;
|
|
395
|
+
try {
|
|
396
|
+
source = import_fs5.default.readFileSync(file, "utf-8") + "\\n//";
|
|
397
|
+
} catch (e) {
|
|
398
|
+
continue;
|
|
399
|
+
}`;
|
|
400
|
+
newContent = `function createSnippets(stepsInFile) {
|
|
401
|
+
for (const file of stepsInFile.keys()) {
|
|
402
|
+
// Skip runtime files to reduce report size
|
|
403
|
+
if (file.includes('@checksum-ai/runtime') || file.includes('node_modules')) {
|
|
404
|
+
continue;
|
|
405
|
+
}
|
|
406
|
+
let source;
|
|
407
|
+
try {
|
|
408
|
+
source = import_fs5.default.readFileSync(file, "utf-8") + "\\n//";
|
|
409
|
+
} catch (e) {
|
|
410
|
+
continue;
|
|
411
|
+
}`;
|
|
412
|
+
replaceContent(file, originalContent, newContent);
|
|
413
|
+
|
|
414
|
+
// Also filter from error codeframe generation
|
|
415
|
+
originalContent = `function createErrorCodeframe(message, location) {
|
|
416
|
+
let source;
|
|
417
|
+
try {
|
|
418
|
+
source = import_fs5.default.readFileSync(location.file, "utf-8") + "\\n//";
|
|
419
|
+
} catch (e) {
|
|
420
|
+
return;
|
|
421
|
+
}`;
|
|
422
|
+
newContent = `function createErrorCodeframe(message, location) {
|
|
423
|
+
// Skip runtime files to reduce report size
|
|
424
|
+
if (location.file && (location.file.includes('@checksum-ai/runtime') || location.file.includes('node_modules'))) {
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
let source;
|
|
428
|
+
try {
|
|
429
|
+
source = import_fs5.default.readFileSync(location.file, "utf-8") + "\\n//";
|
|
430
|
+
} catch (e) {
|
|
431
|
+
return;
|
|
432
|
+
}`;
|
|
433
|
+
replaceContent(file, originalContent, newContent);
|
|
434
|
+
|
|
435
|
+
// Filter out steps with locations in runtime/node_modules files completely
|
|
436
|
+
originalContent = `_createTestStep(dedupedStep, result) {
|
|
437
|
+
const { step, duration, count } = dedupedStep;
|
|
438
|
+
const skipped = dedupedStep.step.annotations?.find((a) => a.type === "skip");
|
|
439
|
+
let title = step.title;
|
|
440
|
+
if (skipped)
|
|
441
|
+
title = \`\${title} (skipped\${skipped.description ? ": " + skipped.description : ""})\`;
|
|
442
|
+
const testStep = {
|
|
443
|
+
title,
|
|
444
|
+
startTime: step.startTime.toISOString(),
|
|
445
|
+
duration,
|
|
446
|
+
steps: dedupeSteps(step.steps).map((s) => this._createTestStep(s, result)),
|
|
447
|
+
attachments: step.attachments.map((s) => {
|
|
448
|
+
const index = result.attachments.indexOf(s);
|
|
449
|
+
if (index === -1)
|
|
450
|
+
throw new Error("Unexpected, attachment not found");
|
|
451
|
+
return index;
|
|
452
|
+
}),
|
|
453
|
+
location: this._relativeLocation(step.location),
|
|
454
|
+
error: step.error?.message,
|
|
455
|
+
count,
|
|
456
|
+
skipped: !!skipped
|
|
457
|
+
};
|
|
458
|
+
if (step.location)
|
|
459
|
+
this._stepsInFile.set(step.location.file, testStep);
|
|
460
|
+
return testStep;
|
|
461
|
+
}`;
|
|
462
|
+
newContent = `_createTestStep(dedupedStep, result) {
|
|
463
|
+
const { step, duration, count } = dedupedStep;
|
|
464
|
+
// Skip "Evaluate" steps with locations in runtime/node_modules files
|
|
465
|
+
if (step.location && step.title === "Evaluate" && (step.location.file.includes('@checksum-ai/runtime') || step.location.file.includes('node_modules'))) {
|
|
466
|
+
// Return null to indicate this step should be filtered out
|
|
467
|
+
return null;
|
|
468
|
+
}
|
|
469
|
+
const skipped = dedupedStep.step.annotations?.find((a) => a.type === "skip");
|
|
470
|
+
let title = step.title;
|
|
471
|
+
if (skipped)
|
|
472
|
+
title = \`\${title} (skipped\${skipped.description ? ": " + skipped.description : ""})\`;
|
|
473
|
+
const testStep = {
|
|
474
|
+
title,
|
|
475
|
+
startTime: step.startTime.toISOString(),
|
|
476
|
+
duration,
|
|
477
|
+
steps: dedupeSteps(step.steps).map((s) => this._createTestStep(s, result)).filter(s => s !== null),
|
|
478
|
+
attachments: step.attachments.map((s) => {
|
|
479
|
+
const index = result.attachments.indexOf(s);
|
|
480
|
+
if (index === -1)
|
|
481
|
+
throw new Error("Unexpected, attachment not found");
|
|
482
|
+
return index;
|
|
483
|
+
}),
|
|
484
|
+
location: this._relativeLocation(step.location),
|
|
485
|
+
error: step.error?.message,
|
|
486
|
+
count,
|
|
487
|
+
skipped: !!skipped
|
|
488
|
+
};
|
|
489
|
+
if (step.location)
|
|
490
|
+
this._stepsInFile.set(step.location.file, testStep);
|
|
491
|
+
return testStep;
|
|
492
|
+
}`;
|
|
493
|
+
replaceContent(file, originalContent, newContent);
|
|
494
|
+
|
|
495
|
+
// Also filter steps when creating test results
|
|
496
|
+
originalContent = `steps: dedupeSteps(result.steps).map((s) => this._createTestStep(s, result)),`;
|
|
497
|
+
newContent = `steps: dedupeSteps(result.steps).map((s) => this._createTestStep(s, result)).filter(s => s !== null),`;
|
|
498
|
+
replaceContent(file, originalContent, newContent);
|
|
499
|
+
|
|
500
|
+
// Normalize locations for steps from runtime/index.js - hide location if it's index.js from runtime
|
|
501
|
+
originalContent = `_relativeLocation(location) {
|
|
502
|
+
if (!location)
|
|
503
|
+
return void 0;
|
|
504
|
+
const file = toPosixPath2(import_path8.default.relative(this._config.rootDir, location.file));
|
|
505
|
+
return {
|
|
506
|
+
file,
|
|
507
|
+
line: location.line,
|
|
508
|
+
column: location.column
|
|
509
|
+
};
|
|
510
|
+
}`;
|
|
511
|
+
newContent = `_relativeLocation(location) {
|
|
512
|
+
if (!location)
|
|
513
|
+
return void 0;
|
|
514
|
+
// Hide location for steps from runtime/index.js to reduce clutter
|
|
515
|
+
if (location.file && location.file.includes('@checksum-ai/runtime') && location.file.endsWith('index.js')) {
|
|
516
|
+
return void 0;
|
|
517
|
+
}
|
|
518
|
+
const file = toPosixPath2(import_path8.default.relative(this._config.rootDir, location.file));
|
|
519
|
+
return {
|
|
520
|
+
file,
|
|
521
|
+
line: location.line,
|
|
522
|
+
column: location.column
|
|
523
|
+
};
|
|
524
|
+
}`;
|
|
525
|
+
replaceContent(file, originalContent, newContent);
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// -------- [Run] -------- //
|
|
529
|
+
|
|
530
|
+
const isRuntime = true || process.env.RUNTIME === "true";
|
|
531
|
+
|
|
532
|
+
function run(projectPath) {
|
|
533
|
+
try {
|
|
534
|
+
if (fs.existsSync(projectPath)) {
|
|
535
|
+
alwaysInjectScripts(projectPath);
|
|
536
|
+
addGenerateSelectorAndLocator(projectPath);
|
|
537
|
+
if (isRuntime) {
|
|
538
|
+
expect(projectPath);
|
|
539
|
+
testInfo(projectPath);
|
|
540
|
+
testType(projectPath);
|
|
541
|
+
channelOwner(projectPath);
|
|
542
|
+
stackTrace(projectPath);
|
|
543
|
+
indexContent(projectPath);
|
|
544
|
+
// Skipped in merge mode (CHECKSUM_SKIP_TRACE_PATCH): this patch makes
|
|
545
|
+
// the HTML reporter sleep ~10s per `trace` attachment waiting for a
|
|
546
|
+
// `checksum-playwright-trace` file that only the live run produces; in
|
|
547
|
+
// the merge path (real Playwright traces) it is pure dead time.
|
|
548
|
+
if (process.env.CHECKSUM_SKIP_TRACE_PATCH !== "true") {
|
|
549
|
+
reportTraceFile(projectPath);
|
|
550
|
+
}
|
|
551
|
+
htmlReporter(projectPath);
|
|
552
|
+
}
|
|
553
|
+
} else {
|
|
554
|
+
console.warn("Project path not found", projectPath);
|
|
555
|
+
}
|
|
556
|
+
} catch (e) {
|
|
557
|
+
// ignore
|
|
558
|
+
console.error(e);
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
module.exports = run;
|