UNPKG

@openfin/automation-cli

Version:

CLI for running automation tests within the OpenFin ecosystem

1 lines â€ĸ 22.8 kB
"use strict";var e=require("commander"),t=require("crypto"),o=require("fs/promises"),r=require("glob"),n=require("path"),i=require("ts-node"),s=require("chalk"),a=require("jasmine"),l=require("jasmine-spec-reporter"),c=require("child_process"),u=require("http"),f=require("os"),p=require("jest"),h=require("mocha"),d=require("@openfin/automation-helpers"),m=require("jszip"),w=require("xml2js");function g(e){var t=Object.create(null);return e&&Object.keys(e).forEach((function(o){if("default"!==o){var r=Object.getOwnPropertyDescriptor(e,o);Object.defineProperty(t,o,r.get?r:{enumerable:!0,get:function(){return e[o]}})}})),t.default=e,Object.freeze(t)}var v=g(i);let b="all";function y(e){"all"===b&&console.log(`🚀 ${s.blue(s.underline(e))}`)}function _(){"all"===b&&console.log()}function O(e){"all"===b&&console.log(s.gray.italic(e))}function T(e){"all"===b&&console.log(`❗ ${s.red(e)}`)}function $(e,t){"all"===b&&(console.log("_______________________________________________________"),console.log(),console.log(`âžĄī¸ ${s.cyan(e)}`),void 0!==t&&console.log(` ${s.gray(t)}`),console.log())}function F(e,t){"all"===b&&(void 0===t?console.log(` âš™ī¸ ${s.green(e)}`):console.log(` âš™ī¸ ${s.green(e)}`,s.gray(t)))}function D(e,t){"all"===b&&(void 0===t?console.log(` ✅ ${s.cyan(e)}`):console.log(` ✅ ${s.cyan(e)}`,s.gray(t)))}async function E(e){try{return(await o.stat(e)).isFile()}catch{return!1}}async function S(e){try{return(await o.stat(e)).isDirectory()}catch{return!1}}async function k(e,t){C()?await V("taskkill",[t?"/F":"","/IM",e,"/T"],void 0,!0,!0):P()&&await V("sh",["-c",`pgrep ${e} | xargs kill -s ${t?"SIGKILL":"SIGINT"}`],void 0,!0,!0)}function R(e){return e instanceof Error}function x(e,t,o,r,n,i){const s=c.spawn(e,t,o);let a="",l="",u="",f="";return s.stdout.setEncoding("utf8"),s.stdout.on("data",(e=>{if(i&&(u+=e.toString()),!r){a+=e.toString();const t=a.split("\n");a=t.pop()??"";for(const e of t)O(e)}})),s.stderr.setEncoding("utf8"),s.stderr.on("data",(e=>{if(i&&(f+=e.toString()),!n){l+=e.toString();const t=l.split("\n");l=t.pop()??"";for(const e of t)T(e)}})),s.on("close",(()=>{a.trim().length>0&&O(a),l.trim().length>0&&T(l),i&&i(u,f)})),s}async function V(e,t,o,r,n){return new Promise((i=>{x(e,t,o,r,n,((e,t)=>{i({output:e,error:t})}))}))}async function j(e){return new Promise((t=>setTimeout(t,e)))}function C(){return"win32"===f.platform()}function P(){return"darwin"===f.platform()}function I(){if(C()){if(!process.env.LocalAppData)throw new Error("LocalAppData is not set in environment");return process.env.LocalAppData}if(P()){if(!process.env.HOME)throw new Error("HOME is not set in environment");return n.join(process.env.HOME,"Applications")}throw new Error(`Can not get application folder on this platform ${f.platform()}`)}async function M(e){let t;if(e)try{t=require(e)}catch(o){try{t=await import(e)}catch(e){if(o instanceof Error&&e instanceof Error&&o.message===e.message)throw new Error(`Error loading hooks module\n${o.message}`);throw new Error(`Error loading hooks module\nLoading as CJS failed with ${o}\nLoading as ESM failed with ${e}`)}}return t}async function N(e,t){const o=u.createServer(((e,o)=>{try{if("GET"===e.method){const r={},n=t[e.url??""];n?.mime&&(r["Content-Type"]=n?.mime),o.writeHead(n?.data?200:404,r),o.end(n?.data??"Not found")}else o.writeHead(400),o.end(Buffer.from("Not supported","utf8"))}catch(e){o.writeHead(400),e instanceof Error?o.end(Buffer.from(e.message,"utf8")):o.end(Buffer.from(JSON.stringify(e),"utf8"))}}));return $("Web Server"),D("Starting",e),new Promise((t=>{o.listen(e,(()=>{D("Started",e),t(o)}))}))}const U="https://chromedriver.storage.googleapis.com/",A="https://googlechromelabs.github.io/chrome-for-testing/latest-versions-per-milestone-with-downloads.json";async function W(e,t,r,i,s,a,l,c){let u,p,h;const w=[],g=I();if(C()){let e=g;if(!e)throw new Error("LocalAppData is not set in environment");if(e=n.join(e,"OpenFin"),c){if(!await E(c))throw new Error("The DOS file specified does not exist")}C()&&(p=await async function(e,t,r){let i,s="";if(C()){if($("Querying Desktop Owner Settings"),!r){const r=f.tmpdir();i=n.join(r,`openfin-dos-${Date.now()}.json`);const s={};"stable"!==e&&(s.workspace={version:e}),"stable"!==t&&(s["notification-center"]={version:t});const a={desktopSettings:{securedAPIDefaultPermission:"allow",systemApps:s}};D("Writing Temporary Desktop Owner Settings",i),await o.writeFile(i,JSON.stringify(a))}const{output:a}=await V('reg query "HKCU\\Software\\OpenFin\\RVM\\Settings" -v "DesktopOwnerSettings"',void 0,{shell:!0},!0,!0);if(await L(`file:\\\\\\${i??r}`),a.length>0){const e=a.split("\n");for(const t of e)if(t.includes("DesktopOwnerSettings")){s=t.replace("DesktopOwnerSettings","").replace("REG_SZ","").trim(),D("Current Desktop Owner Setting",s);break}}}return{currentRegValue:s,tempDosFile:i}}(a,l,c)),h=n.join(e,"OpenFinRVM.exe"),u=await E(h),w.push(h)}else{if(!P())throw new Error(`Platform ${f.platform()} is not currently supported`);h=n.join(g,"OpenFinRVM.app"),u=await S(h),w.push("open"),w.push(h),w.push("--args")}if(!u){if(i)throw new Error(`offline mode is enabled but the OpenFinRVM can not be found at "${h}", make sure the OpenFin runtime is already available`);await async function(e){let t;$("Downloading the OpenFinRVM");try{const r=C()?`https://install.openfin.co/download/?os=win&config=${encodeURI(e)}&dnl=true`:"http://cdn.openfin.co/release/rvm/mac/latest",i=await d.fetchBuffer(r),s=new m,a=await s.loadAsync(i);if(C()){const e=f.tmpdir();t=n.join(e,`openfin-installer-${Date.now()}`),D("Creating temp installer dir",t),await o.mkdir(t,{recursive:!0});const r="openfin-installer.exe",i=n.join(t,r);D("Unzipping OpenFinRVM Installer",i);const s=a.file(r);if(!s)throw new Error(`The zip file does not contain ${r}`);const l=await s.async("nodebuffer");await o.writeFile(i,l),D("Installing OpenFinRVM",i),await V(i)}else if(P()){D(`Unzipping ${"OpenFinRVM.app"} to Applications`);const e=[];a.forEach(((t,o)=>{e.push({relativePath:t,fileEntry:o})}));const t=I();for(const r of e){const e=n.resolve(n.join(t,r.relativePath));D(`Unzipping ${e} to Applications`);const i=await r.fileEntry.async("nodebuffer");if(r.fileEntry.dir)try{await o.mkdir(e,{recursive:!0})}catch{}else await o.writeFile(e,i),r.fileEntry.unixPermissions&&await o.chmod(e,r.fileEntry.unixPermissions)}}}finally{t&&await o.rm(t,{recursive:!0})}}(e)}let v;if(w.push(`--config=${e.replace("file://","")}`),w.push(`--runtime-arguments="--remote-debugging-port=${t}"`),"none"!==e){$("Running OpenFin",h),D("Args",w.slice(-2));const e=x(w[0],w.slice(1),{shell:!0});v=e?.pid,D("OpenFinRVM Process",v)}return{processId:v,currentRegValue:p?.currentRegValue,tempDosFile:p?.tempDosFile,openFinRVMPath:h}}async function L(e){C()&&(0===e.length?(D("Deleting Desktop Owner Setting",e),await V('Echo Y|reg delete "HKCU\\Software\\OpenFin\\RVM\\Settings" /v "DesktopOwnerSettings"',void 0,{shell:!0},!0)):(D("Setting Desktop Owner Setting",e),await V(`Echo Y|reg add "HKCU\\Software\\OpenFin\\RVM\\Settings" -v "DesktopOwnerSettings" -d "${e}"`,void 0,{shell:!0},!0)))}async function q(e){e?D("Cleaning up OpenFin instances"):$("Cleaning up OpenFin instances");const t=["OpenFinRVM","OpenFinRVM.exe","OpenFinRVM.app","OpenFin","OpenFin.exe","OpenFin.app"];for(const e of t)await k(e,!1);for(const e of t)await k(e,!0);D("Cleanup instances complete")}async function H(e,t,r){$("Get Chrome Driver",`Version ${e}`);const i=n.join(t,"chromedriver",e);try{await o.mkdir(i,{recursive:!0})}catch(e){if(!R(e)||"EEXIST"!==e.code)throw e}const s=C()?"chromedriver.exe":"chromedriver",a=n.resolve(n.join(i,s));if(await E(a))D("Chrome Driver already exists",a);else{if(r)throw new Error(`offline mode is set but no chromedriver exists in ${a}, you must manually download and store the correct version from ${U} or ${A}`);let t;t=Number.parseInt(e,10)<115?await async function(e){const t=C()?"win32":"mac64";D("Fetching Chrome Driver Version manifest",U);const o=await d.fetchText(U),r=new w.Parser,n=await r.parseStringPromise(o),i=n?.ListBucketResult?.Contents.filter((o=>o.Key[0].startsWith(e)&&o.Key[0].endsWith(`/chromedriver_${t}.zip`)));if(!i||0===i.length)throw new Error(`Can not find chromedriver for version ${e}`);let s=i[0];if(i.length>1){let e=0;for(const t of i){const o=Number.parseInt(t.Key[0].split(".")[3],10);o>e&&(e=o,s=t)}}const a=C()?"chromedriver.exe":"chromedriver";return{url:`${U}${s.Key}`,filename:a}}(e):await async function(e){D("Fetching Chrome Driver Version manifest",A);const t=await d.fetchJson(A),o=C()?"win32":"mac-x64";if(!t.milestones[e])throw new Error(`Retrieved version data from ${A}, but there is no milestone for version ${e}`);const r=t.milestones[e].downloads.chromedriver?.find((e=>e.platform===o));if(!r)throw new Error(`Retrieved version data from ${A}, version ${e} has no url for platform ${o}`);const n=C()?"chromedriver.exe":"chromedriver";return{url:r.url,filename:`chromedriver-${o}/${n}`}}(e),D("Fetching Chrome Driver",t.url);const n=await d.fetchBuffer(t.url);D("Unzipping Chrome Driver to",a);const i=new m,s=(await i.loadAsync(n)).file(t.filename);if(!s)throw new Error(`The zip file does not contain ${t.filename}`);const l=await s.async("nodebuffer");await o.writeFile(a,l),await o.chmod(a,"755")}return a}const J="OpenFin Automation",B="of-automation",z="1.3.1",G="./automation-hooks.",K=`${G}{js,ts}`,Y={mocha:async function(e,t,o,r,n,i,s,a){const l={};i?.reporter&&(l.reporter=i.reporter,l.reporterOptions=i.options);const c=await M(a);c&&(l.rootHooks={beforeAll:async()=>{globalThis.automation=globalThis.automation??{},globalThis.automation.globalVars={},c.setup&&await c.setup(globalThis.automation.globalVars)},afterAll:async()=>{c.teardown&&await c.teardown(globalThis.automation?.globalVars??{})},beforeEach:async()=>{c.beforeEach&&await c.beforeEach(globalThis.automation?.globalVars??{},globalThis.automation?.currentTestName??"")},afterEach:async()=>{c.afterEach&&await c.afterEach(globalThis.automation?.globalVars??{},globalThis.automation?.currentTestName??"")}});const u=new h(l);$("Running Tests using Mocha",`Version ${u.version}`),u.timeout(1e3*o);for(const e of t)u.addFile(e);if(!s){class e extends h.reporters.Base{}u.reporter(e)}return new Promise((e=>{let t=0;const o=u.run();o.on("test",(e=>{globalThis.automation=globalThis.automation??{},globalThis.automation.currentTestName=e.title})),o.on("fail",(()=>{t++,r>0&&t>=r&&(T(`Fail count ${r} reached, aborting test execution`),o.abort())})),o.on("end",(()=>{e(t>0?1:0)}))}))},jasmine:async function(e,t,o,r,n,i,s,c){const u=new a;$("Running Tests using Jasmine",`Version ${u.coreVersion()}`),u.jasmine.DEFAULT_TIMEOUT_INTERVAL=1e3*o,u.clearReporters(),s&&u.addReporter(new l.SpecReporter);const f=[];u.env.configure({specFilter:e=>(f.push(e),!0)});const p=await M(c);globalThis.automation=globalThis.automation??{},globalThis.automation.globalVars=globalThis.automation.globalVars??{};let h=0;const d={jasmineStarted:async()=>{},jasmineDone:async()=>{globalThis.automation&&p?.teardown&&await(p?.teardown(globalThis.automation.globalVars))},specStarted:async e=>{globalThis.automation&&(globalThis.automation.currentTestName=e.description,p?.beforeEach&&await(p?.beforeEach(globalThis.automation.globalVars,globalThis.automation.currentTestName)))},specDone:async e=>{if("failed"===e.status&&(h++,r>0&&h===r)){for(const e of f)e.exclude();T(`Fail count ${r} reached, aborting test execution`)}globalThis.automation&&p?.afterEach&&await p.afterEach(globalThis.automation.globalVars,globalThis.automation.currentTestName??"")}};if(u.addReporter(d),i?.reporter&&i?.module){const e=new((await import(require.resolve(i?.module)))[i?.reporter])(i?.options);e&&u.addReporter(e)}return u.loadConfig({spec_files:[e],random:!1}),u.exitOnCompletion=!1,globalThis.automation&&p?.setup&&await(p?.setup(globalThis.automation.globalVars)),"failed"===(await u.execute()).overallStatus?1:0},jest:async function(e,t,o,r,i,s,a,l){let c,u;$("Running Tests using Jest",`Version ${p.getVersion()}`),1===t.length?(u=n.resolve(n.dirname(t[0])).replace(/\\/g,"/"),c=[n.basename(t[0])]):(c=t.map((e=>n.resolve(e).replace(/\\/g,"/"))),u=function(e){const t=[];let o,r=1e3;for(const o of e){const e=o.split("/");t.push(e),e.length<r&&(r=e.length)}for(o=0;o<r;o++){if(!t.map((e=>e[o])).every((e=>e===t[0][o])))break}return t[0].slice(0,o).join("/")}(c));for(let e=0;e<c.length;e++)c[e]=`${n.dirname(c[e])}/**/*${n.basename(c[e])}`;const f={testTimeout:1e3*o,verbose:!0,runInBand:!0,reporters:a?["default"]:[],testEnvironment:n.join(__dirname,"./jestEnvironment.js"),globals:JSON.stringify({testFailCount:r,hooksFilename:l}),rootDir:u,projects:c.map((e=>({testMatch:[e.replace(u,"<rootDir>/")]})))};return i&&(f.preset="ts-jest"),s?.reporter&&f.reporters.push([s.reporter,s.options??{}]),(await p.runCLI(f,[u])).results.numFailedTests>0?1:0}};async function Q(e,t,r,n,i){$("Cleaning Up"),D("Closing Chrome Driver");try{const e=["chromedriver.exe","chromedriver"];for(const t of e)await k(t,!0)}catch{}if(r?.currentRegValue)try{D("Restoring DOS"),await L(r.currentRegValue)}catch{}if(r?.tempDosFile)try{D("Removing temporary DOS Settings"),await o.unlink(r.tempDosFile)}catch{}try{r?.processId&&n&&await q(!0)}catch{}if(t){D("Removing temp data directory",t);try{await o.rm(t,{recursive:!0})}catch{}}!function(e,t,o,r){"all"===b&&(console.log("_______________________________________________________"),console.log(),0===e?console.log(`😀 ${t}`):1===e?console.log(`â˜šī¸ ${o}`):console.log(`đŸ’Ŗ ${r}`))}(e,"Successfully ran the tests","Failed running the tests","Application terminated"),process.exit(e)}exports.directoryExists=S,exports.fileExists=E,exports.getApplicationFolder=I,exports.isLinux=function(){return"linux"===f.platform()},exports.isMacOS=P,exports.isNodeError=R,exports.isWindows=C,exports.killProcessByImage=k,exports.launchWebServer=N,exports.loadHooks=M,exports.run=async function(i){process.title=J,C()||P()||(T("The automation CLI will only run on Windows and Mac"),process.exit(1)),(new e.Command).name(B).description("Run Automation Tests using Chrome Driver with an OpenFin UI").version(z).argument("manifestUrl <string>","The url of the manifest to load, can be http|https|file|none").argument("testGlobs <string>","Globs pointing to the tests to run, semi-colon separate for additional folders").addOption(new e.Option("--logLevel <level>","The log level for the webdriver").choices(["debug","silent"]).default("silent")).option("--devToolsPort <number>","The port to run the dev tools on",(e=>Number.parseInt(e,10)),9090).option("--chromeDriverPort <number>","The port to run the chromedriver on",(e=>Number.parseInt(e,10)),4444).option("--storageFolder <path>","The path to store any downloaded or offline data","./storage/").option("--offline","In offline mode no resources are retrieved, they are expected to be in the storageFolder").option("--testTimeout <number>","The timeout in seconds for running tests",(e=>Number.parseInt(e,10)),120).option("--testFailCount <number>","Will exit the tests after given number of failures, 0 will allow all failures",(e=>Number.parseInt(e,10)),0).option("--defaultRuntimeVersion <string>","The OpenFin runtime version to use if not specified in manifest","stable").addOption(new e.Option("--framework <mocha>","The test framework to run the tests with").choices(["mocha","jasmine","jest"]).default("mocha")).addOption(new e.Option("--reporter <file.json>","Additional runner config to be passed to frameworks")).addOption(new e.Option("--driver <node>","The webdriver used to access the runtime").choices(["node","selenium"]).default("node")).addOption(new e.Option("--workspace <version>","The workspace version to use e.g. 9.2.5 for the DOS settings").default("stable")).addOption(new e.Option("--notifications <version>","The notifications version to use e.g. 1.20.2 for the DOS settings").default("stable")).addOption(new e.Option("--closeRuntime <mode>","When to close OpenFin runtimes in the workflow").choices(["never","start","end","both"]).default("both")).addOption(new e.Option("--dos <file.json>","Provide a custom DOS file to use when running tests")).option("--screenshotFolder <path>","The path to store any screen shots that have been taken","./reports/screenshots/").addOption(new e.Option("--verbosity <mode>","What to display from the runner output").choices(["all","none","results"]).default("all")).addOption(new e.Option("--hooks <file>","A file which contains global setup and teardown hooks").default(K)).addOption(new e.Option("--chromeDriverOverride <version>","Override the version of chromedriver e.g. 113")).configureOutput({writeOut:e=>{var t;t=e,"all"===b&&console.log(t)},writeErr:e=>{var t;y(J),_(),T(e.replace("error: ","")),t=`use ${B} --help to show help`,"all"===b&&console.log(`❔ ${s.green(t)}`)}}).exitOverride((()=>{process.exit(1)})).showSuggestionAfterError().action((async(e,i,a)=>{var l,c;l="all"===a.verbosity?"all":"none",b=l,y(`${J} v${z}`),_(),e.startsWith("file://")&&(e=`file://${n.resolve(e.replace("file://",""))}`),i.startsWith("'")&&i.endsWith("'")&&(i=i.slice(1,-1)),F("Manifest Url",e),F("Test Glob Path(s)",i),F("Log Level",a.logLevel),F("Dev Tools Port",a.devToolsPort),F("Chrome Driver Port",a.chromeDriverPort),F("Test Framework",a.framework),a.reporter&&(a.reporter=n.resolve(a.reporter),F("Reporter",a.reporter)),F("Test Timeout",a.testTimeout),a.testFailCount>0&&F("Test Fail Count",a.testFailCount),F("Driver",a.driver),F("Default Runtime Version",a.defaultRuntimeVersion),F("Workspace Version",a.workspace),F("Notifications Version",a.notifications),a.chromeDriverOverride&&F("ChromeDriver Override",a.chromeDriverOverride),F("Storage Folder",a.storageFolder),F("Offline",a.offline?"true":"false"),F("Verbosity",a.verbosity),F("Close Runtime",a.closeRuntime),a.dos&&(C()?(a.dos=n.resolve(a.dos),F("DOS",a.dos)):(c="DOS settings are only available on Windows","all"===b&&console.log(`âš ī¸ ${s.yellow(c)}`))),a.screenshotFolder=n.resolve(a.screenshotFolder),F("Screenshot Folder",a.screenshotFolder);try{await o.rm(a.screenshotFolder,{recursive:!0,force:!0})}catch{}try{await o.mkdir(a.screenshotFolder,{recursive:!0})}catch{}let u,p,h=!1;const m="end"===a.closeRuntime||"both"===a.closeRuntime;let w;try{const s=await r.glob(i.split(";")),l=[];for(let e=0;e<s.length;e++)if(await S(s[e])){const t=await r.glob(n.join(s[e],"**/*").replace(/\\/g,"/"),{nodir:!0});l.push(...t.sort(((e,t)=>e.localeCompare(t))))}else l.push(s[e]);const c=l.some((e=>e.includes(".ts")));if(F("TypeScript",c?"true":"false"),a.hooks===K)a.hooks=n.resolve(`${G}${c?"ts":"js"}`),await E(a.hooks)?F("Hooks",a.hooks):a.hooks=void 0;else if(a.hooks&&(a.hooks=n.resolve(a.hooks),F("Hooks",a.hooks),!await E(a.hooks)))throw new Error("Hook file does not exist");if(process.once("SIGINT",(async()=>{h||(h=!0,await Q(2,u,p,m,w))})),process.once("SIGTERM",(async()=>{h||(h=!0,await Q(2,u,p,m,w))})),"none"===e){const o=await async function(e){const o=Math.floor(1e3*Math.random())+5e3,r={runtime:{version:e},platform:{providerUrl:`http://localhost:${o}/provider.html`,uuid:`dummy-platform-${t.randomUUID()}`}},n="<!DOCTYPE html>\n\t<html>\n\t\t<head>\n\t\t\t<title>Dummy Platform Provider</title>\n\t\t</head>\n\t\t<body>\n\t\t</body>\n\t</html>\n\t",i=await N(o,{"/manifest.fin.json":{data:Buffer.from(JSON.stringify(r),"utf8"),mime:"application/json"},"/provider.html":{data:Buffer.from(n,"utf8"),mime:"text/html"}});return{manifestUrl:`http://localhost:${o}/manifest.fin.json`,server:i}}(a.defaultRuntimeVersion);e=o.manifestUrl}const g=await async function(e,t){let r;if($("Loading manifest",e),/^https?:\/\//.test(e))try{r=await d.fetchJson(e)}catch(t){throw new Error(`Unable to retrieve manifest ${e}, is the web server running?\n${t}`)}else{if(!e.startsWith("file://"))throw new Error(`Unrecognized protocol for manifestUrl ${e}`);{const t=e.replace("file://","");try{const e=await o.readFile(t,"utf8");r=JSON.parse(e)}catch(e){throw new Error(`Unable to load manifest ${t}, does the file exist?\n${e}`)}}}return r.runtime=r.runtime??{},r.runtime.version=r.runtime.version??t,D("Manifest loaded"),r}(e,a.defaultRuntimeVersion),b=await async function(e,t,r){$("Resolving OpenFin runtime version",`Version ${e}`);const i=e.split(".");if(4===i.length)return D("Final Runtime version",e),{runtime:e,chrome:i[1]};let s;if(r){const r=n.join(t,"offline-versions.json");if(D("Loading offline version file",r),!await E(r))throw new Error(`If offlineStorage is set we are expecting a manifest version file to exist at "${r}"`);const i=await o.readFile(r);if(s=JSON.parse(i.toString())[e],!s)throw new Error(`offline-versions.json does not contain an entry for "${e}"`)}else s=await d.fetchText(`https://developer.openfin.co/release/runtime/${e}`);if(s){const e=s.split(".");if(4===e.length)return D("Final Runtime version",s),{runtime:s,chrome:e[1]}}throw new Error(`Unable to resolve runtime version "${e}"`)}(g.runtime.version,a.storageFolder,a.offline),y=await H(a.chromeDriverOverride??b.chrome,a.storageFolder,a.offline);u=await async function(){const e=f.tmpdir(),t=n.join(e,`openfin-test-${Date.now()}`);return $("Creating temp profile directory",t),await o.mkdir(t,{recursive:!0}),D("Directory created"),t}(),"start"!==a.closeRuntime&&"both"!==a.closeRuntime||await q(!1),p=await W(e,a.devToolsPort,a.storageFolder,a.offline,0,a.workspace,a.notifications,a.dos),await async function(e,t){$("Starting Chrome Driver",`${e} port ${t}`);const o=x(e,[`--port=${t}`]);D("Chrome Driver Process",o.pid),D("Waiting for Chrome Driver to be ready"),_();let r=!1;const n=Date.now();do{try{const e=await d.fetchJson(`http://localhost:${t}/status`);e?.value?.ready?r=!0:await j(500)}catch{}}while(!r&&Date.now()-n<1e4);if(!r)throw new Error("Unable to start Chrome Driver");return o.pid}(y,a.chromeDriverPort);const O=await async function(e,t,o,r,n,i,s){return globalThis.webDriver="node"===e?new d.NodeWebDriver:new d.SeleniumWebDriver,await globalThis.webDriver.startSession(t,o,r,n,i,s),globalThis.webDriver}(a.driver,a.devToolsPort,a.chromeDriverPort,a.logLevel,a.screenshotFolder,p.openFinRVMPath,{workspaceVersion:a.workspace,notificationsVersion:a.notifications});if(c){const e={};a.hooks&&(e[a.hooks]="cjs"),v.register({moduleTypes:e})}let T;try{if(a.reporter){const e=await o.readFile(a.reporter);T=JSON.parse(e.toString())}}catch(e){throw new Error(`Failed to load reporter config JSON "${a.reporter}"\n${e}`)}const k=await Y[a.framework](i,l,a.testTimeout,a.testFailCount,c,T,"all"===a.verbosity||"results"===a.verbosity,a.hooks);await async function(e){await e.endSession()}(O),h||(h=!0,await Q(k,u,p,m,w))}catch(e){_(),T(e),h||(h=!0,await Q(1,u,p,m))}})).parse(i)},exports.sleep=j,exports.spawnWithOutput=x,exports.spawnWithOutputWait=V;