momentic 1.0.104 → 1.0.105

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/bin/cli.js CHANGED
@@ -3239,7 +3239,7 @@ ${i.stack}`),a.status(500).send("Internal Server Error")}),n){let i=rb.static(n)
3239
3239
 
3240
3240
  Using Command Prompt on Windows:
3241
3241
  for /f "tokens=5" %a in ('netstat -ano ^| findstr :58888') do taskkill /PID %a /F
3242
- `)}import CO from"events";var ol=class{constructor(e){this.localDiskStorage=e}async getConsoleLogsForRunAttempt(e,t,r){}async getNetworkLogsForRunAttempt(e,t,r){}async getHtmlSnapshot(e,t){}async getA11yTreeSnapshot(e,t){}async getScreenshot(e,t){}async storeConsoleLogsForRunAttempt(e,t,r,o){await this.localDiskStorage.storeFileInRunAttempt({runId:t,runAttemptId:r,fileName:"console-logs.json",contents:JSON.stringify(o)})}async storeNetworkLogsForRunAttempt(e,t,r,o){await this.localDiskStorage.storeFileInRunAttempt({runId:t,runAttemptId:r,fileName:"network-logs.json",contents:JSON.stringify(o)})}async storeScreenshot(e,t,r){}async storeHtmlSnapshot(e,t,r){}async storeA11yTreeSnapshot(e,t,r){}};import ab from"fs";import up from"path";var il=class{constructor(e){this.outputDir=e}async storeFileInRun(e){let{runId:t,fileName:r,contents:o,subDir:i}=e,s=up.join(this.outputDir,t,i??""),a=up.join(s,r);ab.mkdirSync(s,{recursive:!0}),ab.writeFileSync(a,o)}async storeFileInRunAttempt(e){return this.storeFileInRun({runId:e.runId,fileName:e.fileName,contents:e.contents,subDir:up.join("attempts",e.runAttemptId,e.subDir??"")})}};import cw from"fs";import RO from"open";import xl from"path";import{fileURLToPath as AO}from"url";import NL from"diff-lines";import{gt as _L}from"semver";import{execSync as fL}from"child_process";import{platform as yL}from"os";function mp(){return lb()?(w.info("Setting device pixel ratio to 2 automatically since a Mac OS Retina screen was detected."),w.info("If you are using a low pixel-density monitor, you should manually set --pixel-ratio to 1 to avoid incorrect viewport calculations."),w.info("Confirm your device's pixel-ratio at https://www.mydevice.io."),2):(w.info("Setting device pixel ratio to 1."),w.info("If you are using Momentic on a high-pixel density (HiDPI) monitor, relaunch with the --pixel-ratio option to avoid incorrect viewport calculations"),w.info("Confirm your device's pixel-ratio at https://www.mydevice.io."),1)}function lb(){return yL()==="darwin"&&fL("system_profiler SPDisplaysDataType").toString().includes("Retina")}function hp(n){lb()&&n===1&&(w.warn("If you are using Momentic on a Retina screen, relaunch with the --pixel-ratio option to avoid incorrect viewport calculations."),w.warn("Confirm your device's pixel-ratio at https://www.mydevice.io."))}import SL from"@actions/exec";import bL from"@actions/io";import wL from"quote";import vL from"string-argv";async function cb(n,e=!0){let t=vL(n),r=await bL.which(t[0],!0),o=t.slice(1),i=SL.exec(wL(r),o,{delay:100});if(e)return i}import{existsSync as xL,statSync as TL}from"fs";var sl=!!process.env.CI||!process.stdout.isTTY||!!process.env.TF_BUILD;function ts(n){try{return xL(n)&&TL(n).isDirectory()}catch(e){return w.error({err:e},`Error reading path ${n} during directory existence check`),!1}}import EL from"csv-parser";import{createReadStream as CL}from"fs";function gp(n){return new Promise((e,t)=>{let r=[];CL(n).pipe(EL()).on("data",o=>r.push(o)).on("end",()=>e(r)).on("error",o=>t(o))})}import al from"semver";import{z as ll}from"zod";var Tn="1.0.104",RL="https://registry.npmjs.org/momentic",AL=ll.object({versions:ll.record(ll.string(),ll.unknown()).optional()});async function db(n){try{await IL(n)}catch(e){w.warn({err:e},"Failed to check CLI version against NPM servers")}}async function IL(n){if(!Tn){n.warn("Unable to check CLI version because CLI_VERSION is not set");return}let e;for(let r=0;r<2;r++)try{let o=await D(fetch(RL),{milliseconds:5e3});if(!o.ok)throw new Error(`Got error status code ${o.statusText}`);let i=await o.json();e=AL.parse(i).versions;break}catch(o){n.warn({err:o},"Failed to fetch npm registry data")}if(!e){n.warn("Failed to fetch npm registry data. Skipping version check.");return}let t;for(let r of Object.keys(e))al.valid(r)&&(!t||al.gt(r,t))&&al.gt(r,Tn)&&al.lt(r,"2.0.0")&&!r.includes("alpha")&&(t=r);t&&(w.warn(`Update available: v${Tn} -> v${t}`),w.warn("This version may be missing critical fixes, features, and security updates."),w.warn(`Run "npx momentic@${t} -V" to update`))}import{existsSync as LL,mkdirSync as OL,statSync as kL}from"fs";import{dirname as ML}from"path";import ub from"readline/promises";import{hostname as PL}from"os";var W=Za({app:"cli",clientToken:"pub7eb923f18fb3f1d42ac5eba8c5ea13a5",hostname:PL(),disableConsoleLogs:!0}).child({version:"1.0.104"});var fp=!1,mb=(()=>{try{return kL("/.dockerenv"),!0}catch{return!1}})();async function ot(n,e){if(sl||fp||mb)return!0;W.flush(),await new Promise(s=>setTimeout(s,500));let t=ub.createInterface({input:process.stdin,output:process.stdout}),r=n.split("."),o;if(r.length===1)o=n;else{let s=`${r.slice(0,r.length-1).join(".").trim()}.`;e?w.warn(s):w.log(s),o=r[r.length-1].trim()}let i=await t.question(`${o} ('y' for yes / n for no / 'A' to accept all) `);return t.close(),i==="A"?(fp=!0,setTimeout(()=>{fp=!1},3e3),!0):i.toLowerCase()==="y"}async function yp(n){let e=ML(n);return ts(e)?LL(n)?ot(`File '${pb(n)}' already exists. Overwrite existing content?`,!0):!0:await ot(`Directory '${pb(e)}' doesn't exist. Create it now?`,!0)?(OL(e,{recursive:!0}),!0):!1}function pb(n){return n.replace(/(\s+)/g,"\\$1")}async function hb(n,e){if(sl||mb)return e;let t=ub.createInterface({input:process.stdin,output:process.stdout}),r=await t.question(`${n} `);return t.close(),r.trim()||e}async function gb({test:n,fragment:e,entities:t,client:r,logger:o,yes:i}){_L(e.schemaVersion,mt)&&(w.error(`This version of the CLI does not support the schema version of the fragment (${e.schemaVersion}). Please update to the latest version of the CLI and retry this command.`),process.exit(1)),xh(e.steps).forEach(g=>{t.modules[g]||(w.error(`The test patch contains a module with id ${g} that could not be found in the current project. This suggests that either this test patch or your local file system may be out of date.`),process.exit(1))}),e.createdAt.getTime()<Date.now()-7*24*60*60*1e3&&!i&&!await ot("The test patch you are applying is more than 7 days old. Are you sure you want to continue?",!0)&&process.exit(1);let a=n.lastModified.getTime();e.createdAt.getTime()+60*60*1e3<a&&!i&&!await ot("The test patch you are applying was created before the test was last updated. Are you sure you want to continue?",!0)&&process.exit(1);let l=Id(n.fullFilePath,o,t),c=l.steps;if(!Array.isArray(c))throw new Error(`Test ${n.fullFilePath} is missing steps array`);let d,p;if(e.schemaVersion!==mt){let{steps:g,newVersion:f}=await xi({metadata:{id:e.id,schemaVersion:e.schemaVersion},steps:e.steps,logger:w});d=f,p=Ie.array().parse(g)}else p=Ie.array().parse(e.steps);let{stepsToSave:u,moduleUpdates:h}=await Ot({stepLists:{steps:p}});u.beforeSteps=l.beforeSteps??void 0,u.afterSteps=l.afterSteps??void 0;let m=NL(JSON.stringify(c,void 0,2),JSON.stringify(u,void 0,2),{n_surrounding:5});w.dimmed("=".repeat(30)),w.dimmed(m),w.dimmed("=".repeat(30)),w.dimmed(""),d&&w.warn(`If this patch is applied, your test will also be automatically upgraded to the latest schema version (${d}). Schema upgrades have no impact on functionality, although you may notice minor differences in the test YAML.`),!i&&!await ot("Do you want to apply this patch?")&&(w.dimmed("Cancelled."),process.exit(1)),Mi(n.relativePath,u,d??e.schemaVersion,t.project),w.success("Patch applied successfully."),await r.patchTestFragment(e.id,{applied:!0,appliedAt:new Date})}import{cloneDeep as DL}from"lodash-es";async function cl({client:n,skipPrompts:e,project:t}){let r=await n.getAllEnvironments(),o=DL(t.config);o.environments||(o.environments=[]);for(let i of r){let s=o.environments?.find(a=>a.name===i.name);if(s)!e&&!await ot(`Environment ${i.name} already exists in the project configuration file. Would you like to overwrite its variables?`)&&process.exit(1),s.baseUrl=i.variables[Ee],delete i.variables[Ee],s.envVariables=i.variables;else{let a=i.variables[Ee];delete i.variables[Ee],o.environments.push({name:i.name,baseUrl:a,envVariables:i.variables})}}No(o,t.configFilePath),w.success(`Pulled ${r.length} environments successfully! Please make sure to commit any changes to your project configuration file.`)}import{createHash as fb}from"crypto";import yb from"fs";async function bb({testsToFetch:n,client:e,all:t,yes:r}){let{tests:o,modules:i}=await e.getTestYAMLExport({paths:n,all:t}),s=0;for(let[l,c]of Object.entries(o)){let d=Sb(l,me.TEST);!r&&!await yp(d)||(s+=1,yb.writeFileSync(d,c,"utf-8"),W.info({checksum:fb("md5").update(c).digest("hex")},`Wrote '${d}'`))}let a=0;for(let[l,c]of Object.entries(i)){let d=Sb(l,me.MODULE);!r&&!await yp(d)||(a+=1,yb.writeFileSync(d,c,"utf-8"),W.info({checksum:fb("md5").update(c).digest("hex")},`Wrote '${d}'`))}s===0?w.success("Pulled 0 tests."):w.success(`Pulled ${s} test${s>1?"s":""}${a?` and ${a} module${a>1?"s":""}`:""}!`)}function Sb(n,e){switch(e){case me.TEST:return`${ke(n)}.${Pn.TEST}`;case me.MODULE:return`${ke(n)}.${Pn.MODULE}`;default:throw new Error(`Unknown entity type ${e}`)}}async function wb(n){let{project:e,client:t,skipPrompts:r}=n;w.info("Welcome to the Momentic Cloud importer wizard! \u{1F636}\u200D\u{1F32B}\uFE0F"),w.info("Importing environments from Momentic Cloud."),w.info(`This command will overwrite all local environment configuration in ${e.configFilePath} with environments from Momentic Cloud.`),await ot("Are you sure you want to proceed?",!0)||(w.info("Aborting..."),process.exit(1)),await cl({client:t,project:e,skipPrompts:r}),w.success(`Successfully imported environments from Momentic Cloud. We recommend pulling secrets out of ${e.configFilePath} into .env files or dynamically set environment variables for security.`),w.info("Importing tests and modules from Momentic Cloud."),await bb({testsToFetch:[],client:t,all:!0,yes:r}),w.success("Successfully imported tests and modules from Momentic Cloud. You can move them to the desired location in your project. We recommend committing these files to version control so you can share them with team members and run Momentic tests in CI.")}import _t from"fs";import Ur from"path";import FL from"yaml";function vb(){ts("momentic")||(w.error(`The migration command should be ran from the v0 root Momentic directory, which should contain a folder called 'momentic'. No folder named 'momentic' was found in the current working directory (${process.cwd()}).`),process.exit(1));let n={name:"default",include:Ji,environments:[]};w.info("Migrating environments");let e=xb(["momentic/environments"],UL);for(let r of e){let o=FL.parse(_t.readFileSync(r,"utf-8"));try{let i=xs.parse(o),s=i.variables[Ee]??"";delete i.variables[Ee],n.environments?.push({name:i.name,baseUrl:s,envVariables:i.variables})}catch(i){w.error(`${r} failed to parse as a valid environment file.`),w.error(i),process.exit(1)}}w.info("Migrating tests");let t=Ad("./momentic",w);for(let r of t){let o=Ur.join(...r.fullPathSegments),i=Ur.join(Ur.dirname(o),`${r.fileName.slice(0,-5)}.test.yaml`);w.info(`Moving test ${o} to ${i}`),_t.renameSync(o,i)}if(ts("momentic/modules")){w.info("Migrating modules");for(let r of _t.readdirSync("./momentic/modules")){if(!r.endsWith(".yaml"))continue;let o=Ur.resolve(Ur.join("./momentic/modules",r));if(!_t.readFileSync(o,"utf-8").includes("schemaVersion")){w.warn(`Skipping file ${o} since it does not have valid Momentic module contents`);continue}let s=`${o.slice(0,-5)}.module.yaml`;w.info(`Moving module ${o} to ${s}`),_t.renameSync(o,s)}}return w.info("Writing new project configuration file"),No(n,"momentic.config.yaml"),_t.rmSync("./momentic/environments",{recursive:!0,force:!0}),_t.rmSync("./momentic/fixtures",{recursive:!0,force:!0}),w.success("Migration succeeded!"),w.info("Going forward:"),w.info(` - You can store test and module files anywhere under the project root (${process.cwd()})`),w.info(" - Environment details and other common options are tracked in the root momentic.config.yaml file"),n}function xb(n,e,t=new Set){for(let r of n){let o=Ur.resolve(r),i=!1;try{i=_t.existsSync(o)&&_t.statSync(o).isDirectory()}catch(s){w.error({err:s},`Error reading path ${o} during collect paths`)}if(o&&i){let s=_t.readdirSync(o).map(a=>Ur.join(o,a));xb(s,e,t);continue}if(o.endsWith(".yaml")){try{if(!_t.existsSync(o)||!_t.statSync(o).isFile()){w.warn(`File not found or unreadable: ${o}`);continue}}catch(s){w.error({err:s},`Error reading file ${o} during collect paths`);continue}if(!e(o))continue;t.add(o)}}return t}function UL(n){return n.endsWith(".yaml")?_t.readFileSync(n,"utf8").includes("momentic/environment")?!0:(w.warn(`Skipping YAML that is not a Momentic environment: ${n}`),!1):!1}import{Argument as dl,Option as _e}from"@commander-js/extra-typings";import{validateHeaderValue as zL}from"http";import{cpus as Tb}from"os";import{parse as Dse}from"yaml";import{z as G}from"zod";var pl=58888,Sp=30*60*1e3,nr=new _e("--api-key <key>","API key for authentication. If not supplied, attempts to read the MOMENTIC_API_KEY env var."),zr=new _e("--server <server>","Momentic server to use. Leave unchanged unless using Momentic on-premise."),rr=new _e("-y, --yes","Skip all confirmation prompts."),bp=new _e("-w, --wait","Wait for tests to finish running before exiting. Only applicable when running tests remotely").implies({remote:!0}),wp=new _e("--wait-timeout <waitTimeout>",`The maximum number of seconds to wait for tests to complete. Only applicable when the --wait option is specified. Defaults to ${Sp/1e3} seconds.`),ul=new _e("--custom-headers <customHeaders...>","Specify custom headers in the form HEADER=VALUE to be sent with each request during the test. Multiple entries can be provided."),vp=new _e("--no-report","Skip reporting test results to Momentic Cloud when running with the --local flag.").implies({local:!0}),Cb=new _e("--reporter <reporter>","Output report files in a standardized format to a local directory. See the --reporter-dir flag for information on the output directory.").choices(Object.values(Fs)),Rb=new _e("--reporter-dir <reporterDir>","Output directory to store report files. Relative paths are resolved relative to the project root, which is defined by the detected momentic.config.yaml. Defaults to 'reports' if unset."),Ab=new _e("--output-dir <outputDir>","[Alpha] Output directory to store run artifacts such as screenshots, results, and logs. Relative paths are resolved relative to the project root, which is defined by the detected momentic.config.yaml."),Ib=new _e("--include <includePatterns...>","Only include tests that match the provided regex patterns. Multiple patterns can be provided. The patterns will be matched against the test file paths and the pattern only needs to match a part of the path for the test to be included."),Pb=new _e("--exclude <excludePatterns...>","The inverted version of --include: a test that matches any of the provided exclusion patterns will be excluded from running."),xp=new _e("--pixel-ratio <pixelRatio>","Device pixel ratio of your screen or monitor. Mac OS Retina displays and machines marketed as 'HiDPI' should set this to 2. Visit https://www.mydevice.io/ to find your device's pixel ratio."),Lb=new _e("--port <port>",`Port to run the app on. Defaults to ${pl}.`),Tp=new _e("--input-csv <inputCsv>","Path to a CSV file on disk where each row represents a set of inputs that will be made available to all tests that are ran. The first line of the CSV must be the input names."),ml=new _e("--env <env>","Name of the environment to use when running tests."),hl=new _e("--url-override <urlOverride>","Fully qualified url (e.g. https://www.google.com) to start all tests from. Overrides any default starting url set from the test or environment."),Ob=new _e("--shard-index <shardIndex>","The index of the shard to run tests for. Defaults to 1.").default(1).argParser(n=>parseInt(n,10)),Br=new _e("-c, --config <configPath>","Absolute or relative path to a Momentic configuration file (*.momentic.config.yaml)"),Ep=new _e("-f, --filter <filter>","Run tests within the project that has a name equal to the filter provided. This option cannot be used together with file path or directory arguments, but substring matches are allowed."),kb=new _e("--shard-count <shardCount>","The number of shards that tests are being run on. Defaults to 1.").default(1).argParser(n=>parseInt(n,10)),Mb=new dl("<tests...>",`One or more test paths to queue on Momentic Cloud.
3242
+ `)}import CO from"events";var ol=class{constructor(e){this.localDiskStorage=e}async getConsoleLogsForRunAttempt(e,t,r){}async getNetworkLogsForRunAttempt(e,t,r){}async getHtmlSnapshot(e,t){}async getA11yTreeSnapshot(e,t){}async getScreenshot(e,t){}async storeConsoleLogsForRunAttempt(e,t,r,o){await this.localDiskStorage.storeFileInRunAttempt({runId:t,runAttemptId:r,fileName:"console-logs.json",contents:JSON.stringify(o)})}async storeNetworkLogsForRunAttempt(e,t,r,o){await this.localDiskStorage.storeFileInRunAttempt({runId:t,runAttemptId:r,fileName:"network-logs.json",contents:JSON.stringify(o)})}async storeScreenshot(e,t,r){}async storeHtmlSnapshot(e,t,r){}async storeA11yTreeSnapshot(e,t,r){}};import ab from"fs";import up from"path";var il=class{constructor(e){this.outputDir=e}async storeFileInRun(e){let{runId:t,fileName:r,contents:o,subDir:i}=e,s=up.join(this.outputDir,t,i??""),a=up.join(s,r);ab.mkdirSync(s,{recursive:!0}),ab.writeFileSync(a,o)}async storeFileInRunAttempt(e){return this.storeFileInRun({runId:e.runId,fileName:e.fileName,contents:e.contents,subDir:up.join("attempts",e.runAttemptId,e.subDir??"")})}};import cw from"fs";import RO from"open";import xl from"path";import{fileURLToPath as AO}from"url";import NL from"diff-lines";import{gt as _L}from"semver";import{execSync as fL}from"child_process";import{platform as yL}from"os";function mp(){return lb()?(w.info("Setting device pixel ratio to 2 automatically since a Mac OS Retina screen was detected."),w.info("If you are using a low pixel-density monitor, you should manually set --pixel-ratio to 1 to avoid incorrect viewport calculations."),w.info("Confirm your device's pixel-ratio at https://www.mydevice.io."),2):(w.info("Setting device pixel ratio to 1."),w.info("If you are using Momentic on a high-pixel density (HiDPI) monitor, relaunch with the --pixel-ratio option to avoid incorrect viewport calculations"),w.info("Confirm your device's pixel-ratio at https://www.mydevice.io."),1)}function lb(){return yL()==="darwin"&&fL("system_profiler SPDisplaysDataType").toString().includes("Retina")}function hp(n){lb()&&n===1&&(w.warn("If you are using Momentic on a Retina screen, relaunch with the --pixel-ratio option to avoid incorrect viewport calculations."),w.warn("Confirm your device's pixel-ratio at https://www.mydevice.io."))}import SL from"@actions/exec";import bL from"@actions/io";import wL from"quote";import vL from"string-argv";async function cb(n,e=!0){let t=vL(n),r=await bL.which(t[0],!0),o=t.slice(1),i=SL.exec(wL(r),o,{delay:100});if(e)return i}import{existsSync as xL,statSync as TL}from"fs";var sl=!!process.env.CI||!process.stdout.isTTY||!!process.env.TF_BUILD;function ts(n){try{return xL(n)&&TL(n).isDirectory()}catch(e){return w.error({err:e},`Error reading path ${n} during directory existence check`),!1}}import EL from"csv-parser";import{createReadStream as CL}from"fs";function gp(n){return new Promise((e,t)=>{let r=[];CL(n).pipe(EL()).on("data",o=>r.push(o)).on("end",()=>e(r)).on("error",o=>t(o))})}import al from"semver";import{z as ll}from"zod";var Tn="1.0.105",RL="https://registry.npmjs.org/momentic",AL=ll.object({versions:ll.record(ll.string(),ll.unknown()).optional()});async function db(n){try{await IL(n)}catch(e){w.warn({err:e},"Failed to check CLI version against NPM servers")}}async function IL(n){if(!Tn){n.warn("Unable to check CLI version because CLI_VERSION is not set");return}let e;for(let r=0;r<2;r++)try{let o=await D(fetch(RL),{milliseconds:5e3});if(!o.ok)throw new Error(`Got error status code ${o.statusText}`);let i=await o.json();e=AL.parse(i).versions;break}catch(o){n.warn({err:o},"Failed to fetch npm registry data")}if(!e){n.warn("Failed to fetch npm registry data. Skipping version check.");return}let t;for(let r of Object.keys(e))al.valid(r)&&(!t||al.gt(r,t))&&al.gt(r,Tn)&&al.lt(r,"2.0.0")&&!r.includes("alpha")&&(t=r);t&&(w.warn(`Update available: v${Tn} -> v${t}`),w.warn("This version may be missing critical fixes, features, and security updates."),w.warn(`Run "npx momentic@${t} -V" to update`))}import{existsSync as LL,mkdirSync as OL,statSync as kL}from"fs";import{dirname as ML}from"path";import ub from"readline/promises";import{hostname as PL}from"os";var W=Za({app:"cli",clientToken:"pub7eb923f18fb3f1d42ac5eba8c5ea13a5",hostname:PL(),disableConsoleLogs:!0}).child({version:"1.0.105"});var fp=!1,mb=(()=>{try{return kL("/.dockerenv"),!0}catch{return!1}})();async function ot(n,e){if(sl||fp||mb)return!0;W.flush(),await new Promise(s=>setTimeout(s,500));let t=ub.createInterface({input:process.stdin,output:process.stdout}),r=n.split("."),o;if(r.length===1)o=n;else{let s=`${r.slice(0,r.length-1).join(".").trim()}.`;e?w.warn(s):w.log(s),o=r[r.length-1].trim()}let i=await t.question(`${o} ('y' for yes / n for no / 'A' to accept all) `);return t.close(),i==="A"?(fp=!0,setTimeout(()=>{fp=!1},3e3),!0):i.toLowerCase()==="y"}async function yp(n){let e=ML(n);return ts(e)?LL(n)?ot(`File '${pb(n)}' already exists. Overwrite existing content?`,!0):!0:await ot(`Directory '${pb(e)}' doesn't exist. Create it now?`,!0)?(OL(e,{recursive:!0}),!0):!1}function pb(n){return n.replace(/(\s+)/g,"\\$1")}async function hb(n,e){if(sl||mb)return e;let t=ub.createInterface({input:process.stdin,output:process.stdout}),r=await t.question(`${n} `);return t.close(),r.trim()||e}async function gb({test:n,fragment:e,entities:t,client:r,logger:o,yes:i}){_L(e.schemaVersion,mt)&&(w.error(`This version of the CLI does not support the schema version of the fragment (${e.schemaVersion}). Please update to the latest version of the CLI and retry this command.`),process.exit(1)),xh(e.steps).forEach(g=>{t.modules[g]||(w.error(`The test patch contains a module with id ${g} that could not be found in the current project. This suggests that either this test patch or your local file system may be out of date.`),process.exit(1))}),e.createdAt.getTime()<Date.now()-7*24*60*60*1e3&&!i&&!await ot("The test patch you are applying is more than 7 days old. Are you sure you want to continue?",!0)&&process.exit(1);let a=n.lastModified.getTime();e.createdAt.getTime()+60*60*1e3<a&&!i&&!await ot("The test patch you are applying was created before the test was last updated. Are you sure you want to continue?",!0)&&process.exit(1);let l=Id(n.fullFilePath,o,t),c=l.steps;if(!Array.isArray(c))throw new Error(`Test ${n.fullFilePath} is missing steps array`);let d,p;if(e.schemaVersion!==mt){let{steps:g,newVersion:f}=await xi({metadata:{id:e.id,schemaVersion:e.schemaVersion},steps:e.steps,logger:w});d=f,p=Ie.array().parse(g)}else p=Ie.array().parse(e.steps);let{stepsToSave:u,moduleUpdates:h}=await Ot({stepLists:{steps:p}});u.beforeSteps=l.beforeSteps??void 0,u.afterSteps=l.afterSteps??void 0;let m=NL(JSON.stringify(c,void 0,2),JSON.stringify(u,void 0,2),{n_surrounding:5});w.dimmed("=".repeat(30)),w.dimmed(m),w.dimmed("=".repeat(30)),w.dimmed(""),d&&w.warn(`If this patch is applied, your test will also be automatically upgraded to the latest schema version (${d}). Schema upgrades have no impact on functionality, although you may notice minor differences in the test YAML.`),!i&&!await ot("Do you want to apply this patch?")&&(w.dimmed("Cancelled."),process.exit(1)),Mi(n.relativePath,u,d??e.schemaVersion,t.project),w.success("Patch applied successfully."),await r.patchTestFragment(e.id,{applied:!0,appliedAt:new Date})}import{cloneDeep as DL}from"lodash-es";async function cl({client:n,skipPrompts:e,project:t}){let r=await n.getAllEnvironments(),o=DL(t.config);o.environments||(o.environments=[]);for(let i of r){let s=o.environments?.find(a=>a.name===i.name);if(s)!e&&!await ot(`Environment ${i.name} already exists in the project configuration file. Would you like to overwrite its variables?`)&&process.exit(1),s.baseUrl=i.variables[Ee],delete i.variables[Ee],s.envVariables=i.variables;else{let a=i.variables[Ee];delete i.variables[Ee],o.environments.push({name:i.name,baseUrl:a,envVariables:i.variables})}}No(o,t.configFilePath),w.success(`Pulled ${r.length} environments successfully! Please make sure to commit any changes to your project configuration file.`)}import{createHash as fb}from"crypto";import yb from"fs";async function bb({testsToFetch:n,client:e,all:t,yes:r}){let{tests:o,modules:i}=await e.getTestYAMLExport({paths:n,all:t}),s=0;for(let[l,c]of Object.entries(o)){let d=Sb(l,me.TEST);!r&&!await yp(d)||(s+=1,yb.writeFileSync(d,c,"utf-8"),W.info({checksum:fb("md5").update(c).digest("hex")},`Wrote '${d}'`))}let a=0;for(let[l,c]of Object.entries(i)){let d=Sb(l,me.MODULE);!r&&!await yp(d)||(a+=1,yb.writeFileSync(d,c,"utf-8"),W.info({checksum:fb("md5").update(c).digest("hex")},`Wrote '${d}'`))}s===0?w.success("Pulled 0 tests."):w.success(`Pulled ${s} test${s>1?"s":""}${a?` and ${a} module${a>1?"s":""}`:""}!`)}function Sb(n,e){switch(e){case me.TEST:return`${ke(n)}.${Pn.TEST}`;case me.MODULE:return`${ke(n)}.${Pn.MODULE}`;default:throw new Error(`Unknown entity type ${e}`)}}async function wb(n){let{project:e,client:t,skipPrompts:r}=n;w.info("Welcome to the Momentic Cloud importer wizard! \u{1F636}\u200D\u{1F32B}\uFE0F"),w.info("Importing environments from Momentic Cloud."),w.info(`This command will overwrite all local environment configuration in ${e.configFilePath} with environments from Momentic Cloud.`),await ot("Are you sure you want to proceed?",!0)||(w.info("Aborting..."),process.exit(1)),await cl({client:t,project:e,skipPrompts:r}),w.success(`Successfully imported environments from Momentic Cloud. We recommend pulling secrets out of ${e.configFilePath} into .env files or dynamically set environment variables for security.`),w.info("Importing tests and modules from Momentic Cloud."),await bb({testsToFetch:[],client:t,all:!0,yes:r}),w.success("Successfully imported tests and modules from Momentic Cloud. You can move them to the desired location in your project. We recommend committing these files to version control so you can share them with team members and run Momentic tests in CI.")}import _t from"fs";import Ur from"path";import FL from"yaml";function vb(){ts("momentic")||(w.error(`The migration command should be ran from the v0 root Momentic directory, which should contain a folder called 'momentic'. No folder named 'momentic' was found in the current working directory (${process.cwd()}).`),process.exit(1));let n={name:"default",include:Ji,environments:[]};w.info("Migrating environments");let e=xb(["momentic/environments"],UL);for(let r of e){let o=FL.parse(_t.readFileSync(r,"utf-8"));try{let i=xs.parse(o),s=i.variables[Ee]??"";delete i.variables[Ee],n.environments?.push({name:i.name,baseUrl:s,envVariables:i.variables})}catch(i){w.error(`${r} failed to parse as a valid environment file.`),w.error(i),process.exit(1)}}w.info("Migrating tests");let t=Ad("./momentic",w);for(let r of t){let o=Ur.join(...r.fullPathSegments),i=Ur.join(Ur.dirname(o),`${r.fileName.slice(0,-5)}.test.yaml`);w.info(`Moving test ${o} to ${i}`),_t.renameSync(o,i)}if(ts("momentic/modules")){w.info("Migrating modules");for(let r of _t.readdirSync("./momentic/modules")){if(!r.endsWith(".yaml"))continue;let o=Ur.resolve(Ur.join("./momentic/modules",r));if(!_t.readFileSync(o,"utf-8").includes("schemaVersion")){w.warn(`Skipping file ${o} since it does not have valid Momentic module contents`);continue}let s=`${o.slice(0,-5)}.module.yaml`;w.info(`Moving module ${o} to ${s}`),_t.renameSync(o,s)}}return w.info("Writing new project configuration file"),No(n,"momentic.config.yaml"),_t.rmSync("./momentic/environments",{recursive:!0,force:!0}),_t.rmSync("./momentic/fixtures",{recursive:!0,force:!0}),w.success("Migration succeeded!"),w.info("Going forward:"),w.info(` - You can store test and module files anywhere under the project root (${process.cwd()})`),w.info(" - Environment details and other common options are tracked in the root momentic.config.yaml file"),n}function xb(n,e,t=new Set){for(let r of n){let o=Ur.resolve(r),i=!1;try{i=_t.existsSync(o)&&_t.statSync(o).isDirectory()}catch(s){w.error({err:s},`Error reading path ${o} during collect paths`)}if(o&&i){let s=_t.readdirSync(o).map(a=>Ur.join(o,a));xb(s,e,t);continue}if(o.endsWith(".yaml")){try{if(!_t.existsSync(o)||!_t.statSync(o).isFile()){w.warn(`File not found or unreadable: ${o}`);continue}}catch(s){w.error({err:s},`Error reading file ${o} during collect paths`);continue}if(!e(o))continue;t.add(o)}}return t}function UL(n){return n.endsWith(".yaml")?_t.readFileSync(n,"utf8").includes("momentic/environment")?!0:(w.warn(`Skipping YAML that is not a Momentic environment: ${n}`),!1):!1}import{Argument as dl,Option as _e}from"@commander-js/extra-typings";import{validateHeaderValue as zL}from"http";import{cpus as Tb}from"os";import{parse as Dse}from"yaml";import{z as G}from"zod";var pl=58888,Sp=30*60*1e3,nr=new _e("--api-key <key>","API key for authentication. If not supplied, attempts to read the MOMENTIC_API_KEY env var."),zr=new _e("--server <server>","Momentic server to use. Leave unchanged unless using Momentic on-premise."),rr=new _e("-y, --yes","Skip all confirmation prompts."),bp=new _e("-w, --wait","Wait for tests to finish running before exiting. Only applicable when running tests remotely").implies({remote:!0}),wp=new _e("--wait-timeout <waitTimeout>",`The maximum number of seconds to wait for tests to complete. Only applicable when the --wait option is specified. Defaults to ${Sp/1e3} seconds.`),ul=new _e("--custom-headers <customHeaders...>","Specify custom headers in the form HEADER=VALUE to be sent with each request during the test. Multiple entries can be provided."),vp=new _e("--no-report","Skip reporting test results to Momentic Cloud when running with the --local flag.").implies({local:!0}),Cb=new _e("--reporter <reporter>","Output report files in a standardized format to a local directory. See the --reporter-dir flag for information on the output directory.").choices(Object.values(Fs)),Rb=new _e("--reporter-dir <reporterDir>","Output directory to store report files. Relative paths are resolved relative to the project root, which is defined by the detected momentic.config.yaml. Defaults to 'reports' if unset."),Ab=new _e("--output-dir <outputDir>","[Alpha] Output directory to store run artifacts such as screenshots, results, and logs. Relative paths are resolved relative to the project root, which is defined by the detected momentic.config.yaml."),Ib=new _e("--include <includePatterns...>","Only include tests that match the provided regex patterns. Multiple patterns can be provided. The patterns will be matched against the test file paths and the pattern only needs to match a part of the path for the test to be included."),Pb=new _e("--exclude <excludePatterns...>","The inverted version of --include: a test that matches any of the provided exclusion patterns will be excluded from running."),xp=new _e("--pixel-ratio <pixelRatio>","Device pixel ratio of your screen or monitor. Mac OS Retina displays and machines marketed as 'HiDPI' should set this to 2. Visit https://www.mydevice.io/ to find your device's pixel ratio."),Lb=new _e("--port <port>",`Port to run the app on. Defaults to ${pl}.`),Tp=new _e("--input-csv <inputCsv>","Path to a CSV file on disk where each row represents a set of inputs that will be made available to all tests that are ran. The first line of the CSV must be the input names."),ml=new _e("--env <env>","Name of the environment to use when running tests."),hl=new _e("--url-override <urlOverride>","Fully qualified url (e.g. https://www.google.com) to start all tests from. Overrides any default starting url set from the test or environment."),Ob=new _e("--shard-index <shardIndex>","The index of the shard to run tests for. Defaults to 1.").default(1).argParser(n=>parseInt(n,10)),Br=new _e("-c, --config <configPath>","Absolute or relative path to a Momentic configuration file (*.momentic.config.yaml)"),Ep=new _e("-f, --filter <filter>","Run tests within the project that has a name equal to the filter provided. This option cannot be used together with file path or directory arguments, but substring matches are allowed."),kb=new _e("--shard-count <shardCount>","The number of shards that tests are being run on. Defaults to 1.").default(1).argParser(n=>parseInt(n,10)),Mb=new dl("<tests...>",`One or more test paths to queue on Momentic Cloud.
3243
3243
 
3244
3244
  A test path is a lowercased version of your test name where spaces are replaced with dashes: 'npx momentic@latest pull hello-world'.`),Nb=new dl("<tests...>","One or more test file path or folders that exist on the local machine: 'npx momentic@latest run hello-world.test.yaml'.").argOptional(),_b=new dl("<suites...>",`One or more suite paths that exist on Momentic Cloud.
3245
3245
 
@@ -3254,6 +3254,6 @@ ${n.map(p=>` - ${p}`).join(`
3254
3254
  ${n.map(p=>` - ${p}`).join(`
3255
3255
  `)}`),Object.values(e.tests).forEach(p=>{n.some(u=>p.relativePath.includes(u))&&a.add(p.fullFilePath)}))}else{!r&&!await ot("No test paths or substrings were provided. Do you want to run all tests?")&&(w.error("Cancelled by user."),process.exit(1));let c=Object.values(e.tests);w.info(`Reading all ${c.length} tests in the project from local disk.`),c.forEach(d=>{a.add(d.fullFilePath)})}for(let c of a){let d=Lp.relative(t.rootDir,c);o&&!o.some(p=>new RegExp(p).test(d))&&a.delete(c),i&&i.some(p=>new RegExp(p).test(d))&&a.delete(c)}let l=Array.from(a).map(async c=>{try{let d=await ka(c,W,e);if(uO.gt(d.schemaVersion,mt)&&w.warn(`Test ${c} has schema version ${d.schemaVersion}, which is greater than what is currently supported by this SDK. Please update your momentic package version to avoid unexpected behavior.`),d.disabled)return null;if(s&&s.length>0){let p=d.labels||[];if(!s.some(h=>p.includes(h)))return null}return{...d,fullFilePath:c,relativeFilePath:Lp.relative(t.rootDir,c)}}catch(d){w.error(`Failed to read and resolve test at '${c}': ${d}`),process.exit(1)}});return Promise.all(l).then(dO)}import{randomUUID as fO}from"crypto";import{cloneDeep as iw}from"lodash-es";async function ew({orgId:n,codeEvalTools:e,logger:t,outputDefinitions:r,testContext:o}){let i={};for(let s of r){let{name:a,value:l}=s;i[a]=await kt({orgId:n,s:l,localTools:e,logger:t,context:o})}return i}async function tw({baseUrl:n,envName:e,devicePixelRatio:t,apiClient:r,test:o,storageClient:i,codeEvalTools:s,generator:a,orgId:l,variables:c,logger:d,customHeaders:p,testInputs:u,analytics:h,localBrowserConfig:m,visualDiffScreenshotStorage:g}){let f=await ya({advanced:{...m,...o.advanced},customHeaders:p,envVariables:c,envName:e,baseUrl:n,logger:d,localTools:s,orgId:l}),y=await mo.init(l),x={baseUrl:r.baseUrl,apiKey:r.apiKey,logger:W};f.browserType==="Google Chrome"&&await jr(["chrome"]);let S;try{S=await nn.init({baseUrl:n,logger:d,userBrowserSettings:f,storage:i,flagStore:y,enricher:new Rr(x),contextArgs:{viewport:o.advanced.viewport??Zt,deviceScaleFactor:t}})}catch(C){let I=C.message;if(I.includes("Executable doesn't exist")||I.includes("install your dependencies"))w.error("The headless browser used by Momentic is not installed correctly. Re-installing the necessary dependencies before starting the test. We strongly recommend installing browsers correctly before execution."),await jr(Cp,!0),S=await nn.init({baseUrl:n,logger:d,userBrowserSettings:f,storage:i,flagStore:y,enricher:new Rr(x),contextArgs:{viewport:o.advanced.viewport??Zt,deviceScaleFactor:t}});else throw C}let E=new wo({browser:S,generator:a,logger:d,orgId:l,scratchPadId:void 0,storage:i,flagStore:y,localCodeEvalTools:s,visualDiffScreenshotStorage:g,analytics:h,slowMoMs:f.slowMoMs}),v=new Et({baseUrl:n,currentUrl:E.browser.url(),variablesFromEnvironment:c,envName:e});return o.parameters&&await Promise.all(o.parameters.map(async C=>{let{name:I,defaultValue:M,required:P}=C,U=u?.[I];P&&U===void 0&&(w.error(`Required parameter '${I}' is required by test '${o.name}' but not provided`),process.exit(1));let H=await kt({orgId:l,s:U??M,localTools:s,logger:d,context:Et.dummyContext(v.getEnvName())});v.setMomenticSystemVariable(I,H)})),{controller:E,context:v,flagStore:y}}import{randomUUID as mO}from"crypto";import{diff as hO}from"deep-object-diff";import{cloneDeep as rw}from"lodash-es";import{debounce as gO}from"ts-debounce";async function nw({testId:n,apiClient:e,testAdvancedSettings:t,aiSettingsFromEnv:r,logger:o,noReport:i}){if(i)return o.debug("The CLI is not running in a CI environment or --no-report was explicitly passed"),!1;if(t.failureRecovery===!1||t.failureRecovery===void 0&&!r?.failureRecovery)return!1;let s;try{s=await e.getPastTestResults(n,{afterTime:Date.now()-7*24*60*60*1e3})}catch(l){return o.error({err:l},"Test is not eligible for recovery since we failed to fetch the recent test results"),!1}return s.some(l=>l.status==="PASSED")?!0:(o.debug({recentRuns:s},"Test is not eligible for recovery because there are only failures in the past 7 days"),!1)}async function ow({attemptInputs:n,attemptFixtures:e,attemptMetadata:t}){let{attemptNumber:r,orgId:o,runId:i,runAttemptId:s}=t,{controller:a,context:l,flagStore:c,analytics:d,codeEvalTools:p,storageClient:u,debugDataStorage:h,logger:m,apiClient:g,billingReporter:f}=e,{test:y,orgSettings:x,noReport:S}=n;m.info(`Running test '${y.name}' locally${S?"":` and reporting results to https://app.momentic.ai/runs/${i}`}`);let E=rw({steps:y.steps,beforeSteps:y.beforeSteps,afterSteps:y.afterSteps}),v=async N=>{if(!(S||!i))try{if(N.results){let L=rw(N.results);ta(L,m),N.results=L}await g.updateRun(i,{...N,updatedAt:new Date})}catch(L){m.warn({err:L},"Failed to update run data. You may see stale data on the Momentic Cloud Server.")}},C=gO(async N=>{S||await v(N)},1e4,{maxWait:3e4}),I=async N=>{try{await C(N)}catch{}},M={controller:a,storage:u,debugDataStorage:h,billingReporter:f,analytics:d,context:l,logger:m,codeEvalTools:p},P={orgId:o,runId:i||mO(),runAttemptId:s,testMetadata:y,steps:y.steps,beforeSteps:y.beforeSteps,afterSteps:y.afterSteps},U={collectDebugData:!0,reinitializeBrowser:!0,disableHealing:!await nw({noReport:S,currentAttempt:r,testId:y.id,flagStore:c,apiClient:g,testAdvancedSettings:y.advanced,aiSettingsFromEnv:x.ai,logger:m})},H={step:{},test:{onTestComplete:async()=>{await a.browser.cleanup()},onUpdateRun:N=>{I(N)},onSaveFinalRunResults:async N=>{C.cancel(),await v(N)},onProposedTestSteps:async N=>g.uploadProposedSteps(N,m),onTestSuccess:async N=>{let L=hO(E,N);if(Object.keys(L).length!==0){m.debug({changes:L},"Updating steps post-run success in worker");try{let{cachesToSave:k}=await Ot({stepLists:N,cacheCreationParams:{testId:y.id,orgId:o}});await g.updateStepCaches({testId:y.id,entries:k})}catch(k){m.error({err:k},"Failed to save step caches after successful execution. This is not critical, but can impact future performance.")}}}}};return await f.reportBillableEvent(m,"test-run",{eventId:i,testId:y.id,testName:y.name}),await fa({fixtures:M,inputs:P,options:U,callbacks:H})}async function sw(n){let{testDefinition:e}=n,t=new Date;try{return await yO(n)}catch(r){let o="Fatal error running test";return w.error(`${o}: ${r.message}`),W.error({err:r},o),{parameters:n,failureReason:"UnknownError",failureDetails:{errorMessage:r.message,errorStack:r.stack},status:"FAILED",attempts:0,test:e,filePath:e.relativeFilePath,startedAt:t,lastAttemptStartedAt:t,finishedAt:new Date,outputs:{}}}}async function yO(n){let{testDefinition:e,project:t,apiClient:r,debugDataStorage:o,orgId:i,noReport:s,urlOverride:a,runGroupId:l,runSigIntHandlers:c}=n,d=new io(r,i),p=new Wn({httpClient:new At({baseUrl:r.baseUrl,apiKey:r.apiKey,logger:W})}),u=e.steps,h=iw(e.steps);try{await d.resolveStepCacheEntries({organizationId:i,testId:e.id,steps:h,schemaVersion:e.schemaVersion,logger:W})}catch(S){throw W.error({err:S},"Failed to resolve step cache entries"),new Error(`Failed to resolve step cache entries. Please ensure you are running using a supported version of Momentic. If you believe this is a Momentic issue, please contact Support with the following error: ${S}`)}let m=n.envName??bO(e),g,f={};if(m){try{g=Ni(m,t,W)}catch(S){let E=`Failed to resolve environment ${m} for test ${e.name}: ${S}`;throw new Error(E)}f=g.variables}let y=e.baseUrl;if(a)y=a;else if(!y){let S=f[Ee];typeof S=="string"&&(y=S)}if(!y){let S=`Cannot run test with no base URL and no ${Ee} variable defined in its environment`;throw new Error(S)}let x;if(!s)try{x=(await r.createRun({stepsSnapshot:u,runGroupId:l,testId:e.id,testName:e.name,trigger:"CLI",resolvedBaseUrl:y,environmentName:m,cliVersion:Tn,schemaVersion:e.schemaVersion})).id}catch(S){throw W.error({err:S,testId:e.id},"Error creating run on Momentic Cloud"),new Error(`Error creating run on Momentic Cloud. Please ensure Momentic is accessible from your environment and try again later.
3256
3256
  Error: ${S.message}`)}try{return c?.push(async()=>{!x||s||await r.updateRun(x,{status:"CANCELLED",updatedAt:new Date})}),SO({...n,variables:f,envName:m,runId:x,stepsWithCaches:h,resolvedEnv:g,baseUrl:y,storageClient:d,debugDataStorage:o,codeEvalTools:p})}finally{c?.pop()}}async function SO(n){let{testDefinition:e,stepsWithCaches:t,project:r,regenerateGoldenFiles:o,apiClient:i,debugDataStorage:s,generator:a,baseUrl:l,storageClient:c,codeEvalTools:d,runId:p,orgId:u,envName:h,urlOverride:m,customHeaders:g,testInputs:f,variables:y,analytics:x,resolvedEnv:S,noReport:E,retriesOverride:v,billingReporter:C,devicePixelRatio:I,logUpdate:M}=n,P=new Date,U=new Do(r,o),H={ai:r.config.ai},ne={envName:h,urlOverride:m,customHeaders:g,testInputs:f};p&&(y[_g]=p);let N=W.child({testId:e.id,runId:p,orgId:u}),L=x.child({test_id:e.id,test_name:e.name,label_names:e.labels,run_id:p});L.track({type:"execution:test_start"});let k=async Ae=>{if(!(E||!p))try{await i.updateRun(p,Ae)}catch(Dt){N.warn({err:Dt},`Failed to update run status for test ${p}. You may see stale data on the Momentic Cloud Server.`)}},z,Re=Math.abs(v??r.config.retries??e.retries??0),te=[];N.info("Starting test run using CLI");for(let Ae=0;Ae<=Re;Ae++){let Dt=fO(),Q={...e,steps:iw(t)};if(!E&&p)try{Dt=(await i.createRunAttempt(p)).id}catch(Ue){throw N.error({err:Ue,testId:Q.id,runId:p},"Error creating run attempt on Momentic Cloud"),new Error("Error creating run attempt on Momentic Cloud. Please ensure Momentic is accessible from your environment or pass the --no-report flag.")}Ae!==0&&M("RETRY",`attempt ${Ae+1}/${Re+1}`);let Qe=async Ue=>{if(!(E||!p))try{await i.updateRunAttempt(p,Dt,Ue)}catch(Pt){N.warn({err:Pt},`Failed to update run attempt for attempt ${Dt}. You may see stale data on the Momentic Cloud Server.`)}},it=new Date;try{await k({status:"RUNNING",attempts:Ae+1});let{controller:Ue,context:Pt,flagStore:j}=await tw({baseUrl:l,envName:h,apiClient:i,devicePixelRatio:I,logger:N,storageClient:c,codeEvalTools:d,test:Q,generator:a,orgId:u,variables:y,customHeaders:g,testInputs:f,localBrowserConfig:S?.browser??r.config.browser,visualDiffScreenshotStorage:U,analytics:L});z=await ow({attemptMetadata:{attemptNumber:Ae+1,orgId:u,runId:p,runAttemptId:Dt},attemptFixtures:{logger:N,storageClient:c,billingReporter:C,analytics:L,debugDataStorage:s,codeEvalTools:d,flagStore:j,apiClient:i,context:Pt,controller:Ue},attemptInputs:{test:Q,orgSettings:H,noReport:E}}),await Qe({status:z.status,finishedAt:new Date,results:z.results,beforeResults:z.beforeResults,afterResults:z.afterResults}),te.unshift(z.status);let he=await ew({orgId:u,codeEvalTools:d,logger:N,outputDefinitions:e.outputs??[],testContext:Pt}),$e=new Date,Lt=Wu(te),et=$e.getTime()-P.getTime(),ln=Ae+1;if(z.status!=="FAILED")return z.status==="PASSED"&&L.track({type:"execution:test_success",attempt_count:ln,duration_ms:et,is_flake:Lt}),await k({status:z.status,finishedAt:$e,flake:Lt}),{...z,parameters:ne,runId:p,test:Q,filePath:Q.relativeFilePath,startedAt:P,lastAttemptStartedAt:it,finishedAt:$e,attempts:ln,baseUrl:l,outputs:he};let Hr=z.failedStepResult,Jt=Hr?.message||"Unknown failure",Ho=Hr?.failureReason??Em(Jt)??"UnknownError",ir=N.child({errResult:Hr,failureReason:Ho,errorMessage:Jt,numAttempts:Re+1,name:Q.name});if(Ae<Re){ir.warn(`Retrying failed execution attempt for run ${p}: ${Jt}`);continue}ir.error(`Test failed after all exhausting attempts: ${Jt}`);let $o={errorMessage:Jt};return await k({status:"FAILED",finishedAt:$e,failureDetails:$o,failureReason:Ho}),L.track({type:"execution:test_fail",attempt_count:Ae+1,duration_ms:$e.getTime()-P.getTime(),fail_reason:$o.errorMessage}),{...z,parameters:ne,runId:p,failureDetails:$o,failureReason:Ho,test:Q,filePath:Q.relativeFilePath,startedAt:P,lastAttemptStartedAt:it,finishedAt:new Date,attempts:Ae+1,baseUrl:l,outputs:he}}catch(Ue){let Pt=`Encountered fatal platform error while running test '${Q.name}': ${Ue}`,j=new Date,he=Ae+1;N.error({err:Ue},Pt),w.error(Pt);let $e={errorMessage:Ue.message,errStack:Ue.stack},Lt={status:"FAILED",failureDetails:$e,failureReason:"InternalPlatformError",finishedAt:j};return await Promise.all([Qe(Lt),k(Lt)]),L.track({type:"execution:test_fail",attempt_count:he,duration_ms:j.getTime()-P.getTime(),fail_reason:$e.errorMessage}),{...Lt,parameters:ne,runId:p,test:Q,filePath:Q.relativeFilePath,startedAt:P,lastAttemptStartedAt:it,finishedAt:new Date,attempts:he,baseUrl:l,outputs:{}}}}throw new Error("This code should not be reachable")}function bO(n){for(let e of n.envs??[])if(e.default)return e.name}async function aw(n){let{tests:e,yes:t,start:r,waitOn:o,client:i,debugDataStorage:s,billingReporter:a,analytics:l,project:c,report:d,retriesOverride:p,urlOverride:u,envName:h,orgId:m,devicePixelRatio:g,customHeaders:f,testInputMatrix:y,reporter:x,include:S,exclude:E,labels:v,reporterDir:C="reports",waitOnTimeout:I=60,parallel:M=c.config.parallel??1,shardIndex:P=1,shardCount:U=1,regenerateGoldenFiles:H}=n;r&&(W.info({orgId:m},`Executing start command: ${r}`),await cb(r,!1)),o&&(W.info({orgId:m},`Waiting for url: ${o} with timeout: ${I} seconds.`),await vO({resources:[o],interval:2500,timeout:I*1e3,headers:{Accept:"*/*"},followRedirect:!0,verbose:!1,log:!0,strictSSL:!1}));let ne=new ao({baseUrl:i.baseUrl,apiKey:i.apiKey,logger:W}),N=Ze(c,w),L=await Qb({tests:e,momenticFiles:N,yes:t,project:c,include:S,exclude:E,labels:v}),k=[];L.forEach((he,$e)=>{y?y.forEach((Lt,et)=>{k.push({testIndex:$e,inputs:Lt,inputIndex:et})}):k.push({testIndex:$e,inputs:void 0,inputIndex:void 0})}),U&&U>1&&(k=xO(k,P,U));let z=`Running ${k.length} tests with ${M} workers`;W.info({allTestsToRunWithInputs:k,shardCount:U,shardIndex:P,orgId:m},z),w.dimmed(z),k.forEach(he=>{w.dimmed(` - ${[L[he.testIndex].relativeFilePath]}${typeof he.inputIndex=="number"?` with input set ${he.inputIndex}`:""}`)}),w.log("");let Re=[],te=Date.now(),Ae=new Set,Dt=()=>{let he=i.getAppUrl(),$e=zo({results:Re,startTime:te,onFailed:et=>{bl(et,et.filePath)},getDisplayLine:et=>{let ln=` - ${et.filePath}${et.proposedTest?" [AUTO-HEALED] ":""}`;return d&&et.runId&&(ln+=` (${he}/runs/${et.runId})`),ln},entity:"test"}),Lt=Re.filter(et=>!!et.proposedTest);return Lt.length>0&&w.warn(`${Lt.length} tests passed with auto-healing. Please use the run links printed above to review proposed changes and apply them locally.`),$e},Q=await Zb(w),Qe=l.child({commit_sha:Q?.gitCommitSha,commit_sha_short:Q?.gitCommitShaShort,branch_name:Q?.gitBranchName,origin_url:Q?.gitOriginUrl,commit_timestamp:Q?.gitCommitTimestamp,github_repository:Q?.githubRepository,gitlab_project_path:Q?.gitlabProjectPath,pipeline_id:Q?.pipelineId}),it;d&&(it=(await i.createRunGroup({...Q,trigger:Bt.CLI,startedAt:new Date,status:"RUNNING",cliVersion:Tn})).id);let Ue=[],Pt=async()=>{w.warn("SIGINT received. Stopping tests and printing latest results..."),d&&it&&await i.updateRunGroup(it,{status:"CANCELLED",finishedAt:new Date}),Dt(),await Promise.allSettled(Ue.map(he=>he())),process.exit(1)};process.on("SIGINT",Pt);let j={};for(let he=0;he<k.length;he++){let $e=Object.values(j);$e.length===M&&await Promise.race($e.map(ln=>ln.promise));let Lt=k[he],et=`test-${he}`;j[et]={done:!1,promise:(async({testIndex:ln,inputs:Hr})=>{let Jt=L[ln];Ae.add({testIndex:ln,inputs:Hr});let Ho=Jt.relativeFilePath.includes("..")?Jt.fullFilePath:Jt.relativeFilePath,ir=(st,Wo)=>{let $r=(Tl,kp)=>{let pw=Math.floor((kp-Tl.length)/2);return Tl.padStart(pw+Tl.length).padEnd(kp)};st=st.toUpperCase();let En=st;st.includes("FAIL")?En=jo.bgRed.white($r("FAIL",8)):st.includes("PASS")?En=jo.bgGreen.white($r("PASS",8)):st.includes("START")?En=jo.bgBlue.white($r("START",8)):st.includes("CANCEL")?En=jo.bgYellow.white($r("CANCEL",8)):st.includes("RETRY")?En=jo.bgYellow.white($r("RETRY",8)):st.includes("RUN")||st.includes("PROG")?En=jo.bgMagenta.white($r("RUNNING",8)):W.warn(`Unknown status tried to be logged in run test locally: ${st}`),wO||(En=`[${En}]`),w.log(`${En} ${Ho} ${Wo?`${Wo} `:""}(${Ae.size}/${k.length})`)};ir("START");let $o=setInterval(()=>ir("RUN"),5*60*1e3);try{let st=await sw({testDefinition:Jt,project:c,testInputs:Hr,orgId:m,devicePixelRatio:g,apiClient:i,debugDataStorage:s,billingReporter:a,analytics:Qe,generator:ne,retriesOverride:p,urlOverride:u,envName:h,noReport:!d,customHeaders:f,regenerateGoldenFiles:H,logUpdate:ir,runGroupId:it,runSigIntHandlers:d?Ue:void 0});ir(st.status),Re.push(st)}catch(st){let Wo=`Encountered unexpected fatal error when running test '${Jt.name}': ${st.message}`;w.error(Wo),W.error(Wo)}finally{clearInterval($o),j[et].done=!0,delete j[et]}})(Lt)}}if(await Promise.allSettled(Object.values(j).map(he=>he.promise)),d&&it){let he="PASSED";Re.some($e=>$e.status==="FAILED")&&(he="FAILED"),await i.updateRunGroup(it,{status:he,finishedAt:new Date})}return process.off("SIGINT",Pt),x&&await Jb(W,s,x,{suiteName:c.config.name,startedAt:new Date(te),finishedAt:new Date,runs:Re},C??"reports"),Dt()}function xO(n,e,t){if(t>n.length&&(w.warn(`Shard count ${t} is greater than the number of tests ${n.length}! Some workers won't have any tests to run.`),t=Math.max(t,n.length),e>t))return[];let r=Math.floor((e-1)*(n.length/t)),o=Math.floor(e*(n.length/t));return n.sort().filter((s,a)=>a>=r&&a<o)}var lw=new Uo({platform:"cli"},{flushAt:1,flushInterval:0});Cl||W.warn("Sentry is not enabled in this environment due to unsupported node version");var IO=process.argv.some(n=>n.includes("--log-level"))&&process.argv.some(n=>n.includes("debug")),Op=n=>{IO&&w.dimmed(n)},It=new TO;It.name("momentic").description("CLI").version(Tn||"unknown");It.command("install-browsers").option("-f, --force","Force reinstallation even if the browser executables already exist on disk.").argument("[browsers...]","Browsers to install",["chromium"]).action(async(n,e)=>{await jr(zb(n),e.force)});It.addOption(new Xt("--log-level <level>").choices(["debug","info","warn","error"]).default("info")).on("option:log-level",n=>{n==="debug"&&w.info("Enabling debug logging"),w.setMinLevel(n.toLowerCase())});It.addOption(new Xt("--verbose","enable verbose logging")).on("option:verbose",()=>{W.enableConsoleLogs(),w.setMinLevel(0)});It.command("check-config").addOption(Br).action(async n=>{xn({configFilePath:n.config})});var PO=It.command("checks").alias("check").description("Perform various project checks");PO.command("duplicate-names").description("Checks if tests or modules with duplicate names or ids exist").addOption(Br).action(async n=>{let e=xn({configFilePath:n.config}),t=Ze(e,w),r=Object.values(t.tests),o=await La(t,W),i={},s={},a={},l={},c=new Set,d=new Set,p=new Set,u=new Set;for(let m of r)i[m.id]?a[m.id]?a[m.id].push(m.fullFilePath):a[m.id]=[i[m.id],m.fullFilePath]:i[m.id]=m.fullFilePath,s[m.name]?l[m.name]?l[m.name].push(m.fullFilePath):l[m.name]=[s[m.name],m.fullFilePath]:s[m.name]=m.fullFilePath;for(let m of o)u.has(m.moduleId)&&c.add(m.moduleId),p.has(m.name)&&d.add(m.name),u.add(m.moduleId),p.add(m.name);if(Object.entries(a).length>0||Object.entries(l).length>0||c.size>0||d.size>0){for(let[m,g]of Object.entries(a))for(let f of g)w.error(`Test ID ${m} is duplicated in ${f}`);for(let[m,g]of Object.entries(l))for(let f of g)w.error(`Test Name ${m} is duplicated in ${f}`);for(let m of c)w.error(`Module id ${m} is duplicated in your project`);for(let m of d)w.error(`Module name ${m} is duplicated in your project`);process.exit(1)}w.success("No duplicates detected. All test and module names and ids are unique."),process.exit(0)});var LO=It.command("migrate").description("Migrate and upgrade tooling");LO.command("v0-v1").addOption(rr).addOption(nr).action(async n=>{let e=await or(n);if(!e.yes&&!await ot("This command will migrate and then delete your previous Momentic files. All members of your team should transition to the V1 CLI at the same time. Please backup your local directory for safety before proceeding. Continue?",!0)&&process.exit(1),!vb().environments?.length&&await ot("In the V1 CLI, all environment configuration should be committed to a central `momentic.config.yaml` file, which you should commit to your source control repository. Sensitive data can be managed through `.env` files or by injecting the secret into the environment and then referencing them in `envVariables` with ${VAR} syntax. Would you like to pull the latest environments from Momentic Cloud into your `momentic.config.yaml` file?",!0)){let{apiKey:r,server:o}=e,i=xn({}),s=new dt({baseUrl:o,apiKey:r,logger:W});await cl({client:s,project:i,skipPrompts:e.yes}),w.success("Successfully imported environments from Momentic Cloud.")}});It.command("import-from-cloud").addOption(nr).addOption(zr).addOption(Br).addOption(rr).action(async n=>{let e=await or(n),{apiKey:t,server:r,config:o,yes:i}=e,s=xn({configFilePath:o}),a=new dt({baseUrl:r,apiKey:t,logger:W});await wb({client:a,project:s,skipPrompts:i}),process.exit(0)});It.command("init").description("Initialize an empty Momentic project in the current working directory").addOption(new Xt("--name <name>","Name of the project")).action(async n=>{w.info(`Welcome to the Momentic project setup wizard! \u{1F680}
3257
- `),w.info("This wizard will help you bootstrap a new Momentic project. If you need to import existing assets from Momentic Cloud, you can call the 'import-from-cloud' command after initialization."),cw.existsSync(Mo)&&(w.error("A momentic.config.yaml file already exists in this directory. Please rename or remove it to initialize a new project."),process.exit(1));let t={name:n.name??await hb("Choose an identifier for your project, such as a service, product, or team name (default: 'app'):","app"),include:Ji};No(t,Mo),w.success(`Initialized Momentic project file at ${xl.resolve(Mo)}`)});It.command("app").addOption(nr).addOption(zr).addOption(rr).addOption(xp).addOption(Lb).addOption(Br).action(async n=>{let e=await or(n),{apiKey:t,port:r=pl,yes:o,server:i,pixelRatio:s}=e,a=new dt({baseUrl:i,apiKey:t,logger:W});await Rp({client:a,skipPrompts:o,installBrowsers:!0});let l=AO(import.meta.url),c=xl.dirname(l),d=xl.resolve(c,"..","static"),p=xl.resolve(c,"..","assets"),u=s??mp();hp(u),Go(),await ib({momenticServerUrl:i,apiKey:t,serverPort:r,appPort:r,staticDir:d,assetsDir:p,devicePixelRatio:u,version:"1.0.104"});let h=`http://localhost:${r}`;await RO(h)});var dw=It.command("queue").description("Queue tests or suites to run on Momentic Cloud");dw.command("suites").description("Run one or more suites on Momentic Cloud").addOption(nr).addOption(zr).addOption(bp).addOption(wp).addOption(rr).addArgument(_b).addOption(hl).addOption(ml).addOption(ul).action(async(n,e)=>{let{apiKey:t,server:r,wait:o,waitTimeout:i,env:s,urlOverride:a,customHeaders:l}=await or(e),c=new dt({baseUrl:r,apiKey:t,logger:W});(!n||!Array.isArray(n)||!n.length)&&(w.error("Must pass at least one suite to run."),process.exit(1));let{orgId:d}=await c.getAuthInfo();await Wb({client:c,orgId:d,wait:o,suitePaths:n,waitTimeout:i,env:s,urlOverride:a,customHeaders:gl(l)})});dw.command("tests").description("Run one or more tests on Momentic Cloud").addOption(nr).addOption(zr).addOption(rr).addOption(ul).addOption(Tp).addOption(hl).addOption(ml).addOption(bp).addOption(wp).addArgument(Mb).action(async(n,e)=>{let t=await or(e),{all:r,apiKey:o,customHeaders:i,env:s,server:a,inputCsv:l,urlOverride:c,wait:d,waitTimeout:p,yes:u}=t,h=gl(i);for(let y of n)(y.endsWith(".yaml")||cw.existsSync(y))&&w.warn("Are you trying to run a test on your local machine? If so, please use the 'run' command instead of the 'queue' command");let m=new dt({baseUrl:a,apiKey:o,logger:W}),{orgId:g}=await m.getAuthInfo(),f;l&&(f=await gp(l)),await Gb({client:m,orgId:g,tests:n,all:r,customHeaders:h,env:s,urlOverride:c,wait:d,waitTimeout:p,testInputMatrix:f,yes:u}),process.exit(0)});var OO=It.command("run").alias("test").description("Run tests on the local machine");OO.addOption(nr).addOption(zr).addOption(Br).addOption(Ep).addOption(rr).addOption(ul).addOption(Tp).addOption(ml).addOption(hl).addOption(vp).addOption(xp).addOption(new Xt("--start <start>","Arbitrary setup command that will run before Momentic steps begin.")).addOption(new Xt("--wait-on <waitOn>","URL to wait to become accessible before Momentic tests begin.")).addOption(new Xt("--wait-on-timeout <waitOnTimeout>","Max time in seconds to wait for the --wait-on URL to become accessible.")).addOption(new Xt("--retries <retries>","Number of retries to attempt when running tests locally. Defaults to each test's own retry configuration.")).addOption(new Xt("-p, --parallel <parallel>","When running with the --local flag, the number of tests to run in parallel. Defaults to 1.")).addOption(new Xt("--labels <labels...>","Only run tests with the specified label(s).")).addOption(new Xt("--update-golden-files","Update locally stored golden files for steps that this is enabled for.")).addOption(vp).addOption(Cb).addOption(Rb).addOption(Ab).addOption(Ob).addOption(kb).addOption(Ib).addOption(Pb).addArgument(Nb).action(async(n,e)=>{w.debug("Run tests invoked");let t=await or(e);w.debug(t,"Parsed CLI options");let r=gl(t.customHeaders),o=xn({configFilePath:t.config,nameFilter:t.filter});w.debug({projectName:o.config.name},"Identified project config");let i=new dt({baseUrl:t.server,apiKey:t.apiKey,logger:W}),s=new Js(i),a,l=t.outputDir??o.config.outputDir;if(l){let m=new il(l);a=new ol(m)}else a=new Zs(i);let c;t.inputCsv&&(c=await gp(t.inputCsv)),w.debug("Checking API key and dependencies");let{orgId:d,userId:p}=await Rp({client:i,skipPrompts:t.yes,installBrowsers:!0});w.debug("API key check and browser installation complete");let u=t.pixelRatio??mp();hp(u);let h=lw.child({org_id:d,user_id:p,cli_version:Tn,trigger:"CLI"});t.report||(h=new rl);try{(await aw({...t,retriesOverride:t.retries,devicePixelRatio:u,tests:n,project:o,client:i,debugDataStorage:a,billingReporter:s,analytics:h,customHeaders:r,envName:t.env,orgId:d,testInputMatrix:c,regenerateGoldenFiles:t.updateGoldenFiles})).failed>0?process.exit(1):process.exit(0)}catch(m){w.error("Failed to run tests locally. Please check the error message below or run with the --verbose flag."),w.error(m),process.exit(1)}});var kO=It.command("apply").description("Apply an operation to local resources");kO.command("patch").addOption(nr).addOption(zr).addOption(Br).addOption(Ep).addOption(rr).addOption(new Xt("--from <from>","Name or ID of the patch to apply").makeOptionMandatory()).addOption(new Xt("--to <to>","Name or ID of the test to apply the patch to").makeOptionMandatory()).action(async n=>{let e=await or(n),{apiKey:t,server:r,config:o,yes:i}=e,s=xn({configFilePath:o}),a=new dt({baseUrl:r,apiKey:t,logger:W}),l=Ze(s,w),c=l.tests[n.to]??Object.values(l.tests).find(p=>ke(p.name)===n.to.trim());c||(w.error(`No test matching '${n.to}' could be found in the current project.`),process.exit(1));let d=await a.fetchTestFragment(n.from);await gb({client:a,test:c,fragment:d,yes:i,entities:l,logger:W}),process.exit(0)});async function MO(){Op("Main program started"),await db(W),Op("CLI version check complete");try{await It.parseAsync(process.argv),Go()}catch(n){let e={};try{e.playwrightVersion=EO("npx playwright --version").toString()}catch(t){W.error({err:t},"Error fetching debug information")}W.error({err:n,debugInfo:e},"Uncaught error in CLI"),W.flush(),El(n,e),w.error(n),Go(),process.exit(1)}}CO.setMaxListeners(50);process.on("warning",n=>{W.warn({err:n},`Node warning received on CLI: ${n.message}`)});Op("CLI parsing setup complete");MO();
3257
+ `),w.info("This wizard will help you bootstrap a new Momentic project. If you need to import existing assets from Momentic Cloud, you can call the 'import-from-cloud' command after initialization."),cw.existsSync(Mo)&&(w.error("A momentic.config.yaml file already exists in this directory. Please rename or remove it to initialize a new project."),process.exit(1));let t={name:n.name??await hb("Choose an identifier for your project, such as a service, product, or team name (default: 'app'):","app"),include:Ji};No(t,Mo),w.success(`Initialized Momentic project file at ${xl.resolve(Mo)}`)});It.command("app").addOption(nr).addOption(zr).addOption(rr).addOption(xp).addOption(Lb).addOption(Br).action(async n=>{let e=await or(n),{apiKey:t,port:r=pl,yes:o,server:i,pixelRatio:s}=e,a=new dt({baseUrl:i,apiKey:t,logger:W});await Rp({client:a,skipPrompts:o,installBrowsers:!0});let l=AO(import.meta.url),c=xl.dirname(l),d=xl.resolve(c,"..","static"),p=xl.resolve(c,"..","assets"),u=s??mp();hp(u),Go(),await ib({momenticServerUrl:i,apiKey:t,serverPort:r,appPort:r,staticDir:d,assetsDir:p,devicePixelRatio:u,version:"1.0.105"});let h=`http://localhost:${r}`;await RO(h)});var dw=It.command("queue").description("Queue tests or suites to run on Momentic Cloud");dw.command("suites").description("Run one or more suites on Momentic Cloud").addOption(nr).addOption(zr).addOption(bp).addOption(wp).addOption(rr).addArgument(_b).addOption(hl).addOption(ml).addOption(ul).action(async(n,e)=>{let{apiKey:t,server:r,wait:o,waitTimeout:i,env:s,urlOverride:a,customHeaders:l}=await or(e),c=new dt({baseUrl:r,apiKey:t,logger:W});(!n||!Array.isArray(n)||!n.length)&&(w.error("Must pass at least one suite to run."),process.exit(1));let{orgId:d}=await c.getAuthInfo();await Wb({client:c,orgId:d,wait:o,suitePaths:n,waitTimeout:i,env:s,urlOverride:a,customHeaders:gl(l)})});dw.command("tests").description("Run one or more tests on Momentic Cloud").addOption(nr).addOption(zr).addOption(rr).addOption(ul).addOption(Tp).addOption(hl).addOption(ml).addOption(bp).addOption(wp).addArgument(Mb).action(async(n,e)=>{let t=await or(e),{all:r,apiKey:o,customHeaders:i,env:s,server:a,inputCsv:l,urlOverride:c,wait:d,waitTimeout:p,yes:u}=t,h=gl(i);for(let y of n)(y.endsWith(".yaml")||cw.existsSync(y))&&w.warn("Are you trying to run a test on your local machine? If so, please use the 'run' command instead of the 'queue' command");let m=new dt({baseUrl:a,apiKey:o,logger:W}),{orgId:g}=await m.getAuthInfo(),f;l&&(f=await gp(l)),await Gb({client:m,orgId:g,tests:n,all:r,customHeaders:h,env:s,urlOverride:c,wait:d,waitTimeout:p,testInputMatrix:f,yes:u}),process.exit(0)});var OO=It.command("run").alias("test").description("Run tests on the local machine");OO.addOption(nr).addOption(zr).addOption(Br).addOption(Ep).addOption(rr).addOption(ul).addOption(Tp).addOption(ml).addOption(hl).addOption(vp).addOption(xp).addOption(new Xt("--start <start>","Arbitrary setup command that will run before Momentic steps begin.")).addOption(new Xt("--wait-on <waitOn>","URL to wait to become accessible before Momentic tests begin.")).addOption(new Xt("--wait-on-timeout <waitOnTimeout>","Max time in seconds to wait for the --wait-on URL to become accessible.")).addOption(new Xt("--retries <retries>","Number of retries to attempt when running tests locally. Defaults to each test's own retry configuration.")).addOption(new Xt("-p, --parallel <parallel>","When running with the --local flag, the number of tests to run in parallel. Defaults to 1.")).addOption(new Xt("--labels <labels...>","Only run tests with the specified label(s).")).addOption(new Xt("--update-golden-files","Update locally stored golden files for steps that this is enabled for.")).addOption(vp).addOption(Cb).addOption(Rb).addOption(Ab).addOption(Ob).addOption(kb).addOption(Ib).addOption(Pb).addArgument(Nb).action(async(n,e)=>{w.debug("Run tests invoked");let t=await or(e);w.debug(t,"Parsed CLI options");let r=gl(t.customHeaders),o=xn({configFilePath:t.config,nameFilter:t.filter});w.debug({projectName:o.config.name},"Identified project config");let i=new dt({baseUrl:t.server,apiKey:t.apiKey,logger:W}),s=new Js(i),a,l=t.outputDir??o.config.outputDir;if(l){let m=new il(l);a=new ol(m)}else a=new Zs(i);let c;t.inputCsv&&(c=await gp(t.inputCsv)),w.debug("Checking API key and dependencies");let{orgId:d,userId:p}=await Rp({client:i,skipPrompts:t.yes,installBrowsers:!0});w.debug("API key check and browser installation complete");let u=t.pixelRatio??mp();hp(u);let h=lw.child({org_id:d,user_id:p,cli_version:Tn,trigger:"CLI"});t.report||(h=new rl);try{(await aw({...t,retriesOverride:t.retries,devicePixelRatio:u,tests:n,project:o,client:i,debugDataStorage:a,billingReporter:s,analytics:h,customHeaders:r,envName:t.env,orgId:d,testInputMatrix:c,regenerateGoldenFiles:t.updateGoldenFiles})).failed>0?process.exit(1):process.exit(0)}catch(m){w.error("Failed to run tests locally. Please check the error message below or run with the --verbose flag."),w.error(m),process.exit(1)}});var kO=It.command("apply").description("Apply an operation to local resources");kO.command("patch").addOption(nr).addOption(zr).addOption(Br).addOption(Ep).addOption(rr).addOption(new Xt("--from <from>","Name or ID of the patch to apply").makeOptionMandatory()).addOption(new Xt("--to <to>","Name or ID of the test to apply the patch to").makeOptionMandatory()).action(async n=>{let e=await or(n),{apiKey:t,server:r,config:o,yes:i}=e,s=xn({configFilePath:o}),a=new dt({baseUrl:r,apiKey:t,logger:W}),l=Ze(s,w),c=l.tests[n.to]??Object.values(l.tests).find(p=>ke(p.name)===n.to.trim());c||(w.error(`No test matching '${n.to}' could be found in the current project.`),process.exit(1));let d=await a.fetchTestFragment(n.from);await gb({client:a,test:c,fragment:d,yes:i,entities:l,logger:W}),process.exit(0)});async function MO(){Op("Main program started"),await db(W),Op("CLI version check complete");try{await It.parseAsync(process.argv),Go()}catch(n){let e={};try{e.playwrightVersion=EO("npx playwright --version").toString()}catch(t){W.error({err:t},"Error fetching debug information")}W.error({err:n,debugInfo:e},"Uncaught error in CLI"),W.flush(),El(n,e),w.error(n),Go(),process.exit(1)}}CO.setMaxListeners(50);process.on("warning",n=>{W.warn({err:n},`Node warning received on CLI: ${n.message}`)});Op("CLI parsing setup complete");MO();
3258
3258
  //# sourceMappingURL=cli.js.map
3259
3259
  //# debugId=c2c51c7c-b6fe-5e83-8bbe-97452d2717e7
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "momentic",
3
- "version": "1.0.104",
3
+ "version": "1.0.105",
4
4
  "bin": {
5
5
  "momentic": "./bin/cli.js"
6
6
  },