@checksum-ai/runtime 3.0.3-beta → 4.0.0-alpha
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 +2 -2
- package/package.json +2 -2
- package/scripts/playwright_patches/1.60.0.js +506 -0
package/.env
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
CHECKSUM_RUNTIME_BUILD_TIME=2026-05-
|
|
1
|
+
CHECKSUM_RUNTIME_BUILD_TIME=2026-05-12T11:12:18.644Z
|
package/cli.js
CHANGED
|
@@ -749,9 +749,9 @@ ${s.description}`})}async onTestComplete(s){this.testRunAnalytics.testRunEnd({su
|
|
|
749
749
|
|
|
750
750
|
export default getChecksumConfig(${JSON.stringify(this.config,null,2)});
|
|
751
751
|
`;(0,Ug.writeFileSync)(n,s)}async cleanup(){this.runtimeAnalytics&&(Oc("Flushing events..."),await Zd(this.runtimeAnalytics.close(),2e4)),this.deleteVolatileConfig(),this.childProcesses.forEach(n=>{n.kill("SIGTERM")})}async getEnvInfo(){let n={commitHash:"",branch:"branch",environment:process.env.CI?"CI":"local",name:"name",startedAt:Date.now(),runtimeVersion:this.checksumRuntimeVersion,request:kc.testRun.request,platform:kc.testRun.platform,runType:kc.testRun.runType};try{n.commitHash=await this.getCmdOutput("git rev-parse HEAD")}catch(s){console.log("Error getting git hash",s.message)}try{n.branch=await this.getCmdOutput("git rev-parse --abbrev-ref HEAD")}catch(s){console.log("Error getting branch",s.message)}return n.name=await this.getEnvName(),n}async getEnvName(){let n=this.getChecksumArg("title");if(typeof n=="string"&&(n==null?void 0:n.length)>0)return n;try{return await this.getCmdOutput("git log -1 --pretty=%B")}catch(s){console.log("Error getting name",s.message);return}}getVolatileConfigPath(){return(0,b0.join)(this.checksumRoot??"","checksum.config.tmp.ts")}deleteVolatileConfig(){let n=this.getVolatileConfigPath();(0,Ug.existsSync)(n)&&(0,Ug.rmSync)(n)}loadChecksumConfig(n={}){this.config={...this.readChecksumConfigFile(n)||{},...this.volatileChecksumConfig||{}},this.debugMode&&(this.config.options.printLogs=!0),this.traceChecksumConfig()}readChecksumConfigFile(n={}){let s=(0,b0.join)(this.checksumRoot,"checksum.config.ts");if(!(0,Ug.existsSync)(s))return n.muteChecksumConfigMissingError||console.warn(`No checksum.config.ts found at ${s}. Continuing with default configuration.`),{};try{return require(s).default}catch(c){console.log(`Failed to load checksum.config.ts at ${s}:
|
|
752
|
-
${c.message||c}`),process.exit(1)}}traceChecksumConfig(){if(!this.runtimeAnalytics)return;let n=Object.keys(this.config).reduce((s,c)=>({...s,[c]:typeof this.config[c]=="string"?this.config[c]:JSON.stringify(this.config[c])}),{});this.runtimeAnalytics.checksumConfig(this.config)}processConfigArg(){this.deleteVolatileConfig();let n=this.getChecksumArg("config");if(n)try{this.volatileChecksumConfig=JSON.parse(n)}catch(s){this.volatileChecksumConfig=void 0,console.log("Error parsing cksm config",s.message)}}getNonChecksumArgs(){return this.args.filter(n=>!this.isChecksumArg(n))}getArg(n){var u;let s=this.args.find(_=>_.startsWith(`${n}`)),c=(u=s==null?void 0:s.split("=")[1])==null?void 0:u.trim();return c||!!s}getChecksumArg(n){return this.getArg(`--cksm-${n}`)}getChecksumRootOrigin(){return(0,b0.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(),s=n;for(let c=0;c<10;c++){if((0,Ug.existsSync)((0,b0.join)(s,".git"))){if(this.absoluteProjectRootDirectory)break;this.absoluteProjectRootDirectory=s,Oc("Project root directory found at (git root)",this.absoluteProjectRootDirectory)}if((0,b0.parse)(s).root===s)break;s=(0,b0.join)(s,"..")}n=process.cwd();for(let c=0;c<6;c++){let u=(0,b0.join)(n,"node_modules","@checksum-ai","runtime");if((0,Ug.existsSync)(u)){this.projectRootDirectory=n,this.absoluteProjectRootDirectory||(this.absoluteProjectRootDirectory=n),Oc("Project root directory found at (node_modules fallback)",this.projectRootDirectory);return}if((0,b0.parse)(n).root===n)break;n=(0,b0.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,Oc("Checksum root directory found at",this.checksumRoot),this.createInternalFilesDir()}findChecksumRoot(){if(kc.checksumRootFolderBypass){let c=(0,b0.isAbsolute)(kc.checksumRootFolderBypass)?kc.checksumRootFolderBypass:(0,b0.join)(this.projectRootDirectory,kc.checksumRootFolderBypass);this.setChecksumRoot(c);return}let n=t(c=>{try{return(0,Ug.readdirSync)(c).includes("checksum.config.ts")}catch{return!1}},"containsChecksumConfig"),s=[this.projectRootDirectory];for(;s.length;){let c=s.pop(),u=(0,Ug.readdirSync)(c,{withFileTypes:!0});for(let _ of u){let g=(0,b0.join)(c,_.name),b=g;if(_.isSymbolicLink())try{let w=(0,Ug.readlinkSync)(g);if(!(0,Ug.lstatSync)(w).isDirectory())continue;b=w}catch{continue}else if(!_.isDirectory())continue;if(_.name===Bxe){if(n(b)){this.setChecksumRoot(b);return}}else s.push(g)}}throw new Error("Could not find checksum root folder. Run `npx checksumai init` to create one.")}log(n,s=!1){s?this.printError(n):console.log(n)}runtimeTrace(n,s){this.runtimeAnalytics.eventWithMessage(s,n)}isChecksumArg(n){return n.startsWith("--cksm-")}};t(Kdt,"CLICommandBase");var mv=Kdt;var Ume="--cksm-auto-heal",O5={enable:`${Ume}`,createPr:`${Ume}-create-pr`,repoName:`${Ume}-repo-name`,prNumber:`${Ume}-pr-number`,branch:`${Ume}-branch`};function Xor(i){if(!fCn(i.args,O5.enable))return{kind:"disabled"};let n=jme(i.args,O5.createPr),s=jme(i.args,O5.repoName),c=jme(i.args,O5.prNumber),u=jme(i.args,O5.branch),_=pCn(n,!0);if(_===void 0)return{kind:"error",message:`${O5.createPr}: expected "true" or "false" (or omit a value), got "${n}"`};let g;if(c!==void 0&&c!==""){let J=Number(c);if(!Number.isInteger(J)||J<=0)return{kind:"error",message:`${O5.prNumber}: expected a positive integer, got "${c}"`};g=J}let b=uCn(i.env),w=typeof s=="string"&&s!==""?s:b.repoName,k=typeof u=="string"&&u!==""?u:b.branch,O=g??b.prNumber;if(_&&!w)return{kind:"error",message:`${O5.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(O!==void 0&&!w)return{kind:"error",message:`${O5.repoName} is required when ${O5.prNumber} is set`};let G={autoCreatePR:_};return k&&(G.branch=k),O!==void 0&&(G.prNumber=O),w&&(G.repoName=w),{kind:"enabled",options:G}}t(Xor,"resolveAutoHealOptions");function uCn(i){var n;if(i.GITHUB_ACTIONS==="true"){let s=i.GITHUB_REPOSITORY||void 0,c=i.GITHUB_HEAD_REF||i.GITHUB_REF_NAME||void 0,u;if(i.GITHUB_EVENT_NAME==="pull_request"){let _=(n=i.GITHUB_REF)==null?void 0:n.match(/^refs\/pull\/(\d+)\//);if(_){let g=Number(_[1]);Number.isInteger(g)&&g>0&&(u=g)}}return{repoName:s,branch:c,prNumber:u}}if(i.GITLAB_CI==="true"){let s=i.CI_PROJECT_PATH||void 0,c=i.CI_MERGE_REQUEST_SOURCE_BRANCH_NAME||i.CI_COMMIT_REF_NAME||void 0,u;if(i.CI_MERGE_REQUEST_IID){let _=Number(i.CI_MERGE_REQUEST_IID);Number.isInteger(_)&&_>0&&(u=_)}return{repoName:s,branch:c,prNumber:u}}return{}}t(uCn,"detectCiContext");function jme(i,n){for(let s of i){if(s===n)return"";if(s.startsWith(`${n}=`))return s.slice(n.length+1).trim()}}t(jme,"findFlagValue");function fCn(i,n){return jme(i,n)!==void 0}t(fCn,"findFlag");function pCn(i,n){if(i===void 0)return n;if(i===""||i==="true")return!0;if(i==="false")return!1}t(pCn,"parseBoolFlag");var Xdt=require("crypto");function ecr(i,n){return{testSuiteRunId:n,appName:"generic-app",commitHash:"generic-commit-hash",envName:"generic-env-name",branch:"generic-branch",checksumRuntimeVersion:i}}t(ecr,"createDefaultTestSuiteRunInfo");function tcr(i,n,s={}){let c=["mock-app","mock-app-2","mock-app-3"];return{...{appName:c[Math.floor(Math.random()*c.length)],commitHash:(0,Xdt.randomUUID)(),envName:"test-environment",branch:(0,Xdt.randomUUID)(),testSuiteRunId:n,checksumRuntimeVersion:i},...s}}t(tcr,"createMockTestSuiteRunInfo");var Qte=require("fs"),Ute=require("path");var e5e=require("fs"),$1=Pa(kKe());var dCn=/^[0-9a-zA-Z]+$/,_Cn=5;function rcr(i){return i?i.length===_Cn&&dCn.test(i):!1}t(rcr,"isValidChecksumTestId");var n_t=class n_t{extract(n,s){if(!n)return;let c=n.split("/"),u=c[c.length-1];if(!u.endsWith(".checksum.spec.ts"))return;let g=u.replace(".checksum.spec.ts","").split(" - ").pop();return rcr(g)?g:void 0}};t(n_t,"FileNameExtractionStrategy");var e_t=n_t,i_t=class i_t{extract(n,s){if(!(!n||!(0,e5e.existsSync)(n)))try{let c=(0,e5e.readFileSync)(n,"utf-8"),u=$1.createSourceFile(n,c,$1.ScriptTarget.Latest,!0),_=new Map,g=t(b=>{if($1.isCallExpression(b)){let w=b;if(this.isTestCall(w.expression)){let k=w.arguments[0];if(k&&$1.isCallExpression(k)&&$1.isIdentifier(k.expression)&&k.expression.text==="defineChecksumTest"){let O=this.extractDefineChecksumTestArgs(k);O&&rcr(O.checksumTestId)&&_.set(O.testTitle,O.checksumTestId)}}}$1.forEachChild(b,g)},"visit");return g(u),s&&_.has(s)?_.get(s):_.size>0?Array.from(_.values())[0]:void 0}catch{return}}isTestCall(n){return $1.isIdentifier(n)&&n.text==="test"?!0:$1.isPropertyAccessExpression(n)?$1.isIdentifier(n.expression)&&n.expression.text==="test":!1}extractDefineChecksumTestArgs(n){let s=n.arguments;if(s.length<2)return;let c=s[0],u=s[1],_=this.extractStringLiteral(c),g=this.extractStringLiteral(u);if(_&&g)return{testTitle:_,checksumTestId:g}}extractStringLiteral(n){if($1.isStringLiteral(n)||$1.isNoSubstitutionTemplateLiteral(n))return n.text}};t(i_t,"FileContentExtractionStrategy");var t_t=i_t,s_t=class s_t{constructor(n){this.strategies=n||[new t_t,new e_t]}extract(n,s){for(let c of this.strategies){let u=c.extract(n,s);if(u)return u}}};t(s_t,"ChecksumTestIdExtractor");var r_t=s_t;var ncr=new r_t;var a_t=t(i=>isNaN(i)?0:i,"getStat"),mCn=t((i,n,s)=>{if(!i)return;let c=s||n,u;if((0,Ute.isAbsolute)(i))u=i;else{let g=(0,Ute.dirname)(n);u=(0,Ute.resolve)(g,i)}return u.replace(c+"/","")},"toRelativeFilePath"),icr=t(i=>{let n=[];if(i.specs&&n.push(...i.specs),i.suites)for(let s of i.suites)n.push(...icr(s));return n},"extractSpecsFromSuite"),hCn=t(i=>{let n=[];for(let s of i.suites)n.push(...icr(s));return n},"extractFlatSpecsMap"),gCn=t((i,n,s,c,u)=>{let{expected:_,unexpected:g,flaky:b}=i.stats,w=[];hCn(i).forEach(G=>{let J=yCn(G.id,n);J&&(G.recovered=!0);let ae=G.tests[0],ce=G.tags.includes("bug")||ae.annotations.some(vt=>vt.type==="bug"),be=ae.annotations&&ae.annotations.length>0?ae.annotations.map(vt=>({type:vt.type,description:vt.description||""})):[],yt=G.tags||[],_t=s[G.id]||ncr.extract(G.file,G.title);w.push({checksumTestId:_t,playwrightTestId:G.id,success:!!G.ok,recovered:J,bug:ce,annotations:be,tags:yt,outcome:ae.status,filePath:G.file,relativeFilePath:c?mCn(G.file,c,u):G.file})});let O={passed:a_t(b)+a_t(_),failed:a_t(g),bug:w.filter(G=>G.bug).length,recovered:w.filter(G=>G.recovered).length,tests:w};return w.forEach(G=>{G.bug&&(G.outcome==="expected"||G.outcome==="flaky"?O.passed--:G.outcome==="unexpected"&&O.failed--)}),O},"modifyStatsPerTestFile"),yCn=t((i,n)=>Object.values(n[i]||{}).some(s=>{var c,u;return(u=(c=s.autoRecoveryMetadata)==null?void 0:c.data)==null?void 0:u.autoRecovered}),"isAutoRecovered"),scr=t((i,n,s,c,u)=>{(0,Qte.cpSync)(i,i+".backup");try{let _=JSON.parse((0,Qte.readFileSync)(i,"utf-8")),g=gCn(_,n,s,c,u);return _.checksumMetadata=g,(0,Qte.writeFileSync)(i,JSON.stringify(_,null,2)),_}catch(_){return console.log("Error processing JSON report",_.message),null}},"processJSONReport");var c_t=class c_t extends mv{constructor(s,c=!1){super(s,c);this.MAX_COMPLETION_WAIT=2*3600*1e3;this.didFail=!1;this.isolationMode=!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:""}}}get replMode(){return this.getChecksumArg("repl")??!1}async run(){if(this.loadTracer(),this.validateAuthExists(),this.buildVolatileChecksumConfig(),!await this.getSession())return;this.testSuiteAnalytics=Qme.getInstance(this.testSuiteRunInfo,this.isolationMode),this.testSuiteAnalytics.genericEvent(`Project root found at ${this.projectRootDirectory}`,{message:`Checksum root found at ${this.checksumRoot}`});let s={CHECKSUM_ROOT_FOLDER:this.checksumRoot,CHECKSUM_TEST_SUITE_INFO:JSON.stringify(this.testSuiteRunInfo)},c;try{c=await Zd(this.startTestRunMonitor(this.testSuiteRunId),1e4,"test run monitor timeout"),s.CHECKSUM_UPLOAD_AGENT_PORT=c}catch{let w="Error starting test run monitor. Test results will not be available on checksum.";this.testSuiteAnalytics.testRunMonitorError({error:w}),this.log(w)}s.PW_TEST_HTML_REPORT_OPEN="never",this.isolationMode&&(s.CHECKSUM_ISOLATED_MODE="true"),this.testSuiteRunId&&(s.CHECKSUM_TEST_SUITE_ID=this.testSuiteRunId),this.replMode&&(s.CHECKSUM_REPL="true"),this.getChecksumArg("tm")&&(s.CHECKSUM_SHOW_TIME_MACHINE="true");let u=this.getNonChecksumArgs(),_=this.getChecksumArg("rerun-failed");if(_)try{let b=await this.fetchFailedTestPaths(typeof _=="string"?_:void 0);if(b.length===0){console.log("No non-passing tests found \u2014 nothing to rerun."),await this.shutdown(0);return}console.log(`Re-running ${b.length} non-passing test file(s)`),u.unshift(...b)}catch(b){this.printError(`Failed to fetch failed tests: ${b.message}`),await this.shutdown(1);return}this.replMode&&u.push("--debug");let g=`npx playwright test --config "${this.getPlaywrightConfigFilePath()}" ${u.map(b=>`"${b}"`).join(" ")}`;await this.patchPlaywright();try{console.log(`Tests running with @checksum-ai/runtime version ${this.checksumRuntimeVersion}`),this.replMode?(console.log("Running in REPL mode"),this.execCmd(g,s),await this.execCmd(`node ${__dirname}/repl.js`,s)):(this.testSuiteAnalytics.testSuiteStart(),await this.execCmd(g,s)),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..."),this.sendReportUploadRequest(),await this.patchPlaywright(!0),this.completeIndicators.tests=!0,await this.handleCompleteMessage()}}async uploadReport(){var u;this.loadTracer(),this.validateAuthExists(),this.buildVolatileChecksumConfig(),this.config.options.hostReports=!0,this.tryLoadPlaywrightConfigFromFile(),this.loadPlaywrightConfigOptions();let s=this.resolveUserReportPathArg();s&&this.playwrightConfigOptions&&(this.playwrightConfigOptions.reportPath=s);let c=(u=this.playwrightConfigOptions)==null?void 0:u.reportPath;if(!c||!(0,hv.existsSync)(c)){this.printError(`Could not find report at ${c??"(unresolved)"}. Pass an explicit path: npx checksumai upload-report <path>`),await this.shutdown(1);return}if(await this.getSession()){this.testSuiteAnalytics=Qme.getInstance(this.testSuiteRunInfo,this.isolationMode);try{await Zd(this.startTestRunMonitor(this.testSuiteRunId),1e4,"test run monitor timeout")}catch(_){this.printError(`Failed to start test run monitor: ${_.message}. Report will not be uploaded.`),await this.shutdown(1);return}console.log(`Uploading report from ${c} to Checksum...`),this.sendReportUploadRequest(),this.completeIndicators.tests=!0,await this.handleCompleteMessage()}}tryLoadPlaywrightConfigFromFile(){if(!this.playwrightConfig)try{let s=this.getPlaywrightConfigFilePath();if((0,hv.existsSync)(s)){let c=require(s),u=(c==null?void 0:c.default)??c;u&&(this.playwrightConfig=u)}}catch(s){Oc("Could not load playwright config from file, falling back to defaults: "+s.message)}}resolveUserReportPathArg(){let s=this.args.find(u=>!u.startsWith("-")&&!u.includes("="));if(!s)return;let c=(0,q1.isAbsolute)(s)?s:(0,q1.resolve)(process.cwd(),s);if(!(0,hv.existsSync)(c))return c;try{if((0,hv.statSync)(c).isDirectory())return(0,q1.join)(c,"index.html")}catch{}return c}loadPlaywrightConfigOptions(){var k,O;let s=t(G=>(Oc("Using report folder",G),(0,q1.join)(G,"index.html")),"makeFilePath"),c=t(G=>(Oc("Using JSON report folder",G),(0,q1.join)(G,"report.json")),"makeJsonFilePath"),u,_,g=!1,b="never";process.env.PLAYWRIGHT_HTML_OUTPUT_DIR&&(u=s(process.env.PLAYWRIGHT_HTML_OUTPUT_DIR));let w=(k=this.playwrightConfig)==null?void 0:k.reporter;if(w instanceof Array){let G=w.filter(ce=>ce instanceof Array&&ce[0]==="html"),J=w.filter(ce=>ce instanceof Array&&ce[0]==="json");if(!process.env.PLAYWRIGHT_HTML_OUTPUT_DIR){let ce=G.find(be=>{var yt;return(yt=be[1])==null?void 0:yt.outputFolder});ce&&(u=s((0,q1.join)(this.checksumRoot,ce[1].outputFolder)))}let ae=J.find(ce=>{var be;return(be=ce[1])==null?void 0:be.outputFile});ae&&(g=!0,_=(0,q1.join)(this.checksumRoot,ae[1].outputFile));for(let ce of G){let be=(O=ce[1])==null?void 0:O.open;if(be==="always"||be==="on-failure"){b=be;break}}}u||(kc.isRepoMode?u=s((0,q1.join)(kc.runtimeRoot,"playwright-report")):u=s((0,q1.join)(this.projectRootDirectory,"playwright-report"))),_||(kc.isRepoMode?_=c((0,q1.join)(kc.runtimeRoot,"playwright-report")):_=c((0,q1.join)(this.projectRootDirectory,"playwright-report"))),this.playwrightConfigOptions={openReportCriteria:b,reportPath:u,jsonReportPath:_,hasJsonReporter:g}}sendReportUploadRequest(){let s=t(O=>{this.testSuiteAnalytics.testSuiteError({error:O}),this.log(O),this.completeIndicators.report=!0,this.testRunMonitorProcess.stdin.write("cli:report=false")},"makeError");if(!this.playwrightConfigOptions)return s("No playwright config options");let{reportPath:c,openReportCriteria:u,jsonReportPath:_,hasJsonReporter:g}=this.playwrightConfigOptions;if(g&&((0,hv.existsSync)(_)?scr(_,this.playwrightIdToReportMetadata,this.pwTestIdToChecksumTestId,this.checksumRoot,this.absoluteProjectRootDirectory):console.warn(`Could not find JSON report file at ${_}`)),!(0,hv.existsSync)(c))return s(`Could not find report file at ${c}`);Oc("Sending report upload request",c);let b=this.saveMetadataToFile((0,q1.dirname)(c)),w={reportPath:c,pathToChecksumMetadata:b,openReportCriteria:u,didFail:this.didFail,checksumRoot:this.checksumRoot,projectRoot:this.absoluteProjectRootDirectory},k=Buffer.from(JSON.stringify(w)).toString("base64");this.testRunMonitorProcess.stdin.write(`cli:report=${k}`)}saveMetadataToFile(s){try{let c=(0,q1.join)(s,`checksum-metadata-${(0,o_t.randomUUID)()}.json`);return(0,hv.writeFileSync)(c,JSON.stringify(this.playwrightIdToReportMetadata,null,2)),c}catch(c){let u=`Error saving metadata to file - ${c.message}`;this.testSuiteAnalytics.testSuiteError({error:u}),this.log(u);return}}startTestMonitorProcess(s){let u=[JSON.stringify({sessionId:s,apiURL:this.config.apiURL,apiKey:this.config.apiKey}),...this.isolationMode?["isolated"]:[]],_={command:"node",args:[(0,q1.join)(__dirname,"test-run-monitor.js"),...u]};return kc.isRepoMode&&(_.command="yarn",_.args=["test-run-monitor",...u]),acr.spawn(_.command,_.args)}startTestRunMonitor(s){return new Promise(c=>{console.log("Starting test run monitor"),this.testRunMonitorProcess=this.startTestMonitorProcess(s),this.testRunMonitorProcess.stdout.on("data",u=>{let _=this.parseTRMMessages(u.toString().trim());if(_.length)for(let g of _){if(g.startsWith("trace")){this.logTRMMessage(g),this.handleTestRunMonitorTrace(g.substring("trace".length+1));continue}if(!g.startsWith("monitor")){this.logTRMMessage(g);continue}let b=t(()=>{let G=g.substring("monitor".length+1),J=G.indexOf("=");if(J===-1)return{key:G,value:""};let[ae,ce]=[G.substring(0,J),G.substring(J+1)];return{key:ae,value:ce}},"getKeyAndValue"),{key:w,value:k}=b();if(!Object.values(h$e).includes(w)){this.logTRMMessage(`Received unknown monitor message: "${g}"`);continue}let O=w;this.logInterProcessEventMessage(O,k),w==="port"?c(k):this.handleTestRunMonitorMessage(O,k)}}),this.testRunMonitorProcess.on("exit",(u,_)=>{let g=`test run monitor process exited with code ${u} and signal ${_}`;this.testSuiteAnalytics.testRunMonitorError({error:g}),this.log(g)}),this.testRunMonitorProcess.stderr.on("data",u=>{let _=`TRM Error: ${u.toString().substring(0,300)}`;this.testSuiteAnalytics.testRunMonitorError({error:_}),this.log(_)}),this.testRunMonitorProcess.on("error",u=>{let _=`Error starting test run monitor: ${u.message}`;this.testSuiteAnalytics.testRunMonitorError({error:_}),this.log(_)})})}logInterProcessEventMessage(s,c){t(()=>{if(!this.logFilter[s])return!0;if(this.logFilter[s].logOnValueChange&&this.logFilter[s].value!==c)return this.logFilter[s].value=c,!0;if(this.logFilter[s].logEveryMS){let _=Date.now();if(_-this.logFilter[s].lastLogTimestamp>=this.logFilter[s].logEveryMS)return this.logFilter[s].lastLogTimestamp=_,!0}return!1},"shouldLog")()&&this.logTRMMessage(`Received monitor message: "${s}${c?`=${c}`:""}"`)}parseTRMMessages(s){let c=this.trmMessagesBuffer+s,u,_,g=t(()=>{u=c.indexOf("{trm}"),_=c.indexOf("{/trm}")},"findIndexes");g();let b=[];for(;u!==-1&&_!==-1&&_>u;){if(b.push(c.substring(u+"{trm}".length,_)),c=c.slice(_+"{/trm}".length).trim(),c.length&&!c.startsWith("{trm}"))return console.warn("Buffered data does not start with start delimiter",c),this.trmMessagesBuffer="",b;this.trmMessagesBuffer=c,g()}return b}setChecksumTestMetadata(s){let{retry:c,type:u,pwTestId:_,checksumTestId:g}=s;this.playwrightIdToReportMetadata[_]||(this.playwrightIdToReportMetadata[_]={}),this.playwrightIdToReportMetadata[_][c]||(this.playwrightIdToReportMetadata[_][c]={}),this.playwrightIdToReportMetadata[_][c][u]=s,this.pwTestIdToChecksumTestId[_]=g}async handleTestRunMonitorTrace(s){let c=s.indexOf("="),[u,_]=c>-1?[s.substring(0,c),s.substring(c+1)]:[s,""];if(!Object.values(Zke).includes(u)){Oc("Unknown trace event",u);return}try{let g=_?JSON.parse(_):{};switch(u){case"Upload Start":this.testSuiteAnalytics.uploadStart({filename:g.filename});break;case"Upload Complete":this.testSuiteAnalytics.uploadComplete({filename:g.filename});break;case"Upload Failed":this.testSuiteAnalytics.uploadFailed({filename:g.filename,error:g.error});break;case"Trace Processing Error":this.testSuiteAnalytics.traceProcessingError({error:g.error});break}}catch(g){Oc(`Error parsing trace payload, ${g.message}`)}}async handleTestRunMonitorMessage(s,c){var u;switch(s){case"uploads-complete-with-errors":console.log("Error uploading test files to Checksum");try{let g=JSON.parse(c);this.testSuiteAnalytics.uploadsCompleteWithErrors(g)}catch{}this.sendProcessingError().then(()=>{this.completeIndicators.upload=!0});break;case"checksumTestMetadata":try{let g=JSON.parse(c);this.setChecksumTestMetadata(g)}catch(g){let b=`Error parsing test metadata event - ${g.message}`;this.testSuiteAnalytics.testSuiteError({error:b}),this.log(b)}break;case"uploads-complete":this.isolationMode||console.log("Test files uploaded successfully"),this.testSuiteAnalytics.allUploadsComplete(),this.sendUploadsComplete().then(()=>{this.completeIndicators.upload=!0});break;case"report-complete":{if(this.isolationMode){this.completeIndicators.report=!0;break}let g=c.slice(0,c.indexOf(":"))==="true",b=c.slice(c.indexOf(":")+1),w="{}";try{if(b&&(0,hv.existsSync)(b)){w=(0,hv.readFileSync)(b,"utf-8");try{(0,hv.unlinkSync)(b)}catch{console.warn(`Failed to delete temporary stats file: ${b}`)}}else console.warn(`Stats file not found at: ${b}`)}catch(O){console.error(`Error reading stats file: ${O.message}`)}let k=this.handleStats(w);if(this.testSuiteAnalytics.testSuiteEnd({passed:k.passed,failed:k.failed,bug:k.bug,total:((u=k.tests)==null?void 0:u.length)||0}),await this.sendTestRunEnd(k),this.completeIndicators.report=!0,g){let O=`${this.checksumAppUrl}/#/test-runs/${this.testSuiteRunId}`,G=`Checksum report URL: ${O}`,J="*".repeat(G.length);console.log(`${J}
|
|
752
|
+
${c.message||c}`),process.exit(1)}}traceChecksumConfig(){if(!this.runtimeAnalytics)return;let n=Object.keys(this.config).reduce((s,c)=>({...s,[c]:typeof this.config[c]=="string"?this.config[c]:JSON.stringify(this.config[c])}),{});this.runtimeAnalytics.checksumConfig(this.config)}processConfigArg(){this.deleteVolatileConfig();let n=this.getChecksumArg("config");if(n)try{this.volatileChecksumConfig=JSON.parse(n)}catch(s){this.volatileChecksumConfig=void 0,console.log("Error parsing cksm config",s.message)}}getNonChecksumArgs(){return this.args.filter(n=>!this.isChecksumArg(n))}getArg(n){var u;let s=this.args.find(_=>_.startsWith(`${n}`)),c=(u=s==null?void 0:s.split("=")[1])==null?void 0:u.trim();return c||!!s}getChecksumArg(n){return this.getArg(`--cksm-${n}`)}getChecksumRootOrigin(){return(0,b0.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(),s=n;for(let c=0;c<10;c++){if((0,Ug.existsSync)((0,b0.join)(s,".git"))){if(this.absoluteProjectRootDirectory)break;this.absoluteProjectRootDirectory=s,Oc("Project root directory found at (git root)",this.absoluteProjectRootDirectory)}if((0,b0.parse)(s).root===s)break;s=(0,b0.join)(s,"..")}n=process.cwd();for(let c=0;c<6;c++){let u=(0,b0.join)(n,"node_modules","@checksum-ai","runtime");if((0,Ug.existsSync)(u)){this.projectRootDirectory=n,this.absoluteProjectRootDirectory||(this.absoluteProjectRootDirectory=n),Oc("Project root directory found at (node_modules fallback)",this.projectRootDirectory);return}if((0,b0.parse)(n).root===n)break;n=(0,b0.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,Oc("Checksum root directory found at",this.checksumRoot),this.createInternalFilesDir()}findChecksumRoot(){if(kc.checksumRootFolderBypass){let c=(0,b0.isAbsolute)(kc.checksumRootFolderBypass)?kc.checksumRootFolderBypass:(0,b0.join)(this.projectRootDirectory,kc.checksumRootFolderBypass);this.setChecksumRoot(c);return}let n=t(c=>{try{return(0,Ug.readdirSync)(c).includes("checksum.config.ts")}catch{return!1}},"containsChecksumConfig"),s=[this.projectRootDirectory];for(;s.length;){let c=s.pop(),u=(0,Ug.readdirSync)(c,{withFileTypes:!0});for(let _ of u){let g=(0,b0.join)(c,_.name),b=g;if(_.isSymbolicLink())try{let w=(0,Ug.readlinkSync)(g);if(!(0,Ug.lstatSync)(w).isDirectory())continue;b=w}catch{continue}else if(!_.isDirectory())continue;if(_.name===Bxe){if(n(b)){this.setChecksumRoot(b);return}}else s.push(g)}}throw new Error("Could not find checksum root folder. Run `npx checksumai init` to create one.")}log(n,s=!1){s?this.printError(n):console.log(n)}runtimeTrace(n,s){this.runtimeAnalytics.eventWithMessage(s,n)}isChecksumArg(n){return n.startsWith("--cksm-")}};t(Kdt,"CLICommandBase");var mv=Kdt;var Ume="--cksm-auto-heal",O5={enable:`${Ume}`,createPr:`${Ume}-create-pr`,repoName:`${Ume}-repo-name`,prNumber:`${Ume}-pr-number`,branch:`${Ume}-branch`};function Xor(i){if(!fCn(i.args,O5.enable))return{kind:"disabled"};let n=jme(i.args,O5.createPr),s=jme(i.args,O5.repoName),c=jme(i.args,O5.prNumber),u=jme(i.args,O5.branch),_=pCn(n,!0);if(_===void 0)return{kind:"error",message:`${O5.createPr}: expected "true" or "false" (or omit a value), got "${n}"`};let g;if(c!==void 0&&c!==""){let J=Number(c);if(!Number.isInteger(J)||J<=0)return{kind:"error",message:`${O5.prNumber}: expected a positive integer, got "${c}"`};g=J}let b=uCn(i.env),w=typeof s=="string"&&s!==""?s:b.repoName,k=typeof u=="string"&&u!==""?u:b.branch,O=g??b.prNumber;if(_&&!w)return{kind:"error",message:`${O5.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(O!==void 0&&!w)return{kind:"error",message:`${O5.repoName} is required when ${O5.prNumber} is set`};let G={autoCreatePR:_};return k&&(G.branch=k),O!==void 0&&(G.prNumber=O),w&&(G.repoName=w),{kind:"enabled",options:G}}t(Xor,"resolveAutoHealOptions");function uCn(i){var n;if(i.GITHUB_ACTIONS==="true"){let s=i.GITHUB_REPOSITORY||void 0,c=i.GITHUB_HEAD_REF||i.GITHUB_REF_NAME||void 0,u;if(i.GITHUB_EVENT_NAME==="pull_request"){let _=(n=i.GITHUB_REF)==null?void 0:n.match(/^refs\/pull\/(\d+)\//);if(_){let g=Number(_[1]);Number.isInteger(g)&&g>0&&(u=g)}}return{repoName:s,branch:c,prNumber:u}}if(i.GITLAB_CI==="true"){let s=i.CI_PROJECT_PATH||void 0,c=i.CI_MERGE_REQUEST_SOURCE_BRANCH_NAME||i.CI_COMMIT_REF_NAME||void 0,u;if(i.CI_MERGE_REQUEST_IID){let _=Number(i.CI_MERGE_REQUEST_IID);Number.isInteger(_)&&_>0&&(u=_)}return{repoName:s,branch:c,prNumber:u}}return{}}t(uCn,"detectCiContext");function jme(i,n){for(let s of i){if(s===n)return"";if(s.startsWith(`${n}=`))return s.slice(n.length+1).trim()}}t(jme,"findFlagValue");function fCn(i,n){return jme(i,n)!==void 0}t(fCn,"findFlag");function pCn(i,n){if(i===void 0)return n;if(i===""||i==="true")return!0;if(i==="false")return!1}t(pCn,"parseBoolFlag");var Xdt=require("crypto");function ecr(i,n){return{testSuiteRunId:n,appName:"generic-app",commitHash:"generic-commit-hash",envName:"generic-env-name",branch:"generic-branch",checksumRuntimeVersion:i}}t(ecr,"createDefaultTestSuiteRunInfo");function tcr(i,n,s={}){let c=["mock-app","mock-app-2","mock-app-3"];return{...{appName:c[Math.floor(Math.random()*c.length)],commitHash:(0,Xdt.randomUUID)(),envName:"test-environment",branch:(0,Xdt.randomUUID)(),testSuiteRunId:n,checksumRuntimeVersion:i},...s}}t(tcr,"createMockTestSuiteRunInfo");var Qte=require("fs"),Ute=require("path");var e5e=require("fs"),$1=Pa(kKe());var dCn=/^[0-9a-zA-Z]+$/,_Cn=5;function rcr(i){return i?i.length===_Cn&&dCn.test(i):!1}t(rcr,"isValidChecksumTestId");var n_t=class n_t{extract(n,s){if(!n)return;let c=n.split("/"),u=c[c.length-1];if(!u.endsWith(".checksum.spec.ts"))return;let g=u.replace(".checksum.spec.ts","").split(" - ").pop();return rcr(g)?g:void 0}};t(n_t,"FileNameExtractionStrategy");var e_t=n_t,i_t=class i_t{extract(n,s){if(!(!n||!(0,e5e.existsSync)(n)))try{let c=(0,e5e.readFileSync)(n,"utf-8"),u=$1.createSourceFile(n,c,$1.ScriptTarget.Latest,!0),_=new Map,g=t(b=>{if($1.isCallExpression(b)){let w=b;if(this.isTestCall(w.expression)){let k=w.arguments[0];if(k&&$1.isCallExpression(k)&&$1.isIdentifier(k.expression)&&k.expression.text==="defineChecksumTest"){let O=this.extractDefineChecksumTestArgs(k);O&&rcr(O.checksumTestId)&&_.set(O.testTitle,O.checksumTestId)}}}$1.forEachChild(b,g)},"visit");return g(u),s&&_.has(s)?_.get(s):_.size>0?Array.from(_.values())[0]:void 0}catch{return}}isTestCall(n){return $1.isIdentifier(n)&&n.text==="test"?!0:$1.isPropertyAccessExpression(n)?$1.isIdentifier(n.expression)&&n.expression.text==="test":!1}extractDefineChecksumTestArgs(n){let s=n.arguments;if(s.length<2)return;let c=s[0],u=s[1],_=this.extractStringLiteral(c),g=this.extractStringLiteral(u);if(_&&g)return{testTitle:_,checksumTestId:g}}extractStringLiteral(n){if($1.isStringLiteral(n)||$1.isNoSubstitutionTemplateLiteral(n))return n.text}};t(i_t,"FileContentExtractionStrategy");var t_t=i_t,s_t=class s_t{constructor(n){this.strategies=n||[new t_t,new e_t]}extract(n,s){for(let c of this.strategies){let u=c.extract(n,s);if(u)return u}}};t(s_t,"ChecksumTestIdExtractor");var r_t=s_t;var ncr=new r_t;var a_t=t(i=>isNaN(i)?0:i,"getStat"),mCn=t((i,n,s)=>{if(!i)return;let c=s||n,u;if((0,Ute.isAbsolute)(i))u=i;else{let g=(0,Ute.dirname)(n);u=(0,Ute.resolve)(g,i)}return u.replace(c+"/","")},"toRelativeFilePath"),icr=t(i=>{let n=[];if(i.specs&&n.push(...i.specs),i.suites)for(let s of i.suites)n.push(...icr(s));return n},"extractSpecsFromSuite"),hCn=t(i=>{let n=[];for(let s of i.suites)n.push(...icr(s));return n},"extractFlatSpecsMap"),gCn=t((i,n,s,c,u)=>{let{expected:_,unexpected:g,flaky:b}=i.stats,w=[];hCn(i).forEach(G=>{let J=yCn(G.id,n);J&&(G.recovered=!0);let ae=G.tests[0],ce=G.tags.includes("bug")||ae.annotations.some(vt=>vt.type==="bug"),be=ae.annotations&&ae.annotations.length>0?ae.annotations.map(vt=>({type:vt.type,description:vt.description||""})):[],yt=G.tags||[],_t=s[G.id]||ncr.extract(G.file,G.title);w.push({checksumTestId:_t,playwrightTestId:G.id,success:!!G.ok,recovered:J,bug:ce,annotations:be,tags:yt,outcome:ae.status,filePath:G.file,relativeFilePath:c?mCn(G.file,c,u):G.file})});let O={passed:a_t(b)+a_t(_),failed:a_t(g),bug:w.filter(G=>G.bug).length,recovered:w.filter(G=>G.recovered).length,tests:w};return w.forEach(G=>{G.bug&&(G.outcome==="expected"||G.outcome==="flaky"?O.passed--:G.outcome==="unexpected"&&O.failed--)}),O},"modifyStatsPerTestFile"),yCn=t((i,n)=>Object.values(n[i]||{}).some(s=>{var c,u;return(u=(c=s.autoRecoveryMetadata)==null?void 0:c.data)==null?void 0:u.autoRecovered}),"isAutoRecovered"),scr=t((i,n,s,c,u)=>{(0,Qte.cpSync)(i,i+".backup");try{let _=JSON.parse((0,Qte.readFileSync)(i,"utf-8")),g=gCn(_,n,s,c,u);return _.checksumMetadata=g,(0,Qte.writeFileSync)(i,JSON.stringify(_,null,2)),_}catch(_){return console.log("Error processing JSON report",_.message),null}},"processJSONReport");var c_t=class c_t extends mv{constructor(s,c=!1){super(s,c);this.MAX_COMPLETION_WAIT=2*3600*1e3;this.didFail=!1;this.isolationMode=!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:""}}}get replMode(){return this.getChecksumArg("repl")??!1}async run(){if(this.loadTracer(),this.validateAuthExists(),this.buildVolatileChecksumConfig(),!await this.getSession())return;this.testSuiteAnalytics=Qme.getInstance(this.testSuiteRunInfo,this.isolationMode),this.testSuiteAnalytics.genericEvent(`Project root found at ${this.projectRootDirectory}`,{message:`Checksum root found at ${this.checksumRoot}`});let s={CHECKSUM_ROOT_FOLDER:this.checksumRoot,CHECKSUM_TEST_SUITE_INFO:JSON.stringify(this.testSuiteRunInfo)},c;try{c=await Zd(this.startTestRunMonitor(this.testSuiteRunId),1e4,"test run monitor timeout"),s.CHECKSUM_UPLOAD_AGENT_PORT=c}catch{let w="Error starting test run monitor. Test results will not be available on checksum.";this.testSuiteAnalytics.testRunMonitorError({error:w}),this.log(w)}s.PW_TEST_HTML_REPORT_OPEN="never",this.isolationMode&&(s.CHECKSUM_ISOLATED_MODE="true"),this.testSuiteRunId&&(s.CHECKSUM_TEST_SUITE_ID=this.testSuiteRunId),this.replMode&&(s.CHECKSUM_REPL="true"),this.getChecksumArg("tm")&&(s.CHECKSUM_SHOW_TIME_MACHINE="true");let u=this.getNonChecksumArgs(),_=this.getChecksumArg("rerun-failed");if(_)try{let b=await this.fetchFailedTestPaths(typeof _=="string"?_:void 0);if(b.length===0){console.log("No non-passing tests found \u2014 nothing to rerun."),await this.shutdown(0);return}console.log(`Re-running ${b.length} non-passing test file(s)`),u.unshift(...b)}catch(b){this.printError(`Failed to fetch failed tests: ${b.message}`),await this.shutdown(1);return}this.replMode&&u.push("--debug");let g=`npx playwright test --config "${this.getPlaywrightConfigFilePath()}" ${u.map(b=>`"${b}"`).join(" ")}`;await this.patchPlaywright();try{console.log(`Tests running with @checksum-ai/runtime version ${this.checksumRuntimeVersion}`),this.replMode?(console.log("Running in REPL mode"),this.execCmd(g,s),await this.execCmd(`node ${__dirname}/repl.js`,s)):(this.testSuiteAnalytics.testSuiteStart(),await this.execCmd(g,s)),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..."),this.sendReportUploadRequest(),await this.patchPlaywright(!0),this.completeIndicators.tests=!0,await this.handleCompleteMessage()}}async uploadReport(){var u;this.loadTracer(),this.validateAuthExists(),this.buildVolatileChecksumConfig(),this.config.options.hostReports=!0,this.tryLoadPlaywrightConfigFromFile(),this.loadPlaywrightConfigOptions();let s=this.resolveUserReportPathArg();s&&this.playwrightConfigOptions&&(this.playwrightConfigOptions.reportPath=s);let c=(u=this.playwrightConfigOptions)==null?void 0:u.reportPath;if(!c||!(0,hv.existsSync)(c)){this.printError(`Could not find report at ${c??"(unresolved)"}. Pass an explicit path: npx checksumai upload-report <path>`),await this.shutdown(1);return}if(await this.getSession()){this.testSuiteAnalytics=Qme.getInstance(this.testSuiteRunInfo,this.isolationMode);try{await Zd(this.startTestRunMonitor(this.testSuiteRunId),1e4,"test run monitor timeout")}catch(_){this.printError(`Failed to start test run monitor: ${_.message}. Report will not be uploaded.`),await this.shutdown(1);return}console.log(`Uploading report from ${c} to Checksum...`),this.sendReportUploadRequest(),this.completeIndicators.tests=!0,await this.handleCompleteMessage()}}tryLoadPlaywrightConfigFromFile(){if(!this.playwrightConfig)try{let s=this.getPlaywrightConfigFilePath();if((0,hv.existsSync)(s)){let c=require(s),u=(c==null?void 0:c.default)??c;u&&(this.playwrightConfig=u)}}catch(s){Oc("Could not load playwright config from file, falling back to defaults: "+s.message)}}resolveUserReportPathArg(){let s=this.args.find(u=>!u.startsWith("-")&&!u.includes("="));if(!s)return;let c=(0,q1.isAbsolute)(s)?s:(0,q1.resolve)(process.cwd(),s);if(!(0,hv.existsSync)(c))return c;try{if((0,hv.statSync)(c).isDirectory())return(0,q1.join)(c,"index.html")}catch{}return c}loadPlaywrightConfigOptions(){var k,O;let s=t(G=>(Oc("Using report folder",G),(0,q1.join)(G,"index.html")),"makeFilePath"),c=t(G=>(Oc("Using JSON report folder",G),(0,q1.join)(G,"report.json")),"makeJsonFilePath"),u,_,g=!1,b="never";process.env.PLAYWRIGHT_HTML_OUTPUT_DIR&&(u=s(process.env.PLAYWRIGHT_HTML_OUTPUT_DIR));let w=(k=this.playwrightConfig)==null?void 0:k.reporter;if(w instanceof Array){let G=w.filter(ce=>ce instanceof Array&&ce[0]==="html"),J=w.filter(ce=>ce instanceof Array&&ce[0]==="json");if(!process.env.PLAYWRIGHT_HTML_OUTPUT_DIR){let ce=G.find(be=>{var yt;return(yt=be[1])==null?void 0:yt.outputFolder});ce&&(u=s((0,q1.join)(this.checksumRoot,ce[1].outputFolder)))}let ae=J.find(ce=>{var be;return(be=ce[1])==null?void 0:be.outputFile});ae&&(g=!0,_=(0,q1.join)(this.checksumRoot,ae[1].outputFile));for(let ce of G){let be=(O=ce[1])==null?void 0:O.open;if(be==="always"||be==="on-failure"){b=be;break}}}u||(kc.isRepoMode?u=s((0,q1.join)(kc.runtimeRoot,"playwright-report")):u=s((0,q1.join)(this.projectRootDirectory,"playwright-report"))),_||(kc.isRepoMode?_=c((0,q1.join)(kc.runtimeRoot,"playwright-report")):_=c((0,q1.join)(this.projectRootDirectory,"playwright-report"))),this.playwrightConfigOptions={openReportCriteria:b,reportPath:u,jsonReportPath:_,hasJsonReporter:g}}sendReportUploadRequest(){let s=t(O=>{this.testSuiteAnalytics.testSuiteError({error:O}),this.log(O),this.completeIndicators.report=!0,this.testRunMonitorProcess.stdin.write("cli:report=false")},"makeError");if(!this.playwrightConfigOptions)return s("No playwright config options");let{reportPath:c,openReportCriteria:u,jsonReportPath:_,hasJsonReporter:g}=this.playwrightConfigOptions;if(g&&((0,hv.existsSync)(_)?scr(_,this.playwrightIdToReportMetadata,this.pwTestIdToChecksumTestId,this.checksumRoot,this.absoluteProjectRootDirectory):console.warn(`Could not find JSON report file at ${_}`)),!(0,hv.existsSync)(c))return s(`Could not find report file at ${c}`);Oc("Sending report upload request",c);let b=this.saveMetadataToFile((0,q1.dirname)(c)),w={reportPath:c,pathToChecksumMetadata:b,openReportCriteria:u,didFail:this.didFail,checksumRoot:this.checksumRoot,projectRoot:this.absoluteProjectRootDirectory},k=Buffer.from(JSON.stringify(w)).toString("base64");this.testRunMonitorProcess.stdin.write(`cli:report=${k}`)}saveMetadataToFile(s){try{let c=(0,q1.join)(s,`checksum-metadata-${(0,o_t.randomUUID)()}.json`);return(0,hv.writeFileSync)(c,JSON.stringify(this.playwrightIdToReportMetadata,null,2)),c}catch(c){let u=`Error saving metadata to file - ${c.message}`;this.testSuiteAnalytics.testSuiteError({error:u}),this.log(u);return}}startTestMonitorProcess(s){let u=[JSON.stringify({sessionId:s,apiURL:this.config.apiURL,apiKey:this.config.apiKey}),...this.isolationMode?["isolated"]:[]],_={command:"node",args:[(0,q1.join)(__dirname,"test-run-monitor.js"),...u]};return kc.isRepoMode&&(_.command="yarn",_.args=["test-run-monitor",...u]),acr.spawn(_.command,_.args)}startTestRunMonitor(s){return new Promise(c=>{console.log("Starting test run monitor"),this.testRunMonitorProcess=this.startTestMonitorProcess(s),this.testRunMonitorProcess.stdout.on("data",u=>{let _=this.parseTRMMessages(u.toString().trim());if(_.length)for(let g of _){if(g.startsWith("trace")){this.logTRMMessage(g),this.handleTestRunMonitorTrace(g.substring("trace".length+1));continue}if(!g.startsWith("monitor")){this.logTRMMessage(g);continue}let b=t(()=>{let G=g.substring("monitor".length+1),J=G.indexOf("=");if(J===-1)return{key:G,value:""};let[ae,ce]=[G.substring(0,J),G.substring(J+1)];return{key:ae,value:ce}},"getKeyAndValue"),{key:w,value:k}=b();if(!Object.values(h$e).includes(w)){this.logTRMMessage(`Received unknown monitor message: "${g}"`);continue}let O=w;this.logInterProcessEventMessage(O,k),w==="port"?c(k):this.handleTestRunMonitorMessage(O,k)}}),this.testRunMonitorProcess.on("exit",(u,_)=>{let g=`test run monitor process exited with code ${u} and signal ${_}`;this.testSuiteAnalytics.testRunMonitorError({error:g}),this.log(g)}),this.testRunMonitorProcess.stderr.on("data",u=>{let _=`TRM Error: ${u.toString().substring(0,300)}`;this.testSuiteAnalytics.testRunMonitorError({error:_}),this.log(_)}),this.testRunMonitorProcess.on("error",u=>{let _=`Error starting test run monitor: ${u.message}`;this.testSuiteAnalytics.testRunMonitorError({error:_}),this.log(_)})})}logInterProcessEventMessage(s,c){t(()=>{if(!this.logFilter[s])return!0;if(this.logFilter[s].logOnValueChange&&this.logFilter[s].value!==c)return this.logFilter[s].value=c,!0;if(this.logFilter[s].logEveryMS){let _=Date.now();if(_-this.logFilter[s].lastLogTimestamp>=this.logFilter[s].logEveryMS)return this.logFilter[s].lastLogTimestamp=_,!0}return!1},"shouldLog")()&&this.logTRMMessage(`Received monitor message: "${s}${c?`=${c}`:""}"`)}parseTRMMessages(s){let c=this.trmMessagesBuffer+s,u,_,g=t(()=>{u=c.indexOf("{trm}"),_=c.indexOf("{/trm}")},"findIndexes");g();let b=[];for(;u!==-1&&_!==-1&&_>u;){if(b.push(c.substring(u+"{trm}".length,_)),c=c.slice(_+"{/trm}".length).trim(),c.length&&!c.startsWith("{trm}"))return console.warn("Buffered data does not start with start delimiter",c),this.trmMessagesBuffer="",b;this.trmMessagesBuffer=c,g()}return b}setChecksumTestMetadata(s){let{retry:c,type:u,pwTestId:_,checksumTestId:g}=s;this.playwrightIdToReportMetadata[_]||(this.playwrightIdToReportMetadata[_]={}),this.playwrightIdToReportMetadata[_][c]||(this.playwrightIdToReportMetadata[_][c]={}),this.playwrightIdToReportMetadata[_][c][u]=s,this.pwTestIdToChecksumTestId[_]=g}async handleTestRunMonitorTrace(s){let c=s.indexOf("="),[u,_]=c>-1?[s.substring(0,c),s.substring(c+1)]:[s,""];if(!Object.values(Zke).includes(u)){Oc("Unknown trace event",u);return}try{let g=_?JSON.parse(_):{};switch(u){case"Upload Start":this.testSuiteAnalytics.uploadStart({filename:g.filename});break;case"Upload Complete":this.testSuiteAnalytics.uploadComplete({filename:g.filename});break;case"Upload Failed":this.testSuiteAnalytics.uploadFailed({filename:g.filename,error:g.error});break;case"Trace Processing Error":this.testSuiteAnalytics.traceProcessingError({error:g.error});break}}catch(g){Oc(`Error parsing trace payload, ${g.message}`)}}async handleTestRunMonitorMessage(s,c){var u;switch(s){case"uploads-complete-with-errors":console.log("Error uploading test files to Checksum");try{let g=JSON.parse(c);this.testSuiteAnalytics.uploadsCompleteWithErrors(g)}catch{}this.sendProcessingError().then(()=>{this.completeIndicators.upload=!0});break;case"checksumTestMetadata":try{let g=JSON.parse(c);this.setChecksumTestMetadata(g)}catch(g){let b=`Error parsing test metadata event - ${g.message}`;this.testSuiteAnalytics.testSuiteError({error:b}),this.log(b)}break;case"uploads-complete":this.isolationMode||console.log("Test files uploaded successfully"),this.testSuiteAnalytics.allUploadsComplete(),this.sendUploadsComplete().then(()=>{this.completeIndicators.upload=!0});break;case"report-complete":{if(this.isolationMode){this.completeIndicators.report=!0;break}let g=c.slice(0,c.indexOf(":"))==="true",b=c.slice(c.indexOf(":")+1),w="{}";try{if(b&&(0,hv.existsSync)(b)){w=(0,hv.readFileSync)(b,"utf-8");try{(0,hv.unlinkSync)(b)}catch{console.warn(`Failed to delete temporary stats file: ${b}`)}}else console.warn(`Stats file not found at: ${b}`)}catch(O){console.error(`Error reading stats file: ${O.message}`)}let k=this.handleStats(w);if(this.testSuiteAnalytics.testSuiteEnd({passed:k.passed,failed:k.failed,bug:k.bug,total:((u=k.tests)==null?void 0:u.length)||0}),await this.sendTestRunEnd(k),this.completeIndicators.report=!0,this.reportUploadSucceeded=g,g){let O=`${this.checksumAppUrl}/#/test-runs/${this.testSuiteRunId}`,G=`Checksum report URL: ${O}`,J="*".repeat(G.length);console.log(`${J}
|
|
753
753
|
${G}
|
|
754
|
-
${J}`),this.testSuiteAnalytics.reportComplete({stats:k,reportUrl:O})}else this.testSuiteAnalytics.testSuiteError({error:"An error occurred while uploading the test report to Checksum"}),this.testSuiteAnalytics.reportUploadError(),console.log("An error occurred while uploading the test report to Checksum");break}case"upload-progress":{if(this.isolationMode)break;let g=parseInt(c);(g<this.uploadProgress||g>=this.uploadProgress+10||g===100&&this.uploadProgress!==100)&&(this.uploadProgress=g,console.log(`[ Uploads progress: ${this.uploadProgress}% ]`))}break;case"log":console.log(c);break;case"trace":let _=c;if(!Object.values(Zke).includes(_))return;this.testSuiteAnalytics.genericEvent(_);break;case"playwrightConfig":try{this.playwrightConfig||(this.playwrightConfig=JSON.parse(c),this.testSuiteAnalytics.playwrightConfig({config:this.playwrightConfig}),this.loadPlaywrightConfigOptions())}catch(g){let b=`Error parsing playwright config - ${g.message}`;this.testSuiteAnalytics.playwrightConfigError({error:b}),this.log(b)}break;default:this.testSuiteAnalytics.testRunMonitorError({error:`Unhandled test run monitor message: ${s}=${c}`}),this.log(`Unhandled test run monitor message: ${s}=${c}`)}}handleStats(s){let c={};try{s&&s.length>0?(c=JSON.parse(s),c.tests=c.tests.map(u=>{let _=this.playwrightIdToReportMetadata[u.playwrightTestId];if(!_)return u;let g=Object.keys(_??{}),b=!1,w=!1,k=[];g.forEach(G=>{var J;if(_[G]){let ae=_[G].autoRecoverySummaries;(J=ae==null?void 0:ae.data)!=null&&J.length&&k.push(...ae.data)}});let O=g[g.length-1];if(O&&_[O]){let G=_[O].autoRecoveryMetadata;b=(G==null?void 0:G.data.autoRecoveryTriggered)??!1,w=(G==null?void 0:G.data.autoRecovered)??!1}return{...u,recovered:w,autoRecoveryTriggered:b,autoRecoverySummaries:k}})):(this.testSuiteAnalytics.testSuiteError({error:"No stats received from test run monitor"}),Oc("No stats received from test run monitor"))}catch(u){this.testSuiteAnalytics.testSuiteError({error:`Error parsing stats - ${u.message}`}),this.log(`Error parsing stats - ${u.message}`)}return c}async handleCompleteMessage(){let s=Date.now(),c=12;for(;;){if(Date.now()-s>this.MAX_COMPLETION_WAIT){console.log("Warning: Checksum wasn't able to upload all test assets or expected a file the couldn't be found. This might cause issues when viewing the report/trace."),this.testSuiteAnalytics.runtimeTimeout(),this.shutdown(1);return}if(Object.keys(this.completeIndicators).find(u=>!this.completeIndicators[u]))Oc(this.completeIndicators),--c===0&&(this.testSuiteAnalytics.completionStatus({stats:this.completeIndicators}),c=12),await Qh(5e3);else{this.testSuiteAnalytics.runtimeComplete(),this.writeOutputFile(),console.log("Checksum test suite run has completed"),await this.shutdown(this.didFail?1:0);return}}}writeOutputFile(){try{let s=(0,q1.join)(this.getInternalFilesDirPath(),"output.json"),c={testRunId:this.testSuiteRunId
|
|
754
|
+
${J}`),this.testSuiteAnalytics.reportComplete({stats:k,reportUrl:O})}else this.testSuiteAnalytics.testSuiteError({error:"An error occurred while uploading the test report to Checksum"}),this.testSuiteAnalytics.reportUploadError(),console.log("An error occurred while uploading the test report to Checksum");break}case"upload-progress":{if(this.isolationMode)break;let g=parseInt(c);(g<this.uploadProgress||g>=this.uploadProgress+10||g===100&&this.uploadProgress!==100)&&(this.uploadProgress=g,console.log(`[ Uploads progress: ${this.uploadProgress}% ]`))}break;case"log":console.log(c);break;case"trace":let _=c;if(!Object.values(Zke).includes(_))return;this.testSuiteAnalytics.genericEvent(_);break;case"playwrightConfig":try{this.playwrightConfig||(this.playwrightConfig=JSON.parse(c),this.testSuiteAnalytics.playwrightConfig({config:this.playwrightConfig}),this.loadPlaywrightConfigOptions())}catch(g){let b=`Error parsing playwright config - ${g.message}`;this.testSuiteAnalytics.playwrightConfigError({error:b}),this.log(b)}break;default:this.testSuiteAnalytics.testRunMonitorError({error:`Unhandled test run monitor message: ${s}=${c}`}),this.log(`Unhandled test run monitor message: ${s}=${c}`)}}handleStats(s){let c={};try{s&&s.length>0?(c=JSON.parse(s),c.tests=c.tests.map(u=>{let _=this.playwrightIdToReportMetadata[u.playwrightTestId];if(!_)return u;let g=Object.keys(_??{}),b=!1,w=!1,k=[];g.forEach(G=>{var J;if(_[G]){let ae=_[G].autoRecoverySummaries;(J=ae==null?void 0:ae.data)!=null&&J.length&&k.push(...ae.data)}});let O=g[g.length-1];if(O&&_[O]){let G=_[O].autoRecoveryMetadata;b=(G==null?void 0:G.data.autoRecoveryTriggered)??!1,w=(G==null?void 0:G.data.autoRecovered)??!1}return{...u,recovered:w,autoRecoveryTriggered:b,autoRecoverySummaries:k}})):(this.testSuiteAnalytics.testSuiteError({error:"No stats received from test run monitor"}),Oc("No stats received from test run monitor"))}catch(u){this.testSuiteAnalytics.testSuiteError({error:`Error parsing stats - ${u.message}`}),this.log(`Error parsing stats - ${u.message}`)}return c}async handleCompleteMessage(){let s=Date.now(),c=12;for(;;){if(Date.now()-s>this.MAX_COMPLETION_WAIT){console.log("Warning: Checksum wasn't able to upload all test assets or expected a file the couldn't be found. This might cause issues when viewing the report/trace."),this.testSuiteAnalytics.runtimeTimeout(),this.shutdown(1);return}if(Object.keys(this.completeIndicators).find(u=>!this.completeIndicators[u]))Oc(this.completeIndicators),--c===0&&(this.testSuiteAnalytics.completionStatus({stats:this.completeIndicators}),c=12),await Qh(5e3);else{this.testSuiteAnalytics.runtimeComplete(),this.writeOutputFile(),console.log("Checksum test suite run has completed"),await this.shutdown(this.didFail?1:0);return}}}writeOutputFile(){try{let s=(0,q1.join)(this.getInternalFilesDirPath(),"output.json"),c={testRunId:this.testSuiteRunId,...this.reportUploadSucceeded&&{reportUrl:`${this.checksumAppUrl}/#/test-runs/${this.testSuiteRunId}`},failed:this.didFail};(0,hv.writeFileSync)(s,JSON.stringify(c,null,2)),console.log(`Checksum output written to ${s}`)}catch(s){this.log(`Error writing output file: ${s.message}`)}}async fetchWithTimeout(s,c,u=3e4){let _=new AbortController,g=setTimeout(()=>_.abort(),u);try{return await fetch(s,{...c,signal:_.signal})}finally{clearTimeout(g)}}async fetchFailedTestPaths(s){let c=this.config.apiKey,u=this.config.apiURL,_={Accept:"application/json",ChecksumAppCode:c};if(!s){let O=await this.fetchWithTimeout(`${u}/public-api/v1/test-runs/latest`,{headers:_});if(!O.ok){let J=await O.text();throw new Error(`Failed to fetch latest test run: ${O.status} ${J}`)}let G=await O.json();s=G.id,console.log(`Using latest test run: ${s} (branch: ${G.branch})`)}let g=await this.fetchWithTimeout(`${u}/public-api/v1/test-runs/${s}/results`,{headers:_});if(!g.ok){let O=await g.text();throw new Error(`Failed to fetch test run results: ${g.status} ${O}`)}let w=((await g.json()).tests??[]).filter(O=>O.status!=="passed"&&O.status!=="skipped");return[...new Set(w.map(O=>O.testFilePath).filter(Boolean))]}async shutdown(s=0){await this.cleanup(),process.exit(s)}async cleanup(){await super.cleanup(),this.testRunMonitorProcess&&(this.testRunMonitorProcess.stdin.write("cli:shutdown"),this.testRunMonitorProcess.kill())}async getSession(){let s,c;try{if(!this.config.options.hostReports)return this.setIsolatedMode(),!0;if(c=this.config.apiKey,!c||c==="<API key>"){let G="No API key found in checksum config - please set it in the config file - checksum.config.ts";return this.runtimeTrace(G,"Checksum Config Error"),this.log(G,!0),this.shutdown(1),!1}let u=await this.getEnvInfo(),_=Xor({args:this.args,env:process.env});if(_.kind==="error"){let G=`Invalid --cksm-auto-heal* configuration: ${_.message}`;return this.runtimeTrace(G,"Checksum Config Error"),this.log(G,!0),this.shutdown(1),!1}let g=_.kind==="enabled"?_.options:void 0,b=JSON.stringify({...u,isHidden:!!this.config.options.hideReports,...g?{autoHealConfig:g}:{}}),w=await fetch(`${this.config.apiURL}/client-api/test-runs`,{method:"POST",headers:{Accept:"application/json","Content-Type":"application/json",ChecksumAppCode:c},body:b}),{uuid:k,appName:O}=await w.json();return this.testSuiteRunId=k,u.commit=u.name,u.name=O,this.testSuiteRunInfo={appName:O,commitHash:u.commitHash,envName:u.name,branch:u.branch,testSuiteRunId:k,checksumRuntimeVersion:this.checksumRuntimeVersion},!0}catch(u){let _=`Error connecting to Checksum, the report will not be hosted - ${u.message}`;return this.runtimeTrace(_,"Test Suite Init Error"),this.log(_),this.setIsolatedMode(),!0}}setIsolatedMode(){this.testSuiteRunId=process.env.CHECKSUM_TEST_SUITE_ID||(0,o_t.randomUUID)(),this.isolationMode=!0,this.completeIndicators.upload=!0,kc.testRun.monitoring.debugAnalytics?this.testSuiteRunInfo=tcr(this.checksumRuntimeVersion,this.testSuiteRunId):this.testSuiteRunInfo=ecr(this.checksumRuntimeVersion,this.testSuiteRunId)}async sendTestRunEnd(s){if(!this.isolationMode)try{let c="{}";try{c=JSON.stringify({...s,endedAt:Date.now()})}catch(u){this.testSuiteAnalytics.testSuiteError({error:`Error stringifying stats ${u.message}`}),this.log(`Error stringifying stats ${u.message}`,!0)}await this.updateTestRun(`${this.config.apiURL}/client-api/test-runs/${this.testSuiteRunId}`,"PATCH",c)}catch(c){return this.testSuiteAnalytics.testSuiteError({error:`Error sending test run end ${c.message}`}),this.log(`Error sending test run end ${c.message}`,!0),null}}async sendUploadsComplete(){if(!this.isolationMode)try{await this.updateTestRun(`${this.config.apiURL}/client-api/test-runs/${this.testSuiteRunId}/uploads-completed`,"PATCH")}catch(s){this.testSuiteAnalytics.testSuiteError({error:`Error sending test run uploads complete - ${s.message}`}),this.log(`Error sending test run uploads complete - ${s.message}`,!0)}}async sendProcessingError(){if(!this.isolationMode)try{await this.updateTestRun(`${this.config.apiURL}/client-api/test-runs/${this.testSuiteRunId}/process-error`,"PATCH")}catch(s){this.testSuiteAnalytics.testSuiteError({error:`Error sending test run processing error - ${s.message}`}),this.log(`Error sending test run processing error - ${s.message}`,!0)}}async updateTestRun(s,c,u=void 0){let _=await fetch(s,{method:c,headers:{Accept:"application/json","Content-Type":"application/json",ChecksumAppCode:this.config.apiKey},body:u});return Oc("Received update test run response from url:",s),await this.logApiResponse(_),_}async logApiResponse(s){try{if(!s.ok){let{status:u,statusText:_}=s,g=await s.text();this.testSuiteAnalytics.testSuiteError({status:u,statusText:_,error:g});return}s.headers.get("Content-Type").includes("application/json")?s.json().then(u=>{Oc("API Response:",u)}):s.text().then(u=>{Oc("API Response:",u)})}catch(c){this.testSuiteAnalytics.testSuiteError({error:`Error logging API response - ${c.message}`})}}logTRMMessage(s){(Array.isArray(s)?s:[s]).forEach(c=>{Oc("[trm]",c)})}};t(c_t,"TestsRunner");var Jme=c_t;var l_t=class l_t extends mv{constructor(n,s=!1){super(n,s)}async run(n){switch(n){default:console.log(`
|
|
755
755
|
Checksum CLI
|
|
756
756
|
Usage: checksum [command] [options]
|
|
757
757
|
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@checksum-ai/runtime",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0-alpha",
|
|
4
4
|
"description": "Checksum.ai test runtime",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"dependencies": {
|
|
7
|
-
"@playwright/test": "1.
|
|
7
|
+
"@playwright/test": "1.60.0",
|
|
8
8
|
"dotenv": "^16.4.5",
|
|
9
9
|
"jsdom": "^25.0.1",
|
|
10
10
|
"playwright-extra": "^4.3.6",
|
|
@@ -0,0 +1,506 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const { join } = require("path");
|
|
3
|
+
|
|
4
|
+
// Args
|
|
5
|
+
const on = process.argv[2] !== "off";
|
|
6
|
+
|
|
7
|
+
// -------- [Modifiers] -------- //
|
|
8
|
+
|
|
9
|
+
// Amends the file with the given entry point text and append text
|
|
10
|
+
// When "on" is true, the append text is added to the entry point,
|
|
11
|
+
// otherwise the append text is completely removed from the file
|
|
12
|
+
function amend(filePath, entryPointText, appendText) {
|
|
13
|
+
const data = fs.readFileSync(filePath, "utf8");
|
|
14
|
+
if (!data.includes(entryPointText)) {
|
|
15
|
+
throw new Error("Entry point not found!", entryPointText);
|
|
16
|
+
}
|
|
17
|
+
// Ignore if the append text is already present
|
|
18
|
+
if (on && data.includes(appendText)) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
// Add or clear according to on state
|
|
22
|
+
const result = on
|
|
23
|
+
? data.replace(entryPointText, entryPointText + appendText)
|
|
24
|
+
: data.replace(appendText, "");
|
|
25
|
+
|
|
26
|
+
// Write
|
|
27
|
+
fs.writeFileSync(filePath, result, "utf8");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Replaces content.
|
|
31
|
+
// When "on" is true, the new content is written to the file replacing the original content,
|
|
32
|
+
// otherwise the original content is restored.
|
|
33
|
+
function replaceContent(filePath, originalContent, newContent) {
|
|
34
|
+
// Read the file content
|
|
35
|
+
const fileContent = fs.readFileSync(filePath, "utf8");
|
|
36
|
+
|
|
37
|
+
// add a marker for newContent that can be later recognized for "off" state
|
|
38
|
+
newContent = `/* checksumai */ ${newContent}`;
|
|
39
|
+
|
|
40
|
+
if (on && fileContent.includes(newContent)) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Join the lines back into a single string
|
|
45
|
+
const updatedContent = on
|
|
46
|
+
? fileContent.replace(originalContent, newContent)
|
|
47
|
+
: fileContent.replace(newContent, originalContent);
|
|
48
|
+
|
|
49
|
+
// Write the modified content back to the file
|
|
50
|
+
fs.writeFileSync(filePath, updatedContent, "utf8");
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function doesFileExist(filePath) {
|
|
54
|
+
if (!fs.existsSync(filePath)) {
|
|
55
|
+
console.warn("File not found", filePath);
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// -------- [Modifications] -------- //
|
|
62
|
+
|
|
63
|
+
// Playwright 1.60 bundled the entire lib/ surface into a small number of
|
|
64
|
+
// esbuild outputs. The compiled file targets used by previous patch versions
|
|
65
|
+
// no longer exist:
|
|
66
|
+
// playwright-core/lib/server/browserContext.js -> coreBundle.js
|
|
67
|
+
// playwright-core/lib/generated/injectedScriptSource.js -> coreBundle.js
|
|
68
|
+
// playwright-core/lib/client/channelOwner.js -> coreBundle.js
|
|
69
|
+
// playwright-core/lib/utils/isomorphic/stackTrace.js -> coreBundle.js
|
|
70
|
+
// playwright/lib/worker/testInfo.js -> worker/workerProcessEntry.js
|
|
71
|
+
// playwright/lib/common/testType.js -> common/index.js
|
|
72
|
+
// playwright/lib/reporters/html.js -> runner/index.js
|
|
73
|
+
// Bundling also dropped the `(0, import_X.fn)(...)` IIFE wrapping older
|
|
74
|
+
// patches anchored against, and esbuild renamed identifiers to dedupe across
|
|
75
|
+
// inlined files (`options` -> `options2`, `import_fs` -> `import_fs5`, etc.).
|
|
76
|
+
|
|
77
|
+
// Remove conditions for injecting Playwright scripts
|
|
78
|
+
function alwaysInjectScripts(projectRoot) {
|
|
79
|
+
const file = join(
|
|
80
|
+
projectRoot,
|
|
81
|
+
"node_modules/playwright-core/lib/coreBundle.js"
|
|
82
|
+
);
|
|
83
|
+
if (!doesFileExist(file)) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const originalContent = 'if (debugMode() === "console")';
|
|
88
|
+
|
|
89
|
+
const newContent = "";
|
|
90
|
+
|
|
91
|
+
replaceContent(file, originalContent, newContent);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Add implementation for generateSelectorAndLocator and inject to Playwright console API
|
|
95
|
+
function addGenerateSelectorAndLocator(projectRoot) {
|
|
96
|
+
const file = join(
|
|
97
|
+
projectRoot,
|
|
98
|
+
"node_modules/playwright-core/lib/coreBundle.js"
|
|
99
|
+
);
|
|
100
|
+
if (!doesFileExist(file)) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
const entryPointText1 = "this._generateLocator(element, language),\\n ";
|
|
104
|
+
const appendText1 =
|
|
105
|
+
"generateSelectorAndLocator: (element, language) => this._generateSelectorAndLocator(element, language),\\n asLocator,\\n ";
|
|
106
|
+
amend(file, entryPointText1, appendText1);
|
|
107
|
+
|
|
108
|
+
const entryPointText2 = `return asLocator(language || "javascript", selector);\\n }\\n `;
|
|
109
|
+
const appendText2 =
|
|
110
|
+
'_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 ';
|
|
111
|
+
amend(file, entryPointText2, appendText2);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// -------- [Runtime modifications] -------- //
|
|
115
|
+
|
|
116
|
+
function expect(projectRoot) {
|
|
117
|
+
const file = join(
|
|
118
|
+
projectRoot,
|
|
119
|
+
"node_modules/playwright/lib/matchers/expect.js"
|
|
120
|
+
);
|
|
121
|
+
if (!doesFileExist(file)) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
let originalContent, newContent;
|
|
125
|
+
|
|
126
|
+
// originalContent = `return (...args) => {
|
|
127
|
+
// const testInfo = (0, _globals.currentTestInfo)();`;
|
|
128
|
+
// newContent = `return (...args) => {
|
|
129
|
+
// let noSoft = false;
|
|
130
|
+
// if (args.find(arg=>arg==='no-soft')){
|
|
131
|
+
// noSoft = true;
|
|
132
|
+
// args.pop();
|
|
133
|
+
// }
|
|
134
|
+
// const testInfo = (0, _globals.currentTestInfo)();`;
|
|
135
|
+
// replaceContent(file, originalContent, newContent);
|
|
136
|
+
|
|
137
|
+
// originalContent = `step.complete({
|
|
138
|
+
// error
|
|
139
|
+
// })`;
|
|
140
|
+
// newContent = `step.complete({
|
|
141
|
+
// error,
|
|
142
|
+
// noSoft
|
|
143
|
+
// })`;
|
|
144
|
+
// replaceContent(file, originalContent, newContent);
|
|
145
|
+
|
|
146
|
+
// originalContent = `if (this._info.isSoft) testInfo._failWithError(error);else throw error;`;
|
|
147
|
+
// newContent = `if (this._info.isSoft && !noSoft) testInfo._failWithError(error);else throw error;`;
|
|
148
|
+
// replaceContent(file, originalContent, newContent);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function testInfo(projectRoot) {
|
|
152
|
+
const file = join(
|
|
153
|
+
projectRoot,
|
|
154
|
+
"node_modules/playwright/lib/worker/workerProcessEntry.js"
|
|
155
|
+
);
|
|
156
|
+
if (!doesFileExist(file)) {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
let originalContent, newContent;
|
|
160
|
+
let entryPointText, appendText;
|
|
161
|
+
|
|
162
|
+
// originalContent = `const filteredStack = (0, _util.filteredStackTrace)((0, _utils.captureRawStack)());`;
|
|
163
|
+
// newContent = `const filteredStack = (0, _util.filteredStackTrace)((0, _utils.captureRawStack)().filter(s=>!s.includes('@checksum-ai/runtime')));`;
|
|
164
|
+
// replaceContent(file, originalContent, newContent);
|
|
165
|
+
|
|
166
|
+
entryPointText = `location = location || filteredStack[0];`;
|
|
167
|
+
appendText = `\nif (this._checksumInternal) {
|
|
168
|
+
location = undefined;
|
|
169
|
+
this._checksumInternal = false;
|
|
170
|
+
}
|
|
171
|
+
if (this._checksumNoLocation){
|
|
172
|
+
location = undefined;
|
|
173
|
+
}`;
|
|
174
|
+
amend(file, entryPointText, appendText);
|
|
175
|
+
|
|
176
|
+
originalContent = `if (childStep.error && childStep.infectParentStepsWithError) {`;
|
|
177
|
+
newContent = `if (childStep.error && childStep.infectParentStepsWithError && !step.preventInfectParentStepsWithError) {`;
|
|
178
|
+
replaceContent(file, originalContent, newContent);
|
|
179
|
+
|
|
180
|
+
originalContent = `_failWithError(error) {`;
|
|
181
|
+
newContent = `addError(error, message) {
|
|
182
|
+
const serialized = (0, import_util2.testInfoError)(error);
|
|
183
|
+
serialized.message = [message, serialized.message].join('\\n\\n');
|
|
184
|
+
serialized.stack = [message, serialized.stack].join('\\n\\n');
|
|
185
|
+
const step = error[stepSymbol];
|
|
186
|
+
if (step && step.boxedStack) serialized.stack = \`\${error.name}: \${error.message}\\n\${(0, import_utils.stringifyStackFrames)(step.boxedStack).join('\\n')}\`;
|
|
187
|
+
this.errors.push(serialized);
|
|
188
|
+
}
|
|
189
|
+
_failWithError(error) {`;
|
|
190
|
+
replaceContent(file, originalContent, newContent);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function testType(projectRoot) {
|
|
194
|
+
const file = join(projectRoot, "node_modules/playwright/lib/common/index.js");
|
|
195
|
+
if (!doesFileExist(file)) {
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
entryPointText = `return await currentZone().with("stepZone", step).run(async () => {`;
|
|
200
|
+
appendText = `\nif (options.obtainStep){
|
|
201
|
+
options.obtainStep(step);
|
|
202
|
+
}`;
|
|
203
|
+
amend(file, entryPointText, appendText);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function indexContent(projectRoot) {
|
|
207
|
+
const file = join(projectRoot, "node_modules/playwright/lib/index.js");
|
|
208
|
+
if (!doesFileExist(file)) {
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
let originalContent, newContent;
|
|
212
|
+
originalContent = `const browser = await playwright[browserName].launch();`;
|
|
213
|
+
newContent = `
|
|
214
|
+
let browser = playwright[browserName];
|
|
215
|
+
try {
|
|
216
|
+
const { playwrightExtra } = workerInfo?.project?.use || {};
|
|
217
|
+
if (playwrightExtra && playwrightExtra?.length) {
|
|
218
|
+
const pw = require("playwright-extra")
|
|
219
|
+
const PupeteerExtraPlugin = require("puppeteer-extra-plugin").PuppeteerExtraPlugin
|
|
220
|
+
const chromium = pw.chromium;
|
|
221
|
+
|
|
222
|
+
playwrightExtra.forEach((plugin, i) => {
|
|
223
|
+
try {
|
|
224
|
+
if(!(plugin instanceof PupeteerExtraPlugin)){
|
|
225
|
+
console.warn(\`Plugin at index \${i} is not an instance of PupeteerExtraPlugin\`);
|
|
226
|
+
}
|
|
227
|
+
chromium.use(plugin);
|
|
228
|
+
} catch (e) {
|
|
229
|
+
console.warn(e);
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
browser = chromium;
|
|
233
|
+
}
|
|
234
|
+
} catch (e) {
|
|
235
|
+
console.warn(
|
|
236
|
+
"CHECKSUM: Failed to load Playwright Extra, using Playwright instead.",
|
|
237
|
+
e
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
browser = await browser.launch();
|
|
241
|
+
`;
|
|
242
|
+
replaceContent(file, originalContent, newContent);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function channelOwner(projectRoot) {
|
|
246
|
+
const file = join(
|
|
247
|
+
projectRoot,
|
|
248
|
+
"node_modules/playwright-core/lib/coreBundle.js"
|
|
249
|
+
);
|
|
250
|
+
if (!doesFileExist(file)) {
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
let originalContent, newContent;
|
|
254
|
+
let entryPointText, appendText;
|
|
255
|
+
|
|
256
|
+
entryPointText = `async _wrapApiCall(func, options2) {`;
|
|
257
|
+
|
|
258
|
+
appendText = `\nif (this._checksumInternal){
|
|
259
|
+
options2 = options2 || {};
|
|
260
|
+
options2.internal = true;
|
|
261
|
+
}`;
|
|
262
|
+
amend(file, entryPointText, appendText);
|
|
263
|
+
|
|
264
|
+
entryPointText = `const apiZone = { title: options2?.title, apiName: stackTrace.apiName, frames: stackTrace.frames, internal: options2?.internal ?? false, reported: false, userData: void 0, stepId: void 0 };`;
|
|
265
|
+
|
|
266
|
+
appendText = `\nif (!apiZone.internal && this._checksumTitle){
|
|
267
|
+
apiZone.apiName = this._checksumTitle;
|
|
268
|
+
this._checksumTitle = undefined;
|
|
269
|
+
}
|
|
270
|
+
if (!apiZone.apiName){
|
|
271
|
+
options2 = options2 || {};
|
|
272
|
+
options2.internal = true;
|
|
273
|
+
apiZone.internal = true;
|
|
274
|
+
apiZone.reported = true;
|
|
275
|
+
}
|
|
276
|
+
if (apiZone.apiName && apiZone.apiName.startsWith('proxy')) {
|
|
277
|
+
apiZone.apiName = apiZone.apiName.replace('proxy', 'page');
|
|
278
|
+
}`;
|
|
279
|
+
amend(file, entryPointText, appendText);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function stackTrace(projectRoot) {
|
|
283
|
+
const file = join(
|
|
284
|
+
projectRoot,
|
|
285
|
+
"node_modules/playwright-core/lib/coreBundle.js"
|
|
286
|
+
);
|
|
287
|
+
if (!doesFileExist(file)) {
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
let originalContent, newContent;
|
|
292
|
+
|
|
293
|
+
// Create a regex for getting a file for each line in the stacktrace that overrides the original regex
|
|
294
|
+
// This regex is only used for getting files, the original regex is sitll used for the rest of the content
|
|
295
|
+
originalContent = `let file = match[7];`;
|
|
296
|
+
const fileRe = /(\/.*?\.[a-zA-Z0-9]+)(?=:\d+:\d+)/;
|
|
297
|
+
newContent = `
|
|
298
|
+
const fileRe = new RegExp(${JSON.stringify(fileRe.source)}, "${
|
|
299
|
+
fileRe.flags
|
|
300
|
+
}");
|
|
301
|
+
const m = fileRe.exec(match[0] ?? "");
|
|
302
|
+
let file = m ? m[1] : undefined;
|
|
303
|
+
`;
|
|
304
|
+
replaceContent(file, originalContent, newContent);
|
|
305
|
+
|
|
306
|
+
// Filter out checksum-ai/runtime from stack traces
|
|
307
|
+
originalContent = `return stack.split("\\n");`;
|
|
308
|
+
newContent = `return stack.split("\\n").filter(s=>!s.includes('@checksum-ai/runtime'));`;
|
|
309
|
+
replaceContent(file, originalContent, newContent);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function reportTraceFile(projectRoot) {
|
|
313
|
+
const file = join(projectRoot, "node_modules/playwright/lib/runner/index.js");
|
|
314
|
+
if (!doesFileExist(file)) {
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
let originalContent, newContent;
|
|
319
|
+
|
|
320
|
+
originalContent = `const buffer = import_fs5.default.readFileSync(a.path);`;
|
|
321
|
+
newContent = `let buffer = import_fs5.default.readFileSync(a.path);
|
|
322
|
+
if (a.name === "trace") {
|
|
323
|
+
let retries = 2;
|
|
324
|
+
while (!buffer.slice(0,100).toString().startsWith("checksum-playwright-trace") && retries > 0) {
|
|
325
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 5000);
|
|
326
|
+
buffer = import_fs5.default.readFileSync(a.path)
|
|
327
|
+
retries--;
|
|
328
|
+
}
|
|
329
|
+
}`;
|
|
330
|
+
replaceContent(file, originalContent, newContent);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
function htmlReporter(projectRoot) {
|
|
334
|
+
const file = join(projectRoot, "node_modules/playwright/lib/runner/index.js");
|
|
335
|
+
if (!doesFileExist(file)) {
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
let originalContent, newContent;
|
|
340
|
+
|
|
341
|
+
// Filter out runtime files from snippet generation
|
|
342
|
+
originalContent = `function createSnippets(stepsInFile) {
|
|
343
|
+
for (const file of stepsInFile.keys()) {
|
|
344
|
+
let source;
|
|
345
|
+
try {
|
|
346
|
+
source = import_fs5.default.readFileSync(file, "utf-8") + "\\n//";
|
|
347
|
+
} catch (e) {
|
|
348
|
+
continue;
|
|
349
|
+
}`;
|
|
350
|
+
newContent = `function createSnippets(stepsInFile) {
|
|
351
|
+
for (const file of stepsInFile.keys()) {
|
|
352
|
+
// Skip runtime files to reduce report size
|
|
353
|
+
if (file.includes('@checksum-ai/runtime') || file.includes('node_modules')) {
|
|
354
|
+
continue;
|
|
355
|
+
}
|
|
356
|
+
let source;
|
|
357
|
+
try {
|
|
358
|
+
source = import_fs5.default.readFileSync(file, "utf-8") + "\\n//";
|
|
359
|
+
} catch (e) {
|
|
360
|
+
continue;
|
|
361
|
+
}`;
|
|
362
|
+
replaceContent(file, originalContent, newContent);
|
|
363
|
+
|
|
364
|
+
// Also filter from error codeframe generation
|
|
365
|
+
originalContent = `function createErrorCodeframe(message, location) {
|
|
366
|
+
let source;
|
|
367
|
+
try {
|
|
368
|
+
source = import_fs5.default.readFileSync(location.file, "utf-8") + "\\n//";
|
|
369
|
+
} catch (e) {
|
|
370
|
+
return;
|
|
371
|
+
}`;
|
|
372
|
+
newContent = `function createErrorCodeframe(message, location) {
|
|
373
|
+
// Skip runtime files to reduce report size
|
|
374
|
+
if (location.file && (location.file.includes('@checksum-ai/runtime') || location.file.includes('node_modules'))) {
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
let source;
|
|
378
|
+
try {
|
|
379
|
+
source = import_fs5.default.readFileSync(location.file, "utf-8") + "\\n//";
|
|
380
|
+
} catch (e) {
|
|
381
|
+
return;
|
|
382
|
+
}`;
|
|
383
|
+
replaceContent(file, originalContent, newContent);
|
|
384
|
+
|
|
385
|
+
// Filter out steps with locations in runtime/node_modules files completely
|
|
386
|
+
originalContent = `_createTestStep(dedupedStep, result) {
|
|
387
|
+
const { step, duration, count } = dedupedStep;
|
|
388
|
+
const skipped = dedupedStep.step.annotations?.find((a) => a.type === "skip");
|
|
389
|
+
let title = step.title;
|
|
390
|
+
if (skipped)
|
|
391
|
+
title = \`\${title} (skipped\${skipped.description ? ": " + skipped.description : ""})\`;
|
|
392
|
+
const testStep = {
|
|
393
|
+
title,
|
|
394
|
+
startTime: step.startTime.toISOString(),
|
|
395
|
+
duration,
|
|
396
|
+
steps: dedupeSteps(step.steps).map((s) => this._createTestStep(s, result)),
|
|
397
|
+
attachments: step.attachments.map((s) => {
|
|
398
|
+
const index = result.attachments.indexOf(s);
|
|
399
|
+
if (index === -1)
|
|
400
|
+
throw new Error("Unexpected, attachment not found");
|
|
401
|
+
return index;
|
|
402
|
+
}),
|
|
403
|
+
location: this._relativeLocation(step.location),
|
|
404
|
+
error: step.error?.message,
|
|
405
|
+
count,
|
|
406
|
+
skipped: !!skipped
|
|
407
|
+
};
|
|
408
|
+
if (step.location)
|
|
409
|
+
this._stepsInFile.set(step.location.file, testStep);
|
|
410
|
+
return testStep;
|
|
411
|
+
}`;
|
|
412
|
+
newContent = `_createTestStep(dedupedStep, result) {
|
|
413
|
+
const { step, duration, count } = dedupedStep;
|
|
414
|
+
// Skip "Evaluate" steps with locations in runtime/node_modules files
|
|
415
|
+
if (step.location && step.title === "Evaluate" && (step.location.file.includes('@checksum-ai/runtime') || step.location.file.includes('node_modules'))) {
|
|
416
|
+
// Return null to indicate this step should be filtered out
|
|
417
|
+
return null;
|
|
418
|
+
}
|
|
419
|
+
const skipped = dedupedStep.step.annotations?.find((a) => a.type === "skip");
|
|
420
|
+
let title = step.title;
|
|
421
|
+
if (skipped)
|
|
422
|
+
title = \`\${title} (skipped\${skipped.description ? ": " + skipped.description : ""})\`;
|
|
423
|
+
const testStep = {
|
|
424
|
+
title,
|
|
425
|
+
startTime: step.startTime.toISOString(),
|
|
426
|
+
duration,
|
|
427
|
+
steps: dedupeSteps(step.steps).map((s) => this._createTestStep(s, result)).filter(s => s !== null),
|
|
428
|
+
attachments: step.attachments.map((s) => {
|
|
429
|
+
const index = result.attachments.indexOf(s);
|
|
430
|
+
if (index === -1)
|
|
431
|
+
throw new Error("Unexpected, attachment not found");
|
|
432
|
+
return index;
|
|
433
|
+
}),
|
|
434
|
+
location: this._relativeLocation(step.location),
|
|
435
|
+
error: step.error?.message,
|
|
436
|
+
count,
|
|
437
|
+
skipped: !!skipped
|
|
438
|
+
};
|
|
439
|
+
if (step.location)
|
|
440
|
+
this._stepsInFile.set(step.location.file, testStep);
|
|
441
|
+
return testStep;
|
|
442
|
+
}`;
|
|
443
|
+
replaceContent(file, originalContent, newContent);
|
|
444
|
+
|
|
445
|
+
// Also filter steps when creating test results
|
|
446
|
+
originalContent = `steps: dedupeSteps(result.steps).map((s) => this._createTestStep(s, result)),`;
|
|
447
|
+
newContent = `steps: dedupeSteps(result.steps).map((s) => this._createTestStep(s, result)).filter(s => s !== null),`;
|
|
448
|
+
replaceContent(file, originalContent, newContent);
|
|
449
|
+
|
|
450
|
+
// Normalize locations for steps from runtime/index.js - hide location if it's index.js from runtime
|
|
451
|
+
originalContent = `_relativeLocation(location) {
|
|
452
|
+
if (!location)
|
|
453
|
+
return void 0;
|
|
454
|
+
const file = toPosixPath2(import_path8.default.relative(this._config.rootDir, location.file));
|
|
455
|
+
return {
|
|
456
|
+
file,
|
|
457
|
+
line: location.line,
|
|
458
|
+
column: location.column
|
|
459
|
+
};
|
|
460
|
+
}`;
|
|
461
|
+
newContent = `_relativeLocation(location) {
|
|
462
|
+
if (!location)
|
|
463
|
+
return void 0;
|
|
464
|
+
// Hide location for steps from runtime/index.js to reduce clutter
|
|
465
|
+
if (location.file && location.file.includes('@checksum-ai/runtime') && location.file.endsWith('index.js')) {
|
|
466
|
+
return void 0;
|
|
467
|
+
}
|
|
468
|
+
const file = toPosixPath2(import_path8.default.relative(this._config.rootDir, location.file));
|
|
469
|
+
return {
|
|
470
|
+
file,
|
|
471
|
+
line: location.line,
|
|
472
|
+
column: location.column
|
|
473
|
+
};
|
|
474
|
+
}`;
|
|
475
|
+
replaceContent(file, originalContent, newContent);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// -------- [Run] -------- //
|
|
479
|
+
|
|
480
|
+
const isRuntime = true || process.env.RUNTIME === "true";
|
|
481
|
+
|
|
482
|
+
function run(projectPath) {
|
|
483
|
+
try {
|
|
484
|
+
if (fs.existsSync(projectPath)) {
|
|
485
|
+
alwaysInjectScripts(projectPath);
|
|
486
|
+
addGenerateSelectorAndLocator(projectPath);
|
|
487
|
+
if (isRuntime) {
|
|
488
|
+
expect(projectPath);
|
|
489
|
+
testInfo(projectPath);
|
|
490
|
+
testType(projectPath);
|
|
491
|
+
channelOwner(projectPath);
|
|
492
|
+
stackTrace(projectPath);
|
|
493
|
+
indexContent(projectPath);
|
|
494
|
+
reportTraceFile(projectPath);
|
|
495
|
+
htmlReporter(projectPath);
|
|
496
|
+
}
|
|
497
|
+
} else {
|
|
498
|
+
console.warn("Project path not found", projectPath);
|
|
499
|
+
}
|
|
500
|
+
} catch (e) {
|
|
501
|
+
// ignore
|
|
502
|
+
console.error(e);
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
module.exports = run;
|