prompttest 1.3.3 → 1.3.5
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/dist/bin/prompttest.js +6 -6
- package/dist/index.js +6 -6
- package/package.json +9 -1
package/dist/bin/prompttest.js
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
var Ki=Object.defineProperty;var oe=(p,t,e)=>()=>{if(e)throw e[0];try{return p&&(t=p(p=0)),t}catch(s){throw e=[s],s}};var xt=(p,t)=>{for(var e in t)Ki(p,e,{get:t[e],enumerable:!0})};var ue,ti=oe(()=>{"use strict";ue={DEVICES:["devices","-l"],SCREENCAP:["exec-out","screencap","-p"],FOREGROUND_APP:["shell","dumpsys","window"],CURRENT_FOCUS:["shell","dumpsys","activity","activities"],FORCE_STOP:p=>["shell","am","force-stop",p],CLEAR_APP:p=>["shell","pm","clear",p],LAUNCH_APP:p=>["shell","am","start","-a","android.intent.action.MAIN","-c","android.intent.category.LAUNCHER",p],UIAUTOMATOR_DUMP:["exec-out","uiautomator","dump","--compressed","/dev/tty"],INPUT_TAP:(p,t)=>["shell","input","tap",p.toString(),t.toString()],INPUT_TEXT:p=>["shell","input","text",p],INPUT_KEY:p=>["shell","input","keyevent",p.toString()],INPUT_SWIPE:(p,t,e,s,i=300)=>["shell","input","swipe",p.toString(),t.toString(),e.toString(),s.toString(),i.toString()],LOGCAT_CRASH:p=>["shell","logcat","-d","-b","crash","-s","AndroidRuntime:E"],CLEAR_CRASH_LOGS:()=>["shell","logcat","-b","crash","-c"],GRANT_PERMISSION:(p,t)=>["shell","pm","grant",p,t.startsWith("android.permission.")?t:`android.permission.${t}`],INSTALL_APK:p=>["install","-r",p],UNINSTALL_APP:p=>["uninstall",p],DEEP_LINK:p=>["shell","am","start","-a","android.intent.action.VIEW","-d",p],TCPIP:(p=5555)=>["tcpip",p.toString()],CONNECT:p=>["connect",p],DISCONNECT:p=>p?["disconnect",p]:["disconnect"],GET_WLAN_IP:["shell","ip","-f","inet","addr","show","wlan0"]}});function zi(){let p=process.env.PROMPTTEST_SETTLE_FACTOR;if(!p)return 1;let t=parseFloat(p);return isNaN(t)||t<=0?1:t}function Ee(p,t=50){return Math.max(t,Math.round(p*zi()))}function si(p=P.polling.intervalMs,t=P.polling.jitterMs){return Math.round(p+(Math.random()*2-1)*(t/2))}var P,Pe=oe(()=>{"use strict";P={adb:{commandTimeoutMs:2e4,binaryTimeoutMs:45e3,retryCount:2,retryBaseDelayMs:200},polling:{intervalMs:200,jitterMs:80,defaultTimeoutMs:3e3,extendedTimeoutMs:3500,hierarchyCacheTtlMs:3500,verifyCacheTtlMs:2e3},settle:{get tapMs(){return Ee(500)},get backMs(){return Ee(700)},get launchMs(){return Ee(2500)},get stopMs(){return Ee(600)},get keyboardDismissMs(){return Ee(500)},get scrollMs(){return Ee(350)},get toggleMs(){return Ee(350)},get selectMs(){return Ee(600)},get inputFocusMs(){return Ee(250)},get inputClearMs(){return Ee(150)},get inputDoneMs(){return Ee(350)},get submitWaitMs(){return Ee(1800)}},gestures:{swipeDurationMs:300,longPressDurationMs:1e3,bottomNavThresholdRatio:.88,tapTargetYRatio:.96},recorder:{staleTapTimeoutMs:3e3,asyncRefreshDelayMs:80,heartbeatIntervalMs:1500},screen:{fallbackWidth:1080,fallbackHeight:2400},visual:{overlapRatio:.2},device:{maxDeviceRetries:2,autoReconnect:!0},flow:{maxLoopIterations:50,waitUntilTimeoutMs:1e4,defaultRetryCount:3},ai:{provider:"heuristic",model:"gpt-4o-mini",selfHealing:!0,confidenceThreshold:.75,maxHealingAttempts:2,endpoint:""},memory:{staleThresholdDays:parseInt(process.env.PROMPTTEST_STALE_DAYS||"14",10),staleDecayAmount:.2,minConfidenceFloor:.2,maxHealedConfidence:.65},parser:{mode:process.env.PROMPTTEST_XML_PARSER||"regex"},safety:{mode:process.env.PROMPTTEST_SAFETY_MODE||"strict",customBlacklist:process.env.PROMPTTEST_SAFETY_BLACKLIST?process.env.PROMPTTEST_SAFETY_BLACKLIST.split(",").map(p=>p.trim()).filter(Boolean):[]},explorer:{strategy:process.env.PROMPTTEST_STRATEGY||"heuristic",maxTabs:parseInt(process.env.PROMPTTEST_MAX_TABS||"15",10),maxScreens:parseInt(process.env.PROMPTTEST_MAX_SCREENS||"30",10),maxDepth:parseInt(process.env.PROMPTTEST_MAX_DEPTH||"5",10),stepBudget:parseInt(process.env.PROMPTTEST_STEP_BUDGET||"100",10),screenshots:process.env.PROMPTTEST_SCREENSHOTS||"state-change"},locale:"en"}});var Tt,Et,Pt,At=oe(()=>{"use strict";Tt=class extends Error{exitCode;phase;constructor(t,e=1,s){super(t),this.name="PromptTestError",this.exitCode=e,this.phase=s,Object.setPrototypeOf(this,new.target.prototype)}},Et=class extends Tt{constructor(t,e="Device"){super(t,2,e),this.name="DeviceError"}},Pt=class extends Tt{cycle;constructor(t,e,s="SpecLoader"){super(t,3,s),this.name="SpecRecursionError",this.cycle=e}}});import{spawn as ht}from"child_process";import ts from"readline";var tt,Q,Ae=oe(()=>{"use strict";ti();Pe();At();tt=class{defaultSerial=null;cachedDimensions=new Map;inFlightHierarchies=new Map;constructor(t){t&&(this.defaultSerial=t)}setDefaultDevice(t){this.defaultSerial=t}getDefaultDevice(){return this.defaultSerial}async getScreenDimensions(t){let e=t||this.defaultSerial||"default";if(this.cachedDimensions.has(e))return this.cachedDimensions.get(e);try{let{stdout:i}=await this.execAdb(["shell","wm","size"],t),r=i.match(/(?:Physical|Override)\s*size:\s*(\d+)x(\d+)/i);if(r){let n={width:parseInt(r[1],10),height:parseInt(r[2],10)};return this.cachedDimensions.set(e,n),n}}catch{}let s={width:P.screen.fallbackWidth,height:P.screen.fallbackHeight};return this.cachedDimensions.set(e,s),s}clearScreenDimensionsCache(t){t?this.cachedDimensions.delete(t):this.cachedDimensions.clear()}async getNavigationBarHeight(t){try{let{stdout:e}=await this.execAdb(["shell","dumpsys","window","displays"],t).catch(()=>({stdout:""})),s=e.match(/(?:navigationBarHeight|navBarHeight|navigationBarFrame)[=:\s]+(\d+)/i);if(s&&parseInt(s[1],10)>0)return parseInt(s[1],10)}catch{}return 0}isGlobalAdbCommand(t){return t?["devices","connect","disconnect","version","kill-server","start-server","help"].includes(t.toLowerCase()):!0}async execAdbInternal(t,e,s=2e4){let i=e||this.defaultSerial;!i&&!this.isGlobalAdbCommand(t[0])&&(await this.ensureDefaultDevice(),i=this.defaultSerial);let r=i&&!this.isGlobalAdbCommand(t[0])?["-s",i,...t]:[...t];return new Promise((n,o)=>{let a=ht("adb",r),d="",c="",l=!1,h=setTimeout(()=>{l=!0;try{a.kill("SIGKILL")}catch{}o(new Error(`ADB command timed out after ${s}ms: adb ${r.join(" ")}`))},s);a.stdout.on("data",u=>{d+=u.toString("utf-8")}),a.stderr.on("data",u=>{c+=u.toString("utf-8")}),a.on("close",u=>{clearTimeout(h),!l&&(u===0?n({stdout:d,stderr:c}):o(new Error(`ADB command failed (code ${u}): adb ${r.join(" ")}
|
|
3
3
|
Error: ${c}`)))}),a.on("error",u=>{clearTimeout(h),!l&&o(u)})})}async execAdb(t,e,s=P.adb.commandTimeoutMs,i=P.device.maxDeviceRetries){let r;for(let n=0;n<=i;n++)try{return await this.execAdbInternal(t,e,s)}catch(o){r=o;let a=o instanceof Error?o.message:String(o),d=a.toLowerCase(),c=d.includes("device offline")||d.includes("device not found"),l=c||d.includes("error: closed")||d.includes("timed out");if(n>=i||!l||this.isGlobalAdbCommand(t[0]))throw c?new Et(`Target Android device ${e||this.defaultSerial||""} is offline or disconnected: ${a}`):o;if(P.device.autoReconnect&&c)try{await this.execAdbInternal(["reconnect"],e,5e3).catch(()=>{})}catch{}await new Promise(h=>setTimeout(h,P.adb.retryBaseDelayMs*Math.pow(2,n+1)))}throw r||new Error(`ADB command failed after ${i} retries: ${t.join(" ")}`)}async execAdbBinary(t,e,s=45e3){let i=e||this.defaultSerial;!i&&!this.isGlobalAdbCommand(t[0])&&(await this.ensureDefaultDevice(),i=this.defaultSerial);let r=i&&!this.isGlobalAdbCommand(t[0])?["-s",i,...t]:[...t];return new Promise((n,o)=>{let a=ht("adb",r),d=[],c="",l=!1,h=setTimeout(()=>{l=!0;try{a.kill("SIGKILL")}catch{}o(new Error(`ADB binary command timed out after ${s}ms: adb ${r.join(" ")}`))},s);a.stdout.on("data",u=>d.push(Buffer.from(u))),a.stderr.on("data",u=>{c+=u.toString("utf-8")}),a.on("close",u=>{clearTimeout(h),!l&&(u===0?n(Buffer.concat(d)):o(new Error(`ADB binary command failed (code ${u}): adb ${r.join(" ")}
|
|
4
4
|
${c}`)))}),a.on("error",u=>{clearTimeout(h),!l&&o(u)})})}async getConnectedDevices(){let{stdout:t}=await this.execAdb(ue.DEVICES),e=t.split(`
|
|
5
|
-
`).map(o=>o.trim()).filter(Boolean),s=e.findIndex(o=>o.toLowerCase().includes("list of devices attached")),i=s!==-1?e.slice(s+1):e,r=[];return r=(o=>{let a=[];for(let d of o){if(!d||d.startsWith("*")||d.toLowerCase().includes("list of devices"))continue;let c=d.split(/\s+/);if(c.length>=2){let l=c[0],h=c[1],u,g;for(let m of c.slice(2))m.startsWith("model:")&&(u=m.replace("model:","")),m.startsWith("product:")&&(g=m.replace("product:",""));a.push({serial:l,state:h,model:u,product:g})}}return a})(i),r}async wakeDevice(t){try{await this.execAdb(["shell","input","keyevent","224"],t),await this.execAdb(["shell","wm","dismiss-keyguard"],t)}catch{}}async ensureDefaultDevice(t){if(t)return await this.wakeDevice(t),t;if(this.defaultSerial)return await this.wakeDevice(this.defaultSerial),this.defaultSerial;let s=(await this.getConnectedDevices()).find(i=>i.state==="device");return s&&(this.defaultSerial=s.serial,await this.wakeDevice(this.defaultSerial)),this.defaultSerial||void 0}async getDeviceModel(t){try{let{stdout:r}=await this.execAdb(["shell","getprop","ro.product.model"],t),n=r.trim();if(n)return n}catch{}let e=await this.getConnectedDevices(),s=t||this.defaultSerial;return e.find(r=>r.serial===s)?.model||s||"Android Device"}async captureScreenshot(t){return this.execAdbBinary(ue.SCREENCAP,t)}async getUiHierarchy(t){let e=t||this.defaultSerial||"default",s=this.inFlightHierarchies.get(e);if(s)return s;let i=(async()=>{try{let{stdout:r}=await this.execAdb(ue.UIAUTOMATOR_DUMP,t,1e4,0),n=r.indexOf("<hierarchy");return n>=0?r.slice(n):r}finally{this.inFlightHierarchies.delete(e)}})();return this.inFlightHierarchies.set(e,i),i}clearInFlightHierarchies(t){t?this.inFlightHierarchies.delete(t):this.inFlightHierarchies.clear()}async getForegroundPackage(t){let e=s=>{let i=s.toLowerCase();return i==="android"||i.includes("systemui")||i.includes("inputmethod")||i.includes("latin")||i.includes("gboard")||i.includes("swiftkey")||i.includes("ime")};try{let{stdout:s}=await this.execAdb(["shell","dumpsys","activity","activities"],t).catch(()=>({stdout:""})),i=s.match(/(?:topResumedActivity|mResumedActivity)
|
|
6
|
-
`).map(n=>n.trim()).find(n=>n.includes("/")&&!n.includes(" "));if(r){await this.execAdb(["shell","am","start","-n",r],e);return}}catch{}await this.execAdb(ue.LAUNCH_APP(t),e)}async forceStop(t,e){await this.execAdb(ue.FORCE_STOP(t),e)}async clearAppData(t,e){await this.execAdb(ue.CLEAR_APP(t),e)}async restartApp(t,e){await this.forceStop(t,e),await new Promise(s=>setTimeout(s,600)),await this.launchApp(t,e)}async grantPermission(t,e,s){await this.execAdb(ue.GRANT_PERMISSION(t,e),s)}async isAppInstalled(t,e){try{let{stdout:s}=await this.execAdb(["shell","pm","list","packages",t],e);return s.toLowerCase().includes(`package:${t.toLowerCase()}`)||s.includes(t)}catch{return!1}}async installApk(t,e){await this.execAdb(ue.INSTALL_APK(t),e)}async uninstallApp(t,e){await this.execAdb(ue.UNINSTALL_APP(t),e)}async openDeepLink(t,e){await this.execAdb(ue.DEEP_LINK(t),e)}async checkCrashes(t,e){try{let{stdout:s}=await this.execAdb(ue.LOGCAT_CRASH(t),e);if(s&&s.includes("FATAL EXCEPTION")&&s.includes(t))return s}catch{}return null}async clearCrashLogs(t){try{await this.execAdb(ue.CLEAR_CRASH_LOGS(),t)}catch{}}async getDeviceIp(t){try{let{stdout:e}=await this.execAdb(ue.GET_WLAN_IP,t),s=e.match(/inet\s+(\d+\.\d+\.\d+\.\d+)/);if(s&&s[1])return s[1]}catch{}try{let{stdout:e}=await this.execAdb(["shell","ip","route"],t),s=e.match(/src\s+(\d+\.\d+\.\d+\.\d+)/);if(s&&s[1])return s[1]}catch{}return null}async connectWifi(t,e=5555,s){let i=t,r=e;if(t&&t.includes(":")){let l=t.split(":");i=l[0],r=parseInt(l[1],10)||e}if(!i){let l=await this.getDeviceIp(s);if(!l)throw new Error("Could not auto-detect phone Wi-Fi IP. Make sure the phone is on Wi-Fi, or pass IP manually: npx prompttest wifi <ip>");i=l}let n=`${i}:${r}`;if(t)try{let{stdout:l,stderr:h}=await this.execAdb(ue.CONNECT(n)),u=`${l} ${h}`.trim();if(u.toLowerCase().includes("connected")||u.toLowerCase().includes("already"))return this.defaultSerial=n,{success:!0,target:n,message:u}}catch{}try{await this.execAdb(ue.TCPIP(r),s),await new Promise(l=>setTimeout(l,1e3))}catch{}let{stdout:o,stderr:a}=await this.execAdb(ue.CONNECT(n)),d=`${o} ${a}`.trim(),c=d.toLowerCase().includes("connected")||d.toLowerCase().includes("already");return c&&(this.defaultSerial=n),{success:c,target:n,message:d}}async disconnectWifi(t){let{stdout:e,stderr:s}=await this.execAdb(ue.DISCONNECT(t));return`${e} ${s}`.trim()}streamLogcatEvents(t,e){let i=ht("adb",["-s",t,"shell","logcat","-b","events","-v","time"]),r=ts.createInterface({input:i.stdout});return r.on("line",e),i.on("close",()=>{try{r.close()}catch{}}),i.on("error",()=>{}),i}streamLogcatCrashes(t,e){let i=ht("adb",["-s",t,"shell","logcat","-T","1","-s","AndroidRuntime:E","ANRManager:E","-v","time"]),r=ts.createInterface({input:i.stdout});return r.on("line",e),i.on("close",()=>{try{r.close()}catch{}}),i.on("error",()=>{}),i}streamLogcatToasts(t,e){let i=ht("adb",["-s",t,"shell","logcat","-s","Toast:D","Snackbar:D","-v","time"]),r=ts.createInterface({input:i.stdout});return r.on("line",e),i.on("close",()=>{try{r.close()}catch{}}),i.on("error",()=>{}),i}async getLogcatTail(t=150,e){try{let{stdout:s}=await this.execAdb(["logcat","-d","-t",String(t),"-v","time"],e);return s}catch(s){return`Failed to capture logcat: ${s instanceof Error?s.message:String(s)}`}}async startScreenRecording(t="/sdcard/prompttest_temp.mp4",e){let s=e||this.defaultSerial,r=ht("adb",s?["-s",s,"shell","screenrecord",t]:["shell","screenrecord",t]);return r.on("error",()=>{}),{remotePath:t,process:r}}async stopScreenRecording(t,e,s){try{try{t.process.kill("SIGINT")}catch{}return await this.execAdb(["shell","pkill","-2","screenrecord"],s).catch(()=>{}),await new Promise(i=>setTimeout(i,1500)),await this.execAdb(["pull",t.remotePath,e],s),await this.execAdb(["shell","rm","-f",t.remotePath],s).catch(()=>{}),e}catch{return""}}async isKeyboardVisible(t){try{let{stdout:e}=await this.execAdb(["shell","dumpsys","input_method"],t,4e3,0);return e.includes("mInputShown=true")||e.includes("isInputViewShown=true")||e.includes("mHasSurface=true")}catch{return!1}}async ensureAccessibilityServices(t){try{await this.execAdb(["shell","settings","put","secure","accessibility_enabled","1"],t,4e3,0)}catch{}}},Q=new tt});function ri(p){let t=p.trim(),e=t.match(/^"([^"]+)"$/);if(e)return e[1].trim();let s=t.match(/^'([^']+)'$/);return s?s[1].trim():t.replace(/^['"]+|['"]+$/g,"").trim()}function ee(p){for(let t of p)if(t!=null&&t.trim().length>0)return t.trim();return""}function st(p,t){let e=p;if(t)for(let[s,i]of Object.entries(t))e=e.replace(new RegExp(`\\$${s}\\b`,"g"),()=>i);return e=e.replace(/\$ENV\{([A-Z0-9_]+)\}/gi,(s,i)=>process.env[i]||""),e=e.replace(/\$RANDOM_EMAIL\b/g,()=>`test_${Date.now().toString().slice(-6)}@prompttest.io`),e=e.replace(/\$RANDOM_NAME\b/g,()=>`User_${Math.floor(1e3+Math.random()*9e3)}`),e=e.replace(/\$RANDOM_PHONE\b/g,()=>`98${Math.floor(1e7+Math.random()*9e7)}`),e=e.replace(/\$TIMESTAMP\b/g,()=>Date.now().toString()),e=e.replace(/\$UUID\b/g,()=>`${Date.now().toString(16)}-${Math.random().toString(16).substring(2,8)}`),e=e.replace(/\$RANDOM_UUID\b/g,()=>"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,s=>{let i=Math.random()*16|0;return(s==="x"?i:i&3|8).toString(16)})),e=e.replace(/\$RANDOM_CARD\b/g,()=>{let s="4532";for(let i=0;i<12;i++)s+=Math.floor(Math.random()*10);return s}),e=e.replace(/\$DATE_OFFSET\((-?\d+)\)/g,(s,i)=>{let r=parseInt(i,10)||0;return new Date(Date.now()+r*864e5).toISOString().slice(0,10)}),e=e.replace(/\$RANDOM_INT\((\d+),\s*(\d+)\)/g,(s,i,r)=>{let n=parseInt(i,10),o=parseInt(r,10);return String(Math.floor(n+Math.random()*(o-n+1)))}),e}function gt(p){return(p||"").replace(/&#(\d+);/g,(t,e)=>{try{return String.fromCodePoint(parseInt(e,10))}catch{return""}}).replace(/&#x([0-9a-fA-F]+);/g,(t,e)=>{try{return String.fromCodePoint(parseInt(e,16))}catch{return""}}).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,'"').replace(/'/g,"'")}function Z(p){return gt(p||"").trim().toLowerCase().replace(/[\u2018\u2019]/g,"'").replace(/[\u201C\u201D]/g,'"').replace(/\uFFFD/g,"").replace(new RegExp("\\p{Extended_Pictographic}","gu"),"").replace(/[\uE000-\uF8FF\uFFF0-\uFFFF]/g,"").replace(/\s+/g," ").trim()}function ie(p,t){if(!p||!t)return!1;let e=Z(p),s=Z(t);if(e.length<=3){let i=ii.get(e);if(!i){let r=e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");i=new RegExp(`(?:^|\\b)${r}(?:\\b|$)`,"i"),ii.set(e,i)}return i.test(s)}return s.includes(e)}function Ji(p,t){let e=Z(p),s=Z(t),i=e.length,r=s.length;if(i===0)return r;if(r===0)return i;let n=Array.from({length:r+1},(a,d)=>d),o=new Array(r+1);for(let a=1;a<=i;a++){o[0]=a;for(let c=1;c<=r;c++){let l=e[a-1]===s[c-1]?0:1;o[c]=Math.min(n[c]+1,o[c-1]+1,n[c-1]+l)}let d=n;n=o,o=d}return n[r]}function Qi(p,t){let e=Z(p),s=Z(t);if(e===s)return 1;if(!e||!s)return 0;let i=Math.max(e.length,s.length);return 1-Ji(e,s)/i}function it(p,t,e=3,s=.35){return Array.from(new Set(t.map(n=>n.trim()).filter(n=>n.length>0))).map(n=>({text:n,score:Qi(p,n)})).filter(n=>n.score>=s).sort((n,o)=>o.score-n.score).slice(0,e)}var ni,q,W,kt,ii,oi,ai,Oe=oe(()=>{"use strict";ni=/^(\d+(?:\.\d+)*\.?|\-|\*)\s*/;q=`(?:"([^"]+)"|'([^']+)'|([^'"\\s][^'"]*?[^'"\\s]|\\S))`;W={TAP:new RegExp(`^(?:tap|click|press)(?:\\s+on)?\\s+(?:button\\s+|item\\s+)?${q}$`,"i"),ORDINAL_TAP:new RegExp(`^(?:tap|click|press)(?:\\s+on)?\\s+(?:the\\s+)?(1st|2nd|3rd|4th|5th|last|first)\\s+(?:button\\s+|item\\s+)?${q}$`,"i"),PROXIMITY_TAP:new RegExp(`^(?:tap|click|press)(?:\\s+on)?\\s+${q}\\s+(?:next\\s+to|near|beside|inside|for)\\s+${q}$`,"i"),CLEAR:new RegExp(`^(?:clear|empty)(?:\\s+(?:text\\s+(?:in|of)|field|input))?\\s+${q}$`,"i"),LONG_PRESS:new RegExp(`^(?:long\\s+press|press\\s+and\\s+hold)\\s+(?:button\\s+|item\\s+)?${q}$`,"i"),TYPE:new RegExp(`^(?:type|enter|input)\\s+${q}\\s+(?:in|into)\\s+${q}$`,"i"),VERIFY:new RegExp(`^(?:verify|veryfy|check|assert|ensure)\\s+(?:that\\s+)?(?:error\\s+message\\s+|message\\s+|text\\s+|element\\s+|button\\s+|toast\\s+|my\\s+)?${q}\\s*(?:is\\s+(visible|exists|appears|enabled|disabled|not\\s+visible|hidden|absent|not\\s+present|gone))?$`,"i"),BACK:/^(?:press\s+back|go\s+back|navigate\s+back|back)$/i,SCROLL_TO:new RegExp(`^(?:scroll|swipe)(?:\\s+(down|up))?\\s+(?:to|until)\\s+${q}$`,"i"),SCROLL:/^(?:scroll|swipe)\s+(down|up)$/i,DIRECTIONAL_SWIPE:new RegExp(`^(?:swipe|scroll)\\s+(left|right|up|down)(?:\\s+on\\s+${q})?$`,"i"),TOGGLE:new RegExp(`^(?:toggle|turn\\s+(on|off)|check|uncheck)\\s+(?:checkbox\\s+|switch\\s+)?${q}$`,"i"),SELECT_DROPDOWN:new RegExp(`^(?:select|choose)\\s+${q}\\s+from\\s+${q}$`,"i"),LAUNCH_APP:new RegExp(`^(?:launch|open)\\s+(?:app\\s+)?${q}$`,"i"),RESTART_APP:new RegExp(`^(?:restart|relaunch)\\s+(?:app\\s+)?${q}$`,"i"),TERMINATE_APP:new RegExp(`^(?:terminate|close|force\\s*stop)\\s+(?:app\\s+)?(?:${q})?$`,"i"),CLEAR_DATA:new RegExp(`^(?:clear\\s+(?:app\\s+)?data\\s+(?:for\\s+)?|reset\\s+app\\s+)${q}$`,"i"),GRANT_PERMISSION:new RegExp(`^grant\\s+permissions?\\s+${q}(?:\\s+(?:to|for)\\s+${q})?$`,"i"),DEEP_LINK:new RegExp(`^(?:open\\s+deep\\s*link|navigate\\s+to\\s+url)\\s+${q}$`,"i"),WAIT:/^wait\s+(\d+)\s*(?:s|sec|seconds|ms)?$/i,WAIT_UNTIL:new RegExp(`^(?:wait\\s+until|wait\\s+for)\\s+${q}\\s+(?:(?:is\\s+)?(visible|appears?|enabled|exists))?(?:\\s+(?:up\\s+to|for)\\s+(\\d+)\\s*(s|sec|seconds|ms)?)?$`,"i"),IF_VISIBLE:new RegExp(`^if\\s+(?:(?:that\\s+)?${q}\\s+(?:is\\s+)?(visible|present|exists|appears?)|element\\s+${q}\\s+(?:is\\s+)?visible)$`,"i"),ELSE:/^else$/i,END:/^(?:end|end\s*if|end\s*loop|fi|done)$/i,LOOP:/^(?:loop|repeat)\s+(\d+)\s*(?:times)?$/i,FOR_EACH:new RegExp(`^for\\s+each\\s+(?:item\\s+)?(?:in\\s+)?${q}$`,"i"),SET_VARIABLE:/^set\s+\$([a-zA-Z0-9_]+)\s*=\s*(.+)$/i,DEVICE_WIFI:/^(?:turn\s+|set\s+)?wifi\s+(on|off|enable|disable)$/i,DEVICE_AIRPLANE:/^(?:turn\s+|set\\s+)?airplane\s*(?:mode)?\s+(on|off|enable|disable)$/i,DEVICE_ORIENTATION:/^(?:set\s+)?orientation\s+(?:to\s+)?(portrait|landscape)$/i,DEVICE_NOTIFICATIONS:/^(open|expand|pull\s+down|collapse|close)\s+notification(?:s|\s+shade)?$/i,INCLUDE_SPEC:new RegExp(`^(?:include|run_spec|subflow)\\s+${q}$`,"i"),RUN_JS:/^(?:run\s+js|eval\s+js|js)(?::|\s+)(.+)$/i,ENTER_OTP:new RegExp(`^(?:enter|input|type)\\s+(?:the\\s+)?(?:verification\\s+code|otp|2fa\\s+code|sms\\s+code)(?:\\s+just\\s+sent)?(?:\\s+(?:in|into)\\s+${q})?$`,"i"),API_REQUEST:new RegExp(`^(?:api|call\\s+api)\\s+(GET|POST|PUT|DELETE|PATCH)\\s+${q}(?:\\s+with\\s+body\\s+${q})?(?:\\s+store\\s+${q}\\s+as\\s+\\$([a-zA-Z0-9_]+))?(?:\\s+assert\\s+status\\s+(\\d+))?$`,"i"),EMAIL_ASSERT:new RegExp(`^(?:assert|verify)\\s+email\\s+to\\s+${q}\\s+with\\s+subject\\s+${q}(?:\\s+contains\\s+${q})?$`,"i"),OPTIONAL_SUFFIX:/\s*\((?:optional|if\s+present|maybe)\)\s*$/i,FALLBACK_QUOTED_TAP:/^(?:tap|click)\s+.*?['"]([^'"]+)['"]/i},kt={HIERARCHY_ROOT:/<hierarchy[^>]*>([\s\S]*?)<\/hierarchy>/i,NODE_TAG:/<node\b([^>]*?)(\/?)>|<\/node>/gi,ATTR_KEY_VALUE:/([a-zA-Z0-9_-]+)="([^"]*)"/g,BOUNDS_RECT:/\[(\d+),(\d+)\]\[(\d+),(\d+)\]/};ii=new Map;oi=["sign in","signin","login","log in","submit","save","continue","next","done","send","confirm","proceed","post","rate","enter","join","apply","update"],ai=["sign in","signin","login","log in","submit","save","next","continue","done","like","fav","favorite","star","bookmark","add","post","confirm","apply","explore","search","filter"]});function ci(p){let t=p.match(kt.BOUNDS_RECT);if(!t)return{x1:0,y1:0,x2:0,y2:0,width:0,height:0,centerX:0,centerY:0};let e=parseInt(t[1],10),s=parseInt(t[2],10),i=parseInt(t[3],10),r=parseInt(t[4],10);return{x1:e,y1:s,x2:i,y2:r,width:i-e,height:r-s,centerX:Math.round((e+i)/2),centerY:Math.round((s+r)/2)}}function qi(p){let t=p.match(kt.HIERARCHY_ROOT);if(!t)return{root:[],flat:[]};let e=t[1],s=new RegExp(kt.NODE_TAG.source,"gi"),i=new RegExp(kt.ATTR_KEY_VALUE.source,"g"),r=[],n=[],o=[],a;for(;(a=s.exec(e))!==null;){let d=a[0],c=a[1],l=a[2]==="/";if(d.startsWith("</node>")){o.pop();continue}let h={};i.lastIndex=0;let u;for(;(u=i.exec(c))!==null;)h[u[1]]=u[2];let g={index:parseInt(h.index||"0",10),text:gt(h.text||""),resourceId:h["resource-id"]||"",className:h.class||"",packageName:h.package||"",contentDesc:gt(h["content-desc"]||""),bounds:ci(h.bounds||""),clickable:h.clickable==="true",enabled:h.enabled==="true",focused:h.focused==="true",scrollable:h.scrollable==="true",password:h.password==="true",selected:h.selected==="true",checked:h.checked!==void 0?h.checked==="true":void 0,checkable:h.checkable!==void 0?h.checkable==="true":void 0,children:[]};n.push(g),o.length===0?r.push(g):o[o.length-1].children.push(g),l||o.push(g)}return{root:r,flat:n}}function Zi(p){let t=[],e=[],s=[],i=p.length,r=0;for(;r<i;){let n=p.indexOf("<",r);if(n===-1)break;if(p.startsWith("<!--",n)){let S=p.indexOf("-->",n+4);r=S===-1?i:S+3;continue}if(p.startsWith("<?",n)){let S=p.indexOf("?>",n+2);r=S===-1?i:S+2;continue}if(p.startsWith("</",n)){let S=p.indexOf(">",n+2);if(S===-1)break;p.slice(n+2,S).trim()==="node"&&s.pop(),r=S+1;continue}let o=-1,a=null,d=!1;for(let S=n+1;S<i;S++){let b=p[S];if(a)b===a&&(a=null);else if(b==='"'||b==="'")a=b;else if(b===">"){o=S,p[S-1]==="/"&&(d=!0);break}}if(o===-1)break;let c=p.slice(n+1,d?o-1:o).trim();r=o+1;let l=0;for(;l<c.length&&!/\s/.test(c[l]);)l++;if(c.slice(0,l)!=="node")continue;let u={},g=l,m=c.length;for(;g<m;){for(;g<m&&/\s/.test(c[g]);)g++;if(g>=m)break;let S=g;for(;g<m&&c[g]!=="="&&!/\s/.test(c[g]);)g++;let b=c.slice(S,g);for(;g<m&&/\s/.test(c[g]);)g++;if(g>=m||c[g]!=="=")break;for(g++;g<m&&/\s/.test(c[g]);)g++;if(g>=m)break;let f=c[g];if(f==='"'||f==="'"){g++;let x=g;for(;g<m&&c[g]!==f;)g++;u[b]=c.slice(x,g),g<m&&g++}else{let x=g;for(;g<m&&!/\s/.test(c[g]);)g++;u[b]=c.slice(x,g)}}let y={index:parseInt(u.index||"0",10),text:gt(u.text||""),resourceId:u["resource-id"]||"",className:u.class||"",packageName:u.package||"",contentDesc:gt(u["content-desc"]||""),bounds:ci(u.bounds||""),clickable:u.clickable==="true",enabled:u.enabled==="true",focused:u.focused==="true",scrollable:u.scrollable==="true",password:u.password==="true",selected:u.selected==="true",checked:u.checked!==void 0?u.checked==="true":void 0,checkable:u.checkable!==void 0?u.checkable==="true":void 0,children:[]};e.push(y),s.length===0?t.push(y):s[s.length-1].children.push(y),d||s.push(y)}return{root:t,flat:e}}function pe(p,t){if((t||P.parser?.mode||"regex")==="streaming")try{return Zi(p)}catch{}return qi(p)}function ke(p){if(p.resourceId&&/logbox/i.test(p.resourceId)||p.className&&/logbox/i.test(p.className))return!0;let t=`${p.text} ${p.contentDesc}`.trim();return!!en.some(e=>e.test(t))}function _e(p){return p.filter(t=>ke(t)?!1:(t.className.includes("EditText")||t.className.includes("TextInputEditText")||t.focused&&t.clickable)&&t.bounds.width>0&&t.bounds.height>0)}function ss(p){return p.className.includes("EditText")||p.className.includes("TextInputEditText")?!0:(p.children||[]).some(ss)}function nt(p,t){let e=t?.width||P.screen.fallbackWidth,s=t?.height||P.screen.fallbackHeight,i=s*.08,r=s*.88,n=p.filter(a=>!ke(a)&&(a.className.includes("RecyclerView")||a.className.includes("ListView")||a.className.includes("ScrollView"))),o=[];for(let a of n)for(let d of a.children){if(ke(d)||d.bounds.centerY<i||d.bounds.centerY>r)continue;(d.clickable||d.children.some(l=>!ke(l)&&l.clickable&&l.bounds.width>e*.1))&&d.bounds.width>e*.08&&d.bounds.height>s*.02&&!ss(d)&&o.push(d)}return o.length===0?p.filter(a=>{if(ke(a))return!1;let d=(a.text||a.contentDesc||"").trim();return d.length<=1||/^(remember me|forgot password|sign in|sign up|login|submit|cancel)$/i.test(d)?!1:a.clickable&&a.bounds.width>e*.14&&a.bounds.height>s*.033&&a.bounds.centerY>i&&a.bounds.centerY<r&&!a.className.includes("Button")&&!a.className.includes("EditText")&&!ss(a)}):o}function Ue(p){return p.filter(t=>{if(ke(t)||!t.clickable||t.bounds.width===0||t.className.includes("EditText"))return!1;let e=t.className.includes("Button")||t.className.includes("ImageButton"),s=ai.some(i=>ie(i,t.text)||ie(i,t.contentDesc)||ie(i,t.resourceId));return e||s})}function Dt(p,t=P.screen.fallbackHeight,e){if(!p||p.length===0)return[];let s=p.find(u=>u.bounds.height>=t*.6&&u.bounds.y1<=t*.15),i=s?.bounds?.y1!==void 0&&s.bounds.y1>=0?s.bounds.y1:0,r=s?.bounds?.y2&&s.bounds.y2>i?s.bounds.y2:t,n=r-i,o=s?.bounds?.x2&&s.bounds.x2>0?s.bounds.x2-(s.bounds.x1||0):P.screen.fallbackWidth,a=i+n*.78,d=p.filter(u=>ke(u)||e&&u.packageName&&!u.packageName.includes(e)||!u.clickable||u.bounds.width<=0||u.bounds.height<=0||!(u.bounds.centerY>=a||u.bounds.y2>=r-20)?!1:u.bounds.width>=o*.12&&u.bounds.width<=o*.55);if(d.length<=1)return d;let c=[];for(let u of d){let g=c.find(m=>Math.abs(m[0].bounds.centerY-u.bounds.centerY)<=30||Math.abs(m[0].bounds.y2-u.bounds.y2)<=20);g||(g=[],c.push(g)),g.push(u)}let l=c.map(u=>{u.sort((m,y)=>m.bounds.centerX-y.bounds.centerX);let g=[];for(let m of u)g.some(y=>Math.abs(y.bounds.centerX-m.bounds.centerX)<o*.08)||g.push(m);return g}).filter(u=>u.length<2||u.length>6?!1:u[u.length-1].bounds.x2-u[0].bounds.x1>=o*.6);return l.length===0?d.slice(0,5):(l.sort((u,g)=>g[0].bounds.centerY-u[0].bounds.centerY),l[0].slice(0,5))}function li(p){let t=[/fatal\s+exception/i,/unfortunately.*stopped/i,/(?:app|system|process).*(?:isn't|is not|not)\s+responding/i,/(?:isn't|is not|not)\s+responding/i,/force\s+close/i,/network\s+connection\s+(?:failed|lost|error)/i,/something went wrong/i,/(?:failed|unable)\s+to\s+(?:connect|load|fetch)/i];return p.filter(e=>{let s=`${e.text} ${e.contentDesc}`;return t.some(i=>i.test(s))})}function Ie(p,t,e=P.safety?.mode||"strict"){if(e==="disabled")return!1;let s=`${p.text} ${p.contentDesc} ${p.resourceId}`.trim();if((e==="moderate"?sn:tn).some(n=>n.test(s)))return!0;let r=t||P.safety?.customBlacklist||[];for(let n of r)if(typeof n=="string"&&n.length>0){if(s.toLowerCase().includes(n.toLowerCase()))return!0}else if(n instanceof RegExp&&n.test(s))return!0;return!1}function Re(p){let t=`${p.text} ${p.contentDesc} ${p.resourceId}`.trim();return nn.some(e=>e.test(t))}function $t(p){for(let t of p){let e=`${t.text} ${t.contentDesc} ${t.resourceId}`;if(t.resourceId.toLowerCase().includes("logbox")||e.includes("Unhandled Promise Rejection")||e.includes("SyntaxError")||e.includes("Encountered two children with the same key")||e.includes("Warning:")||e.includes("Console Error")||e.includes("Console Warning")||e.includes("Console Info")||e.includes("TypeError:")||e.includes("ReferenceError:")||/Log \d+ of \d+/i.test(e)||e.includes("LogBox")&&(t.className.includes("Modal")||t.className.includes("ViewGroup"))){let i="error";/warning/i.test(e)?i="warning":/info/i.test(e)&&(i="info");let r=t.resourceId.includes("notification")||t.bounds.height>0&&t.bounds.height<250,n,o=[];for(let c of p){let l=`${c.text} ${c.contentDesc}`.trim();if(!n){let h=l.match(/\b([a-zA-Z0-9_.-]+\.(?:tsx?|jsx?|js|ts):\d+:\d+)\b/);h&&(n=h[1])}/\b(?:at\s+|\()?[a-zA-Z0-9_.-]+\.(?:tsx?|jsx?|js|ts):\d+/i.test(l)&&!o.includes(l)&&l.length<200&&o.push(l)}let a=p.find(c=>c.clickable&&c.bounds.width>0&&(/^(?:dismiss|close)$/i.test((c.text||"").trim())||/^(?:dismiss|close)$/i.test((c.contentDesc||"").trim())));if(!a&&!r){let l=p.filter(h=>h.clickable&&h.bounds.width>0&&h.bounds.height>0&&!/minimize|copy/i.test(`${h.text} ${h.contentDesc}`)).filter(h=>h.bounds.centerY>P.screen.fallbackHeight*.8);l.length>0&&(l.sort((h,u)=>h.bounds.x1-u.bounds.x1),a=l[0])}!a&&r&&(a=p.find(c=>c.clickable&&c.bounds.y1>=t.bounds.y1-20&&c.bounds.y2<=t.bounds.y2+20&&c.bounds.x1>=t.bounds.x2-120));let d=t.text||t.contentDesc||"React Native LogBox Console Overlay";return{type:"logbox",severity:i,node:t,errorText:d,sourceLocation:n,stackTrace:o.length>0?o:void 0,dismissButton:a,isToast:r}}if(t.resourceId.includes("alertTitle")&&(e.includes("isn't responding")||e.includes("has stopped"))){let i=p.find(r=>r.text.toLowerCase().includes("close app")||r.text.toLowerCase().includes("ok"));return{type:"anr",severity:"error",node:t,errorText:t.text||"Application Not Responding (ANR)",dismissButton:i}}}return null}function mt(p){return p.filter(t=>ke(t)?!1:(t.checkable||t.className.includes("CheckBox")||t.className.includes("Switch")||t.className.includes("RadioButton")||t.className.includes("SwitchCompat"))&&t.bounds.width>0&&t.bounds.height>0)}function Ke(p,t=P.screen.fallbackHeight,e=P.screen.fallbackWidth){let s=t*.22,i=/(?:menu|hamburger|drawer|navigation|\u2630|open[_\s-]*drawer)/i,r=p.find(n=>{if(!n.clickable||n.bounds.width===0||n.bounds.centerY>s)return!1;let o=`${n.contentDesc} ${n.text} ${n.resourceId}`;return i.test(o)});return r||p.find(n=>{if(!n.clickable||n.bounds.width===0||n.bounds.height===0||n.bounds.centerY>s||(n.text||n.contentDesc||"").trim().length>0)return!1;let a=n.bounds.x1<=e*.25,d=n.bounds.width<=250&&n.bounds.height<=250;return a&&d})||null}function di(p,t=P.screen.fallbackHeight,e=P.screen.fallbackWidth){let s=t*.22;return p.filter(i=>{if(!i.clickable||i.bounds.width===0||i.bounds.height===0||i.bounds.centerY>s)return!1;let r=i.bounds.x2>=e*.75,n=i.bounds.width<=250&&i.bounds.height<=250;return r&&n})}function ui(p){return p.filter(t=>{if(t.bounds.width===0||t.bounds.height===0)return!1;let e=t.className.includes("ScrollView")||t.className.includes("RecyclerView")||t.className.includes("ListView")||t.className.includes("NestedScrollView");return t.scrollable||e})}function pi(p,t=P.screen.fallbackWidth,e=P.screen.fallbackHeight,s=P.visual.overlapRatio){let i=[];for(let h of p)(h.text||h.clickable)&&h.bounds.width>0&&(h.bounds.x2>t+10&&h.bounds.x1<t&&i.push({type:"OVERFLOW",severity:"WARN",description:`Element "${h.text||h.contentDesc||h.className}" overflows right screen boundary by ${h.bounds.x2-t}px`,elements:[h.text||h.contentDesc||h.className]}),h.bounds.y2>e+20&&h.bounds.y1<e&&i.push({type:"OVERFLOW",severity:"WARN",description:`Element "${h.text||h.contentDesc||h.className}" overflows bottom screen boundary by ${h.bounds.y2-e}px`,elements:[h.text||h.contentDesc||h.className]}));let r=p.filter(h=>h.text&&h.text.trim().length>1&&h.bounds.width>0&&h.bounds.height>0),n=200,o=new Map;for(let h of r){let u=Math.floor(h.bounds.y1/n),g=Math.floor(h.bounds.y2/n);for(let m=u;m<=g;m++)o.has(m)||o.set(m,[]),o.get(m).push(h)}let a=new Set;for(let[,h]of o)for(let u=0;u<h.length;u++)for(let g=u+1;g<h.length;g++){let m=h[u],y=h[g];if(m===y||m.text===y.text)continue;let S=m.bounds.x1<y.bounds.x1?`${m.bounds.x1},${m.bounds.y1}:${y.bounds.x1},${y.bounds.y1}`:`${y.bounds.x1},${y.bounds.y1}:${m.bounds.x1},${m.bounds.y1}`;if(a.has(S))continue;a.add(S);let b=m.bounds,f=y.bounds;if(Math.abs(b.centerY-f.centerY)>(b.height+f.height)/2)continue;let x=Math.max(0,Math.min(b.x2,f.x2)-Math.max(b.x1,f.x1)),T=Math.max(0,Math.min(b.y2,f.y2)-Math.max(b.y1,f.y1)),$=x*T;if($>0){let v=b.width*b.height,C=f.width*f.height,U=Math.min(v,C);U>0&&$/U>s&&i.push({type:"OVERLAP",severity:"WARN",description:`Visual text collision: "${m.text}" overlaps with "${y.text}" (${Math.round($)}px\xB2 collision)`,elements:[m.text,y.text]})}}let d=p.filter(h=>(h.className.includes("ProgressBar")||h.className.includes("ActivityIndicator"))&&h.bounds.width>0);d.length>0&&i.push({type:"STALLED_LOADER",severity:"WARN",description:`Active loading indicator (${d[0].className}) detected on screen`,elements:d.map(h=>h.className)});let c=[],l=new Set;for(let h of i){let u=`${h.type}::${[...h.elements].sort().join("::")}`;l.has(u)||(l.add(u),c.push(h))}return c}function is(p,t,e){let s=t?.height||P.screen.fallbackHeight,i=t?.width||P.screen.fallbackWidth,r=Dt(p,s,e);if(r.length>1&&r[0].bounds.centerY>=s*.75)return{paradigm:"bottom_tabs",roots:r};let n=s*.05,o=s*.22,a=p.filter(c=>{if(ke(c)||e&&c.packageName&&!c.packageName.includes(e))return!1;let l=c.className.includes("Tab")||c.resourceId.toLowerCase().includes("tab")||c.resourceId.toLowerCase().includes("header_tab")||/(?:tab\s*\d|\bphotos\b|\bvideos\b|\baudios\b)/i.test(`${c.text} ${c.contentDesc}`),h=c.bounds.centerY>=n&&c.bounds.centerY<=o,u=c.bounds.height>25&&c.bounds.height<=220;return(l||h)&&c.clickable&&c.bounds.width>50&&u});if(a.length>=2){let c=[];for(let h of a){let u=c.find(g=>Math.abs(g[0].bounds.centerY-h.bounds.centerY)<=20);u||(u=[],c.push(u)),u.push(h)}let l=c.find(h=>h.length>=2);if(l){l.sort((g,m)=>g.bounds.centerX-m.bounds.centerX);let h=[];for(let g of l)h.some(m=>Math.abs(m.bounds.centerX-g.bounds.centerX)<40)||h.push(g);if(!(h.length===2&&h[0].bounds.x1<=i*.25&&h[1].bounds.x2>=i*.75&&!h.some(g=>g.className.includes("Tab")||g.resourceId.toLowerCase().includes("tab")))&&h.length>=2)return{paradigm:"top_tabs",roots:h.slice(0,5)}}}let d=Ke(p,s,i);return d?{paradigm:"drawer",roots:[d]}:{paradigm:"single_hub",roots:[]}}function hi(p,t=2){if(p.length<=t)return p;let e=[];for(let i of p){let r=e.find(n=>{let o=n[0],a=o.className===i.className,d=Math.abs(o.bounds.width-i.bounds.width)<=30,c=Math.abs(o.bounds.height-i.bounds.height)<=30,l=Math.abs(o.bounds.x1-i.bounds.x1)<=30;return a&&d&&c&&l});r||(r=[],e.push(r)),r.push(i)}let s=[];for(let i of e)s.push(...i.slice(0,t));return s}function De(p,t=P.screen.fallbackHeight,e=P.screen.fallbackWidth,s=!1){let i=t*.22,r=e*.28,n=/(?:navigate[_\s-]*up|go[_\s-]*back|^back$|\bback\b|arrow_back|ic_arrow_back|chevron[_\s-]*left|ic_back|btn_back|action_bar_up|header[_\s-]*left|prev(?:ious)?|^close$|\bclose\b|^cancel$|^dismiss$|^done$|^exit$|[<←«‹❮⟨◁◀✕✖×]|\u2190|\u2196|\u2039|\u27E8|\u276E|\u25C1|\u25C0|\u00AB|\u2715|\u2716|\u00D7|[\uF060\uF104\uF053\uF2EA\uF00D\uE5C4\uE5CD])/i,o=a=>a.bounds.width>0&&a.bounds.height>0&&a.bounds.centerY<=i;for(let a of p){if(ke(a)||!o(a))continue;let d=`${a.text||""} ${a.contentDesc||""} ${a.resourceId||""}`.trim();if(n.test(d)||d.length<=2&&/^[<‹«←x✕×\-]$/i.test(d))return a.clickable?a:p.find(l=>l.clickable&&l.bounds.x1<=a.bounds.x1&&l.bounds.x2>=a.bounds.x2&&l.bounds.y1<=a.bounds.y1&&l.bounds.y2>=a.bounds.y2&&l.bounds.width<=300&&l.bounds.height<=300)||a}for(let a of p){if(ke(a)||!o(a)||a.bounds.x1>r)continue;if(a.className.includes("ImageButton")||a.className.includes("ImageView")||a.className.includes("ViewGroup")&&a.bounds.width<=250&&a.bounds.height<=250){let c=`${a.text||""} ${a.contentDesc||""} ${a.resourceId||""}`.toLowerCase();if(c.includes("back")||c.includes("up")||c.includes("arrow")||c.includes("chevron")||c.includes("nav")||c.includes("close")||c.includes("cancel"))return a.clickable?a:p.find(h=>h.clickable&&h.bounds.x1<=a.bounds.x1&&h.bounds.x2>=a.bounds.x2&&h.bounds.y1<=a.bounds.y1&&h.bounds.y2>=a.bounds.y2)||a}}if(s){let a=p.find(d=>{if(ke(d)||!o(d)||!d.clickable)return!1;let c=d.bounds.x1<=r,l=d.bounds.width<=250&&d.bounds.height<=250;return c&&l&&d.bounds.width<e*.4});if(a)return a}return null}var en,tn,sn,nn,je=oe(()=>{"use strict";Pe();Oe();en=[/logbox/i,/^Log \d+ of \d+/i,/Console (?:Error|Warning|Log|Info)/i,/^Call Stack$/i,/^Source Map$/i,/^(?:Dismiss|Minimize|Copy|View error log)$/i,/\b[\w-]+\.(?:tsx?|jsx?|js|ts):\d+:\d+\b/i,/Unhandled Promise Rejection/i,/SyntaxError/i,/TypeError:/i,/ReferenceError:/i,/Warning:/i,/LogBoxNotificationOverlay/i];tn=[/(?:\b|[_\s-])(?:log[_\s-]*out|sign[_\s-]*out)(?:\b|[_\s-])/i,/(?:\b|[_\s-])(?:delete|remove)(?:[_\s-]+(?:account|profile|all|data|item|cart|user))?(?:\b|[_\s-])/i,/(?:\b|[_\s-])(?:clear[_\s-]+data|wipe|force[_\s-]+stop|factory[_\s-]+reset|uninstall)(?:\b|[_\s-])/i,/(?:\b|[_\s-])(?:pay|purchase|checkout|buy[_\s-]+now|order[_\s-]+now)(?:\b|[_\s-])/i],sn=[/(?:\b|[_\s-])(?:log[_\s-]*out|sign[_\s-]*out)(?:\b|[_\s-])/i,/(?:\b|[_\s-])(?:delete|remove)[_\s-]+(?:account|profile|user)(?:\b|[_\s-])/i,/(?:\b|[_\s-])(?:clear[_\s-]+data|wipe|force[_\s-]+stop|factory[_\s-]+reset|uninstall)(?:\b|[_\s-])/i],nn=[/(?:\b|[_\s-])(?:rate[_\s-]*us|review[_\s-]*(?:on|in)[_\s-]*(?:play|store)|play[_\s-]*store)(?:\b|[_\s-])/i,/(?:\b|[_\s-])(?:visit[_\s-]*(?:our)?[_\s-]*website|privacy[_\s-]*policy|terms[_\s-]*of[_\s-]*service|terms[_\s-]*&[_\s-]*conditions)(?:\b|[_\s-])/i,/(?:\b|[_\s-])(?:follow[_\s-]*us|share[_\s-]*via|share[_\s-]*app|open[_\s-]*in[_\s-]*browser)(?:\b|[_\s-])/i,/^https?:\/\//i,/(?:www\.[a-z0-9-]+\.[a-z]{2,})/i]});import ns from"chalk";var rs,Bt,Be,ft=oe(()=>{"use strict";rs={DEBUG:0,INFO:1,WARN:2,ERROR:3,SILENT:4},Bt=class{currentLevel="INFO";constructor(t){let e=(process.env.LOG_LEVEL||"").toUpperCase();t?this.currentLevel=t:rs[e]!==void 0?this.currentLevel=e:this.currentLevel="INFO"}setLevel(t){this.currentLevel=t}getLevel(){return this.currentLevel}isLevelEnabled(t){return rs[this.currentLevel]<=rs[t]}isJsonMode(){return(process.env.LOG_FORMAT||"").toLowerCase()==="json"}emitJson(t,...e){let s=e.map(r=>typeof r=="object"?JSON.stringify(r):String(r)).join(" "),i={ts:new Date().toISOString(),level:t,msg:s};t==="ERROR"||t==="WARN"?console.error(JSON.stringify(i)):console.log(JSON.stringify(i))}debug(...t){this.isLevelEnabled("DEBUG")&&(this.isJsonMode()?this.emitJson("DEBUG",...t):console.log(ns.gray("[DEBUG]"),...t))}info(...t){this.isLevelEnabled("INFO")&&(this.isJsonMode()?this.emitJson("INFO",...t):console.log(...t))}warn(...t){this.isLevelEnabled("WARN")&&(this.isJsonMode()?this.emitJson("WARN",...t):console.warn(ns.yellow("[WARN]"),...t))}error(...t){this.isLevelEnabled("ERROR")&&(this.isJsonMode()?this.emitJson("ERROR",...t):console.error(ns.red("[ERROR]"),...t))}},Be=new Bt});import gi from"fs";import Ht from"path";import ae from"chalk";function os(p,t=new Set){let e=Ht.resolve(p);if(!gi.existsSync(e))throw new Error(`Included spec file not found: ${e}`);if(t.has(e)){let n=Array.from(t).concat(e);throw new Pt(`Circular spec inclusion detected: ${n.join(" -> ")}`,n)}t.add(e);let i=gi.readFileSync(e,"utf-8").split(`
|
|
5
|
+
`).map(o=>o.trim()).filter(Boolean),s=e.findIndex(o=>o.toLowerCase().includes("list of devices attached")),i=s!==-1?e.slice(s+1):e,r=[];return r=(o=>{let a=[];for(let d of o){if(!d||d.startsWith("*")||d.toLowerCase().includes("list of devices"))continue;let c=d.split(/\s+/);if(c.length>=2){let l=c[0],h=c[1],u,g;for(let m of c.slice(2))m.startsWith("model:")&&(u=m.replace("model:","")),m.startsWith("product:")&&(g=m.replace("product:",""));a.push({serial:l,state:h,model:u,product:g})}}return a})(i),r}async wakeDevice(t){try{await this.execAdb(["shell","input","keyevent","224"],t),await this.execAdb(["shell","wm","dismiss-keyguard"],t)}catch{}}async ensureDefaultDevice(t){if(t)return await this.wakeDevice(t),t;if(this.defaultSerial)return await this.wakeDevice(this.defaultSerial),this.defaultSerial;let s=(await this.getConnectedDevices()).find(i=>i.state==="device");return s&&(this.defaultSerial=s.serial,await this.wakeDevice(this.defaultSerial)),this.defaultSerial||void 0}async getDeviceModel(t){try{let{stdout:r}=await this.execAdb(["shell","getprop","ro.product.model"],t),n=r.trim();if(n)return n}catch{}let e=await this.getConnectedDevices(),s=t||this.defaultSerial;return e.find(r=>r.serial===s)?.model||s||"Android Device"}async captureScreenshot(t){return this.execAdbBinary(ue.SCREENCAP,t)}async getUiHierarchy(t){let e=t||this.defaultSerial||"default",s=this.inFlightHierarchies.get(e);if(s)return s;let i=(async()=>{try{let{stdout:r}=await this.execAdb(ue.UIAUTOMATOR_DUMP,t,1e4,0),n=r.indexOf("<hierarchy");return n>=0?r.slice(n):r}finally{this.inFlightHierarchies.delete(e)}})();return this.inFlightHierarchies.set(e,i),i}clearInFlightHierarchies(t){t?this.inFlightHierarchies.delete(t):this.inFlightHierarchies.clear()}async getForegroundPackage(t){let e=s=>{let i=s.toLowerCase();return i==="android"||i.includes("systemui")||i.includes("inputmethod")||i.includes("latin")||i.includes("gboard")||i.includes("swiftkey")||i.includes("ime")};try{let{stdout:s}=await this.execAdb(["shell","dumpsys","activity","activities"],t).catch(()=>({stdout:""})),i=s.match(/(?:topResumedActivity|mResumedActivity|ResumedActivity)[=:\s]+(?:ActivityRecord\{[^}]*?\s+)?([a-zA-Z0-9._]+)\//i);if(i&&i[1]&&!e(i[1]))return i[1];let{stdout:r}=await this.execAdb(ue.FOREGROUND_APP,t).catch(()=>({stdout:""})),n=r.matchAll(/(?:mCurrentFocus|mFocusedWindow|mFocusedApp|topResumedActivity|mResumedActivity|mFocusedActivity)[=:\s]+(?:Window\{[^}]*?\s+|ActivityRecord\{[^}]*?\s+|AppWindowToken\{[^}]*?\s+)?([a-zA-Z0-9._]+)\//gi);for(let o of n)if(o&&o[1]&&!e(o[1]))return o[1]}catch{}return null}async tap(t,e,s){await this.execAdb(ue.INPUT_TAP(t,e),s)}async inputText(t,e){let s=t.replace(/\\/g,"\\\\").replace(/"/g,'\\"').replace(/'/g,"\\'").replace(/%/g,"\\%").replace(/ /g,"%s").replace(/&/g,"\\&").replace(/</g,"\\<").replace(/>/g,"\\>").replace(/\(/g,"\\(").replace(/\)/g,"\\)").replace(/;/g,"\\;").replace(/\|/g,"\\|").replace(/\$/g,"\\$").replace(/#/g,"\\#").replace(/\*/g,"\\*").replace(/\?/g,"\\?").replace(/~/g,"\\~").replace(/`/g,"\\`");try{await this.execAdb(ue.INPUT_TEXT(s),e)}catch{for(let i of t)if(i===" ")await this.pressKey(62,e);else if(i==="@")await this.pressKey(77,e);else{let r=i.replace(/([\\"'&<>()|$;#*?~`%])/g,"\\$1");await this.execAdb(["shell","input","text",r],e).catch(()=>{})}}}async pressKey(t,e){await this.execAdb(ue.INPUT_KEY(t),e)}async pressEnter(t){await this.pressKey(66,t)}async pressTab(t){await this.pressKey(61,t)}async pressHome(t){await this.pressKey(3,t)}async pressBack(t){await this.pressKey(4,t)}async setWifiEnabled(t,e){await this.execAdb(["shell","svc","wifi",t?"enable":"disable"],e)}async setAirplaneMode(t,e){await this.execAdb(["shell","cmd","connectivity","airplane-mode",t?"enable":"disable"],e)}async setOrientation(t,e){await this.execAdb(["shell","content","insert","--uri","content://settings/system","--bind","name:s:accelerometer_rotation","--bind","value:i:0"],e).catch(()=>{});let s=t==="landscape"?"1":"0";await this.execAdb(["shell","settings","put","system","user_rotation",s],e)}async openNotificationShade(t){await this.execAdb(["shell","cmd","statusbar","expand-notifications"],t)}async collapseNotificationShade(t){await this.execAdb(["shell","cmd","statusbar","collapse"],t)}async clearInput(t=45,e){let s=Array(t).fill("67");await this.execAdb(["shell","input","keyevent",...s],e)}async clearField(t,e=80){await this.pressKey(123,t);let s=Math.max(50,e+10),i=Array(s).fill("67");await this.execAdb(["shell","input","keyevent",...i],t).catch(()=>{}),await this.pressKey(122,t);let r=Array(Math.max(30,Math.floor(e/2))).fill("112");await this.execAdb(["shell","input","keyevent",...r],t).catch(()=>{})}async swipe(t,e,s,i,r=300,n){await this.execAdb(ue.INPUT_SWIPE(t,e,s,i,r),n)}async launchApp(t,e){try{let{stdout:s}=await this.execAdb(["shell","cmd","package","resolve-activity","--brief",t],e),r=(s||"").split(`
|
|
6
|
+
`).map(n=>n.trim()).find(n=>n.includes("/")&&!n.includes(" "));if(r){await this.execAdb(["shell","am","start","-n",r],e);return}}catch{}await this.execAdb(ue.LAUNCH_APP(t),e)}async forceStop(t,e){await this.execAdb(ue.FORCE_STOP(t),e)}async clearAppData(t,e){await this.execAdb(ue.CLEAR_APP(t),e)}async restartApp(t,e){await this.forceStop(t,e),await new Promise(s=>setTimeout(s,600)),await this.launchApp(t,e)}async grantPermission(t,e,s){await this.execAdb(ue.GRANT_PERMISSION(t,e),s)}async isAppInstalled(t,e){try{let{stdout:s}=await this.execAdb(["shell","pm","list","packages",t],e);return s.toLowerCase().includes(`package:${t.toLowerCase()}`)||s.includes(t)}catch{return!1}}async installApk(t,e){await this.execAdb(ue.INSTALL_APK(t),e)}async uninstallApp(t,e){await this.execAdb(ue.UNINSTALL_APP(t),e)}async openDeepLink(t,e){await this.execAdb(ue.DEEP_LINK(t),e)}async checkCrashes(t,e){try{let{stdout:s}=await this.execAdb(ue.LOGCAT_CRASH(t),e);if(s&&s.includes("FATAL EXCEPTION")&&s.includes(t))return s}catch{}return null}async clearCrashLogs(t){try{await this.execAdb(ue.CLEAR_CRASH_LOGS(),t)}catch{}}async getDeviceIp(t){try{let{stdout:e}=await this.execAdb(ue.GET_WLAN_IP,t),s=e.match(/inet\s+(\d+\.\d+\.\d+\.\d+)/);if(s&&s[1])return s[1]}catch{}try{let{stdout:e}=await this.execAdb(["shell","ip","route"],t),s=e.match(/src\s+(\d+\.\d+\.\d+\.\d+)/);if(s&&s[1])return s[1]}catch{}return null}async connectWifi(t,e=5555,s){let i=t,r=e;if(t&&t.includes(":")){let l=t.split(":");i=l[0],r=parseInt(l[1],10)||e}if(!i){let l=await this.getDeviceIp(s);if(!l)throw new Error("Could not auto-detect phone Wi-Fi IP. Make sure the phone is on Wi-Fi, or pass IP manually: npx prompttest wifi <ip>");i=l}let n=`${i}:${r}`;if(t)try{let{stdout:l,stderr:h}=await this.execAdb(ue.CONNECT(n)),u=`${l} ${h}`.trim();if(u.toLowerCase().includes("connected")||u.toLowerCase().includes("already"))return this.defaultSerial=n,{success:!0,target:n,message:u}}catch{}try{await this.execAdb(ue.TCPIP(r),s),await new Promise(l=>setTimeout(l,1e3))}catch{}let{stdout:o,stderr:a}=await this.execAdb(ue.CONNECT(n)),d=`${o} ${a}`.trim(),c=d.toLowerCase().includes("connected")||d.toLowerCase().includes("already");return c&&(this.defaultSerial=n),{success:c,target:n,message:d}}async disconnectWifi(t){let{stdout:e,stderr:s}=await this.execAdb(ue.DISCONNECT(t));return`${e} ${s}`.trim()}streamLogcatEvents(t,e){let i=ht("adb",["-s",t,"shell","logcat","-b","events","-v","time"]),r=ts.createInterface({input:i.stdout});return r.on("line",e),i.on("close",()=>{try{r.close()}catch{}}),i.on("error",()=>{}),i}streamLogcatCrashes(t,e){let i=ht("adb",["-s",t,"shell","logcat","-T","1","-s","AndroidRuntime:E","ANRManager:E","-v","time"]),r=ts.createInterface({input:i.stdout});return r.on("line",e),i.on("close",()=>{try{r.close()}catch{}}),i.on("error",()=>{}),i}streamLogcatToasts(t,e){let i=ht("adb",["-s",t,"shell","logcat","-s","Toast:D","Snackbar:D","-v","time"]),r=ts.createInterface({input:i.stdout});return r.on("line",e),i.on("close",()=>{try{r.close()}catch{}}),i.on("error",()=>{}),i}async getLogcatTail(t=150,e){try{let{stdout:s}=await this.execAdb(["logcat","-d","-t",String(t),"-v","time"],e);return s}catch(s){return`Failed to capture logcat: ${s instanceof Error?s.message:String(s)}`}}async startScreenRecording(t="/sdcard/prompttest_temp.mp4",e){let s=e||this.defaultSerial,r=ht("adb",s?["-s",s,"shell","screenrecord",t]:["shell","screenrecord",t]);return r.on("error",()=>{}),{remotePath:t,process:r}}async stopScreenRecording(t,e,s){try{try{t.process.kill("SIGINT")}catch{}return await this.execAdb(["shell","pkill","-2","screenrecord"],s).catch(()=>{}),await new Promise(i=>setTimeout(i,1500)),await this.execAdb(["pull",t.remotePath,e],s),await this.execAdb(["shell","rm","-f",t.remotePath],s).catch(()=>{}),e}catch{return""}}async isKeyboardVisible(t){try{let{stdout:e}=await this.execAdb(["shell","dumpsys","input_method"],t,4e3,0);return e.includes("mInputShown=true")||e.includes("isInputViewShown=true")||e.includes("mHasSurface=true")}catch{return!1}}async ensureAccessibilityServices(t){try{await this.execAdb(["shell","settings","put","secure","accessibility_enabled","1"],t,4e3,0)}catch{}}},Q=new tt});function ri(p){let t=p.trim(),e=t.match(/^"([^"]+)"$/);if(e)return e[1].trim();let s=t.match(/^'([^']+)'$/);return s?s[1].trim():t.replace(/^['"]+|['"]+$/g,"").trim()}function ee(p){for(let t of p)if(t!=null&&t.trim().length>0)return t.trim();return""}function st(p,t){let e=p;if(t)for(let[s,i]of Object.entries(t))e=e.replace(new RegExp(`\\$${s}\\b`,"g"),()=>i);return e=e.replace(/\$ENV\{([A-Z0-9_]+)\}/gi,(s,i)=>process.env[i]||""),e=e.replace(/\$RANDOM_EMAIL\b/g,()=>`test_${Date.now().toString().slice(-6)}@prompttest.io`),e=e.replace(/\$RANDOM_NAME\b/g,()=>`User_${Math.floor(1e3+Math.random()*9e3)}`),e=e.replace(/\$RANDOM_PHONE\b/g,()=>`98${Math.floor(1e7+Math.random()*9e7)}`),e=e.replace(/\$TIMESTAMP\b/g,()=>Date.now().toString()),e=e.replace(/\$UUID\b/g,()=>`${Date.now().toString(16)}-${Math.random().toString(16).substring(2,8)}`),e=e.replace(/\$RANDOM_UUID\b/g,()=>"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,s=>{let i=Math.random()*16|0;return(s==="x"?i:i&3|8).toString(16)})),e=e.replace(/\$RANDOM_CARD\b/g,()=>{let s="4532";for(let i=0;i<12;i++)s+=Math.floor(Math.random()*10);return s}),e=e.replace(/\$DATE_OFFSET\((-?\d+)\)/g,(s,i)=>{let r=parseInt(i,10)||0;return new Date(Date.now()+r*864e5).toISOString().slice(0,10)}),e=e.replace(/\$RANDOM_INT\((\d+),\s*(\d+)\)/g,(s,i,r)=>{let n=parseInt(i,10),o=parseInt(r,10);return String(Math.floor(n+Math.random()*(o-n+1)))}),e}function gt(p){return(p||"").replace(/&#(\d+);/g,(t,e)=>{try{return String.fromCodePoint(parseInt(e,10))}catch{return""}}).replace(/&#x([0-9a-fA-F]+);/g,(t,e)=>{try{return String.fromCodePoint(parseInt(e,16))}catch{return""}}).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,'"').replace(/'/g,"'")}function Z(p){return gt(p||"").trim().toLowerCase().replace(/[\u2018\u2019]/g,"'").replace(/[\u201C\u201D]/g,'"').replace(/\uFFFD/g,"").replace(new RegExp("\\p{Extended_Pictographic}","gu"),"").replace(/[\uE000-\uF8FF\uFFF0-\uFFFF]/g,"").replace(/\s+/g," ").trim()}function ie(p,t){if(!p||!t)return!1;let e=Z(p),s=Z(t);if(e.length<=3){let i=ii.get(e);if(!i){let r=e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");i=new RegExp(`(?:^|\\b)${r}(?:\\b|$)`,"i"),ii.set(e,i)}return i.test(s)}return s.includes(e)}function Ji(p,t){let e=Z(p),s=Z(t),i=e.length,r=s.length;if(i===0)return r;if(r===0)return i;let n=Array.from({length:r+1},(a,d)=>d),o=new Array(r+1);for(let a=1;a<=i;a++){o[0]=a;for(let c=1;c<=r;c++){let l=e[a-1]===s[c-1]?0:1;o[c]=Math.min(n[c]+1,o[c-1]+1,n[c-1]+l)}let d=n;n=o,o=d}return n[r]}function Qi(p,t){let e=Z(p),s=Z(t);if(e===s)return 1;if(!e||!s)return 0;let i=Math.max(e.length,s.length);return 1-Ji(e,s)/i}function it(p,t,e=3,s=.35){return Array.from(new Set(t.map(n=>n.trim()).filter(n=>n.length>0))).map(n=>({text:n,score:Qi(p,n)})).filter(n=>n.score>=s).sort((n,o)=>o.score-n.score).slice(0,e)}var ni,q,W,kt,ii,oi,ai,Oe=oe(()=>{"use strict";ni=/^(\d+(?:\.\d+)*\.?|\-|\*)\s*/;q=`(?:"([^"]+)"|'([^']+)'|([^'"\\s][^'"]*?[^'"\\s]|\\S))`;W={TAP:new RegExp(`^(?:tap|click|press)(?:\\s+on)?\\s+(?:button\\s+|item\\s+)?${q}$`,"i"),ORDINAL_TAP:new RegExp(`^(?:tap|click|press)(?:\\s+on)?\\s+(?:the\\s+)?(1st|2nd|3rd|4th|5th|last|first)\\s+(?:button\\s+|item\\s+)?${q}$`,"i"),PROXIMITY_TAP:new RegExp(`^(?:tap|click|press)(?:\\s+on)?\\s+${q}\\s+(?:next\\s+to|near|beside|inside|for)\\s+${q}$`,"i"),CLEAR:new RegExp(`^(?:clear|empty)(?:\\s+(?:text\\s+(?:in|of)|field|input))?\\s+${q}$`,"i"),LONG_PRESS:new RegExp(`^(?:long\\s+press|press\\s+and\\s+hold)\\s+(?:button\\s+|item\\s+)?${q}$`,"i"),TYPE:new RegExp(`^(?:type|enter|input)\\s+${q}\\s+(?:in|into)\\s+${q}$`,"i"),VERIFY:new RegExp(`^(?:verify|veryfy|check|assert|ensure)\\s+(?:that\\s+)?(?:error\\s+message\\s+|message\\s+|text\\s+|element\\s+|button\\s+|toast\\s+|my\\s+)?${q}\\s*(?:is\\s+(visible|exists|appears|enabled|disabled|not\\s+visible|hidden|absent|not\\s+present|gone))?$`,"i"),BACK:/^(?:press\s+back|go\s+back|navigate\s+back|back)$/i,SCROLL_TO:new RegExp(`^(?:scroll|swipe)(?:\\s+(down|up))?\\s+(?:to|until)\\s+${q}$`,"i"),SCROLL:/^(?:scroll|swipe)\s+(down|up)$/i,DIRECTIONAL_SWIPE:new RegExp(`^(?:swipe|scroll)\\s+(left|right|up|down)(?:\\s+on\\s+${q})?$`,"i"),TOGGLE:new RegExp(`^(?:toggle|turn\\s+(on|off)|check|uncheck)\\s+(?:checkbox\\s+|switch\\s+)?${q}$`,"i"),SELECT_DROPDOWN:new RegExp(`^(?:select|choose)\\s+${q}\\s+from\\s+${q}$`,"i"),LAUNCH_APP:new RegExp(`^(?:launch|open)\\s+(?:app\\s+)?${q}$`,"i"),RESTART_APP:new RegExp(`^(?:restart|relaunch)\\s+(?:app\\s+)?${q}$`,"i"),TERMINATE_APP:new RegExp(`^(?:terminate|close|force\\s*stop)\\s+(?:app\\s+)?(?:${q})?$`,"i"),CLEAR_DATA:new RegExp(`^(?:clear\\s+(?:app\\s+)?data\\s+(?:for\\s+)?|reset\\s+app\\s+)${q}$`,"i"),GRANT_PERMISSION:new RegExp(`^grant\\s+permissions?\\s+${q}(?:\\s+(?:to|for)\\s+${q})?$`,"i"),DEEP_LINK:new RegExp(`^(?:open\\s+deep\\s*link|navigate\\s+to\\s+url)\\s+${q}$`,"i"),WAIT:/^wait\s+(\d+)\s*(?:s|sec|seconds|ms)?$/i,WAIT_UNTIL:new RegExp(`^(?:wait\\s+until|wait\\s+for)\\s+${q}\\s+(?:(?:is\\s+)?(visible|appears?|enabled|exists))?(?:\\s+(?:up\\s+to|for)\\s+(\\d+)\\s*(s|sec|seconds|ms)?)?$`,"i"),IF_VISIBLE:new RegExp(`^if\\s+(?:(?:that\\s+)?${q}\\s+(?:is\\s+)?(visible|present|exists|appears?)|element\\s+${q}\\s+(?:is\\s+)?visible)$`,"i"),ELSE:/^else$/i,END:/^(?:end|end\s*if|end\s*loop|fi|done)$/i,LOOP:/^(?:loop|repeat)\s+(\d+)\s*(?:times)?$/i,FOR_EACH:new RegExp(`^for\\s+each\\s+(?:item\\s+)?(?:in\\s+)?${q}$`,"i"),SET_VARIABLE:/^set\s+\$([a-zA-Z0-9_]+)\s*=\s*(.+)$/i,DEVICE_WIFI:/^(?:turn\s+|set\s+)?wifi\s+(on|off|enable|disable)$/i,DEVICE_AIRPLANE:/^(?:turn\s+|set\\s+)?airplane\s*(?:mode)?\s+(on|off|enable|disable)$/i,DEVICE_ORIENTATION:/^(?:set\s+)?orientation\s+(?:to\s+)?(portrait|landscape)$/i,DEVICE_NOTIFICATIONS:/^(open|expand|pull\s+down|collapse|close)\s+notification(?:s|\s+shade)?$/i,INCLUDE_SPEC:new RegExp(`^(?:include|run_spec|subflow)\\s+${q}$`,"i"),RUN_JS:/^(?:run\s+js|eval\s+js|js)(?::|\s+)(.+)$/i,ENTER_OTP:new RegExp(`^(?:enter|input|type)\\s+(?:the\\s+)?(?:verification\\s+code|otp|2fa\\s+code|sms\\s+code)(?:\\s+just\\s+sent)?(?:\\s+(?:in|into)\\s+${q})?$`,"i"),API_REQUEST:new RegExp(`^(?:api|call\\s+api)\\s+(GET|POST|PUT|DELETE|PATCH)\\s+${q}(?:\\s+with\\s+body\\s+${q})?(?:\\s+store\\s+${q}\\s+as\\s+\\$([a-zA-Z0-9_]+))?(?:\\s+assert\\s+status\\s+(\\d+))?$`,"i"),EMAIL_ASSERT:new RegExp(`^(?:assert|verify)\\s+email\\s+to\\s+${q}\\s+with\\s+subject\\s+${q}(?:\\s+contains\\s+${q})?$`,"i"),OPTIONAL_SUFFIX:/\s*\((?:optional|if\s+present|maybe)\)\s*$/i,FALLBACK_QUOTED_TAP:/^(?:tap|click)\s+.*?['"]([^'"]+)['"]/i},kt={HIERARCHY_ROOT:/<hierarchy[^>]*>([\s\S]*?)<\/hierarchy>/i,NODE_TAG:/<node\b([^>]*?)(\/?)>|<\/node>/gi,ATTR_KEY_VALUE:/([a-zA-Z0-9_-]+)="([^"]*)"/g,BOUNDS_RECT:/\[(\d+),(\d+)\]\[(\d+),(\d+)\]/};ii=new Map;oi=["sign in","signin","login","log in","submit","save","continue","next","done","send","confirm","proceed","post","rate","enter","join","apply","update"],ai=["sign in","signin","login","log in","submit","save","next","continue","done","like","fav","favorite","star","bookmark","add","post","confirm","apply","explore","search","filter"]});function ci(p){let t=p.match(kt.BOUNDS_RECT);if(!t)return{x1:0,y1:0,x2:0,y2:0,width:0,height:0,centerX:0,centerY:0};let e=parseInt(t[1],10),s=parseInt(t[2],10),i=parseInt(t[3],10),r=parseInt(t[4],10);return{x1:e,y1:s,x2:i,y2:r,width:i-e,height:r-s,centerX:Math.round((e+i)/2),centerY:Math.round((s+r)/2)}}function qi(p){let t=p.match(kt.HIERARCHY_ROOT);if(!t)return{root:[],flat:[]};let e=t[1],s=new RegExp(kt.NODE_TAG.source,"gi"),i=new RegExp(kt.ATTR_KEY_VALUE.source,"g"),r=[],n=[],o=[],a;for(;(a=s.exec(e))!==null;){let d=a[0],c=a[1],l=a[2]==="/";if(d.startsWith("</node>")){o.pop();continue}let h={};i.lastIndex=0;let u;for(;(u=i.exec(c))!==null;)h[u[1]]=u[2];let g={index:parseInt(h.index||"0",10),text:gt(h.text||""),resourceId:h["resource-id"]||"",className:h.class||"",packageName:h.package||"",contentDesc:gt(h["content-desc"]||""),bounds:ci(h.bounds||""),clickable:h.clickable==="true",enabled:h.enabled==="true",focused:h.focused==="true",scrollable:h.scrollable==="true",password:h.password==="true",selected:h.selected==="true",checked:h.checked!==void 0?h.checked==="true":void 0,checkable:h.checkable!==void 0?h.checkable==="true":void 0,children:[]};n.push(g),o.length===0?r.push(g):o[o.length-1].children.push(g),l||o.push(g)}return{root:r,flat:n}}function Zi(p){let t=[],e=[],s=[],i=p.length,r=0;for(;r<i;){let n=p.indexOf("<",r);if(n===-1)break;if(p.startsWith("<!--",n)){let S=p.indexOf("-->",n+4);r=S===-1?i:S+3;continue}if(p.startsWith("<?",n)){let S=p.indexOf("?>",n+2);r=S===-1?i:S+2;continue}if(p.startsWith("</",n)){let S=p.indexOf(">",n+2);if(S===-1)break;p.slice(n+2,S).trim()==="node"&&s.pop(),r=S+1;continue}let o=-1,a=null,d=!1;for(let S=n+1;S<i;S++){let b=p[S];if(a)b===a&&(a=null);else if(b==='"'||b==="'")a=b;else if(b===">"){o=S,p[S-1]==="/"&&(d=!0);break}}if(o===-1)break;let c=p.slice(n+1,d?o-1:o).trim();r=o+1;let l=0;for(;l<c.length&&!/\s/.test(c[l]);)l++;if(c.slice(0,l)!=="node")continue;let u={},g=l,m=c.length;for(;g<m;){for(;g<m&&/\s/.test(c[g]);)g++;if(g>=m)break;let S=g;for(;g<m&&c[g]!=="="&&!/\s/.test(c[g]);)g++;let b=c.slice(S,g);for(;g<m&&/\s/.test(c[g]);)g++;if(g>=m||c[g]!=="=")break;for(g++;g<m&&/\s/.test(c[g]);)g++;if(g>=m)break;let f=c[g];if(f==='"'||f==="'"){g++;let x=g;for(;g<m&&c[g]!==f;)g++;u[b]=c.slice(x,g),g<m&&g++}else{let x=g;for(;g<m&&!/\s/.test(c[g]);)g++;u[b]=c.slice(x,g)}}let y={index:parseInt(u.index||"0",10),text:gt(u.text||""),resourceId:u["resource-id"]||"",className:u.class||"",packageName:u.package||"",contentDesc:gt(u["content-desc"]||""),bounds:ci(u.bounds||""),clickable:u.clickable==="true",enabled:u.enabled==="true",focused:u.focused==="true",scrollable:u.scrollable==="true",password:u.password==="true",selected:u.selected==="true",checked:u.checked!==void 0?u.checked==="true":void 0,checkable:u.checkable!==void 0?u.checkable==="true":void 0,children:[]};e.push(y),s.length===0?t.push(y):s[s.length-1].children.push(y),d||s.push(y)}return{root:t,flat:e}}function pe(p,t){if((t||P.parser?.mode||"regex")==="streaming")try{return Zi(p)}catch{}return qi(p)}function ke(p){if(p.resourceId&&/logbox/i.test(p.resourceId)||p.className&&/logbox/i.test(p.className))return!0;let t=`${p.text} ${p.contentDesc}`.trim();return!!en.some(e=>e.test(t))}function _e(p){return p.filter(t=>ke(t)?!1:(t.className.includes("EditText")||t.className.includes("TextInputEditText")||t.focused&&t.clickable)&&t.bounds.width>0&&t.bounds.height>0)}function ss(p){return p.className.includes("EditText")||p.className.includes("TextInputEditText")?!0:(p.children||[]).some(ss)}function nt(p,t){let e=t?.width||P.screen.fallbackWidth,s=t?.height||P.screen.fallbackHeight,i=s*.08,r=s*.88,n=p.filter(a=>!ke(a)&&(a.className.includes("RecyclerView")||a.className.includes("ListView")||a.className.includes("ScrollView"))),o=[];for(let a of n)for(let d of a.children){if(ke(d)||d.bounds.centerY<i||d.bounds.centerY>r)continue;(d.clickable||d.children.some(l=>!ke(l)&&l.clickable&&l.bounds.width>e*.1))&&d.bounds.width>e*.08&&d.bounds.height>s*.02&&!ss(d)&&o.push(d)}return o.length===0?p.filter(a=>{if(ke(a))return!1;let d=(a.text||a.contentDesc||"").trim();return d.length<=1||/^(remember me|forgot password|sign in|sign up|login|submit|cancel)$/i.test(d)?!1:a.clickable&&a.bounds.width>e*.14&&a.bounds.height>s*.033&&a.bounds.centerY>i&&a.bounds.centerY<r&&!a.className.includes("Button")&&!a.className.includes("EditText")&&!ss(a)}):o}function Ue(p){return p.filter(t=>{if(ke(t)||!t.clickable||t.bounds.width===0||t.className.includes("EditText"))return!1;let e=t.className.includes("Button")||t.className.includes("ImageButton"),s=ai.some(i=>ie(i,t.text)||ie(i,t.contentDesc)||ie(i,t.resourceId));return e||s})}function Dt(p,t=P.screen.fallbackHeight,e){if(!p||p.length===0)return[];let s=p.find(u=>u.bounds.height>=t*.6&&u.bounds.y1<=t*.15),i=s?.bounds?.y1!==void 0&&s.bounds.y1>=0?s.bounds.y1:0,r=s?.bounds?.y2&&s.bounds.y2>i?s.bounds.y2:t,n=r-i,o=s?.bounds?.x2&&s.bounds.x2>0?s.bounds.x2-(s.bounds.x1||0):P.screen.fallbackWidth,a=i+n*.78,d=p.filter(u=>ke(u)||e&&u.packageName&&!u.packageName.includes(e)||!u.clickable||u.bounds.width<=0||u.bounds.height<=0||!(u.bounds.centerY>=a||u.bounds.y2>=r-20)?!1:u.bounds.width>=o*.12&&u.bounds.width<=o*.55);if(d.length<=1)return d;let c=[];for(let u of d){let g=c.find(m=>Math.abs(m[0].bounds.centerY-u.bounds.centerY)<=30||Math.abs(m[0].bounds.y2-u.bounds.y2)<=20);g||(g=[],c.push(g)),g.push(u)}let l=c.map(u=>{u.sort((m,y)=>m.bounds.centerX-y.bounds.centerX);let g=[];for(let m of u)g.some(y=>Math.abs(y.bounds.centerX-m.bounds.centerX)<o*.08)||g.push(m);return g}).filter(u=>u.length<2||u.length>6?!1:u[u.length-1].bounds.x2-u[0].bounds.x1>=o*.6);return l.length===0?d.slice(0,5):(l.sort((u,g)=>g[0].bounds.centerY-u[0].bounds.centerY),l[0].slice(0,5))}function li(p){let t=[/fatal\s+exception/i,/unfortunately.*stopped/i,/(?:app|system|process).*(?:isn't|is not|not)\s+responding/i,/(?:isn't|is not|not)\s+responding/i,/force\s+close/i,/network\s+connection\s+(?:failed|lost|error)/i,/something went wrong/i,/(?:failed|unable)\s+to\s+(?:connect|load|fetch)/i];return p.filter(e=>{let s=`${e.text} ${e.contentDesc}`;return t.some(i=>i.test(s))})}function Ie(p,t,e=P.safety?.mode||"strict"){if(e==="disabled")return!1;let s=`${p.text} ${p.contentDesc} ${p.resourceId}`.trim();if((e==="moderate"?sn:tn).some(n=>n.test(s)))return!0;let r=t||P.safety?.customBlacklist||[];for(let n of r)if(typeof n=="string"&&n.length>0){if(s.toLowerCase().includes(n.toLowerCase()))return!0}else if(n instanceof RegExp&&n.test(s))return!0;return!1}function Re(p){let t=`${p.text} ${p.contentDesc} ${p.resourceId}`.trim();return nn.some(e=>e.test(t))}function $t(p){for(let t of p){let e=`${t.text} ${t.contentDesc} ${t.resourceId}`;if(t.resourceId.toLowerCase().includes("logbox")||e.includes("Unhandled Promise Rejection")||e.includes("SyntaxError")||e.includes("Encountered two children with the same key")||e.includes("Warning:")||e.includes("Console Error")||e.includes("Console Warning")||e.includes("Console Info")||e.includes("TypeError:")||e.includes("ReferenceError:")||/Log \d+ of \d+/i.test(e)||e.includes("LogBox")&&(t.className.includes("Modal")||t.className.includes("ViewGroup"))){let i="error";/warning/i.test(e)?i="warning":/info/i.test(e)&&(i="info");let r=t.resourceId.includes("notification")||t.bounds.height>0&&t.bounds.height<250,n,o=[];for(let c of p){let l=`${c.text} ${c.contentDesc}`.trim();if(!n){let h=l.match(/\b([a-zA-Z0-9_.-]+\.(?:tsx?|jsx?|js|ts):\d+:\d+)\b/);h&&(n=h[1])}/\b(?:at\s+|\()?[a-zA-Z0-9_.-]+\.(?:tsx?|jsx?|js|ts):\d+/i.test(l)&&!o.includes(l)&&l.length<200&&o.push(l)}let a=p.find(c=>c.clickable&&c.bounds.width>0&&(/^(?:dismiss|close)$/i.test((c.text||"").trim())||/^(?:dismiss|close)$/i.test((c.contentDesc||"").trim())));if(!a&&!r){let l=p.filter(h=>h.clickable&&h.bounds.width>0&&h.bounds.height>0&&!/minimize|copy/i.test(`${h.text} ${h.contentDesc}`)).filter(h=>h.bounds.centerY>P.screen.fallbackHeight*.8);l.length>0&&(l.sort((h,u)=>h.bounds.x1-u.bounds.x1),a=l[0])}!a&&r&&(a=p.find(c=>c.clickable&&c.bounds.y1>=t.bounds.y1-20&&c.bounds.y2<=t.bounds.y2+20&&c.bounds.x1>=t.bounds.x2-120));let d=t.text||t.contentDesc||"React Native LogBox Console Overlay";return{type:"logbox",severity:i,node:t,errorText:d,sourceLocation:n,stackTrace:o.length>0?o:void 0,dismissButton:a,isToast:r}}if(t.resourceId.includes("alertTitle")&&(e.includes("isn't responding")||e.includes("has stopped"))){let i=p.find(r=>r.text.toLowerCase().includes("close app")||r.text.toLowerCase().includes("ok"));return{type:"anr",severity:"error",node:t,errorText:t.text||"Application Not Responding (ANR)",dismissButton:i}}}return null}function mt(p){return p.filter(t=>ke(t)?!1:(t.checkable||t.className.includes("CheckBox")||t.className.includes("Switch")||t.className.includes("RadioButton")||t.className.includes("SwitchCompat"))&&t.bounds.width>0&&t.bounds.height>0)}function Ke(p,t=P.screen.fallbackHeight,e=P.screen.fallbackWidth){let s=t*.22,i=/(?:menu|hamburger|drawer|navigation|\u2630|open[_\s-]*drawer)/i,r=p.find(n=>{if(!n.clickable||n.bounds.width===0||n.bounds.centerY>s)return!1;let o=`${n.contentDesc} ${n.text} ${n.resourceId}`;return i.test(o)});return r||p.find(n=>{if(!n.clickable||n.bounds.width===0||n.bounds.height===0||n.bounds.centerY>s||(n.text||n.contentDesc||"").trim().length>0)return!1;let a=n.bounds.x1<=e*.25,d=n.bounds.width<=250&&n.bounds.height<=250;return a&&d})||null}function di(p,t=P.screen.fallbackHeight,e=P.screen.fallbackWidth){let s=t*.22;return p.filter(i=>{if(!i.clickable||i.bounds.width===0||i.bounds.height===0||i.bounds.centerY>s)return!1;let r=i.bounds.x2>=e*.75,n=i.bounds.width<=250&&i.bounds.height<=250;return r&&n})}function ui(p){return p.filter(t=>{if(t.bounds.width===0||t.bounds.height===0)return!1;let e=t.className.includes("ScrollView")||t.className.includes("RecyclerView")||t.className.includes("ListView")||t.className.includes("NestedScrollView");return t.scrollable||e})}function pi(p,t=P.screen.fallbackWidth,e=P.screen.fallbackHeight,s=P.visual.overlapRatio){let i=[];for(let h of p)(h.text||h.clickable)&&h.bounds.width>0&&(h.bounds.x2>t+10&&h.bounds.x1<t&&i.push({type:"OVERFLOW",severity:"WARN",description:`Element "${h.text||h.contentDesc||h.className}" overflows right screen boundary by ${h.bounds.x2-t}px`,elements:[h.text||h.contentDesc||h.className]}),h.bounds.y2>e+20&&h.bounds.y1<e&&i.push({type:"OVERFLOW",severity:"WARN",description:`Element "${h.text||h.contentDesc||h.className}" overflows bottom screen boundary by ${h.bounds.y2-e}px`,elements:[h.text||h.contentDesc||h.className]}));let r=p.filter(h=>h.text&&h.text.trim().length>1&&h.bounds.width>0&&h.bounds.height>0),n=200,o=new Map;for(let h of r){let u=Math.floor(h.bounds.y1/n),g=Math.floor(h.bounds.y2/n);for(let m=u;m<=g;m++)o.has(m)||o.set(m,[]),o.get(m).push(h)}let a=new Set;for(let[,h]of o)for(let u=0;u<h.length;u++)for(let g=u+1;g<h.length;g++){let m=h[u],y=h[g];if(m===y||m.text===y.text)continue;let S=m.bounds.x1<y.bounds.x1?`${m.bounds.x1},${m.bounds.y1}:${y.bounds.x1},${y.bounds.y1}`:`${y.bounds.x1},${y.bounds.y1}:${m.bounds.x1},${m.bounds.y1}`;if(a.has(S))continue;a.add(S);let b=m.bounds,f=y.bounds;if(Math.abs(b.centerY-f.centerY)>(b.height+f.height)/2)continue;let x=Math.max(0,Math.min(b.x2,f.x2)-Math.max(b.x1,f.x1)),T=Math.max(0,Math.min(b.y2,f.y2)-Math.max(b.y1,f.y1)),$=x*T;if($>0){let v=b.width*b.height,C=f.width*f.height,U=Math.min(v,C);U>0&&$/U>s&&i.push({type:"OVERLAP",severity:"WARN",description:`Visual text collision: "${m.text}" overlaps with "${y.text}" (${Math.round($)}px\xB2 collision)`,elements:[m.text,y.text]})}}let d=p.filter(h=>(h.className.includes("ProgressBar")||h.className.includes("ActivityIndicator"))&&h.bounds.width>0);d.length>0&&i.push({type:"STALLED_LOADER",severity:"WARN",description:`Active loading indicator (${d[0].className}) detected on screen`,elements:d.map(h=>h.className)});let c=[],l=new Set;for(let h of i){let u=`${h.type}::${[...h.elements].sort().join("::")}`;l.has(u)||(l.add(u),c.push(h))}return c}function is(p,t,e){let s=t?.height||P.screen.fallbackHeight,i=t?.width||P.screen.fallbackWidth,r=Dt(p,s,e);if(r.length>1&&r[0].bounds.centerY>=s*.75)return{paradigm:"bottom_tabs",roots:r};let n=s*.05,o=s*.22,a=p.filter(c=>{if(ke(c)||e&&c.packageName&&!c.packageName.includes(e))return!1;let l=c.className.includes("Tab")||c.resourceId.toLowerCase().includes("tab")||c.resourceId.toLowerCase().includes("header_tab")||/(?:tab\s*\d|\bphotos\b|\bvideos\b|\baudios\b)/i.test(`${c.text} ${c.contentDesc}`),h=c.bounds.centerY>=n&&c.bounds.centerY<=o,u=c.bounds.height>25&&c.bounds.height<=220;return(l||h)&&c.clickable&&c.bounds.width>50&&u});if(a.length>=2){let c=[];for(let h of a){let u=c.find(g=>Math.abs(g[0].bounds.centerY-h.bounds.centerY)<=20);u||(u=[],c.push(u)),u.push(h)}let l=c.find(h=>h.length>=2);if(l){l.sort((g,m)=>g.bounds.centerX-m.bounds.centerX);let h=[];for(let g of l)h.some(m=>Math.abs(m.bounds.centerX-g.bounds.centerX)<40)||h.push(g);if(!(h.length===2&&h[0].bounds.x1<=i*.25&&h[1].bounds.x2>=i*.75&&!h.some(g=>g.className.includes("Tab")||g.resourceId.toLowerCase().includes("tab")))&&h.length>=2)return{paradigm:"top_tabs",roots:h.slice(0,5)}}}let d=Ke(p,s,i);return d?{paradigm:"drawer",roots:[d]}:{paradigm:"single_hub",roots:[]}}function hi(p,t=2){if(p.length<=t)return p;let e=[];for(let i of p){let r=e.find(n=>{let o=n[0],a=o.className===i.className,d=Math.abs(o.bounds.width-i.bounds.width)<=30,c=Math.abs(o.bounds.height-i.bounds.height)<=30,l=Math.abs(o.bounds.x1-i.bounds.x1)<=30;return a&&d&&c&&l});r||(r=[],e.push(r)),r.push(i)}let s=[];for(let i of e)s.push(...i.slice(0,t));return s}function De(p,t=P.screen.fallbackHeight,e=P.screen.fallbackWidth,s=!1){let i=t*.22,r=e*.28,n=/(?:navigate[_\s-]*up|go[_\s-]*back|^back$|\bback\b|arrow_back|ic_arrow_back|chevron[_\s-]*left|ic_back|btn_back|action_bar_up|header[_\s-]*left|prev(?:ious)?|^close$|\bclose\b|^cancel$|^dismiss$|^done$|^exit$|[<←«‹❮⟨◁◀✕✖×]|\u2190|\u2196|\u2039|\u27E8|\u276E|\u25C1|\u25C0|\u00AB|\u2715|\u2716|\u00D7|[\uF060\uF104\uF053\uF2EA\uF00D\uE5C4\uE5CD])/i,o=a=>a.bounds.width>0&&a.bounds.height>0&&a.bounds.centerY<=i;for(let a of p){if(ke(a)||!o(a))continue;let d=`${a.text||""} ${a.contentDesc||""} ${a.resourceId||""}`.trim();if(n.test(d)||d.length<=2&&/^[<‹«←x✕×\-]$/i.test(d))return a.clickable?a:p.find(l=>l.clickable&&l.bounds.x1<=a.bounds.x1&&l.bounds.x2>=a.bounds.x2&&l.bounds.y1<=a.bounds.y1&&l.bounds.y2>=a.bounds.y2&&l.bounds.width<=300&&l.bounds.height<=300)||a}for(let a of p){if(ke(a)||!o(a)||a.bounds.x1>r)continue;if(a.className.includes("ImageButton")||a.className.includes("ImageView")||a.className.includes("ViewGroup")&&a.bounds.width<=250&&a.bounds.height<=250){let c=`${a.text||""} ${a.contentDesc||""} ${a.resourceId||""}`.toLowerCase();if(c.includes("back")||c.includes("up")||c.includes("arrow")||c.includes("chevron")||c.includes("nav")||c.includes("close")||c.includes("cancel"))return a.clickable?a:p.find(h=>h.clickable&&h.bounds.x1<=a.bounds.x1&&h.bounds.x2>=a.bounds.x2&&h.bounds.y1<=a.bounds.y1&&h.bounds.y2>=a.bounds.y2)||a}}if(s){let a=p.find(d=>{if(ke(d)||!o(d)||!d.clickable)return!1;let c=d.bounds.x1<=r,l=d.bounds.width<=250&&d.bounds.height<=250;return c&&l&&d.bounds.width<e*.4});if(a)return a}return null}var en,tn,sn,nn,je=oe(()=>{"use strict";Pe();Oe();en=[/logbox/i,/^Log \d+ of \d+/i,/Console (?:Error|Warning|Log|Info)/i,/^Call Stack$/i,/^Source Map$/i,/^(?:Dismiss|Minimize|Copy|View error log)$/i,/\b[\w-]+\.(?:tsx?|jsx?|js|ts):\d+:\d+\b/i,/Unhandled Promise Rejection/i,/SyntaxError/i,/TypeError:/i,/ReferenceError:/i,/Warning:/i,/LogBoxNotificationOverlay/i];tn=[/(?:\b|[_\s-])(?:log[_\s-]*out|sign[_\s-]*out)(?:\b|[_\s-])/i,/(?:\b|[_\s-])(?:delete|remove)(?:[_\s-]+(?:account|profile|all|data|item|cart|user))?(?:\b|[_\s-])/i,/(?:\b|[_\s-])(?:clear[_\s-]+data|wipe|force[_\s-]+stop|factory[_\s-]+reset|uninstall)(?:\b|[_\s-])/i,/(?:\b|[_\s-])(?:pay|purchase|checkout|buy[_\s-]+now|order[_\s-]+now)(?:\b|[_\s-])/i,/(?:\b|[_\s-])(?:exit[_\s-]*app|close[_\s-]*app|^exit$|^quit$)(?:\b|[_\s-])/i],sn=[/(?:\b|[_\s-])(?:log[_\s-]*out|sign[_\s-]*out)(?:\b|[_\s-])/i,/(?:\b|[_\s-])(?:delete|remove)[_\s-]+(?:account|profile|user)(?:\b|[_\s-])/i,/(?:\b|[_\s-])(?:clear[_\s-]+data|wipe|force[_\s-]+stop|factory[_\s-]+reset|uninstall)(?:\b|[_\s-])/i],nn=[/(?:\b|[_\s-])(?:rate[_\s-]*us|review[_\s-]*(?:on|in)[_\s-]*(?:play|store)|play[_\s-]*store)(?:\b|[_\s-])/i,/(?:\b|[_\s-])(?:visit[_\s-]*(?:our)?[_\s-]*website|privacy[_\s-]*policy|terms[_\s-]*of[_\s-]*service|terms[_\s-]*&[_\s-]*conditions)(?:\b|[_\s-])/i,/(?:\b|[_\s-])(?:follow[_\s-]*us|share[_\s-]*via|share[_\s-]*app|open[_\s-]*in[_\s-]*browser)(?:\b|[_\s-])/i,/^https?:\/\//i,/(?:www\.[a-z0-9-]+\.[a-z]{2,})/i]});import ns from"chalk";var rs,Bt,Be,ft=oe(()=>{"use strict";rs={DEBUG:0,INFO:1,WARN:2,ERROR:3,SILENT:4},Bt=class{currentLevel="INFO";constructor(t){let e=(process.env.LOG_LEVEL||"").toUpperCase();t?this.currentLevel=t:rs[e]!==void 0?this.currentLevel=e:this.currentLevel="INFO"}setLevel(t){this.currentLevel=t}getLevel(){return this.currentLevel}isLevelEnabled(t){return rs[this.currentLevel]<=rs[t]}isJsonMode(){return(process.env.LOG_FORMAT||"").toLowerCase()==="json"}emitJson(t,...e){let s=e.map(r=>typeof r=="object"?JSON.stringify(r):String(r)).join(" "),i={ts:new Date().toISOString(),level:t,msg:s};t==="ERROR"||t==="WARN"?console.error(JSON.stringify(i)):console.log(JSON.stringify(i))}debug(...t){this.isLevelEnabled("DEBUG")&&(this.isJsonMode()?this.emitJson("DEBUG",...t):console.log(ns.gray("[DEBUG]"),...t))}info(...t){this.isLevelEnabled("INFO")&&(this.isJsonMode()?this.emitJson("INFO",...t):console.log(...t))}warn(...t){this.isLevelEnabled("WARN")&&(this.isJsonMode()?this.emitJson("WARN",...t):console.warn(ns.yellow("[WARN]"),...t))}error(...t){this.isLevelEnabled("ERROR")&&(this.isJsonMode()?this.emitJson("ERROR",...t):console.error(ns.red("[ERROR]"),...t))}},Be=new Bt});import gi from"fs";import Ht from"path";import ae from"chalk";function os(p,t=new Set){let e=Ht.resolve(p);if(!gi.existsSync(e))throw new Error(`Included spec file not found: ${e}`);if(t.has(e)){let n=Array.from(t).concat(e);throw new Pt(`Circular spec inclusion detected: ${n.join(" -> ")}`,n)}t.add(e);let i=gi.readFileSync(e,"utf-8").split(`
|
|
7
7
|
`),r=[];for(let n of i){let o=n.trim(),a=o.match(W.INCLUDE_SPEC);if(a&&!o.startsWith("#")){let d=ee([a[1],a[2],a[3]]),c=Ht.isAbsolute(d)?d:Ht.resolve(Ht.dirname(e),d),l=os(c,new Set(t));r.push(l)}else r.push(n)}return r.join(`
|
|
8
8
|
`)}function rn(p){let t=[],e=[];for(let s of p){if(s._isControl==="END"){e.length>0&&e.pop();continue}if(s._isControl==="ELSE"){e.length>0&&e[e.length-1].step.type==="IF_VISIBLE"&&(e[e.length-1].inElse=!0);continue}let i=t;if(e.length>0){let r=e[e.length-1];r.step.type==="IF_VISIBLE"?r.inElse?(r.step.elseSteps=r.step.elseSteps||[],i=r.step.elseSteps):(r.step.thenSteps=r.step.thenSteps||[],i=r.step.thenSteps):r.step.type==="LOOP"&&(r.step.childSteps=r.step.childSteps||[],i=r.step.childSteps)}i.push(s),(s.type==="IF_VISIBLE"||s.type==="LOOP")&&e.push({step:s,inElse:!1})}return t}function Ut(p,t){let e=p.split(`
|
|
9
9
|
`).map(i=>i.trim()).filter(i=>i.length>0&&!i.startsWith("#")),s=[];for(let i of e){let r=st(i,t),n=r.replace(ni,"").trim(),o=W.OPTIONAL_SUFFIX.test(n)||/^[(\[]\s*optional\s*[)\]]/i.test(n)||/^optional\s*:/i.test(n),a=n.replace(W.OPTIONAL_SUFFIX,"").replace(/^[(\[]\s*optional\s*[)\]]\s*/i,"").replace(/^optional\s*:\s*/i,"").trim();if(W.BACK.test(a)){s.push({originalText:r,type:"BACK",optional:o});continue}let d=a.match(W.WAIT);if(d){let E=parseInt(d[1],10),w=a.toLowerCase().includes("ms")?E:E*1e3;s.push({originalText:r,type:"WAIT",durationMs:w,optional:o});continue}let c=a.match(W.WAIT_UNTIL);if(c){let E=ee([c[1],c[2],c[3]]),N=(c[4]||"visible").toLowerCase().includes("enable")?"enabled":"visible",L=c[5]?parseInt(c[5],10):void 0,le=(c[6]||"s").toLowerCase(),j=L?le.includes("ms")?L:L*1e3:void 0;s.push({originalText:r,type:"WAIT_UNTIL",target:E,expectedState:N,durationMs:j,optional:o});continue}let l=a.match(W.IF_VISIBLE);if(l){let E=ee([l[1],l[2],l[3],l[5],l[6],l[7]]);s.push({originalText:r,type:"IF_VISIBLE",conditionTarget:E,thenSteps:[],elseSteps:[],optional:o});continue}if(W.ELSE.test(a)){s.push({originalText:r,type:"IF_VISIBLE",_isControl:"ELSE"});continue}if(W.END.test(a)){s.push({originalText:r,type:"IF_VISIBLE",_isControl:"END"});continue}let h=a.match(W.LOOP);if(h){let E=parseInt(h[1],10);s.push({originalText:r,type:"LOOP",loopCount:E,childSteps:[],optional:o});continue}let u=a.match(W.FOR_EACH);if(u){let E=ee([u[1],u[2],u[3]]);s.push({originalText:r,type:"LOOP",target:E,loopVariable:"item",childSteps:[],optional:o});continue}let g=a.match(W.SET_VARIABLE);if(g){let E=g[1].trim(),w=g[2].trim();s.push({originalText:r,type:"SET_VARIABLE",variableName:E,variableExpression:w,optional:o});continue}let m=a.match(W.DEVICE_WIFI);if(m){let E=m[1].toLowerCase();s.push({originalText:r,type:"DEVICE_STATE",deviceAction:"wifi",deviceStateValue:E==="on"||E==="enable",optional:o});continue}let y=a.match(W.DEVICE_AIRPLANE);if(y){let E=y[1].toLowerCase();s.push({originalText:r,type:"DEVICE_STATE",deviceAction:"airplane",deviceStateValue:E==="on"||E==="enable",optional:o});continue}let S=a.match(W.DEVICE_ORIENTATION);if(S){let E=S[1].toLowerCase();s.push({originalText:r,type:"DEVICE_STATE",deviceAction:"orientation",deviceStateValue:E,optional:o});continue}let b=a.match(W.DEVICE_NOTIFICATIONS);if(b){let E=b[1].toLowerCase(),w=E.startsWith("open")||E.startsWith("expand")||E.startsWith("pull");s.push({originalText:r,type:"DEVICE_STATE",deviceAction:"notifications",deviceStateValue:w?"open":"collapse",optional:o});continue}let f=a.match(W.RUN_JS);if(f){let E=f[1].trim();s.push({originalText:r,type:"RUN_JS",jsCode:E,optional:o});continue}let x=a.match(W.ENTER_OTP);if(x){let E=ee([x[1],x[2],x[3]])||void 0;s.push({originalText:r,type:"ENTER_OTP",target:E,optional:o});continue}let T=a.match(W.API_REQUEST);if(T){let E=T[1].toUpperCase(),w=ee([T[2],T[3],T[4]]),N=ee([T[5],T[6],T[7]])||void 0,L=ee([T[8],T[9],T[10]])||void 0,le=T[11]||void 0,j=T[12]?parseInt(T[12],10):void 0;s.push({originalText:r,type:"API_REQUEST",apiMethod:E,apiUrl:w,apiBody:N,apiStorePath:L,apiStoreVariable:le,apiExpectedStatus:j,optional:o});continue}let $=a.match(W.EMAIL_ASSERT);if($){let E=ee([$[1],$[2],$[3]]),w=ee([$[4],$[5],$[6]]),N=ee([$[7],$[8],$[9]])||void 0;s.push({originalText:r,type:"EMAIL_ASSERT",emailRecipient:E,emailSubject:w,emailBodyContains:N,optional:o});continue}let v=a.match(W.LAUNCH_APP);if(v){let E=ee([v[1],v[2],v[3]]);s.push({originalText:r,type:"LAUNCH_APP",target:E,optional:o});continue}let C=a.match(W.RESTART_APP);if(C){let E=ee([C[1],C[2],C[3]]);s.push({originalText:r,type:"RESTART_APP",target:E,optional:o});continue}let U=a.match(W.TERMINATE_APP);if(U){let E=ee([U[1],U[2],U[3]])||void 0;s.push({originalText:r,type:"TERMINATE_APP",target:E,optional:o});continue}let G=a.match(W.CLEAR_DATA);if(G){let E=ee([G[1],G[2],G[3]]);s.push({originalText:r,type:"CLEAR_DATA",target:E,optional:o});continue}let H=a.match(W.GRANT_PERMISSION);if(H){let E=ee([H[1],H[2],H[3]]),w=ee([H[4],H[5],H[6]])||void 0;s.push({originalText:r,type:"GRANT_PERMISSION",value:E,target:w,optional:o});continue}let be=a.match(W.DEEP_LINK);if(be){let E=ee([be[1],be[2],be[3]]);s.push({originalText:r,type:"DEEP_LINK",target:E,optional:o});continue}let J=a.match(W.DIRECTIONAL_SWIPE);if(J){let E=J[1].toLowerCase(),w=ee([J[2],J[3],J[4]])||void 0;s.push({originalText:r,type:"SWIPE",direction:E,target:w,optional:o});continue}let re=a.match(W.TOGGLE);if(re){let E=(re[1]||"").toLowerCase(),w=ee([re[2],re[3],re[4]]),N=a.toLowerCase().startsWith("uncheck")||E==="off"?!1:E==="on"||a.toLowerCase().startsWith("check")?!0:void 0;s.push({originalText:r,type:"TOGGLE",target:w,desiredState:N,optional:o});continue}let _=a.match(W.SELECT_DROPDOWN);if(_){let E=ee([_[1],_[2],_[3]]),w=ee([_[4],_[5],_[6]]);s.push({originalText:r,type:"SELECT",value:E,target:w,optional:o});continue}let me=a.match(W.SCROLL_TO);if(me){let E=(me[1]||"down").toLowerCase(),w=ee([me[2],me[3],me[4]]);s.push({originalText:r,type:"SCROLL",target:w,direction:E,optional:o});continue}let se=a.match(W.SCROLL);if(se){let E=se[1].toLowerCase();s.push({originalText:r,type:"SCROLL",target:E,direction:E,optional:o});continue}let X=a.match(W.LONG_PRESS);if(X){let E=ee([X[1],X[2],X[3]]);s.push({originalText:r,type:"LONG_PRESS",target:E,optional:o});continue}let O=a.match(W.ORDINAL_TAP);if(O){let E=O[1].toLowerCase(),w=ee([O[2],O[3],O[4]]);s.push({originalText:r,type:"TAP",target:w,ordinal:E,optional:o});continue}let Y=a.match(W.PROXIMITY_TAP);if(Y){let E=ee([Y[1],Y[2],Y[3]]),w=ee([Y[4],Y[5],Y[6]]);s.push({originalText:r,type:"TAP",target:E,relativeTo:w,optional:o});continue}let A=a.match(W.TYPE);if(A){let E=ee([A[1],A[2],A[3]]),w=ee([A[4],A[5],A[6]]);s.push({originalText:r,type:"TYPE",value:E,target:w,optional:o});continue}let M=a.match(W.CLEAR);if(M){let E=ee([M[1],M[2],M[3]]);s.push({originalText:r,type:"CLEAR",target:E,optional:o});continue}let R=a.match(W.VERIFY);if(R){let E=ee([R[1],R[2],R[3]]),w=(R[4]||"visible").toLowerCase(),N=w.includes("not visible")||w.includes("not present")||w.includes("absent")||w.includes("hidden")||w.includes("gone"),L=N?"absent":w==="exists"||w==="enabled"||w==="disabled"?w:"visible";s.push({originalText:r,type:"VERIFY",target:E,expectedState:L,negative:N,optional:o});continue}let F=a.match(W.TAP);if(F&&!a.toLowerCase().includes("back")){let E=ee([F[1],F[2],F[3]]);s.push({originalText:r,type:"TAP",target:E,optional:o});continue}let V=a.match(W.FALLBACK_QUOTED_TAP);if(V&&(a.toLowerCase().startsWith("tap")||a.toLowerCase().startsWith("click"))){s.push({originalText:r,type:"TAP",target:V[1],optional:o});continue}Be.warn(`Unrecognized prompt step: "${i}". Skipping.`)}return rn(s)}function fi(p,t,e){let s=t.map(n=>(n.text||n.contentDesc||"").trim()).filter(n=>n.length>1),i=it(p,s,3),r=i.length>0?`
|
|
@@ -835,11 +835,11 @@ ${t}`),console.log(I.bold.green(" \u{1F4BE} Recording Session Saved Successful
|
|
|
835
835
|
</table>
|
|
836
836
|
</div>
|
|
837
837
|
</body>
|
|
838
|
-
</html>`}var qs=oe(()=>{"use strict"});Ae();import ye from"fs";import Te from"path";import k from"chalk";Ae();je();Pe();rt();import D from"chalk";Ae();je();Oe();Pe();import wi from"fs";import an from"path";var ot=class{testData={};dataPath;isLoaded=!1;driver;constructor(t,e){this.dataPath=t||an.resolve(process.cwd(),"test-data.json"),this.driver=e||Q}setTestData(t){this.testData=t,this.isLoaded=!0}async ensureDataLoaded(){if(!this.isLoaded){if(wi.existsSync(this.dataPath))try{let t=await wi.promises.readFile(this.dataPath,"utf-8");this.testData=JSON.parse(t)}catch{this.testData={}}this.isLoaded=!0}}async loadTestData(t){return t&&(this.dataPath=t),this.isLoaded=!1,await this.ensureDataLoaded(),this.testData}inferValueForNode(t,e){let s=[t.text,t.contentDesc,t.resourceId].join(" ").toLowerCase(),i="",r=e.filter(o=>o.text&&!o.clickable&&o.bounds.centerY<t.bounds.centerY&&t.bounds.centerY-o.bounds.centerY<250&&Math.abs(o.bounds.centerX-t.bounds.centerX)<400);r.length>0&&(i=r[r.length-1].text.toLowerCase());let n=`${s} ${i}`.toLowerCase();if(n.includes("review")||n.includes("feedback")||n.includes("comment")||n.includes("write a review")||n.includes("experience")){if(!this.testData.review?.text)throw new Error("Missing test-data.json field: review.text");return this.testData.review.text}if(n.includes("card")||n.includes("credit")||n.includes("debit")||n.includes("card number")){let o=this.testData.payment?.card;if(o)return String(o);let a="4532";for(let d=0;d<12;d++)a+=Math.floor(Math.random()*10);return a}if(n.includes("phone")||n.includes("mobile")||!n.includes("card")&&n.includes("number")||n.includes("contact")){let o=this.testData.auth?.phone||this.testData.profile?.phone;if(!o)throw new Error("Missing test-data.json field: auth.phone or profile.phone");return o}if(n.includes("email")||n.includes("e-mail")||n.includes("mail")){let o=this.testData.profile?.email||this.testData.auth?.email;if(!o)throw new Error("Missing test-data.json field: profile.email or auth.email");return o}if(n.includes("cvv")||n.includes("cvc")||n.includes("security code"))return"123";if(n.includes("expiry")||n.includes("exp date")||n.includes("expiration"))return"12/28";if(n.includes("pincode")||n.includes("zip")||n.includes("postal")){if(!this.testData.profile?.pincode)throw new Error("Missing test-data.json field: profile.pincode");return this.testData.profile.pincode}if(t.password||n.includes("password")||/\bpin\b/.test(n)&&!n.includes("pincode")||n.includes("passcode")){if(!this.testData.auth?.password)throw new Error("Missing test-data.json field: auth.password");return this.testData.auth.password}if(n.includes("otp")||n.includes("verification")||n.includes("code")&&!n.includes("postal")&&!n.includes("zip")){if(!this.testData.auth?.otp)throw new Error("Missing test-data.json field: auth.otp");return this.testData.auth.otp}if(n.includes("address")||n.includes("street")||n.includes("location")){if(!this.testData.profile?.address)throw new Error("Missing test-data.json field: profile.address");return this.testData.profile.address}if(n.includes("city")||n.includes("state")){if(!this.testData.profile?.city)throw new Error("Missing test-data.json field: profile.city");return this.testData.profile.city}if(n.includes("name")||n.includes("full name")||n.includes("first name")){if(!this.testData.profile?.name)throw new Error("Missing test-data.json field: profile.name");return this.testData.profile.name}if(n.includes("search")||n.includes("find")||n.includes("query")){if(!this.testData.search?.query)throw new Error("Missing test-data.json field: search.query");return this.testData.search.query}return this.testData.review?.text||"PromptTest Auto QA Test Data"}async fillAndSubmitForm(t,e,s=!0,i=!1){await this.ensureDataLoaded();let r=_e(t),n=[];if(r.length===0)return{filledFields:[],submitted:!1};let o=!1;for(let c of r){let l=c.resourceId||c.text||`input_${c.bounds.centerX}_${c.bounds.centerY}`,h=(c.text||"").trim(),u=!h||/^enter\s+/i.test(h)||/^type\s+/i.test(h)||/^e\.g\./i.test(h)||/^(email|email address|password|phone|name|search)$/i.test(h);if(!i&&!u){n.push({fieldId:l,fieldLabel:c.text||c.contentDesc||c.resourceId,valueEntered:h});continue}let g=this.inferValueForNode(c,t);await this.driver.tap(c.bounds.centerX,c.bounds.centerY,e),await new Promise(m=>setTimeout(m,400)),await this.driver.clearField(e,g.length+10),await new Promise(m=>setTimeout(m,200)),await this.driver.inputText(g,e),await new Promise(m=>setTimeout(m,300)),o=!0,n.push({fieldId:l,fieldLabel:c.text||c.contentDesc||c.resourceId,valueEntered:g})}o&&(await this.driver.pressKey(4,e),await new Promise(c=>setTimeout(c,500)));let a=!1,d;if(s){let c=t.filter(l=>!l.clickable||l.bounds.width===0?!1:oi.some(h=>ie(h,l.text)||ie(h,l.contentDesc)||ie(h,l.resourceId)));if(c.length>0){let l=["submit","sign in","signin","login","save","confirm","proceed","continue"];c.sort((u,g)=>{let m=(u.text||u.contentDesc||u.resourceId||"").toLowerCase(),y=(g.text||g.contentDesc||g.resourceId||"").toLowerCase(),S=l.findIndex(T=>m.includes(T)),b=l.findIndex(T=>y.includes(T)),f=S>=0?S:99,x=b>=0?b:99;return f!==x?f-x:g.bounds.centerY-u.bounds.centerY});let h=c[0];d=h.text||h.contentDesc||h.resourceId,await this.driver.tap(h.bounds.centerX,h.bounds.centerY,e),await new Promise(u=>setTimeout(u,P.settle.submitWaitMs));try{let u=await this.driver.getUiHierarchy(e),{flat:g}=pe(u),m=_e(g);a=!(m.length>0&&r.every(S=>m.some(b=>b.resourceId&&b.resourceId===S.resourceId)))}catch{a=!0}}}return{filledFields:n,submitted:a,submitButtonText:d}}},xi=new ot;Je();Qe();It();je();rt();Ae();Je();Qe();import K from"chalk";var Vt=class{driver;reporter;memory;formFiller;screenLedger=new Map;callStack=[];executedStepCount=0;visitedScreenHashes=new Set;devDefectsList=[];safetyHitList=[];externalSkippedList=[];constructor(t=Q,e=bt,s=ve){this.driver=t,this.reporter=e,this.memory=s,this.formFiller=new ot(void 0,t)}async getCleanUiHierarchy(t,e,s=5){let i=await this.driver.getUiHierarchy(e),{flat:r}=pe(i),n=0;for(;n<s;){let o=$t(r);if(!o)break;n++;let a=`[${(o.severity||"error").toUpperCase()} - ${o.type.toUpperCase()}]`,d=o.sourceLocation?` [${o.sourceLocation}]`:"";console.log(K.red(` \u{1F6A8} Global Dev Console Intercepted: ${a} "${o.errorText.slice(0,80)}"${d}`)),this.devDefectsList.push(o.sourceLocation?`${o.errorText} (${o.sourceLocation})`:o.errorText);let c=await this.driver.captureScreenshot(e);this.reporter.logStep("Dev Overlay Triage",`Intercepted and triaged ${a}: "${o.errorText}"${d}`,"Verify","FAIL",o.errorText,c,{errors:[o.sourceLocation?`${o.errorText} at ${o.sourceLocation}`:o.errorText]}),o.dismissButton&&o.dismissButton.bounds.width>0?(console.log(K.gray(" Dismissing overlay via dismiss button...")),await this.driver.tap(o.dismissButton.bounds.centerX,o.dismissButton.bounds.centerY,e)):o.isToast&&o.node&&o.node.bounds.width>0?(console.log(K.gray(" Expanding toast notification to access Dismiss button...")),await this.driver.tap(o.node.bounds.centerX,o.node.bounds.centerY,e)):(console.log(K.gray(" Pressing Back to dismiss overlay...")),await this.driver.pressBack(e)),await new Promise(l=>setTimeout(l,800)),i=await this.driver.getUiHierarchy(e),r=pe(i).flat}return{xml:i,flat:r}}async dismissSoftKeyboard(t){try{await this.driver.pressKey(111,t),await new Promise(e=>setTimeout(e,150))}catch{}}async packageJailGuard(t,e){let s=await this.driver.getForegroundPackage(e);if(!s||!s.includes(t)){console.log(K.yellow(` \u{1F6A8} External App Jailbreak Detected [${s||"Unknown"}]. Recovering...`)),await this.driver.pressBack(e),await new Promise(r=>setTimeout(r,800));let i=await this.driver.getForegroundPackage(e);return(!i||!i.includes(t))&&(console.log(K.red(` Force restoring ${t} foreground...`)),await this.driver.launchApp(t,e),await new Promise(r=>setTimeout(r,1500)),this.callStack=this.callStack.slice(0,1)),!0}return!1}async handleExternalMediaChooser(t,e){let s=await this.driver.getForegroundPackage(e);if(s==="android"||s?.includes("resolver")||s?.includes("intentresolver")||s&&!s.includes(t)){console.log(K.yellow(` \u{1F6E1}\uFE0F External Media Chooser / Third-Party App Intercepted [${s||"Unknown"}]. Dismissing to stay in-app...`)),await this.driver.pressBack(e),await new Promise(n=>setTimeout(n,800));let r=await this.driver.getForegroundPackage(e);return(!r||!r.includes(t))&&(await this.driver.launchApp(t,e),await new Promise(n=>setTimeout(n,1200)),this.callStack=this.callStack.slice(0,1)),!0}return!1}catalogScreen(t,e,s=[]){let i=[],r=e.height,n=r*.06,o=r*.88,a=f=>s.some(x=>Math.abs(x.bounds.centerX-f.bounds.centerX)<30&&Math.abs(x.bounds.centerY-f.bounds.centerY)<30),d=_e(t);for(let f of d){if(a(f))continue;let x=f.text||f.contentDesc||f.resourceId||"Text Input";i.push({node:f,type:"input",label:x,tested:!1})}let c=mt(t);for(let f of c){if(a(f))continue;let x=f.text||f.contentDesc||f.resourceId||"Toggle Control";i.push({node:f,type:"toggle",label:x,tested:!1})}let l=Ue(t);for(let f of l){if(a(f)||Ke([f],r,e.width)||De([f],r,e.width,!0))continue;let x=f.text||f.contentDesc||f.resourceId||"Action Button";if(Re(f)){this.externalSkippedList.push(x);continue}let T=f.selected!==void 0||f.checkable||/(?:tab|chip|pill|filter|segment|toggle)/i.test(`${f.resourceId} ${f.className} ${f.contentDesc}`)||/(?:today|yesterday|all|active|archived|pending|completed|present|absent)/i.test(x);i.push({node:f,type:T?"toggle":"transition",label:x,tested:!1})}let h=di(t,r,e.width);for(let f of h){if(a(f)||De([f],r,e.width,!0)||i.some(T=>Math.abs(T.node.bounds.centerX-f.bounds.centerX)<25&&Math.abs(T.node.bounds.centerY-f.bounds.centerY)<25))continue;let x=f.text||f.contentDesc||f.resourceId||"Notification / Header Action";if(Re(f)){this.externalSkippedList.push(x);continue}i.push({node:f,type:"transition",label:x,tested:!1})}let u=t.filter(f=>{if(!f.clickable||f.bounds.width===0)return!1;let x=`${f.text} ${f.contentDesc} ${f.resourceId}`.toLowerCase();return x.includes("now playing")||x.includes("play")||x.includes("pause")});for(let f of u){if(a(f)||i.some(T=>Math.abs(T.node.bounds.centerX-f.bounds.centerX)<25&&Math.abs(T.node.bounds.centerY-f.bounds.centerY)<25))continue;let x=f.text||f.contentDesc||f.resourceId||"Media Control";i.push({node:f,type:"toggle",label:x,tested:!1})}let g=nt(t,e).filter(f=>!a(f)&&f.bounds.centerY>n&&f.bounds.centerY<o&&!De([f],r,e.width,!0)&&!Re(f)),m=hi(g,2);for(let f of m){let x=f.text||f.contentDesc||f.resourceId||`Item (${f.bounds.centerX},${f.bounds.centerY})`;i.push({node:f,type:"transition",label:x,tested:!1})}let y=0,S=0,b=[];for(let f of i){let x=/\.(?:mp4|mkv|mov|webm|flv|avi)\b/i.test(f.label),T=/\.(?:mp3|wav|m4a|aac|ogg)\b/i.test(f.label);if(x){if(y++,y>1)continue}else if(T&&(S++,S>1))continue;b.push(f)}return b}async safeBacktrack(t,e,s,i,r=!0){let{flat:n}=await this.getCleanUiHierarchy(e,s),{height:o,width:a}=await this.driver.getScreenDimensions(s),d=We(n,{width:a,height:o});if(d===t)return!0;let c=De(n,o,a,!0);if(c&&c.bounds.width>0){console.log(K.gray(` [Backtrack] Tapping in-app back/close button "${c.text||c.contentDesc||c.className}"...`)),await this.driver.tap(c.bounds.centerX,c.bounds.centerY,s),await new Promise(u=>setTimeout(u,1e3)),await this.packageJailGuard(e,s);let l=await this.getCleanUiHierarchy(e,s),h=We(l.flat,{width:a,height:o});if(h===t||h!==d)return!0}if(i&&i.bounds.width>0){console.log(K.gray(` [Backtrack] Restoring root domain via tab "${i.text||i.contentDesc||"Root Tab"}"...`)),await this.driver.tap(i.bounds.centerX,i.bounds.centerY,s),await new Promise(u=>setTimeout(u,1e3)),await this.packageJailGuard(e,s);let l=await this.getCleanUiHierarchy(e,s),h=We(l.flat,{width:a,height:o});if(h===t||h!==d)return!0}if(r){if(console.log(K.gray(" [Backtrack] Sending fallback KEYCODE_BACK...")),await this.driver.pressKey(4,s),await new Promise(g=>setTimeout(g,1e3)),await this.packageJailGuard(e,s))return console.log(K.yellow(" \u26A0\uFE0F Backtrack via KEYCODE_BACK caused app jailbreak. Aborting branch restoration.")),!1;let h=await this.getCleanUiHierarchy(e,s),u=We(h.flat,{width:a,height:o});if(u===t||u!==d)return!0}return!1}async explore(t){let{packageName:e,serial:s,maxScreens:i=30,maxDepth:r=3,stepBudget:n=100,maxInteractionsPerScreen:o=6,safetyMode:a="strict",safetyBlacklist:d=[]}=t;console.log(K.bold.cyan(`
|
|
838
|
+
</html>`}var qs=oe(()=>{"use strict"});Ae();import ye from"fs";import Te from"path";import k from"chalk";Ae();je();Pe();rt();import D from"chalk";Ae();je();Oe();Pe();import wi from"fs";import an from"path";var ot=class{testData={};dataPath;isLoaded=!1;driver;constructor(t,e){this.dataPath=t||an.resolve(process.cwd(),"test-data.json"),this.driver=e||Q}setTestData(t){this.testData=t,this.isLoaded=!0}async ensureDataLoaded(){if(!this.isLoaded){if(wi.existsSync(this.dataPath))try{let t=await wi.promises.readFile(this.dataPath,"utf-8");this.testData=JSON.parse(t)}catch{this.testData={}}this.isLoaded=!0}}async loadTestData(t){return t&&(this.dataPath=t),this.isLoaded=!1,await this.ensureDataLoaded(),this.testData}inferValueForNode(t,e){let s=[t.text,t.contentDesc,t.resourceId].join(" ").toLowerCase(),i="",r=e.filter(o=>o.text&&!o.clickable&&o.bounds.centerY<t.bounds.centerY&&t.bounds.centerY-o.bounds.centerY<250&&Math.abs(o.bounds.centerX-t.bounds.centerX)<400);r.length>0&&(i=r[r.length-1].text.toLowerCase());let n=`${s} ${i}`.toLowerCase();if(n.includes("review")||n.includes("feedback")||n.includes("comment")||n.includes("write a review")||n.includes("experience")){if(!this.testData.review?.text)throw new Error("Missing test-data.json field: review.text");return this.testData.review.text}if(n.includes("card")||n.includes("credit")||n.includes("debit")||n.includes("card number")){let o=this.testData.payment?.card;if(o)return String(o);let a="4532";for(let d=0;d<12;d++)a+=Math.floor(Math.random()*10);return a}if(n.includes("phone")||n.includes("mobile")||!n.includes("card")&&n.includes("number")||n.includes("contact")){let o=this.testData.auth?.phone||this.testData.profile?.phone;if(!o)throw new Error("Missing test-data.json field: auth.phone or profile.phone");return o}if(n.includes("email")||n.includes("e-mail")||n.includes("mail")){let o=this.testData.profile?.email||this.testData.auth?.email;if(!o)throw new Error("Missing test-data.json field: profile.email or auth.email");return o}if(n.includes("cvv")||n.includes("cvc")||n.includes("security code"))return"123";if(n.includes("expiry")||n.includes("exp date")||n.includes("expiration"))return"12/28";if(n.includes("pincode")||n.includes("zip")||n.includes("postal")){if(!this.testData.profile?.pincode)throw new Error("Missing test-data.json field: profile.pincode");return this.testData.profile.pincode}if(t.password||n.includes("password")||/\bpin\b/.test(n)&&!n.includes("pincode")||n.includes("passcode")){if(!this.testData.auth?.password)throw new Error("Missing test-data.json field: auth.password");return this.testData.auth.password}if(n.includes("otp")||n.includes("verification")||n.includes("code")&&!n.includes("postal")&&!n.includes("zip")){if(!this.testData.auth?.otp)throw new Error("Missing test-data.json field: auth.otp");return this.testData.auth.otp}if(n.includes("address")||n.includes("street")||n.includes("location")){if(!this.testData.profile?.address)throw new Error("Missing test-data.json field: profile.address");return this.testData.profile.address}if(n.includes("city")||n.includes("state")){if(!this.testData.profile?.city)throw new Error("Missing test-data.json field: profile.city");return this.testData.profile.city}if(n.includes("name")||n.includes("full name")||n.includes("first name")){if(!this.testData.profile?.name)throw new Error("Missing test-data.json field: profile.name");return this.testData.profile.name}if(n.includes("search")||n.includes("find")||n.includes("query")){if(!this.testData.search?.query)throw new Error("Missing test-data.json field: search.query");return this.testData.search.query}return this.testData.review?.text||"PromptTest Auto QA Test Data"}async fillAndSubmitForm(t,e,s=!0,i=!1){await this.ensureDataLoaded();let r=_e(t),n=[];if(r.length===0)return{filledFields:[],submitted:!1};let o=!1;for(let c of r){let l=c.resourceId||c.text||`input_${c.bounds.centerX}_${c.bounds.centerY}`,h=(c.text||"").trim(),u=!h||/^enter\s+/i.test(h)||/^type\s+/i.test(h)||/^e\.g\./i.test(h)||/^(email|email address|password|phone|name|search)$/i.test(h);if(!i&&!u){n.push({fieldId:l,fieldLabel:c.text||c.contentDesc||c.resourceId,valueEntered:h});continue}let g=this.inferValueForNode(c,t);await this.driver.tap(c.bounds.centerX,c.bounds.centerY,e),await new Promise(m=>setTimeout(m,400)),await this.driver.clearField(e,g.length+10),await new Promise(m=>setTimeout(m,200)),await this.driver.inputText(g,e),await new Promise(m=>setTimeout(m,300)),o=!0,n.push({fieldId:l,fieldLabel:c.text||c.contentDesc||c.resourceId,valueEntered:g})}o&&(await this.driver.pressKey(4,e),await new Promise(c=>setTimeout(c,500)));let a=!1,d;if(s){let c=t.filter(l=>!l.clickable||l.bounds.width===0?!1:oi.some(h=>ie(h,l.text)||ie(h,l.contentDesc)||ie(h,l.resourceId)));if(c.length>0){let l=["submit","sign in","signin","login","save","confirm","proceed","continue"];c.sort((u,g)=>{let m=(u.text||u.contentDesc||u.resourceId||"").toLowerCase(),y=(g.text||g.contentDesc||g.resourceId||"").toLowerCase(),S=l.findIndex(T=>m.includes(T)),b=l.findIndex(T=>y.includes(T)),f=S>=0?S:99,x=b>=0?b:99;return f!==x?f-x:g.bounds.centerY-u.bounds.centerY});let h=c[0];d=h.text||h.contentDesc||h.resourceId,await this.driver.tap(h.bounds.centerX,h.bounds.centerY,e),await new Promise(u=>setTimeout(u,P.settle.submitWaitMs));try{let u=await this.driver.getUiHierarchy(e),{flat:g}=pe(u),m=_e(g);a=!(m.length>0&&r.every(S=>m.some(b=>b.resourceId&&b.resourceId===S.resourceId)))}catch{a=!0}}}return{filledFields:n,submitted:a,submitButtonText:d}}},xi=new ot;Je();Qe();It();je();rt();Ae();Je();Qe();import K from"chalk";var Vt=class{driver;reporter;memory;formFiller;screenLedger=new Map;callStack=[];executedStepCount=0;visitedScreenHashes=new Set;devDefectsList=[];safetyHitList=[];externalSkippedList=[];constructor(t=Q,e=bt,s=ve){this.driver=t,this.reporter=e,this.memory=s,this.formFiller=new ot(void 0,t)}async getCleanUiHierarchy(t,e,s=5){let i=await this.driver.getUiHierarchy(e),{flat:r}=pe(i),n=0;for(;n<s;){let o=$t(r);if(!o)break;n++;let a=`[${(o.severity||"error").toUpperCase()} - ${o.type.toUpperCase()}]`,d=o.sourceLocation?` [${o.sourceLocation}]`:"";console.log(K.red(` \u{1F6A8} Global Dev Console Intercepted: ${a} "${o.errorText.slice(0,80)}"${d}`)),this.devDefectsList.push(o.sourceLocation?`${o.errorText} (${o.sourceLocation})`:o.errorText);let c=await this.driver.captureScreenshot(e);this.reporter.logStep("Dev Overlay Triage",`Intercepted and triaged ${a}: "${o.errorText}"${d}`,"Verify","FAIL",o.errorText,c,{errors:[o.sourceLocation?`${o.errorText} at ${o.sourceLocation}`:o.errorText]}),o.dismissButton&&o.dismissButton.bounds.width>0?(console.log(K.gray(" Dismissing overlay via dismiss button...")),await this.driver.tap(o.dismissButton.bounds.centerX,o.dismissButton.bounds.centerY,e)):o.isToast&&o.node&&o.node.bounds.width>0?(console.log(K.gray(" Expanding toast notification to access Dismiss button...")),await this.driver.tap(o.node.bounds.centerX,o.node.bounds.centerY,e)):(console.log(K.gray(" Pressing Back to dismiss overlay...")),await this.driver.pressBack(e)),await new Promise(l=>setTimeout(l,800)),i=await this.driver.getUiHierarchy(e),r=pe(i).flat}return{xml:i,flat:r}}async dismissSoftKeyboard(t){try{await this.driver.pressKey(111,t),await new Promise(e=>setTimeout(e,150))}catch{}}async packageJailGuard(t,e){let s=await this.driver.getForegroundPackage(e);if(!s)return!1;if(!s.includes(t)){let i=s.includes("launcher")||s.includes("home")||s.includes("trebuchet");console.log(K.yellow(` \u{1F6A8} External App Jailbreak Detected [${s}]. Recovering...`)),i||(await this.driver.pressBack(e),await new Promise(n=>setTimeout(n,800)));let r=await this.driver.getForegroundPackage(e);return(!r||!r.includes(t))&&(console.log(K.red(` Force restoring ${t} foreground...`)),await this.driver.launchApp(t,e),await new Promise(n=>setTimeout(n,1500)),this.callStack=this.callStack.slice(0,1)),!0}return!1}async handleExternalMediaChooser(t,e){let s=await this.driver.getForegroundPackage(e);if(!s)return!1;let i=s==="android"||s.includes("resolver")||s.includes("intentresolver");if(i){console.log(K.yellow(` \u{1F6E1}\uFE0F External Media Chooser Intercepted [${s}]. Dismissing to stay in-app...`)),await this.driver.pressBack(e),await new Promise(n=>setTimeout(n,800));let r=await this.driver.getForegroundPackage(e);return r&&!r.includes(t)&&i&&(await this.driver.launchApp(t,e),await new Promise(n=>setTimeout(n,1200)),this.callStack=this.callStack.slice(0,1)),!0}return!1}catalogScreen(t,e,s=[]){let i=[],r=e.height,n=r*.06,o=r*.88,a=f=>s.some(x=>Math.abs(x.bounds.centerX-f.bounds.centerX)<30&&Math.abs(x.bounds.centerY-f.bounds.centerY)<30),d=_e(t);for(let f of d){if(a(f))continue;let x=f.text||f.contentDesc||f.resourceId||"Text Input";i.push({node:f,type:"input",label:x,tested:!1})}let c=mt(t);for(let f of c){if(a(f))continue;let x=f.text||f.contentDesc||f.resourceId||"Toggle Control";i.push({node:f,type:"toggle",label:x,tested:!1})}let l=Ue(t);for(let f of l){if(a(f)||Ke([f],r,e.width)||De([f],r,e.width,!0))continue;let x=f.text||f.contentDesc||f.resourceId||"Action Button";if(Re(f)){this.externalSkippedList.push(x);continue}let T=f.selected!==void 0||f.checkable||/(?:tab|chip|pill|filter|segment|toggle)/i.test(`${f.resourceId} ${f.className} ${f.contentDesc}`)||/(?:today|yesterday|all|active|archived|pending|completed|present|absent)/i.test(x);i.push({node:f,type:T?"toggle":"transition",label:x,tested:!1})}let h=di(t,r,e.width);for(let f of h){if(a(f)||De([f],r,e.width,!0)||i.some(T=>Math.abs(T.node.bounds.centerX-f.bounds.centerX)<25&&Math.abs(T.node.bounds.centerY-f.bounds.centerY)<25))continue;let x=f.text||f.contentDesc||f.resourceId||"Notification / Header Action";if(Re(f)){this.externalSkippedList.push(x);continue}i.push({node:f,type:"transition",label:x,tested:!1})}let u=t.filter(f=>{if(!f.clickable||f.bounds.width===0)return!1;let x=`${f.text} ${f.contentDesc} ${f.resourceId}`.toLowerCase();return x.includes("now playing")||x.includes("play")||x.includes("pause")});for(let f of u){if(a(f)||i.some(T=>Math.abs(T.node.bounds.centerX-f.bounds.centerX)<25&&Math.abs(T.node.bounds.centerY-f.bounds.centerY)<25))continue;let x=f.text||f.contentDesc||f.resourceId||"Media Control";i.push({node:f,type:"toggle",label:x,tested:!1})}let g=nt(t,e).filter(f=>!a(f)&&f.bounds.centerY>n&&f.bounds.centerY<o&&!De([f],r,e.width,!0)&&!Re(f)),m=hi(g,2);for(let f of m){let x=f.text||f.contentDesc||f.resourceId||`Item (${f.bounds.centerX},${f.bounds.centerY})`;i.push({node:f,type:"transition",label:x,tested:!1})}let y=0,S=0,b=[];for(let f of i){let x=/\.(?:mp4|mkv|mov|webm|flv|avi)\b/i.test(f.label),T=/\.(?:mp3|wav|m4a|aac|ogg)\b/i.test(f.label);if(x){if(y++,y>1)continue}else if(T&&(S++,S>1))continue;b.push(f)}return b}async safeBacktrack(t,e,s,i,r=!0){let{flat:n}=await this.getCleanUiHierarchy(e,s),{height:o,width:a}=await this.driver.getScreenDimensions(s),d=We(n,{width:a,height:o});if(d===t)return!0;let c=De(n,o,a,!0);if(c&&c.bounds.width>0){console.log(K.gray(` [Backtrack] Tapping in-app back/close button "${c.text||c.contentDesc||c.className}"...`)),await this.driver.tap(c.bounds.centerX,c.bounds.centerY,s),await new Promise(u=>setTimeout(u,1e3)),await this.packageJailGuard(e,s);let l=await this.getCleanUiHierarchy(e,s),h=We(l.flat,{width:a,height:o});if(h===t||h!==d)return!0}if(i&&i.bounds.width>0){console.log(K.gray(` [Backtrack] Restoring root domain via tab "${i.text||i.contentDesc||"Root Tab"}"...`)),await this.driver.tap(i.bounds.centerX,i.bounds.centerY,s),await new Promise(u=>setTimeout(u,1e3)),await this.packageJailGuard(e,s);let l=await this.getCleanUiHierarchy(e,s),h=We(l.flat,{width:a,height:o});if(h===t||h!==d)return!0}if(r){if(console.log(K.gray(" [Backtrack] Sending fallback KEYCODE_BACK...")),await this.driver.pressKey(4,s),await new Promise(g=>setTimeout(g,1e3)),await this.packageJailGuard(e,s))return console.log(K.yellow(" \u26A0\uFE0F Backtrack via KEYCODE_BACK caused app jailbreak. Aborting branch restoration.")),!1;let h=await this.getCleanUiHierarchy(e,s),u=We(h.flat,{width:a,height:o});if(u===t||u!==d)return!0}return!1}async explore(t){let{packageName:e,serial:s,maxScreens:i=30,maxDepth:r=3,stepBudget:n=100,maxInteractionsPerScreen:o=6,safetyMode:a="strict",safetyBlacklist:d=[]}=t;console.log(K.bold.cyan(`
|
|
839
839
|
\u{1F916} PromptTest Universal State-Graph DFS Engine`)),console.log(K.gray(`Target Package: ${e}`)),console.log(K.gray(`Safety Policy: ${a.toUpperCase()}`)),console.log(K.gray(`Limits: Max Screens = ${i}, Step Budget = ${n}, Max Depth = ${r}`)),await this.driver.clearCrashLogs(s);let c=await this.driver.getScreenDimensions(s),l=await this.driver.getForegroundPackage(s);(!l||!l.includes(e))&&(console.log(K.gray(`Launching ${e}...`)),await this.driver.launchApp(e,s),await new Promise(S=>setTimeout(S,2500)));let{flat:h}=await this.getCleanUiHierarchy(e,s);await this.dismissSoftKeyboard(s);let u=is(h,c,e);console.log(K.magenta(`\u26A1 Universal Paradigm Detected: [${u.paradigm.toUpperCase()}] with ${u.roots.length} root node(s).`));let g=[],m;if(u.paradigm==="drawer"&&u.roots.length>0){m=u.roots[0];let S={...h[0]||{},text:"Home Screen / Main Hub",contentDesc:"Home Screen / Main Hub",clickable:!0,bounds:{x1:0,y1:0,x2:c.width,y2:c.height,centerX:c.width/2,centerY:c.height/2,width:c.width,height:c.height}};g.push(S),console.log(K.gray(" Opening drawer to catalog root destinations...")),await this.driver.tap(m.bounds.centerX,m.bounds.centerY,s),await new Promise(v=>setTimeout(v,1200));let b=await this.getCleanUiHierarchy(e,s),f=b.flat.filter(v=>{if(!v.clickable||v.bounds.width===0||v.bounds.height===0||v.bounds.width>=c.width*.95&&v.bounds.height>=c.height*.95||De([v],c.height,c.width,!0))return!1;let C=`${v.contentDesc} ${v.text}`.trim(),U=v.bounds.width>c.width*.15&&v.bounds.height>c.height*.05;return C.toLowerCase().includes("developed by")||v.bounds.centerY>c.height*.85?!1:C.length>=2||U});for(let v of f)g.some(C=>Math.abs(C.bounds.centerX-v.bounds.centerX)<30&&Math.abs(C.bounds.centerY-v.bounds.centerY)<30)||g.push(v);let x=De(b.flat,c.height,c.width,!0);x?await this.driver.tap(x.bounds.centerX,x.bounds.centerY,s):await this.driver.tap(m.bounds.centerX,m.bounds.centerY,s),await new Promise(v=>setTimeout(v,1200));let T=await this.getCleanUiHierarchy(e,s);if(T.flat.some(v=>`${v.contentDesc} ${v.text}`.includes("Gallery")&&`${v.contentDesc} ${v.text}`.includes("Planner"))){let v=De(T.flat,c.height,c.width,!0);v?await this.driver.tap(v.bounds.centerX,v.bounds.centerY,s):await this.driver.tap(c.width-50,c.height/2,s),await new Promise(C=>setTimeout(C,1e3))}console.log(K.magenta(` Discovered ${g.length} destinations (1 Main Hub + ${g.length-1} Drawer destinations).`))}else u.roots.length>0?g=u.roots:g=[h[0]||{text:"Main Hub",clickable:!0}];let y=1;for(let S of g){if(this.executedStepCount>=n||this.visitedScreenHashes.size>=i)break;let b=S.text||S.contentDesc||`Root #${y}`;if(console.log(K.yellow(`
|
|
840
840
|
[Domain ${y}/${g.length}] Entering Root Tree: "${b}"...`)),u.paradigm==="bottom_tabs"||u.paradigm==="top_tabs"){let T=S.bounds.height<25?Math.max(S.bounds.y1-10,S.bounds.centerY):Math.min(S.bounds.centerY,S.bounds.y2-4);await this.driver.tap(S.bounds.centerX,T,s),this.executedStepCount++,await new Promise($=>setTimeout($,1200))}else u.paradigm==="drawer"&&y>1&&m&&(await this.dismissSoftKeyboard(s),console.log(K.gray(` Opening drawer to select "${b}"...`)),await this.driver.tap(m.bounds.centerX,m.bounds.centerY,s),await new Promise(T=>setTimeout(T,1e3)),await this.driver.tap(S.bounds.centerX,S.bounds.centerY,s),this.executedStepCount++,await new Promise(T=>setTimeout(T,1500)));h=(await this.getCleanUiHierarchy(e,s)).flat,await this.dismissSoftKeyboard(s);let x=y===1?Math.max(25,Math.ceil(n*.45)):Math.max(8,Math.ceil(n*.55/Math.max(1,g.length-1)));if(await this.exploreDfsNode({packageName:e,serial:s,screenDims:c,rootNodes:u.roots,activeRootNode:S,currentDepth:0,maxDepth:r,maxScreens:i,stepBudget:n,domainStepLimit:this.executedStepCount+x,maxInteractionsPerScreen:o,safetyMode:a,safetyBlacklist:d,domainLabel:b}),u.paradigm==="drawer"&&y>1){let T=0;for(;T<4;){let $=await this.getCleanUiHierarchy(e,s),v=Ke($.flat,c.height,c.width),C=$.flat.some(G=>`${G.contentDesc} ${G.text}`.includes("Gallery")&&`${G.contentDesc} ${G.text}`.includes("Planner"));if(v&&!C)break;let U=De($.flat,c.height,c.width,!0);if(U)console.log(K.gray(" [Domain Wrap-Up] Returning to Home Screen via in-app back...")),await this.driver.tap(U.bounds.centerX,U.bounds.centerY,s),await new Promise(G=>setTimeout(G,1e3)),T++;else break}}if(y===1&&g.length===1){let T=await this.getCleanUiHierarchy(e,s),$=is(T.flat,c,e);if(($.paradigm==="bottom_tabs"||$.paradigm==="top_tabs")&&$.roots.length>1){console.log(K.magenta(`
|
|
841
841
|
\u26A1 Post-Auth Navigation Discovered: [${$.paradigm.toUpperCase()}] with ${$.roots.length} root domain(s)!`)),u.paradigm=$.paradigm,u.roots=$.roots;let v=$.roots.slice(1);g.push(...v.length>0?v:$.roots)}}y++}for(let S of this.visitedScreenHashes)this.memory.markScreenCrawled(S);await this.memory.save(),console.log(K.green(`
|
|
842
|
-
\u2714 Universal DFS Traversal Complete!`)),console.log(K.cyan(` Visited Screens: ${this.visitedScreenHashes.size}`)),console.log(K.cyan(` Total Steps Executed: ${this.executedStepCount}/${n}`)),console.log(K.cyan(` Defects Triaged: ${this.devDefectsList.length}`))}async exploreDfsNode(t){let{packageName:e,serial:s,screenDims:i,rootNodes:r,activeRootNode:n,currentDepth:o,maxDepth:a,maxScreens:d,stepBudget:c,domainStepLimit:l,maxInteractionsPerScreen:h,safetyMode:u,safetyBlacklist:g,domainLabel:m}=t;if(this.executedStepCount>=c||this.visitedScreenHashes.size>=d||l&&this.executedStepCount>=l)return;let{flat:y}=await this.getCleanUiHierarchy(e,s),S=ge(y),b=We(y,i);this.visitedScreenHashes.add(S);let f=this.screenLedger.get(b);if(f){if(f.status==="FULLY_EXPLORED"){console.log(K.gray(` \u26A1 Cycle Detected: Screen [${b.slice(0,8)}] already fully explored. Pruning branch.`));return}}else{let v=this.catalogScreen(y,i,r);f={hash:S,semanticHash:b,title:`${m} (depth ${o})`,elements:v,status:"IN_PROGRESS",depth:o,interactionCount:0},this.screenLedger.set(b,f),console.log(K.cyan(` \u{1F50E} Discovered Screen [${b.slice(0,8)}] at depth ${o} with ${v.length} candidate element(s).`))}this.callStack.push({screenHash:S,semanticHash:b,title:f.title,timestamp:Date.now()});let x=f.elements.filter(v=>v.type==="input"&&!v.tested);if(x.length>0&&f.interactionCount<h){console.log(K.magenta(` Filling in-place form fields (${x.length} inputs)...`));let v=await this.formFiller.fillAndSubmitForm(y,s,!0,!1);this.executedStepCount++,f.interactionCount++;for(let C of x)C.tested=!0;await this.dismissSoftKeyboard(s),await this.packageJailGuard(e,s),this.reporter.logStep("In-Place Form Fill",`Populated ${v.filledFields.length} field(s) on [${b.slice(0,8)}]`,"FormSubmit","PASS",v.submitButtonText||"Auto")}let T=f.elements.filter(v=>v.type==="toggle"&&!v.tested);for(let v of T){if(this.executedStepCount>=c||f.interactionCount>=h)break;if(v.tested=!0,Ie(v.node,g,u)){this.safetyHitList.push(v.label);continue}console.log(K.gray(` Exercising toggle: "${v.label.slice(0,30)}"...`)),this.executedStepCount++,f.interactionCount++,await this.driver.tap(v.node.bounds.centerX,v.node.bounds.centerY,s),await new Promise(C=>setTimeout(C,600)),this.reporter.logStep("In-Place Toggle",`Toggled control "${v.label}"`,"Tap","PASS",v.label)}let $=f.elements.filter(v=>v.type==="transition"&&!v.tested);for(let v of $){if(this.executedStepCount>=c||this.visitedScreenHashes.size>=d||l&&this.executedStepCount>=l&&!(o===0&&m.includes("Home Screen"))||f.interactionCount>=h)break;if(v.tested=!0,Re(v.node)){this.externalSkippedList.push(v.label);continue}if(Ie(v.node,g,u)){this.safetyHitList.push(v.label);continue}console.log(K.magenta(` \u26A1 [DFS Depth ${o}] Testing transition: "${v.label.slice(0,40)}"...`)),this.executedStepCount++,f.interactionCount++,await this.driver.tap(v.node.bounds.centerX,v.node.bounds.centerY,s),await new Promise(H=>setTimeout(H,1500)),await this.dismissSoftKeyboard(s),await this.handleExternalMediaChooser(e,s),await this.packageJailGuard(e,s);let C=await this.getCleanUiHierarchy(e,s),G=We(C.flat,i)!==b;if(this.reporter.logStep("DFS Transition",`Tapped "${v.label}" [${G?"Navigated":"In-Place"}]`,"Tap","PASS",v.label),G){if(o<a){let J=o===0&&m.includes("Home Screen")?Math.min(this.executedStepCount+3,l||this.executedStepCount+3):l;await this.exploreDfsNode({packageName:e,serial:s,screenDims:i,rootNodes:r,activeRootNode:n,domainStepLimit:J,currentDepth:o+1,maxDepth:a,maxScreens:d,stepBudget:c,maxInteractionsPerScreen:h,safetyMode:u,safetyBlacklist:g,domainLabel:`${m} > ${v.label.slice(0,20)}`})}console.log(K.gray(` [DFS Backtrack] Returning to [${b.slice(0,8)}]...`)),await this.safeBacktrack(b,e,s,o===0?n:void 0,o>0),await this.dismissSoftKeyboard(s);let H=await this.getCleanUiHierarchy(e,s);if(We(H.flat,i)!==b){let J=De(H.flat,i.height,i.width,!0);J&&(console.log(K.gray(" [Backtrack Verification] Re-tapping in-app back/close button...")),await this.driver.tap(J.bounds.centerX,J.bounds.centerY,s),await new Promise(me=>setTimeout(me,1e3)),await this.packageJailGuard(e,s));let re=await this.getCleanUiHierarchy(e,s);if(We(re.flat,i)!==b){console.log(K.yellow(` \u26A0\uFE0F Unable to restore parent screen [${b.slice(0,8)}]. Unwinding branch to prevent jailbreak loop.`));break}}}}f.status="FULLY_EXPLORED",this.callStack.pop()}};var Rt=class{driver;formFiller;reporter;memory;safetyHitList=[];externalSkippedList=[];devDefectsList=[];visitedScreenHashes=new Set;executedStepCount=0;constructor(t,e,s,i){this.driver=t||Q,this.formFiller=e||xi,this.reporter=s||bt,this.memory=i||ve}getMemory(){return this.memory}getSafetyHitList(){return this.safetyHitList}getExternalSkippedList(){return this.externalSkippedList}getDevDefectsList(){return this.devDefectsList}getVisitedScreensCount(){return this.visitedScreenHashes.size}async packageJailGuard(t,e){let s=await this.driver.getForegroundPackage(e);if(s&&!s.includes(t)){console.log(D.yellow(` \u{1F6E1}\uFE0F Jail Containment: App escaped to "${s}". Executing containment recovery...`)),await this.driver.pressBack(e),await new Promise(
|
|
842
|
+
\u2714 Universal DFS Traversal Complete!`)),console.log(K.cyan(` Visited Screens: ${this.visitedScreenHashes.size}`)),console.log(K.cyan(` Total Steps Executed: ${this.executedStepCount}/${n}`)),console.log(K.cyan(` Defects Triaged: ${this.devDefectsList.length}`))}async exploreDfsNode(t){let{packageName:e,serial:s,screenDims:i,rootNodes:r,activeRootNode:n,currentDepth:o,maxDepth:a,maxScreens:d,stepBudget:c,domainStepLimit:l,maxInteractionsPerScreen:h,safetyMode:u,safetyBlacklist:g,domainLabel:m}=t;if(this.executedStepCount>=c||this.visitedScreenHashes.size>=d||l&&this.executedStepCount>=l)return;let{flat:y}=await this.getCleanUiHierarchy(e,s),S=ge(y),b=We(y,i);this.visitedScreenHashes.add(S);let f=this.screenLedger.get(b);if(f){if(f.status==="FULLY_EXPLORED"){console.log(K.gray(` \u26A1 Cycle Detected: Screen [${b.slice(0,8)}] already fully explored. Pruning branch.`));return}}else{let v=this.catalogScreen(y,i,r);f={hash:S,semanticHash:b,title:`${m} (depth ${o})`,elements:v,status:"IN_PROGRESS",depth:o,interactionCount:0},this.screenLedger.set(b,f),console.log(K.cyan(` \u{1F50E} Discovered Screen [${b.slice(0,8)}] at depth ${o} with ${v.length} candidate element(s).`))}this.callStack.push({screenHash:S,semanticHash:b,title:f.title,timestamp:Date.now()});let x=f.elements.filter(v=>v.type==="input"&&!v.tested);if(x.length>0&&f.interactionCount<h){console.log(K.magenta(` Filling in-place form fields (${x.length} inputs)...`));let v=await this.formFiller.fillAndSubmitForm(y,s,!0,!1);this.executedStepCount++,f.interactionCount++;for(let C of x)C.tested=!0;await this.dismissSoftKeyboard(s),await this.packageJailGuard(e,s),this.reporter.logStep("In-Place Form Fill",`Populated ${v.filledFields.length} field(s) on [${b.slice(0,8)}]`,"FormSubmit","PASS",v.submitButtonText||"Auto")}let T=f.elements.filter(v=>v.type==="toggle"&&!v.tested);for(let v of T){if(this.executedStepCount>=c||f.interactionCount>=h)break;if(v.tested=!0,Ie(v.node,g,u)){this.safetyHitList.push(v.label);continue}console.log(K.gray(` Exercising toggle: "${v.label.slice(0,30)}"...`)),this.executedStepCount++,f.interactionCount++,await this.driver.tap(v.node.bounds.centerX,v.node.bounds.centerY,s),await new Promise(C=>setTimeout(C,600)),this.reporter.logStep("In-Place Toggle",`Toggled control "${v.label}"`,"Tap","PASS",v.label)}let $=f.elements.filter(v=>v.type==="transition"&&!v.tested);for(let v of $){if(this.executedStepCount>=c||this.visitedScreenHashes.size>=d||l&&this.executedStepCount>=l&&!(o===0&&m.includes("Home Screen"))||f.interactionCount>=h)break;if(v.tested=!0,Re(v.node)){this.externalSkippedList.push(v.label);continue}if(Ie(v.node,g,u)){this.safetyHitList.push(v.label);continue}console.log(K.magenta(` \u26A1 [DFS Depth ${o}] Testing transition: "${v.label.slice(0,40)}"...`)),this.executedStepCount++,f.interactionCount++,await this.driver.tap(v.node.bounds.centerX,v.node.bounds.centerY,s),await new Promise(H=>setTimeout(H,1500)),await this.dismissSoftKeyboard(s),await this.handleExternalMediaChooser(e,s),await this.packageJailGuard(e,s);let C=await this.getCleanUiHierarchy(e,s),G=We(C.flat,i)!==b;if(this.reporter.logStep("DFS Transition",`Tapped "${v.label}" [${G?"Navigated":"In-Place"}]`,"Tap","PASS",v.label),G){if(o<a){let J=o===0&&m.includes("Home Screen")?Math.min(this.executedStepCount+3,l||this.executedStepCount+3):l;await this.exploreDfsNode({packageName:e,serial:s,screenDims:i,rootNodes:r,activeRootNode:n,domainStepLimit:J,currentDepth:o+1,maxDepth:a,maxScreens:d,stepBudget:c,maxInteractionsPerScreen:h,safetyMode:u,safetyBlacklist:g,domainLabel:`${m} > ${v.label.slice(0,20)}`})}console.log(K.gray(` [DFS Backtrack] Returning to [${b.slice(0,8)}]...`)),await this.safeBacktrack(b,e,s,o===0?n:void 0,o>0),await this.dismissSoftKeyboard(s);let H=await this.getCleanUiHierarchy(e,s);if(We(H.flat,i)!==b){let J=De(H.flat,i.height,i.width,!0);J&&(console.log(K.gray(" [Backtrack Verification] Re-tapping in-app back/close button...")),await this.driver.tap(J.bounds.centerX,J.bounds.centerY,s),await new Promise(me=>setTimeout(me,1e3)),await this.packageJailGuard(e,s));let re=await this.getCleanUiHierarchy(e,s);if(We(re.flat,i)!==b){console.log(K.yellow(` \u26A0\uFE0F Unable to restore parent screen [${b.slice(0,8)}]. Unwinding branch to prevent jailbreak loop.`));break}}}}f.status="FULLY_EXPLORED",this.callStack.pop()}};var Rt=class{driver;formFiller;reporter;memory;safetyHitList=[];externalSkippedList=[];devDefectsList=[];visitedScreenHashes=new Set;executedStepCount=0;constructor(t,e,s,i){this.driver=t||Q,this.formFiller=e||xi,this.reporter=s||bt,this.memory=i||ve}getMemory(){return this.memory}getSafetyHitList(){return this.safetyHitList}getExternalSkippedList(){return this.externalSkippedList}getDevDefectsList(){return this.devDefectsList}getVisitedScreensCount(){return this.visitedScreenHashes.size}async packageJailGuard(t,e){let s=await this.driver.getForegroundPackage(e);if(s&&!s.includes(t)){let i=s.includes("launcher")||s.includes("home")||s.includes("trebuchet");console.log(D.yellow(` \u{1F6E1}\uFE0F Jail Containment: App escaped to "${s}". Executing containment recovery...`)),i||(await this.driver.pressBack(e),await new Promise(n=>setTimeout(n,600)));let r=await this.driver.getForegroundPackage(e);return(!r||!r.includes(t))&&(console.log(D.gray(` Force re-launching ${t} to restore foreground...`)),await this.driver.launchApp(t,e),await new Promise(n=>setTimeout(n,1200))),this.reporter.logStep("Jail Containment",`App escaped to "${s}"; successfully contained and restored "${t}"`,"Back","PASS"),!0}return!1}async getCleanUiHierarchy(t,e,s=5){let i=await this.driver.getUiHierarchy(e),{flat:r}=pe(i),n=0;for(;n<s;){let o=$t(r);if(!o)break;n++;let a=`[${(o.severity||"error").toUpperCase()} - ${o.type.toUpperCase()}]`,d=o.sourceLocation?` [${o.sourceLocation}]`:"";console.log(D.red(` \u{1F6A8} Global Dev Console Intercepted: ${a} "${o.errorText.slice(0,80)}"${d}`)),this.devDefectsList.push(o.sourceLocation?`${o.errorText} (${o.sourceLocation})`:o.errorText);let c=await this.driver.captureScreenshot(e);this.reporter.logStep("Dev Overlay Triage",`Intercepted and triaged ${a}: "${o.errorText}"${d}`,"Verify","FAIL",o.errorText,c,{errors:[o.sourceLocation?`${o.errorText} at ${o.sourceLocation}`:o.errorText]}),o.dismissButton&&o.dismissButton.bounds.width>0?(console.log(D.gray(" Dismissing overlay via dismiss button...")),await this.driver.tap(o.dismissButton.bounds.centerX,o.dismissButton.bounds.centerY,e)):o.isToast&&o.node&&o.node.bounds.width>0?(console.log(D.gray(" Expanding toast notification to access Dismiss button...")),await this.driver.tap(o.node.bounds.centerX,o.node.bounds.centerY,e)):(console.log(D.gray(" Pressing Back to dismiss overlay...")),await this.driver.pressBack(e)),await new Promise(l=>setTimeout(l,800)),i=await this.driver.getUiHierarchy(e),r=pe(i).flat}return{xml:i,flat:r}}async handleDevOverlays(t,e,s){let i=$t(t);if(!i)return!1;let r=`[${(i.severity||"error").toUpperCase()} - ${i.type.toUpperCase()}]`,n=i.sourceLocation?` [${i.sourceLocation}]`:"";console.log(D.red(` \u{1F6A8} Dev Overlay Detected: ${r} "${i.errorText.slice(0,80)}"${n}`)),this.devDefectsList.push(i.sourceLocation?`${i.errorText} (${i.sourceLocation})`:i.errorText);let o=await this.driver.captureScreenshot(s);return this.reporter.logStep("Dev Overlay Triage",`Detected and triaged ${r} error: "${i.errorText}"${n}`,"Verify","FAIL",i.errorText,o,{errors:[i.sourceLocation?`${i.errorText} (${i.sourceLocation})`:i.errorText]}),i.dismissButton&&i.dismissButton.bounds.width>0?(console.log(D.gray(" Dismissing error overlay via dismiss button...")),await this.driver.tap(i.dismissButton.bounds.centerX,i.dismissButton.bounds.centerY,s)):i.isToast&&i.node&&i.node.bounds.width>0?(console.log(D.gray(" Expanding toast notification to access Dismiss button...")),await this.driver.tap(i.node.bounds.centerX,i.node.bounds.centerY,s)):(console.log(D.gray(" Pressing Back to dismiss error overlay...")),await this.driver.pressBack(s)),await new Promise(a=>setTimeout(a,800)),!0}async dismissSoftKeyboard(t){try{await this.driver.pressKey(111,t),await new Promise(e=>setTimeout(e,200))}catch{}}async explore(t){let{packageName:e,serial:s,maxTabs:i=P.explorer?.maxTabs||15,exploreLists:r=!0,skipAlreadyCrawled:n=!1,safetyMode:o=P.safety?.mode||"strict",safetyBlacklist:a=P.safety?.customBlacklist?[...P.safety.customBlacklist]:[],maxScreens:d=P.explorer?.maxScreens||30,stepBudget:c=P.explorer?.stepBudget||100,screenshots:l=P.explorer?.screenshots||"state-change"}=t;console.log(D.cyan(`
|
|
843
843
|
\u{1F916} PromptTest Autonomous Exploration Brain v3`)),console.log(D.gray("Target Package: ")+D.bold.white(e)),console.log(D.gray("Safety Policy: ")+D.bold.yellow(o.toUpperCase())+(a.length>0?D.gray(` (+${a.length} custom blacklist)`):"")),console.log(D.gray("Screenshots Policy: ")+D.bold.white(l));let h=async(y,S=!0)=>{if(!(l==="failure-only"&&y!=="FAIL")&&!(l==="state-change"&&!S&&y!=="FAIL"))try{return await this.driver.captureScreenshot(s)}catch{return}};await this.memory.init(e);let u=s||this.driver.getDefaultDevice(),g=u||"Default Device";this.reporter.init(e,g);let m=null;u&&(m=ct(u)),this.safetyHitList=[],this.externalSkippedList=[],this.devDefectsList=[],this.visitedScreenHashes.clear(),this.executedStepCount=0;try{if(await this.driver.clearCrashLogs(s),(t.strategy||P.explorer?.strategy||"heuristic")==="dfs"){let se=new Vt(this.driver,this.reporter,this.memory);await se.explore({packageName:e,serial:s,maxScreens:d,maxDepth:t.maxDepth||P.explorer?.maxDepth||5,stepBudget:c,safetyMode:o,safetyBlacklist:a,screenshotPolicy:l,skipAlreadyCrawled:n}),this.safetyHitList=se.safetyHitList,this.externalSkippedList=se.externalSkippedList,this.devDefectsList=se.devDefectsList,this.visitedScreenHashes=se.visitedScreenHashes,this.executedStepCount=se.executedStepCount;let X=this.memory.getStats();this.reporter.logStep("Coverage Audit",`State Graph Coverage: Visited ${this.visitedScreenHashes.size} unique screen state(s). Memory Engine cached ${X.totalScreens} crawled screen(s).`,"Verify","PASS"),await this.reporter.generateReport(),console.log(D.bold.green(`
|
|
844
844
|
\u2728 Full Audit Report generated at: output/prompttest-report.md`));return}let{width:S,height:b}=await this.driver.getScreenDimensions(s);console.log(D.yellow(`
|
|
845
845
|
[1/5] Verifying Foreground State...`));let f=await this.driver.getForegroundPackage(s);(!f||!f.includes(e))&&(console.log(D.gray(`Launching ${e}...`)),await this.driver.launchApp(e,s),await new Promise(se=>setTimeout(se,2500)));let x=await this.runErrorSentinel(e,s,"Post-Launch"),T=x.hasError?"FAIL":"PASS",$=await h(T,!0);this.reporter.logStep("Init",`Launched and verified foreground for ${e}`,"Launch",T,e,$,x.hasError?{errors:x.errors}:void 0),console.log(T==="PASS"?D.green("\u2714 App is active in foreground."):D.red(`\u2716 Issues detected during launch for ${e}.`)),console.log(D.yellow(`
|
|
@@ -847,8 +847,8 @@ ${t}`),console.log(I.bold.green(" \u{1F4BE} Recording Session Saved Successful
|
|
|
847
847
|
[3/5] Inspecting Interactive Action Controls...`));let X=Ue(C).filter(A=>{let M=(A.text+" "+A.contentDesc).toLowerCase();if(M.includes("submit")||M.includes("cancel")||Ke([A],b))return!1;if(Re(A)){let R=A.text||A.contentDesc||A.resourceId||"External Link";return console.log(D.gray(` \u{1F517} External Link Bypassed: "${R}"`)),this.externalSkippedList.push(R),!1}if(Ie(A,a,o)){let R=A.text||A.contentDesc||A.resourceId||"Action";return console.log(D.yellow(` \u{1F6E1}\uFE0F Safety Guard: Blocked destructive action "${R}"`)),this.safetyHitList.push(R),!1}return!0});if(X.length>0){let A=X[0],M=A.text||A.contentDesc||A.resourceId;console.log(D.magenta(`\u26A1 Found action button: "${M}". Testing interaction...`));let R=ge(C);this.executedStepCount++,await this.driver.tap(A.bounds.centerX,A.bounds.centerY,s),await new Promise(j=>setTimeout(j,1200)),await this.packageJailGuard(e,s);let F=await this.getCleanUiHierarchy(e,s);v=F.xml,C=F.flat;let V=ge(C);this.visitedScreenHashes.add(V);let E=await this.runErrorSentinel(e,s,"Post-Action-Tap"),w=R!==V,N="PASS",L="";E.hasError?(N="FAIL",L="Action triggered error or crash"):w?(N="PASS",L="Screen transitioned successfully"):(N="WARN",L="No visible screen transition or state change observed (No-Op)",console.log(D.yellow(` \u26A0\uFE0F Outcome Warning: Action "${M}" produced no UI state change.`)));let le=await h(N,w);this.reporter.logStep("Action Testing",`Tapped action button "${M}" [${L}]`,"Tap",N,M,le,E.hasError?{errors:E.errors}:void 0),console.log(N==="PASS"?D.green(`\u2714 Action interaction complete (${L}).`):N==="WARN"?D.yellow(`\u2714 Action interaction recorded with warning (${L}).`):D.red("\u2716 Action interaction triggered an error.")),N!=="FAIL"&&(this.memory.recordSuccess("TAP",M,A.resourceId||A.text||A.contentDesc||M,"text",1,void 0,R),this.memory.markScreenCrawled(R,[M]))}else console.log(D.gray("No standalone action buttons to test."));let O=mt(C).filter(A=>!Ie(A,a,o));if(O.length>0){console.log(D.yellow(`
|
|
848
848
|
[3b/5] Inspecting Toggle & Checkable Controls...`));let A=O[0],M=A.text||A.contentDesc||A.resourceId||"Switch/Checkbox",R=A.checked;console.log(D.magenta(`\u26A1 Found toggle: "${M}" (checked: ${R}). Testing toggle interaction...`)),this.executedStepCount++,await this.driver.tap(A.bounds.centerX,A.bounds.centerY,s),await new Promise(j=>setTimeout(j,1e3)),v=await this.driver.getUiHierarchy(s),C=pe(v).flat;let F=ge(C);this.visitedScreenHashes.add(F);let E=mt(C).find(j=>Math.abs(j.bounds.centerX-A.bounds.centerX)<25&&Math.abs(j.bounds.centerY-A.bounds.centerY)<25),w=E?E.checked!==R:!0,N=await this.runErrorSentinel(e,s,"Post-Toggle"),L=N.hasError?"FAIL":w?"PASS":"WARN",le=await h(L,w);this.reporter.logStep("Toggle Testing",`Toggled "${M}" (Checked: ${R} -> ${E?E.checked:"flipped"})`,"Toggle",L,M,le,N.hasError?{errors:N.errors}:void 0),console.log(L==="PASS"?D.green("\u2714 Toggle control flipped successfully!"):L==="WARN"?D.yellow("\u26A0\uFE0F Toggle tapped but checked state did not flip."):D.red("\u2716 Toggle control triggered an error.")),L!=="FAIL"&&this.memory.markScreenCrawled(F,[M])}let Y=Ke(C,b);if(Y&&!Ie(Y,a,o)){let A=Y.contentDesc||Y.text||Y.resourceId||"Drawer Menu";console.log(D.yellow(`
|
|
849
849
|
[3c/5] Inspecting Side Navigation Drawer...`)),console.log(D.magenta(`\u26A1 Found drawer trigger: "${A}". Opening side drawer...`));let M=ge(C);this.executedStepCount++,await this.driver.tap(Y.bounds.centerX,Y.bounds.centerY,s),await new Promise(L=>setTimeout(L,1200)),await this.packageJailGuard(e,s);let R=await this.getCleanUiHierarchy(e,s);v=R.xml,C=R.flat;let F=ge(C);this.visitedScreenHashes.add(F);let V=M!==F,E=await this.runErrorSentinel(e,s,"Drawer-Open"),w=E.hasError?"FAIL":V?"PASS":"WARN",N=await h(w,V);if(this.reporter.logStep("Drawer Navigation",`Opened side drawer via "${A}"`,"Tap",w,A,N,E.hasError?{errors:E.errors}:void 0),w==="PASS"){console.log(D.green("\u2714 Side drawer opened successfully!"));let L=Ue(C).filter(j=>!Ie(j,a,o)&&!Re(j)&&j.bounds.centerY>Y.bounds.y2);if(L.length>0){let j=L[0],Se=j.text||j.contentDesc||j.resourceId||"Drawer Item";console.log(D.gray(` - Testing drawer item: "${Se}"...`)),this.executedStepCount++,await this.driver.tap(j.bounds.centerX,j.bounds.centerY,s),await new Promise(ne=>setTimeout(ne,1200)),await this.packageJailGuard(e,s);let Ne=await this.getCleanUiHierarchy(e,s);v=Ne.xml,C=Ne.flat,this.visitedScreenHashes.add(ge(C))}console.log(D.gray(" Closing side drawer to restore main view...")),await this.driver.pressBack(s),await new Promise(j=>setTimeout(j,1e3));let le=await this.getCleanUiHierarchy(e,s);v=le.xml,C=le.flat,this.visitedScreenHashes.add(ge(C)),console.log(D.green("\u2714 Main view restored from side drawer."))}else w==="WARN"&&console.log(D.yellow("\u26A0\uFE0F Drawer button tapped but no visible screen change occurred."));w!=="FAIL"&&this.memory.markScreenCrawled(M,[A])}if(r){console.log(D.yellow(`
|
|
850
|
-
[4/5] Inspecting Lists & Master-Detail Flows...`));let A=ui(C);if(A.length>0){console.log(D.magenta(`\u26A1 Found ${A.length} scrollable container(s). Performing vertical scroll exploration...`));let R=ge(C),F=Math.round(S*.5);await this.driver.swipe(F,Math.round(b*.7),F,Math.round(b*.3),300,s),await new Promise(L=>setTimeout(L,1200));let V=await this.getCleanUiHierarchy(e,s);v=V.xml,C=V.flat;let E=ge(C);this.visitedScreenHashes.add(E);let w=R!==E,N=await h("PASS",w);this.reporter.logStep("Scroll Exploration",w?"Scrolled vertically to discover off-screen elements":"Scrolled but content remained at edge","Swipe","PASS","SwipeUp",N),w&&console.log(D.green("\u2714 Vertical scroll succeeded; discovered off-screen content below fold."))}let M=nt(C).filter(R=>{if(Re(R)){let F=R.text||R.contentDesc||"External Link";return this.externalSkippedList.push(F),!1}if(Ie(R,a,o)){let F=R.text||R.contentDesc||"List Item";return console.log(D.yellow(` \u{1F6E1}\uFE0F Safety Guard: Blocked destructive list item "${F}"`)),this.safetyHitList.push(F),!1}return!0});if(M.length>0){let R=M[0],F=R.text||R.contentDesc||`Item at (${R.bounds.centerX}, ${R.bounds.centerY})`;console.log(D.magenta(`\u26A1 Found list item: "${F}". Diving into detail view...`));let V=ge(C);await this.driver.tap(R.bounds.centerX,R.bounds.centerY,s),await new Promise(Me=>setTimeout(Me,1800));let E=await this.getCleanUiHierarchy(e,s);v=E.xml,C=E.flat;let w=ge(C);this.visitedScreenHashes.add(w);let N=await this.runErrorSentinel(e,s,"Detail-View"),L=V!==w,le=N.hasError?"FAIL":L?"PASS":"WARN",j=await h(le,L);this.reporter.logStep("List Dive",`Dived into detail view for "${F}" [${L?"Navigated":"Unchanged"}]`,"Tap",le,F,j,N.hasError?{errors:N.errors}:void 0),console.log(le==="PASS"?D.green("\u2714 Detail screen loaded."):le==="WARN"?D.yellow("\u26A0\uFE0F Detail view tapped but no navigation detected."):D.red("\u2716 Detail view triggered an error."));let Se=De(E.flat,P.screen.fallbackHeight,P.screen.fallbackWidth,!0);Se&&Se.bounds.width>0?(console.log(D.gray(`Navigating back via in-app back button "${Se.text||Se.contentDesc||Se.className}"...`)),await this.driver.tap(Se.bounds.centerX,Se.bounds.centerY,s)):(console.log(D.gray("Navigating back to main view...")),await this.driver.pressKey(4,s)),await new Promise(Me=>setTimeout(Me,1200));let Ne=await this.driver.getForegroundPackage(s);
|
|
851
|
-
[5/5] Inspecting Navigation Tabs...`));let H=await this.driver.getForegroundPackage(s);
|
|
850
|
+
[4/5] Inspecting Lists & Master-Detail Flows...`));let A=ui(C);if(A.length>0){console.log(D.magenta(`\u26A1 Found ${A.length} scrollable container(s). Performing vertical scroll exploration...`));let R=ge(C),F=Math.round(S*.5);await this.driver.swipe(F,Math.round(b*.7),F,Math.round(b*.3),300,s),await new Promise(L=>setTimeout(L,1200));let V=await this.getCleanUiHierarchy(e,s);v=V.xml,C=V.flat;let E=ge(C);this.visitedScreenHashes.add(E);let w=R!==E,N=await h("PASS",w);this.reporter.logStep("Scroll Exploration",w?"Scrolled vertically to discover off-screen elements":"Scrolled but content remained at edge","Swipe","PASS","SwipeUp",N),w&&console.log(D.green("\u2714 Vertical scroll succeeded; discovered off-screen content below fold."))}let M=nt(C).filter(R=>{if(Re(R)){let F=R.text||R.contentDesc||"External Link";return this.externalSkippedList.push(F),!1}if(Ie(R,a,o)){let F=R.text||R.contentDesc||"List Item";return console.log(D.yellow(` \u{1F6E1}\uFE0F Safety Guard: Blocked destructive list item "${F}"`)),this.safetyHitList.push(F),!1}return!0});if(M.length>0){let R=M[0],F=R.text||R.contentDesc||`Item at (${R.bounds.centerX}, ${R.bounds.centerY})`;console.log(D.magenta(`\u26A1 Found list item: "${F}". Diving into detail view...`));let V=ge(C);await this.driver.tap(R.bounds.centerX,R.bounds.centerY,s),await new Promise(Me=>setTimeout(Me,1800));let E=await this.getCleanUiHierarchy(e,s);v=E.xml,C=E.flat;let w=ge(C);this.visitedScreenHashes.add(w);let N=await this.runErrorSentinel(e,s,"Detail-View"),L=V!==w,le=N.hasError?"FAIL":L?"PASS":"WARN",j=await h(le,L);this.reporter.logStep("List Dive",`Dived into detail view for "${F}" [${L?"Navigated":"Unchanged"}]`,"Tap",le,F,j,N.hasError?{errors:N.errors}:void 0),console.log(le==="PASS"?D.green("\u2714 Detail screen loaded."):le==="WARN"?D.yellow("\u26A0\uFE0F Detail view tapped but no navigation detected."):D.red("\u2716 Detail view triggered an error."));let Se=De(E.flat,P.screen.fallbackHeight,P.screen.fallbackWidth,!0);Se&&Se.bounds.width>0?(console.log(D.gray(`Navigating back via in-app back button "${Se.text||Se.contentDesc||Se.className}"...`)),await this.driver.tap(Se.bounds.centerX,Se.bounds.centerY,s)):(console.log(D.gray("Navigating back to main view...")),await this.driver.pressKey(4,s)),await new Promise(Me=>setTimeout(Me,1200));let Ne=await this.driver.getForegroundPackage(s);Ne&&!Ne.includes(e)&&(console.log(D.yellow(`Restoring ${e} foreground after back navigation...`)),await this.driver.launchApp(e,s),await new Promise(Me=>setTimeout(Me,2e3)));let ne=await this.getCleanUiHierarchy(e,s);v=ne.xml,C=ne.flat,this.visitedScreenHashes.add(ge(C));let fe=await this.runErrorSentinel(e,s,"Post-Back-Restore"),Ge=fe.hasError?"FAIL":"PASS",_t=await h(Ge,!0);this.reporter.logStep("List Dive","Navigated back to master screen","Back",Ge,"KEYCODE_BACK",_t,fe.hasError?{errors:fe.errors}:void 0),Ge==="PASS"&&console.log(D.green("\u2714 Master view restored."))}else console.log(D.gray("No list items found for master-detail exploration."))}}console.log(D.yellow(`
|
|
851
|
+
[5/5] Inspecting Navigation Tabs...`));let H=await this.driver.getForegroundPackage(s);H&&!H.includes(e)&&(console.log(D.gray(`Restoring ${e} foreground for tab traversal...`)),await this.driver.launchApp(e,s),await new Promise(se=>setTimeout(se,2e3))),await this.dismissSoftKeyboard(s);let be=await this.getCleanUiHierarchy(e,s);v=be.xml,C=be.flat;let J=Dt(C,void 0,e);if(J.length>1){console.log(D.magenta(`\u26A1 Found ${J.length} navigation tab(s). Traversal sequence initiated...`));let se=J.slice(0,i),X=1;for(let O of se){let Y=O.text||O.contentDesc||`Tab #${X}`;console.log(D.gray(` - Visiting ${Y}...`)),await this.dismissSoftKeyboard(s);let A=Math.min(O.bounds.centerY,O.bounds.y2-4);this.executedStepCount++,await this.driver.tap(O.bounds.centerX,A,s),await new Promise(w=>setTimeout(w,1500)),await this.packageJailGuard(e,s);let M=await this.getCleanUiHierarchy(e,s);v=M.xml,C=M.flat;let R=ge(C);this.visitedScreenHashes.add(R),this.memory.markScreenCrawled(R,[Y]);let F=await this.runErrorSentinel(e,s,`Tab-${Y}`),V=F.hasError?"FAIL":"PASS",E=await h(V,!0);if(this.reporter.logStep("Tab Traversal",`Navigated to tab "${Y}"`,"Tap",V,Y,E,F.hasError?{errors:F.errors}:void 0),console.log(V==="PASS"?D.green(` \u2714 Tab "${Y}" loaded cleanly.`):D.red(` \u2716 Tab "${Y}" triggered errors.`)),this.executedStepCount<c&&this.visitedScreenHashes.size<d){let w=[],N=b*.08,L=b*.88,le=ne=>ne.bounds.centerY>N&&ne.bounds.centerY<L&&ne.clickable&&ne.bounds.width>30&&ne.bounds.height>20&&J.every(fe=>Math.abs(fe.bounds.centerX-ne.bounds.centerX)>40||Math.abs(fe.bounds.centerY-ne.bounds.centerY)>40),j=nt(C,{width:S,height:b}).filter(le);for(let ne of j.slice(0,3))w.push({node:ne,type:"list"});let Se=Ue(C).filter(ne=>!(!le(ne)||Ke([ne],b)));for(let ne of Se.slice(0,4))w.push({node:ne,type:"button"});let Ne=mt(C).filter(le);for(let ne of Ne.slice(0,2))w.push({node:ne,type:"toggle"});for(let ne of w){if(this.executedStepCount>=c||this.visitedScreenHashes.size>=d)break;let fe=ne.node;if(Re(fe)){this.externalSkippedList.push(fe.text||fe.contentDesc||"External Link");continue}if(Ie(fe,a,o)){let pt=fe.text||fe.contentDesc||"Destructive Action";this.safetyHitList.push(pt);continue}let Ge=fe.text||fe.contentDesc||fe.resourceId||`${ne.type} element`;console.log(D.gray(` Sub-crawl [${Y}]: Exercising ${ne.type} "${Ge.slice(0,40)}"...`)),this.executedStepCount++,await this.driver.tap(fe.bounds.centerX,fe.bounds.centerY,s),await new Promise(pt=>setTimeout(pt,1200)),await this.dismissSoftKeyboard(s),await this.packageJailGuard(e,s);let _t=await this.getCleanUiHierarchy(e,s);v=_t.xml,C=_t.flat;let Me=ge(C),Yi=!this.visitedScreenHashes.has(Me);this.visitedScreenHashes.add(Me);let es=await this.runErrorSentinel(e,s,`Sub-${Ge.slice(0,30)}`),Zs=es.hasError?"FAIL":"PASS",Vi=await h(Zs,Yi);if(this.reporter.logStep("Tab Sub-Crawl",`Tested ${ne.type} "${Ge}" under tab "${Y}"`,"Tap",Zs,Ge,Vi,es.hasError?{errors:es.errors}:void 0),Me!==R){console.log(D.cyan(` \u{1F50E} Screen transitioned. Discovered new screen state [${Me.slice(0,8)}]`));let pt=Ue(C).filter(Fe=>le(Fe)&&!Ie(Fe,a,o)&&!Re(Fe));if(pt.length>0&&this.executedStepCount<c){let Fe=pt[0],Xi=Fe.text||Fe.contentDesc||"Child Action";console.log(D.gray(` Exercising child action "${Xi.slice(0,30)}"...`)),this.executedStepCount++,await this.driver.tap(Fe.bounds.centerX,Fe.bounds.centerY,s),await new Promise(Gi=>setTimeout(Gi,1e3)),await this.dismissSoftKeyboard(s)}console.log(D.gray(` Restoring ${Y} master view via Back...`)),await this.driver.pressBack(s),await new Promise(Fe=>setTimeout(Fe,1e3)),await this.dismissSoftKeyboard(s);let ei=await this.getCleanUiHierarchy(e,s);v=ei.xml,C=ei.flat}}}X++}console.log(D.green("\u2714 Navigation tabs traversed successfully!"))}else console.log(D.gray("Single-screen or tabs not detected at screen footer."));for(let se of this.visitedScreenHashes)this.memory.markScreenCrawled(se);await this.memory.save();let re=this.memory.getStats();this.reporter.logStep("Coverage Audit",`State Graph Coverage: Visited ${this.visitedScreenHashes.size} unique screen state(s). Memory Engine cached ${re.crawledScreens} crawled screen(s).`,"Verify","PASS"),await this.runErrorSentinel(e,s,"Final-State");let _=await this.reporter.generateReport(),me=_.failedSteps===0&&_.errorsDetected.length===0;console.log(me?D.bold.green(`
|
|
852
852
|
\u2728 PromptTest Exploration Complete \u2014 All Steps Passed!`):D.bold.red(`
|
|
853
853
|
\u26A0\uFE0F PromptTest Exploration Complete \u2014 Issues Detected!`)),console.log(D.white(`Summary: ${_.passedSteps} passed, ${_.warnSteps} warnings, ${_.failedSteps} failed, ${_.errorsDetected.length} errors detected.`)),this.safetyHitList.length>0&&console.log(D.cyan(`\u{1F6E1}\uFE0F Safety Shield: Blocked ${this.safetyHitList.length} destructive action(s) [${this.safetyHitList.join(", ")}].`)),console.log(D.cyan(`\u{1F5FA}\uFE0F State Graph Coverage: Visited ${this.visitedScreenHashes.size} unique screen state(s).`)),console.log(D.cyan(`\u{1F9E0} Memory Engine Coverage: ${re.crawledScreens} crawled screen(s) indexed offline.`)),console.log(D.cyan(`Full Audit Report generated at: output/prompttest-report.md
|
|
854
854
|
`))}finally{m&<(m)}}async runErrorSentinel(t,e,s){let i=[];try{let[r,n]=await Promise.all([this.driver.checkCrashes(t,e),this.driver.getUiHierarchy(e)]);if(r){let d=`Crash in ${s}: ${r.slice(0,300)}`;console.log(D.bold.red(`\u{1F6A8} FATAL EXCEPTION DETECTED in ${t} [${s}]!`)),this.reporter.logError(d),i.push(d)}let{flat:o}=pe(n),a=li(o);if(a.length>0)for(let d of a){let c=d.text||d.contentDesc,l=`UI Error dialog in ${s}: "${c}"`;console.log(D.bold.yellow(`\u26A0\uFE0F Error Dialog/Banner on screen [${s}]: "${c}"`)),this.reporter.logError(l),i.push(l)}}catch{}return{hasError:i.length>0,errors:i}}},Pi=new Rt;Ft();je();Qe();Je();Ft();Ae();Qe();Je();Qt();Xt();Ft();Ae();Qe();Je();Qt();ft();It();Xt();At();je();Oe();Yt();Pe();Pe();Os();js();Ys();rt();Hs();Gs();Ks();zs();qs();function Wi(p,t,e){return new Ze(p,t,e)}At();function Cn(){try{let p=new URL(import.meta.url).pathname.replace(/^\/([a-zA-Z]:)/,"$1"),t=Te.dirname(p),e=[Te.resolve(t,"../package.json"),Te.resolve(t,"../../package.json"),Te.resolve(process.cwd(),"package.json")];for(let s of e)if(ye.existsSync(s)){let i=JSON.parse(ye.readFileSync(s,"utf-8"));if(i.name==="prompttest"&&i.version)return i.version}}catch{}return"1.1.0"}async function In(){let p=process.argv.slice(2),t=p[0]||"help";if(p.includes("--version")||p.includes("-v")||t==="version"){console.log(Cn());return}switch(t){case"devices":{console.log(k.cyan(`
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import Ce from"fs";import me from"path";import R from"chalk";import{spawn as et}from"child_process";import Bt from"readline";var ae={DEVICES:["devices","-l"],SCREENCAP:["exec-out","screencap","-p"],FOREGROUND_APP:["shell","dumpsys","window"],CURRENT_FOCUS:["shell","dumpsys","activity","activities"],FORCE_STOP:p=>["shell","am","force-stop",p],CLEAR_APP:p=>["shell","pm","clear",p],LAUNCH_APP:p=>["shell","am","start","-a","android.intent.action.MAIN","-c","android.intent.category.LAUNCHER",p],UIAUTOMATOR_DUMP:["exec-out","uiautomator","dump","--compressed","/dev/tty"],INPUT_TAP:(p,e)=>["shell","input","tap",p.toString(),e.toString()],INPUT_TEXT:p=>["shell","input","text",p],INPUT_KEY:p=>["shell","input","keyevent",p.toString()],INPUT_SWIPE:(p,e,t,s,i=300)=>["shell","input","swipe",p.toString(),e.toString(),t.toString(),s.toString(),i.toString()],LOGCAT_CRASH:p=>["shell","logcat","-d","-b","crash","-s","AndroidRuntime:E"],CLEAR_CRASH_LOGS:()=>["shell","logcat","-b","crash","-c"],GRANT_PERMISSION:(p,e)=>["shell","pm","grant",p,e.startsWith("android.permission.")?e:`android.permission.${e}`],INSTALL_APK:p=>["install","-r",p],UNINSTALL_APP:p=>["uninstall",p],DEEP_LINK:p=>["shell","am","start","-a","android.intent.action.VIEW","-d",p],TCPIP:(p=5555)=>["tcpip",p.toString()],CONNECT:p=>["connect",p],DISCONNECT:p=>p?["disconnect",p]:["disconnect"],GET_WLAN_IP:["shell","ip","-f","inet","addr","show","wlan0"]};function bi(){let p=process.env.PROMPTTEST_SETTLE_FACTOR;if(!p)return 1;let e=parseFloat(p);return isNaN(e)||e<=0?1:e}function ye(p,e=50){return Math.max(e,Math.round(p*bi()))}var E={adb:{commandTimeoutMs:2e4,binaryTimeoutMs:45e3,retryCount:2,retryBaseDelayMs:200},polling:{intervalMs:200,jitterMs:80,defaultTimeoutMs:3e3,extendedTimeoutMs:3500,hierarchyCacheTtlMs:3500,verifyCacheTtlMs:2e3},settle:{get tapMs(){return ye(500)},get backMs(){return ye(700)},get launchMs(){return ye(2500)},get stopMs(){return ye(600)},get keyboardDismissMs(){return ye(500)},get scrollMs(){return ye(350)},get toggleMs(){return ye(350)},get selectMs(){return ye(600)},get inputFocusMs(){return ye(250)},get inputClearMs(){return ye(150)},get inputDoneMs(){return ye(350)},get submitWaitMs(){return ye(1800)}},gestures:{swipeDurationMs:300,longPressDurationMs:1e3,bottomNavThresholdRatio:.88,tapTargetYRatio:.96},recorder:{staleTapTimeoutMs:3e3,asyncRefreshDelayMs:80,heartbeatIntervalMs:1500},screen:{fallbackWidth:1080,fallbackHeight:2400},visual:{overlapRatio:.2},device:{maxDeviceRetries:2,autoReconnect:!0},flow:{maxLoopIterations:50,waitUntilTimeoutMs:1e4,defaultRetryCount:3},ai:{provider:"heuristic",model:"gpt-4o-mini",selfHealing:!0,confidenceThreshold:.75,maxHealingAttempts:2,endpoint:""},memory:{staleThresholdDays:parseInt(process.env.PROMPTTEST_STALE_DAYS||"14",10),staleDecayAmount:.2,minConfidenceFloor:.2,maxHealedConfidence:.65},parser:{mode:process.env.PROMPTTEST_XML_PARSER||"regex"},safety:{mode:process.env.PROMPTTEST_SAFETY_MODE||"strict",customBlacklist:process.env.PROMPTTEST_SAFETY_BLACKLIST?process.env.PROMPTTEST_SAFETY_BLACKLIST.split(",").map(p=>p.trim()).filter(Boolean):[]},explorer:{strategy:process.env.PROMPTTEST_STRATEGY||"heuristic",maxTabs:parseInt(process.env.PROMPTTEST_MAX_TABS||"15",10),maxScreens:parseInt(process.env.PROMPTTEST_MAX_SCREENS||"30",10),maxDepth:parseInt(process.env.PROMPTTEST_MAX_DEPTH||"5",10),stepBudget:parseInt(process.env.PROMPTTEST_STEP_BUDGET||"100",10),screenshots:process.env.PROMPTTEST_SCREENSHOTS||"state-change"},locale:"en"};function ks(p=E.polling.intervalMs,e=E.polling.jitterMs){return Math.round(p+(Math.random()*2-1)*(e/2))}var Cs=(n=>(n[n.SUCCESS=0]="SUCCESS",n[n.TEST_FAILED=1]="TEST_FAILED",n[n.DEVICE_UNAVAILABLE=2]="DEVICE_UNAVAILABLE",n[n.CONFIG_ERROR=3]="CONFIG_ERROR",n[n.LOCKED=4]="LOCKED",n))(Cs||{}),Fe=class extends Error{exitCode;phase;constructor(e,t=1,s){super(e),this.name="PromptTestError",this.exitCode=t,this.phase=s,Object.setPrototypeOf(this,new.target.prototype)}},gt=class extends Fe{constructor(e,t="Device"){super(e,2,t),this.name="DeviceError"}},Ft=class extends Fe{target;candidateMatches;constructor(e,t,s,i="Locator"){super(e,1,i),this.name="LocatorError",this.target=t,this.candidateMatches=s}},_t=class extends Fe{constructor(e,t="Config"){super(e,3,t),this.name="ConfigError"}},Ot=class extends Fe{serial;constructor(e,t,s="Lock"){super(e,4,s),this.name="LockError",this.serial=t}},mt=class extends Fe{cycle;constructor(e,t,s="SpecLoader"){super(e,3,s),this.name="SpecRecursionError",this.cycle=t}};var tt=class{defaultSerial=null;cachedDimensions=new Map;inFlightHierarchies=new Map;constructor(e){e&&(this.defaultSerial=e)}setDefaultDevice(e){this.defaultSerial=e}getDefaultDevice(){return this.defaultSerial}async getScreenDimensions(e){let t=e||this.defaultSerial||"default";if(this.cachedDimensions.has(t))return this.cachedDimensions.get(t);try{let{stdout:i}=await this.execAdb(["shell","wm","size"],e),n=i.match(/(?:Physical|Override)\s*size:\s*(\d+)x(\d+)/i);if(n){let r={width:parseInt(n[1],10),height:parseInt(n[2],10)};return this.cachedDimensions.set(t,r),r}}catch{}let s={width:E.screen.fallbackWidth,height:E.screen.fallbackHeight};return this.cachedDimensions.set(t,s),s}clearScreenDimensionsCache(e){e?this.cachedDimensions.delete(e):this.cachedDimensions.clear()}async getNavigationBarHeight(e){try{let{stdout:t}=await this.execAdb(["shell","dumpsys","window","displays"],e).catch(()=>({stdout:""})),s=t.match(/(?:navigationBarHeight|navBarHeight|navigationBarFrame)[=:\s]+(\d+)/i);if(s&&parseInt(s[1],10)>0)return parseInt(s[1],10)}catch{}return 0}isGlobalAdbCommand(e){return e?["devices","connect","disconnect","version","kill-server","start-server","help"].includes(e.toLowerCase()):!0}async execAdbInternal(e,t,s=2e4){let i=t||this.defaultSerial;!i&&!this.isGlobalAdbCommand(e[0])&&(await this.ensureDefaultDevice(),i=this.defaultSerial);let n=i&&!this.isGlobalAdbCommand(e[0])?["-s",i,...e]:[...e];return new Promise((r,o)=>{let a=et("adb",n),d="",c="",l=!1,h=setTimeout(()=>{l=!0;try{a.kill("SIGKILL")}catch{}o(new Error(`ADB command timed out after ${s}ms: adb ${n.join(" ")}`))},s);a.stdout.on("data",u=>{d+=u.toString("utf-8")}),a.stderr.on("data",u=>{c+=u.toString("utf-8")}),a.on("close",u=>{clearTimeout(h),!l&&(u===0?r({stdout:d,stderr:c}):o(new Error(`ADB command failed (code ${u}): adb ${n.join(" ")}
|
|
2
2
|
Error: ${c}`)))}),a.on("error",u=>{clearTimeout(h),!l&&o(u)})})}async execAdb(e,t,s=E.adb.commandTimeoutMs,i=E.device.maxDeviceRetries){let n;for(let r=0;r<=i;r++)try{return await this.execAdbInternal(e,t,s)}catch(o){n=o;let a=o instanceof Error?o.message:String(o),d=a.toLowerCase(),c=d.includes("device offline")||d.includes("device not found"),l=c||d.includes("error: closed")||d.includes("timed out");if(r>=i||!l||this.isGlobalAdbCommand(e[0]))throw c?new gt(`Target Android device ${t||this.defaultSerial||""} is offline or disconnected: ${a}`):o;if(E.device.autoReconnect&&c)try{await this.execAdbInternal(["reconnect"],t,5e3).catch(()=>{})}catch{}await new Promise(h=>setTimeout(h,E.adb.retryBaseDelayMs*Math.pow(2,r+1)))}throw n||new Error(`ADB command failed after ${i} retries: ${e.join(" ")}`)}async execAdbBinary(e,t,s=45e3){let i=t||this.defaultSerial;!i&&!this.isGlobalAdbCommand(e[0])&&(await this.ensureDefaultDevice(),i=this.defaultSerial);let n=i&&!this.isGlobalAdbCommand(e[0])?["-s",i,...e]:[...e];return new Promise((r,o)=>{let a=et("adb",n),d=[],c="",l=!1,h=setTimeout(()=>{l=!0;try{a.kill("SIGKILL")}catch{}o(new Error(`ADB binary command timed out after ${s}ms: adb ${n.join(" ")}`))},s);a.stdout.on("data",u=>d.push(Buffer.from(u))),a.stderr.on("data",u=>{c+=u.toString("utf-8")}),a.on("close",u=>{clearTimeout(h),!l&&(u===0?r(Buffer.concat(d)):o(new Error(`ADB binary command failed (code ${u}): adb ${n.join(" ")}
|
|
3
3
|
${c}`)))}),a.on("error",u=>{clearTimeout(h),!l&&o(u)})})}async getConnectedDevices(){let{stdout:e}=await this.execAdb(ae.DEVICES),t=e.split(`
|
|
4
|
-
`).map(o=>o.trim()).filter(Boolean),s=t.findIndex(o=>o.toLowerCase().includes("list of devices attached")),i=s!==-1?t.slice(s+1):t,n=[];return n=(o=>{let a=[];for(let d of o){if(!d||d.startsWith("*")||d.toLowerCase().includes("list of devices"))continue;let c=d.split(/\s+/);if(c.length>=2){let l=c[0],h=c[1],u,g;for(let f of c.slice(2))f.startsWith("model:")&&(u=f.replace("model:","")),f.startsWith("product:")&&(g=f.replace("product:",""));a.push({serial:l,state:h,model:u,product:g})}}return a})(i),n}async wakeDevice(e){try{await this.execAdb(["shell","input","keyevent","224"],e),await this.execAdb(["shell","wm","dismiss-keyguard"],e)}catch{}}async ensureDefaultDevice(e){if(e)return await this.wakeDevice(e),e;if(this.defaultSerial)return await this.wakeDevice(this.defaultSerial),this.defaultSerial;let s=(await this.getConnectedDevices()).find(i=>i.state==="device");return s&&(this.defaultSerial=s.serial,await this.wakeDevice(this.defaultSerial)),this.defaultSerial||void 0}async getDeviceModel(e){try{let{stdout:n}=await this.execAdb(["shell","getprop","ro.product.model"],e),r=n.trim();if(r)return r}catch{}let t=await this.getConnectedDevices(),s=e||this.defaultSerial;return t.find(n=>n.serial===s)?.model||s||"Android Device"}async captureScreenshot(e){return this.execAdbBinary(ae.SCREENCAP,e)}async getUiHierarchy(e){let t=e||this.defaultSerial||"default",s=this.inFlightHierarchies.get(t);if(s)return s;let i=(async()=>{try{let{stdout:n}=await this.execAdb(ae.UIAUTOMATOR_DUMP,e,1e4,0),r=n.indexOf("<hierarchy");return r>=0?n.slice(r):n}finally{this.inFlightHierarchies.delete(t)}})();return this.inFlightHierarchies.set(t,i),i}clearInFlightHierarchies(e){e?this.inFlightHierarchies.delete(e):this.inFlightHierarchies.clear()}async getForegroundPackage(e){let t=s=>{let i=s.toLowerCase();return i==="android"||i.includes("systemui")||i.includes("inputmethod")||i.includes("latin")||i.includes("gboard")||i.includes("swiftkey")||i.includes("ime")};try{let{stdout:s}=await this.execAdb(["shell","dumpsys","activity","activities"],e).catch(()=>({stdout:""})),i=s.match(/(?:topResumedActivity|mResumedActivity)
|
|
5
|
-
`).map(r=>r.trim()).find(r=>r.includes("/")&&!r.includes(" "));if(n){await this.execAdb(["shell","am","start","-n",n],t);return}}catch{}await this.execAdb(ae.LAUNCH_APP(e),t)}async forceStop(e,t){await this.execAdb(ae.FORCE_STOP(e),t)}async clearAppData(e,t){await this.execAdb(ae.CLEAR_APP(e),t)}async restartApp(e,t){await this.forceStop(e,t),await new Promise(s=>setTimeout(s,600)),await this.launchApp(e,t)}async grantPermission(e,t,s){await this.execAdb(ae.GRANT_PERMISSION(e,t),s)}async isAppInstalled(e,t){try{let{stdout:s}=await this.execAdb(["shell","pm","list","packages",e],t);return s.toLowerCase().includes(`package:${e.toLowerCase()}`)||s.includes(e)}catch{return!1}}async installApk(e,t){await this.execAdb(ae.INSTALL_APK(e),t)}async uninstallApp(e,t){await this.execAdb(ae.UNINSTALL_APP(e),t)}async openDeepLink(e,t){await this.execAdb(ae.DEEP_LINK(e),t)}async checkCrashes(e,t){try{let{stdout:s}=await this.execAdb(ae.LOGCAT_CRASH(e),t);if(s&&s.includes("FATAL EXCEPTION")&&s.includes(e))return s}catch{}return null}async clearCrashLogs(e){try{await this.execAdb(ae.CLEAR_CRASH_LOGS(),e)}catch{}}async getDeviceIp(e){try{let{stdout:t}=await this.execAdb(ae.GET_WLAN_IP,e),s=t.match(/inet\s+(\d+\.\d+\.\d+\.\d+)/);if(s&&s[1])return s[1]}catch{}try{let{stdout:t}=await this.execAdb(["shell","ip","route"],e),s=t.match(/src\s+(\d+\.\d+\.\d+\.\d+)/);if(s&&s[1])return s[1]}catch{}return null}async connectWifi(e,t=5555,s){let i=e,n=t;if(e&&e.includes(":")){let l=e.split(":");i=l[0],n=parseInt(l[1],10)||t}if(!i){let l=await this.getDeviceIp(s);if(!l)throw new Error("Could not auto-detect phone Wi-Fi IP. Make sure the phone is on Wi-Fi, or pass IP manually: npx prompttest wifi <ip>");i=l}let r=`${i}:${n}`;if(e)try{let{stdout:l,stderr:h}=await this.execAdb(ae.CONNECT(r)),u=`${l} ${h}`.trim();if(u.toLowerCase().includes("connected")||u.toLowerCase().includes("already"))return this.defaultSerial=r,{success:!0,target:r,message:u}}catch{}try{await this.execAdb(ae.TCPIP(n),s),await new Promise(l=>setTimeout(l,1e3))}catch{}let{stdout:o,stderr:a}=await this.execAdb(ae.CONNECT(r)),d=`${o} ${a}`.trim(),c=d.toLowerCase().includes("connected")||d.toLowerCase().includes("already");return c&&(this.defaultSerial=r),{success:c,target:r,message:d}}async disconnectWifi(e){let{stdout:t,stderr:s}=await this.execAdb(ae.DISCONNECT(e));return`${t} ${s}`.trim()}streamLogcatEvents(e,t){let i=et("adb",["-s",e,"shell","logcat","-b","events","-v","time"]),n=Bt.createInterface({input:i.stdout});return n.on("line",t),i.on("close",()=>{try{n.close()}catch{}}),i.on("error",()=>{}),i}streamLogcatCrashes(e,t){let i=et("adb",["-s",e,"shell","logcat","-T","1","-s","AndroidRuntime:E","ANRManager:E","-v","time"]),n=Bt.createInterface({input:i.stdout});return n.on("line",t),i.on("close",()=>{try{n.close()}catch{}}),i.on("error",()=>{}),i}streamLogcatToasts(e,t){let i=et("adb",["-s",e,"shell","logcat","-s","Toast:D","Snackbar:D","-v","time"]),n=Bt.createInterface({input:i.stdout});return n.on("line",t),i.on("close",()=>{try{n.close()}catch{}}),i.on("error",()=>{}),i}async getLogcatTail(e=150,t){try{let{stdout:s}=await this.execAdb(["logcat","-d","-t",String(e),"-v","time"],t);return s}catch(s){return`Failed to capture logcat: ${s instanceof Error?s.message:String(s)}`}}async startScreenRecording(e="/sdcard/prompttest_temp.mp4",t){let s=t||this.defaultSerial,n=et("adb",s?["-s",s,"shell","screenrecord",e]:["shell","screenrecord",e]);return n.on("error",()=>{}),{remotePath:e,process:n}}async stopScreenRecording(e,t,s){try{try{e.process.kill("SIGINT")}catch{}return await this.execAdb(["shell","pkill","-2","screenrecord"],s).catch(()=>{}),await new Promise(i=>setTimeout(i,1500)),await this.execAdb(["pull",e.remotePath,t],s),await this.execAdb(["shell","rm","-f",e.remotePath],s).catch(()=>{}),t}catch{return""}}async isKeyboardVisible(e){try{let{stdout:t}=await this.execAdb(["shell","dumpsys","input_method"],e,4e3,0);return t.includes("mInputShown=true")||t.includes("isInputViewShown=true")||t.includes("mHasSurface=true")}catch{return!1}}async ensureAccessibilityServices(e){try{await this.execAdb(["shell","settings","put","secure","accessibility_enabled","1"],e,4e3,0)}catch{}}},he=new tt;var Ls=/^(\d+(?:\.\d+)*\.?|\-|\*)\s*/;function Rs(p){let e=p.trim(),t=e.match(/^"([^"]+)"$/);if(t)return t[1].trim();let s=e.match(/^'([^']+)'$/);return s?s[1].trim():e.replace(/^['"]+|['"]+$/g,"").trim()}var V=`(?:"([^"]+)"|'([^']+)'|([^'"\\s][^'"]*?[^'"\\s]|\\S))`;function W(p){for(let e of p)if(e!=null&&e.trim().length>0)return e.trim();return""}function Xe(p,e){let t=p;if(e)for(let[s,i]of Object.entries(e))t=t.replace(new RegExp(`\\$${s}\\b`,"g"),()=>i);return t=t.replace(/\$ENV\{([A-Z0-9_]+)\}/gi,(s,i)=>process.env[i]||""),t=t.replace(/\$RANDOM_EMAIL\b/g,()=>`test_${Date.now().toString().slice(-6)}@prompttest.io`),t=t.replace(/\$RANDOM_NAME\b/g,()=>`User_${Math.floor(1e3+Math.random()*9e3)}`),t=t.replace(/\$RANDOM_PHONE\b/g,()=>`98${Math.floor(1e7+Math.random()*9e7)}`),t=t.replace(/\$TIMESTAMP\b/g,()=>Date.now().toString()),t=t.replace(/\$UUID\b/g,()=>`${Date.now().toString(16)}-${Math.random().toString(16).substring(2,8)}`),t=t.replace(/\$RANDOM_UUID\b/g,()=>"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,s=>{let i=Math.random()*16|0;return(s==="x"?i:i&3|8).toString(16)})),t=t.replace(/\$RANDOM_CARD\b/g,()=>{let s="4532";for(let i=0;i<12;i++)s+=Math.floor(Math.random()*10);return s}),t=t.replace(/\$DATE_OFFSET\((-?\d+)\)/g,(s,i)=>{let n=parseInt(i,10)||0;return new Date(Date.now()+n*864e5).toISOString().slice(0,10)}),t=t.replace(/\$RANDOM_INT\((\d+),\s*(\d+)\)/g,(s,i,n)=>{let r=parseInt(i,10),o=parseInt(n,10);return String(Math.floor(r+Math.random()*(o-r+1)))}),t}var O={TAP:new RegExp(`^(?:tap|click|press)(?:\\s+on)?\\s+(?:button\\s+|item\\s+)?${V}$`,"i"),ORDINAL_TAP:new RegExp(`^(?:tap|click|press)(?:\\s+on)?\\s+(?:the\\s+)?(1st|2nd|3rd|4th|5th|last|first)\\s+(?:button\\s+|item\\s+)?${V}$`,"i"),PROXIMITY_TAP:new RegExp(`^(?:tap|click|press)(?:\\s+on)?\\s+${V}\\s+(?:next\\s+to|near|beside|inside|for)\\s+${V}$`,"i"),CLEAR:new RegExp(`^(?:clear|empty)(?:\\s+(?:text\\s+(?:in|of)|field|input))?\\s+${V}$`,"i"),LONG_PRESS:new RegExp(`^(?:long\\s+press|press\\s+and\\s+hold)\\s+(?:button\\s+|item\\s+)?${V}$`,"i"),TYPE:new RegExp(`^(?:type|enter|input)\\s+${V}\\s+(?:in|into)\\s+${V}$`,"i"),VERIFY:new RegExp(`^(?:verify|veryfy|check|assert|ensure)\\s+(?:that\\s+)?(?:error\\s+message\\s+|message\\s+|text\\s+|element\\s+|button\\s+|toast\\s+|my\\s+)?${V}\\s*(?:is\\s+(visible|exists|appears|enabled|disabled|not\\s+visible|hidden|absent|not\\s+present|gone))?$`,"i"),BACK:/^(?:press\s+back|go\s+back|navigate\s+back|back)$/i,SCROLL_TO:new RegExp(`^(?:scroll|swipe)(?:\\s+(down|up))?\\s+(?:to|until)\\s+${V}$`,"i"),SCROLL:/^(?:scroll|swipe)\s+(down|up)$/i,DIRECTIONAL_SWIPE:new RegExp(`^(?:swipe|scroll)\\s+(left|right|up|down)(?:\\s+on\\s+${V})?$`,"i"),TOGGLE:new RegExp(`^(?:toggle|turn\\s+(on|off)|check|uncheck)\\s+(?:checkbox\\s+|switch\\s+)?${V}$`,"i"),SELECT_DROPDOWN:new RegExp(`^(?:select|choose)\\s+${V}\\s+from\\s+${V}$`,"i"),LAUNCH_APP:new RegExp(`^(?:launch|open)\\s+(?:app\\s+)?${V}$`,"i"),RESTART_APP:new RegExp(`^(?:restart|relaunch)\\s+(?:app\\s+)?${V}$`,"i"),TERMINATE_APP:new RegExp(`^(?:terminate|close|force\\s*stop)\\s+(?:app\\s+)?(?:${V})?$`,"i"),CLEAR_DATA:new RegExp(`^(?:clear\\s+(?:app\\s+)?data\\s+(?:for\\s+)?|reset\\s+app\\s+)${V}$`,"i"),GRANT_PERMISSION:new RegExp(`^grant\\s+permissions?\\s+${V}(?:\\s+(?:to|for)\\s+${V})?$`,"i"),DEEP_LINK:new RegExp(`^(?:open\\s+deep\\s*link|navigate\\s+to\\s+url)\\s+${V}$`,"i"),WAIT:/^wait\s+(\d+)\s*(?:s|sec|seconds|ms)?$/i,WAIT_UNTIL:new RegExp(`^(?:wait\\s+until|wait\\s+for)\\s+${V}\\s+(?:(?:is\\s+)?(visible|appears?|enabled|exists))?(?:\\s+(?:up\\s+to|for)\\s+(\\d+)\\s*(s|sec|seconds|ms)?)?$`,"i"),IF_VISIBLE:new RegExp(`^if\\s+(?:(?:that\\s+)?${V}\\s+(?:is\\s+)?(visible|present|exists|appears?)|element\\s+${V}\\s+(?:is\\s+)?visible)$`,"i"),ELSE:/^else$/i,END:/^(?:end|end\s*if|end\s*loop|fi|done)$/i,LOOP:/^(?:loop|repeat)\s+(\d+)\s*(?:times)?$/i,FOR_EACH:new RegExp(`^for\\s+each\\s+(?:item\\s+)?(?:in\\s+)?${V}$`,"i"),SET_VARIABLE:/^set\s+\$([a-zA-Z0-9_]+)\s*=\s*(.+)$/i,DEVICE_WIFI:/^(?:turn\s+|set\s+)?wifi\s+(on|off|enable|disable)$/i,DEVICE_AIRPLANE:/^(?:turn\s+|set\\s+)?airplane\s*(?:mode)?\s+(on|off|enable|disable)$/i,DEVICE_ORIENTATION:/^(?:set\s+)?orientation\s+(?:to\s+)?(portrait|landscape)$/i,DEVICE_NOTIFICATIONS:/^(open|expand|pull\s+down|collapse|close)\s+notification(?:s|\s+shade)?$/i,INCLUDE_SPEC:new RegExp(`^(?:include|run_spec|subflow)\\s+${V}$`,"i"),RUN_JS:/^(?:run\s+js|eval\s+js|js)(?::|\s+)(.+)$/i,ENTER_OTP:new RegExp(`^(?:enter|input|type)\\s+(?:the\\s+)?(?:verification\\s+code|otp|2fa\\s+code|sms\\s+code)(?:\\s+just\\s+sent)?(?:\\s+(?:in|into)\\s+${V})?$`,"i"),API_REQUEST:new RegExp(`^(?:api|call\\s+api)\\s+(GET|POST|PUT|DELETE|PATCH)\\s+${V}(?:\\s+with\\s+body\\s+${V})?(?:\\s+store\\s+${V}\\s+as\\s+\\$([a-zA-Z0-9_]+))?(?:\\s+assert\\s+status\\s+(\\d+))?$`,"i"),EMAIL_ASSERT:new RegExp(`^(?:assert|verify)\\s+email\\s+to\\s+${V}\\s+with\\s+subject\\s+${V}(?:\\s+contains\\s+${V})?$`,"i"),OPTIONAL_SUFFIX:/\s*\((?:optional|if\s+present|maybe)\)\s*$/i,FALLBACK_QUOTED_TAP:/^(?:tap|click)\s+.*?['"]([^'"]+)['"]/i},ft={HIERARCHY_ROOT:/<hierarchy[^>]*>([\s\S]*?)<\/hierarchy>/i,NODE_TAG:/<node\b([^>]*?)(\/?)>|<\/node>/gi,ATTR_KEY_VALUE:/([a-zA-Z0-9_-]+)="([^"]*)"/g,BOUNDS_RECT:/\[(\d+),(\d+)\]\[(\d+),(\d+)\]/};function st(p){return(p||"").replace(/&#(\d+);/g,(e,t)=>{try{return String.fromCodePoint(parseInt(t,10))}catch{return""}}).replace(/&#x([0-9a-fA-F]+);/g,(e,t)=>{try{return String.fromCodePoint(parseInt(t,16))}catch{return""}}).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,'"').replace(/'/g,"'")}function X(p){return st(p||"").trim().toLowerCase().replace(/[\u2018\u2019]/g,"'").replace(/[\u201C\u201D]/g,'"').replace(/\uFFFD/g,"").replace(new RegExp("\\p{Extended_Pictographic}","gu"),"").replace(/[\uE000-\uF8FF\uFFF0-\uFFFF]/g,"").replace(/\s+/g," ").trim()}var Is=new Map;function z(p,e){if(!p||!e)return!1;let t=X(p),s=X(e);if(t.length<=3){let i=Is.get(t);if(!i){let n=t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");i=new RegExp(`(?:^|\\b)${n}(?:\\b|$)`,"i"),Is.set(t,i)}return i.test(s)}return s.includes(t)}var Ns=["sign in","signin","login","log in","submit","save","continue","next","done","send","confirm","proceed","post","rate","enter","join","apply","update"],Ms=["sign in","signin","login","log in","submit","save","next","continue","done","like","fav","favorite","star","bookmark","add","post","confirm","apply","explore","search","filter"];function yi(p,e){let t=X(p),s=X(e),i=t.length,n=s.length;if(i===0)return n;if(n===0)return i;let r=Array.from({length:n+1},(a,d)=>d),o=new Array(n+1);for(let a=1;a<=i;a++){o[0]=a;for(let c=1;c<=n;c++){let l=t[a-1]===s[c-1]?0:1;o[c]=Math.min(r[c]+1,o[c-1]+1,r[c-1]+l)}let d=r;r=o,o=d}return r[n]}function Si(p,e){let t=X(p),s=X(e);if(t===s)return 1;if(!t||!s)return 0;let i=Math.max(t.length,s.length);return 1-yi(t,s)/i}function We(p,e,t=3,s=.35){return Array.from(new Set(e.map(r=>r.trim()).filter(r=>r.length>0))).map(r=>({text:r,score:Si(p,r)})).filter(r=>r.score>=s).sort((r,o)=>o.score-r.score).slice(0,t)}function Fs(p){let e=p.match(ft.BOUNDS_RECT);if(!e)return{x1:0,y1:0,x2:0,y2:0,width:0,height:0,centerX:0,centerY:0};let t=parseInt(e[1],10),s=parseInt(e[2],10),i=parseInt(e[3],10),n=parseInt(e[4],10);return{x1:t,y1:s,x2:i,y2:n,width:i-t,height:n-s,centerX:Math.round((t+i)/2),centerY:Math.round((s+n)/2)}}function wi(p){let e=p.match(ft.HIERARCHY_ROOT);if(!e)return{root:[],flat:[]};let t=e[1],s=new RegExp(ft.NODE_TAG.source,"gi"),i=new RegExp(ft.ATTR_KEY_VALUE.source,"g"),n=[],r=[],o=[],a;for(;(a=s.exec(t))!==null;){let d=a[0],c=a[1],l=a[2]==="/";if(d.startsWith("</node>")){o.pop();continue}let h={};i.lastIndex=0;let u;for(;(u=i.exec(c))!==null;)h[u[1]]=u[2];let g={index:parseInt(h.index||"0",10),text:st(h.text||""),resourceId:h["resource-id"]||"",className:h.class||"",packageName:h.package||"",contentDesc:st(h["content-desc"]||""),bounds:Fs(h.bounds||""),clickable:h.clickable==="true",enabled:h.enabled==="true",focused:h.focused==="true",scrollable:h.scrollable==="true",password:h.password==="true",selected:h.selected==="true",checked:h.checked!==void 0?h.checked==="true":void 0,checkable:h.checkable!==void 0?h.checkable==="true":void 0,children:[]};r.push(g),o.length===0?n.push(g):o[o.length-1].children.push(g),l||o.push(g)}return{root:n,flat:r}}function vi(p){let e=[],t=[],s=[],i=p.length,n=0;for(;n<i;){let r=p.indexOf("<",n);if(r===-1)break;if(p.startsWith("<!--",r)){let S=p.indexOf("-->",r+4);n=S===-1?i:S+3;continue}if(p.startsWith("<?",r)){let S=p.indexOf("?>",r+2);n=S===-1?i:S+2;continue}if(p.startsWith("</",r)){let S=p.indexOf(">",r+2);if(S===-1)break;p.slice(r+2,S).trim()==="node"&&s.pop(),n=S+1;continue}let o=-1,a=null,d=!1;for(let S=r+1;S<i;S++){let b=p[S];if(a)b===a&&(a=null);else if(b==='"'||b==="'")a=b;else if(b===">"){o=S,p[S-1]==="/"&&(d=!0);break}}if(o===-1)break;let c=p.slice(r+1,d?o-1:o).trim();n=o+1;let l=0;for(;l<c.length&&!/\s/.test(c[l]);)l++;if(c.slice(0,l)!=="node")continue;let u={},g=l,f=c.length;for(;g<f;){for(;g<f&&/\s/.test(c[g]);)g++;if(g>=f)break;let S=g;for(;g<f&&c[g]!=="="&&!/\s/.test(c[g]);)g++;let b=c.slice(S,g);for(;g<f&&/\s/.test(c[g]);)g++;if(g>=f||c[g]!=="=")break;for(g++;g<f&&/\s/.test(c[g]);)g++;if(g>=f)break;let m=c[g];if(m==='"'||m==="'"){g++;let v=g;for(;g<f&&c[g]!==m;)g++;u[b]=c.slice(v,g),g<f&&g++}else{let v=g;for(;g<f&&!/\s/.test(c[g]);)g++;u[b]=c.slice(v,g)}}let y={index:parseInt(u.index||"0",10),text:st(u.text||""),resourceId:u["resource-id"]||"",className:u.class||"",packageName:u.package||"",contentDesc:st(u["content-desc"]||""),bounds:Fs(u.bounds||""),clickable:u.clickable==="true",enabled:u.enabled==="true",focused:u.focused==="true",scrollable:u.scrollable==="true",password:u.password==="true",selected:u.selected==="true",checked:u.checked!==void 0?u.checked==="true":void 0,checkable:u.checkable!==void 0?u.checkable==="true":void 0,children:[]};t.push(y),s.length===0?e.push(y):s[s.length-1].children.push(y),d||s.push(y)}return{root:e,flat:t}}function ue(p,e){if((e||E.parser?.mode||"regex")==="streaming")try{return vi(p)}catch{}return wi(p)}function ir(p,e){return p.filter(t=>{if(e.clickable!==void 0&&t.clickable!==e.clickable||e.className&&!t.className.includes(e.className))return!1;if(e.text){if(typeof e.text=="string"){if(!t.text.toLowerCase().includes(e.text.toLowerCase()))return!1}else if(!e.text.test(t.text))return!1}if(e.contentDesc){if(typeof e.contentDesc=="string"){if(!t.contentDesc.toLowerCase().includes(e.contentDesc.toLowerCase()))return!1}else if(!e.contentDesc.test(t.contentDesc))return!1}if(e.resourceId){if(typeof e.resourceId=="string"){if(!t.resourceId.toLowerCase().includes(e.resourceId.toLowerCase()))return!1}else if(!e.resourceId.test(t.resourceId))return!1}return!0})}var xi=[/logbox/i,/^Log \d+ of \d+/i,/Console (?:Error|Warning|Log|Info)/i,/^Call Stack$/i,/^Source Map$/i,/^(?:Dismiss|Minimize|Copy|View error log)$/i,/\b[\w-]+\.(?:tsx?|jsx?|js|ts):\d+:\d+\b/i,/Unhandled Promise Rejection/i,/SyntaxError/i,/TypeError:/i,/ReferenceError:/i,/Warning:/i,/LogBoxNotificationOverlay/i];function Se(p){if(p.resourceId&&/logbox/i.test(p.resourceId)||p.className&&/logbox/i.test(p.className))return!0;let e=`${p.text} ${p.contentDesc}`.trim();return!!xi.some(t=>t.test(e))}function _e(p){return p.filter(e=>Se(e)?!1:(e.className.includes("EditText")||e.className.includes("TextInputEditText")||e.focused&&e.clickable)&&e.bounds.width>0&&e.bounds.height>0)}function Ht(p){return p.className.includes("EditText")||p.className.includes("TextInputEditText")?!0:(p.children||[]).some(Ht)}function bt(p,e){let t=e?.width||E.screen.fallbackWidth,s=e?.height||E.screen.fallbackHeight,i=s*.08,n=s*.88,r=p.filter(a=>!Se(a)&&(a.className.includes("RecyclerView")||a.className.includes("ListView")||a.className.includes("ScrollView"))),o=[];for(let a of r)for(let d of a.children){if(Se(d)||d.bounds.centerY<i||d.bounds.centerY>n)continue;(d.clickable||d.children.some(l=>!Se(l)&&l.clickable&&l.bounds.width>t*.1))&&d.bounds.width>t*.08&&d.bounds.height>s*.02&&!Ht(d)&&o.push(d)}return o.length===0?p.filter(a=>{if(Se(a))return!1;let d=(a.text||a.contentDesc||"").trim();return d.length<=1||/^(remember me|forgot password|sign in|sign up|login|submit|cancel)$/i.test(d)?!1:a.clickable&&a.bounds.width>t*.14&&a.bounds.height>s*.033&&a.bounds.centerY>i&&a.bounds.centerY<n&&!a.className.includes("Button")&&!a.className.includes("EditText")&&!Ht(a)}):o}function Ge(p){return p.filter(e=>{if(Se(e)||!e.clickable||e.bounds.width===0||e.className.includes("EditText"))return!1;let t=e.className.includes("Button")||e.className.includes("ImageButton"),s=Ms.some(i=>z(i,e.text)||z(i,e.contentDesc)||z(i,e.resourceId));return t||s})}function Ut(p,e=E.screen.fallbackHeight,t){if(!p||p.length===0)return[];let s=p.find(u=>u.bounds.height>=e*.6&&u.bounds.y1<=e*.15),i=s?.bounds?.y1!==void 0&&s.bounds.y1>=0?s.bounds.y1:0,n=s?.bounds?.y2&&s.bounds.y2>i?s.bounds.y2:e,r=n-i,o=s?.bounds?.x2&&s.bounds.x2>0?s.bounds.x2-(s.bounds.x1||0):E.screen.fallbackWidth,a=i+r*.78,d=p.filter(u=>Se(u)||t&&u.packageName&&!u.packageName.includes(t)||!u.clickable||u.bounds.width<=0||u.bounds.height<=0||!(u.bounds.centerY>=a||u.bounds.y2>=n-20)?!1:u.bounds.width>=o*.12&&u.bounds.width<=o*.55);if(d.length<=1)return d;let c=[];for(let u of d){let g=c.find(f=>Math.abs(f[0].bounds.centerY-u.bounds.centerY)<=30||Math.abs(f[0].bounds.y2-u.bounds.y2)<=20);g||(g=[],c.push(g)),g.push(u)}let l=c.map(u=>{u.sort((f,y)=>f.bounds.centerX-y.bounds.centerX);let g=[];for(let f of u)g.some(y=>Math.abs(y.bounds.centerX-f.bounds.centerX)<o*.08)||g.push(f);return g}).filter(u=>u.length<2||u.length>6?!1:u[u.length-1].bounds.x2-u[0].bounds.x1>=o*.6);return l.length===0?d.slice(0,5):(l.sort((u,g)=>g[0].bounds.centerY-u[0].bounds.centerY),l[0].slice(0,5))}function _s(p){let e=[/fatal\s+exception/i,/unfortunately.*stopped/i,/(?:app|system|process).*(?:isn't|is not|not)\s+responding/i,/(?:isn't|is not|not)\s+responding/i,/force\s+close/i,/network\s+connection\s+(?:failed|lost|error)/i,/something went wrong/i,/(?:failed|unable)\s+to\s+(?:connect|load|fetch)/i];return p.filter(t=>{let s=`${t.text} ${t.contentDesc}`;return e.some(i=>i.test(s))})}var Ti=[/(?:\b|[_\s-])(?:log[_\s-]*out|sign[_\s-]*out)(?:\b|[_\s-])/i,/(?:\b|[_\s-])(?:delete|remove)(?:[_\s-]+(?:account|profile|all|data|item|cart|user))?(?:\b|[_\s-])/i,/(?:\b|[_\s-])(?:clear[_\s-]+data|wipe|force[_\s-]+stop|factory[_\s-]+reset|uninstall)(?:\b|[_\s-])/i,/(?:\b|[_\s-])(?:pay|purchase|checkout|buy[_\s-]+now|order[_\s-]+now)(?:\b|[_\s-])/i],Ei=[/(?:\b|[_\s-])(?:log[_\s-]*out|sign[_\s-]*out)(?:\b|[_\s-])/i,/(?:\b|[_\s-])(?:delete|remove)[_\s-]+(?:account|profile|user)(?:\b|[_\s-])/i,/(?:\b|[_\s-])(?:clear[_\s-]+data|wipe|force[_\s-]+stop|factory[_\s-]+reset|uninstall)(?:\b|[_\s-])/i],Pi=[/(?:\b|[_\s-])(?:rate[_\s-]*us|review[_\s-]*(?:on|in)[_\s-]*(?:play|store)|play[_\s-]*store)(?:\b|[_\s-])/i,/(?:\b|[_\s-])(?:visit[_\s-]*(?:our)?[_\s-]*website|privacy[_\s-]*policy|terms[_\s-]*of[_\s-]*service|terms[_\s-]*&[_\s-]*conditions)(?:\b|[_\s-])/i,/(?:\b|[_\s-])(?:follow[_\s-]*us|share[_\s-]*via|share[_\s-]*app|open[_\s-]*in[_\s-]*browser)(?:\b|[_\s-])/i,/^https?:\/\//i,/(?:www\.[a-z0-9-]+\.[a-z]{2,})/i];function Ee(p,e,t=E.safety?.mode||"strict"){if(t==="disabled")return!1;let s=`${p.text} ${p.contentDesc} ${p.resourceId}`.trim();if((t==="moderate"?Ei:Ti).some(r=>r.test(s)))return!0;let n=e||E.safety?.customBlacklist||[];for(let r of n)if(typeof r=="string"&&r.length>0){if(s.toLowerCase().includes(r.toLowerCase()))return!0}else if(r instanceof RegExp&&r.test(s))return!0;return!1}function Pe(p){let e=`${p.text} ${p.contentDesc} ${p.resourceId}`.trim();return Pi.some(t=>t.test(e))}function yt(p){for(let e of p){let t=`${e.text} ${e.contentDesc} ${e.resourceId}`;if(e.resourceId.toLowerCase().includes("logbox")||t.includes("Unhandled Promise Rejection")||t.includes("SyntaxError")||t.includes("Encountered two children with the same key")||t.includes("Warning:")||t.includes("Console Error")||t.includes("Console Warning")||t.includes("Console Info")||t.includes("TypeError:")||t.includes("ReferenceError:")||/Log \d+ of \d+/i.test(t)||t.includes("LogBox")&&(e.className.includes("Modal")||e.className.includes("ViewGroup"))){let i="error";/warning/i.test(t)?i="warning":/info/i.test(t)&&(i="info");let n=e.resourceId.includes("notification")||e.bounds.height>0&&e.bounds.height<250,r,o=[];for(let c of p){let l=`${c.text} ${c.contentDesc}`.trim();if(!r){let h=l.match(/\b([a-zA-Z0-9_.-]+\.(?:tsx?|jsx?|js|ts):\d+:\d+)\b/);h&&(r=h[1])}/\b(?:at\s+|\()?[a-zA-Z0-9_.-]+\.(?:tsx?|jsx?|js|ts):\d+/i.test(l)&&!o.includes(l)&&l.length<200&&o.push(l)}let a=p.find(c=>c.clickable&&c.bounds.width>0&&(/^(?:dismiss|close)$/i.test((c.text||"").trim())||/^(?:dismiss|close)$/i.test((c.contentDesc||"").trim())));if(!a&&!n){let l=p.filter(h=>h.clickable&&h.bounds.width>0&&h.bounds.height>0&&!/minimize|copy/i.test(`${h.text} ${h.contentDesc}`)).filter(h=>h.bounds.centerY>E.screen.fallbackHeight*.8);l.length>0&&(l.sort((h,u)=>h.bounds.x1-u.bounds.x1),a=l[0])}!a&&n&&(a=p.find(c=>c.clickable&&c.bounds.y1>=e.bounds.y1-20&&c.bounds.y2<=e.bounds.y2+20&&c.bounds.x1>=e.bounds.x2-120));let d=e.text||e.contentDesc||"React Native LogBox Console Overlay";return{type:"logbox",severity:i,node:e,errorText:d,sourceLocation:r,stackTrace:o.length>0?o:void 0,dismissButton:a,isToast:n}}if(e.resourceId.includes("alertTitle")&&(t.includes("isn't responding")||t.includes("has stopped"))){let i=p.find(n=>n.text.toLowerCase().includes("close app")||n.text.toLowerCase().includes("ok"));return{type:"anr",severity:"error",node:e,errorText:e.text||"Application Not Responding (ANR)",dismissButton:i}}}return null}function it(p){return p.filter(e=>Se(e)?!1:(e.checkable||e.className.includes("CheckBox")||e.className.includes("Switch")||e.className.includes("RadioButton")||e.className.includes("SwitchCompat"))&&e.bounds.width>0&&e.bounds.height>0)}function Oe(p,e=E.screen.fallbackHeight,t=E.screen.fallbackWidth){let s=e*.22,i=/(?:menu|hamburger|drawer|navigation|\u2630|open[_\s-]*drawer)/i,n=p.find(r=>{if(!r.clickable||r.bounds.width===0||r.bounds.centerY>s)return!1;let o=`${r.contentDesc} ${r.text} ${r.resourceId}`;return i.test(o)});return n||p.find(r=>{if(!r.clickable||r.bounds.width===0||r.bounds.height===0||r.bounds.centerY>s||(r.text||r.contentDesc||"").trim().length>0)return!1;let a=r.bounds.x1<=t*.25,d=r.bounds.width<=250&&r.bounds.height<=250;return a&&d})||null}function Os(p,e=E.screen.fallbackHeight,t=E.screen.fallbackWidth){let s=e*.22;return p.filter(i=>{if(!i.clickable||i.bounds.width===0||i.bounds.height===0||i.bounds.centerY>s)return!1;let n=i.bounds.x2>=t*.75,r=i.bounds.width<=250&&i.bounds.height<=250;return n&&r})}function Bs(p){return p.filter(e=>{if(e.bounds.width===0||e.bounds.height===0)return!1;let t=e.className.includes("ScrollView")||e.className.includes("RecyclerView")||e.className.includes("ListView")||e.className.includes("NestedScrollView");return e.scrollable||t})}function Hs(p,e=E.screen.fallbackWidth,t=E.screen.fallbackHeight,s=E.visual.overlapRatio){let i=[];for(let h of p)(h.text||h.clickable)&&h.bounds.width>0&&(h.bounds.x2>e+10&&h.bounds.x1<e&&i.push({type:"OVERFLOW",severity:"WARN",description:`Element "${h.text||h.contentDesc||h.className}" overflows right screen boundary by ${h.bounds.x2-e}px`,elements:[h.text||h.contentDesc||h.className]}),h.bounds.y2>t+20&&h.bounds.y1<t&&i.push({type:"OVERFLOW",severity:"WARN",description:`Element "${h.text||h.contentDesc||h.className}" overflows bottom screen boundary by ${h.bounds.y2-t}px`,elements:[h.text||h.contentDesc||h.className]}));let n=p.filter(h=>h.text&&h.text.trim().length>1&&h.bounds.width>0&&h.bounds.height>0),r=200,o=new Map;for(let h of n){let u=Math.floor(h.bounds.y1/r),g=Math.floor(h.bounds.y2/r);for(let f=u;f<=g;f++)o.has(f)||o.set(f,[]),o.get(f).push(h)}let a=new Set;for(let[,h]of o)for(let u=0;u<h.length;u++)for(let g=u+1;g<h.length;g++){let f=h[u],y=h[g];if(f===y||f.text===y.text)continue;let S=f.bounds.x1<y.bounds.x1?`${f.bounds.x1},${f.bounds.y1}:${y.bounds.x1},${y.bounds.y1}`:`${y.bounds.x1},${y.bounds.y1}:${f.bounds.x1},${f.bounds.y1}`;if(a.has(S))continue;a.add(S);let b=f.bounds,m=y.bounds;if(Math.abs(b.centerY-m.centerY)>(b.height+m.height)/2)continue;let v=Math.max(0,Math.min(b.x2,m.x2)-Math.max(b.x1,m.x1)),x=Math.max(0,Math.min(b.y2,m.y2)-Math.max(b.y1,m.y1)),D=v*x;if(D>0){let w=b.width*b.height,A=m.width*m.height,B=Math.min(w,A);B>0&&D/B>s&&i.push({type:"OVERLAP",severity:"WARN",description:`Visual text collision: "${f.text}" overlaps with "${y.text}" (${Math.round(D)}px\xB2 collision)`,elements:[f.text,y.text]})}}let d=p.filter(h=>(h.className.includes("ProgressBar")||h.className.includes("ActivityIndicator"))&&h.bounds.width>0);d.length>0&&i.push({type:"STALLED_LOADER",severity:"WARN",description:`Active loading indicator (${d[0].className}) detected on screen`,elements:d.map(h=>h.className)});let c=[],l=new Set;for(let h of i){let u=`${h.type}::${[...h.elements].sort().join("::")}`;l.has(u)||(l.add(u),c.push(h))}return c}function Yt(p,e,t){let s=e?.height||E.screen.fallbackHeight,i=e?.width||E.screen.fallbackWidth,n=Ut(p,s,t);if(n.length>1&&n[0].bounds.centerY>=s*.75)return{paradigm:"bottom_tabs",roots:n};let r=s*.05,o=s*.22,a=p.filter(c=>{if(Se(c)||t&&c.packageName&&!c.packageName.includes(t))return!1;let l=c.className.includes("Tab")||c.resourceId.toLowerCase().includes("tab")||c.resourceId.toLowerCase().includes("header_tab")||/(?:tab\s*\d|\bphotos\b|\bvideos\b|\baudios\b)/i.test(`${c.text} ${c.contentDesc}`),h=c.bounds.centerY>=r&&c.bounds.centerY<=o,u=c.bounds.height>25&&c.bounds.height<=220;return(l||h)&&c.clickable&&c.bounds.width>50&&u});if(a.length>=2){let c=[];for(let h of a){let u=c.find(g=>Math.abs(g[0].bounds.centerY-h.bounds.centerY)<=20);u||(u=[],c.push(u)),u.push(h)}let l=c.find(h=>h.length>=2);if(l){l.sort((g,f)=>g.bounds.centerX-f.bounds.centerX);let h=[];for(let g of l)h.some(f=>Math.abs(f.bounds.centerX-g.bounds.centerX)<40)||h.push(g);if(!(h.length===2&&h[0].bounds.x1<=i*.25&&h[1].bounds.x2>=i*.75&&!h.some(g=>g.className.includes("Tab")||g.resourceId.toLowerCase().includes("tab")))&&h.length>=2)return{paradigm:"top_tabs",roots:h.slice(0,5)}}}let d=Oe(p,s,i);return d?{paradigm:"drawer",roots:[d]}:{paradigm:"single_hub",roots:[]}}function Us(p,e=2){if(p.length<=e)return p;let t=[];for(let i of p){let n=t.find(r=>{let o=r[0],a=o.className===i.className,d=Math.abs(o.bounds.width-i.bounds.width)<=30,c=Math.abs(o.bounds.height-i.bounds.height)<=30,l=Math.abs(o.bounds.x1-i.bounds.x1)<=30;return a&&d&&c&&l});n||(n=[],t.push(n)),n.push(i)}let s=[];for(let i of t)s.push(...i.slice(0,e));return s}function we(p,e=E.screen.fallbackHeight,t=E.screen.fallbackWidth,s=!1){let i=e*.22,n=t*.28,r=/(?:navigate[_\s-]*up|go[_\s-]*back|^back$|\bback\b|arrow_back|ic_arrow_back|chevron[_\s-]*left|ic_back|btn_back|action_bar_up|header[_\s-]*left|prev(?:ious)?|^close$|\bclose\b|^cancel$|^dismiss$|^done$|^exit$|[<←«‹❮⟨◁◀✕✖×]|\u2190|\u2196|\u2039|\u27E8|\u276E|\u25C1|\u25C0|\u00AB|\u2715|\u2716|\u00D7|[\uF060\uF104\uF053\uF2EA\uF00D\uE5C4\uE5CD])/i,o=a=>a.bounds.width>0&&a.bounds.height>0&&a.bounds.centerY<=i;for(let a of p){if(Se(a)||!o(a))continue;let d=`${a.text||""} ${a.contentDesc||""} ${a.resourceId||""}`.trim();if(r.test(d)||d.length<=2&&/^[<‹«←x✕×\-]$/i.test(d))return a.clickable?a:p.find(l=>l.clickable&&l.bounds.x1<=a.bounds.x1&&l.bounds.x2>=a.bounds.x2&&l.bounds.y1<=a.bounds.y1&&l.bounds.y2>=a.bounds.y2&&l.bounds.width<=300&&l.bounds.height<=300)||a}for(let a of p){if(Se(a)||!o(a)||a.bounds.x1>n)continue;if(a.className.includes("ImageButton")||a.className.includes("ImageView")||a.className.includes("ViewGroup")&&a.bounds.width<=250&&a.bounds.height<=250){let c=`${a.text||""} ${a.contentDesc||""} ${a.resourceId||""}`.toLowerCase();if(c.includes("back")||c.includes("up")||c.includes("arrow")||c.includes("chevron")||c.includes("nav")||c.includes("close")||c.includes("cancel"))return a.clickable?a:p.find(h=>h.clickable&&h.bounds.x1<=a.bounds.x1&&h.bounds.x2>=a.bounds.x2&&h.bounds.y1<=a.bounds.y1&&h.bounds.y2>=a.bounds.y2)||a}}if(s){let a=p.find(d=>{if(Se(d)||!o(d)||!d.clickable)return!1;let c=d.bounds.x1<=n,l=d.bounds.width<=250&&d.bounds.height<=250;return c&&l&&d.bounds.width<t*.4});if(a)return a}return null}import Le from"fs";import Be from"path";var rt=class{outputDir;steps=[];errorsDetected=[];startTime=new Date;packageName="Unknown";deviceId="Unknown";pendingWrites=[];formats=["json","md","junit","html"];videoPath;embedScreenshots=!1;screenshotBuffers=new Map;stepListeners=[];completeListeners=[];errorListeners=[];constructor(e,t){typeof e=="object"&&e!==null?(this.outputDir=e.outputDir||Be.resolve(process.cwd(),"output"),e.formats&&e.formats.length>0&&(this.formats=e.formats),this.embedScreenshots=!!e.embedScreenshots):(this.outputDir=e||Be.resolve(process.cwd(),"output"),t&&t.length>0&&(this.formats=t)),Le.existsSync(this.outputDir)||Le.mkdirSync(this.outputDir,{recursive:!0})}setEmbedScreenshots(e){this.embedScreenshots=e}onStep(e){return this.stepListeners.push(e),()=>{let t=this.stepListeners.indexOf(e);t!==-1&&this.stepListeners.splice(t,1)}}onComplete(e){return this.completeListeners.push(e),()=>{let t=this.completeListeners.indexOf(e);t!==-1&&this.completeListeners.splice(t,1)}}onError(e){return this.errorListeners.push(e),()=>{let t=this.errorListeners.indexOf(e);t!==-1&&this.errorListeners.splice(t,1)}}setFormats(e){this.formats=e}setVideoPath(e){this.videoPath=e}init(e,t){this.packageName=e,this.deviceId=t,this.startTime=new Date,this.steps=[],this.errorsDetected=[],this.pendingWrites=[],this.videoPath=void 0,this.screenshotBuffers.clear()}clearScreenshotBuffers(){this.screenshotBuffers.clear()}logStep(e,t,s,i,n,r,o,a,d){let c=this.steps.length+1,l;if(r){this.embedScreenshots&&this.screenshotBuffers.set(c,r);let u=`step_${c}_${e.toLowerCase().replace(/[^a-z0-9]/g,"_")}.png`,g=Be.join(this.outputDir,u),f=Le.promises.writeFile(g,r).catch(y=>{console.error(`Warning: Failed to write screenshot ${u}:`,y)}).finally(()=>{let y=this.pendingWrites.indexOf(f);y!==-1&&this.pendingWrites.splice(y,1)});this.pendingWrites.push(f),l=u}let h={stepIndex:c,timestamp:new Date().toISOString(),phase:e,description:t,action:s,target:n,status:i,screenshotPath:l,durationMs:a,triageBundle:d,details:o};this.steps.push(h);for(let u of this.stepListeners)try{u(h)}catch{}return h}logError(e){this.errorsDetected.push(e);for(let t of this.errorListeners)try{t(e)}catch{}}async generateReport(e){this.pendingWrites.length>0&&await Promise.all(this.pendingWrites);let t=new Date,s=Math.round((t.getTime()-this.startTime.getTime())/1e3),i={packageName:this.packageName,deviceId:this.deviceId,startTime:this.startTime.toISOString(),endTime:t.toISOString(),durationSeconds:s,totalSteps:this.steps.length,passedSteps:this.steps.filter(r=>r.status==="PASS").length,failedSteps:this.steps.filter(r=>r.status==="FAIL").length,skippedSteps:this.steps.filter(r=>r.status==="SKIPPED").length,warnSteps:this.steps.filter(r=>r.status==="WARN").length,errorsDetected:this.errorsDetected,videoPath:this.videoPath,steps:this.steps},n=e||"prompttest";if(this.formats.includes("json")){let r=Be.join(this.outputDir,`${n}-results.json`);await Le.promises.writeFile(r,JSON.stringify(i,null,2),"utf-8")}if(this.formats.includes("md")){let r=Be.join(this.outputDir,`${n}-report.md`),o=this.formatMarkdownReport(i);await Le.promises.writeFile(r,o,"utf-8")}if(this.formats.includes("junit")){let r=Be.join(this.outputDir,`${n}-junit.xml`),o=this.formatJunitXml(i,n);await Le.promises.writeFile(r,o,"utf-8")}if(this.formats.includes("html")){let r=Be.join(this.outputDir,`${n}-report.html`),o=this.formatHtmlReport(i,n);await Le.promises.writeFile(r,o,"utf-8")}for(let r of this.completeListeners)try{r(i)}catch{}return this.clearScreenshotBuffers(),i}escapeXml(e){return(e||"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}escapeHtml(e){return(e||"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}formatJunitXml(e,t){let s=e.durationSeconds,i=e.failedSteps+(e.errorsDetected.length>0&&e.failedSteps===0?1:0),n=['<?xml version="1.0" encoding="UTF-8"?>',`<testsuites name="PromptTest" tests="${e.totalSteps}" failures="${i}" skipped="${e.skippedSteps}" time="${s}">`,` <testsuite name="${this.escapeXml(t)}" tests="${e.totalSteps}" failures="${i}" skipped="${e.skippedSteps}" time="${s}">`];for(let r of e.steps){let o=`Step ${r.stepIndex}: ${this.escapeXml(r.action)} - ${this.escapeXml(r.description)}`,a=r.durationMs!==void 0?(r.durationMs/1e3).toFixed(3):"1.000";n.push(` <testcase classname="${this.escapeXml(t)}" name="${o}" time="${a}">`),r.status==="FAIL"?n.push(` <failure message="${this.escapeXml(r.description)}">${this.escapeXml(r.description)}</failure>`):r.status==="SKIPPED"&&n.push(` <skipped message="${this.escapeXml(r.description)}"/>`),n.push(" </testcase>")}return e.failedSteps===0&&e.errorsDetected.length>0&&n.push(` <testcase classname="${this.escapeXml(t)}" name="Error Sentinel Crash Detection" time="0.000">`,` <failure message="${this.escapeXml(e.errorsDetected[0])}">${this.escapeXml(e.errorsDetected.join(`
|
|
4
|
+
`).map(o=>o.trim()).filter(Boolean),s=t.findIndex(o=>o.toLowerCase().includes("list of devices attached")),i=s!==-1?t.slice(s+1):t,n=[];return n=(o=>{let a=[];for(let d of o){if(!d||d.startsWith("*")||d.toLowerCase().includes("list of devices"))continue;let c=d.split(/\s+/);if(c.length>=2){let l=c[0],h=c[1],u,g;for(let f of c.slice(2))f.startsWith("model:")&&(u=f.replace("model:","")),f.startsWith("product:")&&(g=f.replace("product:",""));a.push({serial:l,state:h,model:u,product:g})}}return a})(i),n}async wakeDevice(e){try{await this.execAdb(["shell","input","keyevent","224"],e),await this.execAdb(["shell","wm","dismiss-keyguard"],e)}catch{}}async ensureDefaultDevice(e){if(e)return await this.wakeDevice(e),e;if(this.defaultSerial)return await this.wakeDevice(this.defaultSerial),this.defaultSerial;let s=(await this.getConnectedDevices()).find(i=>i.state==="device");return s&&(this.defaultSerial=s.serial,await this.wakeDevice(this.defaultSerial)),this.defaultSerial||void 0}async getDeviceModel(e){try{let{stdout:n}=await this.execAdb(["shell","getprop","ro.product.model"],e),r=n.trim();if(r)return r}catch{}let t=await this.getConnectedDevices(),s=e||this.defaultSerial;return t.find(n=>n.serial===s)?.model||s||"Android Device"}async captureScreenshot(e){return this.execAdbBinary(ae.SCREENCAP,e)}async getUiHierarchy(e){let t=e||this.defaultSerial||"default",s=this.inFlightHierarchies.get(t);if(s)return s;let i=(async()=>{try{let{stdout:n}=await this.execAdb(ae.UIAUTOMATOR_DUMP,e,1e4,0),r=n.indexOf("<hierarchy");return r>=0?n.slice(r):n}finally{this.inFlightHierarchies.delete(t)}})();return this.inFlightHierarchies.set(t,i),i}clearInFlightHierarchies(e){e?this.inFlightHierarchies.delete(e):this.inFlightHierarchies.clear()}async getForegroundPackage(e){let t=s=>{let i=s.toLowerCase();return i==="android"||i.includes("systemui")||i.includes("inputmethod")||i.includes("latin")||i.includes("gboard")||i.includes("swiftkey")||i.includes("ime")};try{let{stdout:s}=await this.execAdb(["shell","dumpsys","activity","activities"],e).catch(()=>({stdout:""})),i=s.match(/(?:topResumedActivity|mResumedActivity|ResumedActivity)[=:\s]+(?:ActivityRecord\{[^}]*?\s+)?([a-zA-Z0-9._]+)\//i);if(i&&i[1]&&!t(i[1]))return i[1];let{stdout:n}=await this.execAdb(ae.FOREGROUND_APP,e).catch(()=>({stdout:""})),r=n.matchAll(/(?:mCurrentFocus|mFocusedWindow|mFocusedApp|topResumedActivity|mResumedActivity|mFocusedActivity)[=:\s]+(?:Window\{[^}]*?\s+|ActivityRecord\{[^}]*?\s+|AppWindowToken\{[^}]*?\s+)?([a-zA-Z0-9._]+)\//gi);for(let o of r)if(o&&o[1]&&!t(o[1]))return o[1]}catch{}return null}async tap(e,t,s){await this.execAdb(ae.INPUT_TAP(e,t),s)}async inputText(e,t){let s=e.replace(/\\/g,"\\\\").replace(/"/g,'\\"').replace(/'/g,"\\'").replace(/%/g,"\\%").replace(/ /g,"%s").replace(/&/g,"\\&").replace(/</g,"\\<").replace(/>/g,"\\>").replace(/\(/g,"\\(").replace(/\)/g,"\\)").replace(/;/g,"\\;").replace(/\|/g,"\\|").replace(/\$/g,"\\$").replace(/#/g,"\\#").replace(/\*/g,"\\*").replace(/\?/g,"\\?").replace(/~/g,"\\~").replace(/`/g,"\\`");try{await this.execAdb(ae.INPUT_TEXT(s),t)}catch{for(let i of e)if(i===" ")await this.pressKey(62,t);else if(i==="@")await this.pressKey(77,t);else{let n=i.replace(/([\\"'&<>()|$;#*?~`%])/g,"\\$1");await this.execAdb(["shell","input","text",n],t).catch(()=>{})}}}async pressKey(e,t){await this.execAdb(ae.INPUT_KEY(e),t)}async pressEnter(e){await this.pressKey(66,e)}async pressTab(e){await this.pressKey(61,e)}async pressHome(e){await this.pressKey(3,e)}async pressBack(e){await this.pressKey(4,e)}async setWifiEnabled(e,t){await this.execAdb(["shell","svc","wifi",e?"enable":"disable"],t)}async setAirplaneMode(e,t){await this.execAdb(["shell","cmd","connectivity","airplane-mode",e?"enable":"disable"],t)}async setOrientation(e,t){await this.execAdb(["shell","content","insert","--uri","content://settings/system","--bind","name:s:accelerometer_rotation","--bind","value:i:0"],t).catch(()=>{});let s=e==="landscape"?"1":"0";await this.execAdb(["shell","settings","put","system","user_rotation",s],t)}async openNotificationShade(e){await this.execAdb(["shell","cmd","statusbar","expand-notifications"],e)}async collapseNotificationShade(e){await this.execAdb(["shell","cmd","statusbar","collapse"],e)}async clearInput(e=45,t){let s=Array(e).fill("67");await this.execAdb(["shell","input","keyevent",...s],t)}async clearField(e,t=80){await this.pressKey(123,e);let s=Math.max(50,t+10),i=Array(s).fill("67");await this.execAdb(["shell","input","keyevent",...i],e).catch(()=>{}),await this.pressKey(122,e);let n=Array(Math.max(30,Math.floor(t/2))).fill("112");await this.execAdb(["shell","input","keyevent",...n],e).catch(()=>{})}async swipe(e,t,s,i,n=300,r){await this.execAdb(ae.INPUT_SWIPE(e,t,s,i,n),r)}async launchApp(e,t){try{let{stdout:s}=await this.execAdb(["shell","cmd","package","resolve-activity","--brief",e],t),n=(s||"").split(`
|
|
5
|
+
`).map(r=>r.trim()).find(r=>r.includes("/")&&!r.includes(" "));if(n){await this.execAdb(["shell","am","start","-n",n],t);return}}catch{}await this.execAdb(ae.LAUNCH_APP(e),t)}async forceStop(e,t){await this.execAdb(ae.FORCE_STOP(e),t)}async clearAppData(e,t){await this.execAdb(ae.CLEAR_APP(e),t)}async restartApp(e,t){await this.forceStop(e,t),await new Promise(s=>setTimeout(s,600)),await this.launchApp(e,t)}async grantPermission(e,t,s){await this.execAdb(ae.GRANT_PERMISSION(e,t),s)}async isAppInstalled(e,t){try{let{stdout:s}=await this.execAdb(["shell","pm","list","packages",e],t);return s.toLowerCase().includes(`package:${e.toLowerCase()}`)||s.includes(e)}catch{return!1}}async installApk(e,t){await this.execAdb(ae.INSTALL_APK(e),t)}async uninstallApp(e,t){await this.execAdb(ae.UNINSTALL_APP(e),t)}async openDeepLink(e,t){await this.execAdb(ae.DEEP_LINK(e),t)}async checkCrashes(e,t){try{let{stdout:s}=await this.execAdb(ae.LOGCAT_CRASH(e),t);if(s&&s.includes("FATAL EXCEPTION")&&s.includes(e))return s}catch{}return null}async clearCrashLogs(e){try{await this.execAdb(ae.CLEAR_CRASH_LOGS(),e)}catch{}}async getDeviceIp(e){try{let{stdout:t}=await this.execAdb(ae.GET_WLAN_IP,e),s=t.match(/inet\s+(\d+\.\d+\.\d+\.\d+)/);if(s&&s[1])return s[1]}catch{}try{let{stdout:t}=await this.execAdb(["shell","ip","route"],e),s=t.match(/src\s+(\d+\.\d+\.\d+\.\d+)/);if(s&&s[1])return s[1]}catch{}return null}async connectWifi(e,t=5555,s){let i=e,n=t;if(e&&e.includes(":")){let l=e.split(":");i=l[0],n=parseInt(l[1],10)||t}if(!i){let l=await this.getDeviceIp(s);if(!l)throw new Error("Could not auto-detect phone Wi-Fi IP. Make sure the phone is on Wi-Fi, or pass IP manually: npx prompttest wifi <ip>");i=l}let r=`${i}:${n}`;if(e)try{let{stdout:l,stderr:h}=await this.execAdb(ae.CONNECT(r)),u=`${l} ${h}`.trim();if(u.toLowerCase().includes("connected")||u.toLowerCase().includes("already"))return this.defaultSerial=r,{success:!0,target:r,message:u}}catch{}try{await this.execAdb(ae.TCPIP(n),s),await new Promise(l=>setTimeout(l,1e3))}catch{}let{stdout:o,stderr:a}=await this.execAdb(ae.CONNECT(r)),d=`${o} ${a}`.trim(),c=d.toLowerCase().includes("connected")||d.toLowerCase().includes("already");return c&&(this.defaultSerial=r),{success:c,target:r,message:d}}async disconnectWifi(e){let{stdout:t,stderr:s}=await this.execAdb(ae.DISCONNECT(e));return`${t} ${s}`.trim()}streamLogcatEvents(e,t){let i=et("adb",["-s",e,"shell","logcat","-b","events","-v","time"]),n=Bt.createInterface({input:i.stdout});return n.on("line",t),i.on("close",()=>{try{n.close()}catch{}}),i.on("error",()=>{}),i}streamLogcatCrashes(e,t){let i=et("adb",["-s",e,"shell","logcat","-T","1","-s","AndroidRuntime:E","ANRManager:E","-v","time"]),n=Bt.createInterface({input:i.stdout});return n.on("line",t),i.on("close",()=>{try{n.close()}catch{}}),i.on("error",()=>{}),i}streamLogcatToasts(e,t){let i=et("adb",["-s",e,"shell","logcat","-s","Toast:D","Snackbar:D","-v","time"]),n=Bt.createInterface({input:i.stdout});return n.on("line",t),i.on("close",()=>{try{n.close()}catch{}}),i.on("error",()=>{}),i}async getLogcatTail(e=150,t){try{let{stdout:s}=await this.execAdb(["logcat","-d","-t",String(e),"-v","time"],t);return s}catch(s){return`Failed to capture logcat: ${s instanceof Error?s.message:String(s)}`}}async startScreenRecording(e="/sdcard/prompttest_temp.mp4",t){let s=t||this.defaultSerial,n=et("adb",s?["-s",s,"shell","screenrecord",e]:["shell","screenrecord",e]);return n.on("error",()=>{}),{remotePath:e,process:n}}async stopScreenRecording(e,t,s){try{try{e.process.kill("SIGINT")}catch{}return await this.execAdb(["shell","pkill","-2","screenrecord"],s).catch(()=>{}),await new Promise(i=>setTimeout(i,1500)),await this.execAdb(["pull",e.remotePath,t],s),await this.execAdb(["shell","rm","-f",e.remotePath],s).catch(()=>{}),t}catch{return""}}async isKeyboardVisible(e){try{let{stdout:t}=await this.execAdb(["shell","dumpsys","input_method"],e,4e3,0);return t.includes("mInputShown=true")||t.includes("isInputViewShown=true")||t.includes("mHasSurface=true")}catch{return!1}}async ensureAccessibilityServices(e){try{await this.execAdb(["shell","settings","put","secure","accessibility_enabled","1"],e,4e3,0)}catch{}}},he=new tt;var Ls=/^(\d+(?:\.\d+)*\.?|\-|\*)\s*/;function Rs(p){let e=p.trim(),t=e.match(/^"([^"]+)"$/);if(t)return t[1].trim();let s=e.match(/^'([^']+)'$/);return s?s[1].trim():e.replace(/^['"]+|['"]+$/g,"").trim()}var V=`(?:"([^"]+)"|'([^']+)'|([^'"\\s][^'"]*?[^'"\\s]|\\S))`;function W(p){for(let e of p)if(e!=null&&e.trim().length>0)return e.trim();return""}function Xe(p,e){let t=p;if(e)for(let[s,i]of Object.entries(e))t=t.replace(new RegExp(`\\$${s}\\b`,"g"),()=>i);return t=t.replace(/\$ENV\{([A-Z0-9_]+)\}/gi,(s,i)=>process.env[i]||""),t=t.replace(/\$RANDOM_EMAIL\b/g,()=>`test_${Date.now().toString().slice(-6)}@prompttest.io`),t=t.replace(/\$RANDOM_NAME\b/g,()=>`User_${Math.floor(1e3+Math.random()*9e3)}`),t=t.replace(/\$RANDOM_PHONE\b/g,()=>`98${Math.floor(1e7+Math.random()*9e7)}`),t=t.replace(/\$TIMESTAMP\b/g,()=>Date.now().toString()),t=t.replace(/\$UUID\b/g,()=>`${Date.now().toString(16)}-${Math.random().toString(16).substring(2,8)}`),t=t.replace(/\$RANDOM_UUID\b/g,()=>"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,s=>{let i=Math.random()*16|0;return(s==="x"?i:i&3|8).toString(16)})),t=t.replace(/\$RANDOM_CARD\b/g,()=>{let s="4532";for(let i=0;i<12;i++)s+=Math.floor(Math.random()*10);return s}),t=t.replace(/\$DATE_OFFSET\((-?\d+)\)/g,(s,i)=>{let n=parseInt(i,10)||0;return new Date(Date.now()+n*864e5).toISOString().slice(0,10)}),t=t.replace(/\$RANDOM_INT\((\d+),\s*(\d+)\)/g,(s,i,n)=>{let r=parseInt(i,10),o=parseInt(n,10);return String(Math.floor(r+Math.random()*(o-r+1)))}),t}var O={TAP:new RegExp(`^(?:tap|click|press)(?:\\s+on)?\\s+(?:button\\s+|item\\s+)?${V}$`,"i"),ORDINAL_TAP:new RegExp(`^(?:tap|click|press)(?:\\s+on)?\\s+(?:the\\s+)?(1st|2nd|3rd|4th|5th|last|first)\\s+(?:button\\s+|item\\s+)?${V}$`,"i"),PROXIMITY_TAP:new RegExp(`^(?:tap|click|press)(?:\\s+on)?\\s+${V}\\s+(?:next\\s+to|near|beside|inside|for)\\s+${V}$`,"i"),CLEAR:new RegExp(`^(?:clear|empty)(?:\\s+(?:text\\s+(?:in|of)|field|input))?\\s+${V}$`,"i"),LONG_PRESS:new RegExp(`^(?:long\\s+press|press\\s+and\\s+hold)\\s+(?:button\\s+|item\\s+)?${V}$`,"i"),TYPE:new RegExp(`^(?:type|enter|input)\\s+${V}\\s+(?:in|into)\\s+${V}$`,"i"),VERIFY:new RegExp(`^(?:verify|veryfy|check|assert|ensure)\\s+(?:that\\s+)?(?:error\\s+message\\s+|message\\s+|text\\s+|element\\s+|button\\s+|toast\\s+|my\\s+)?${V}\\s*(?:is\\s+(visible|exists|appears|enabled|disabled|not\\s+visible|hidden|absent|not\\s+present|gone))?$`,"i"),BACK:/^(?:press\s+back|go\s+back|navigate\s+back|back)$/i,SCROLL_TO:new RegExp(`^(?:scroll|swipe)(?:\\s+(down|up))?\\s+(?:to|until)\\s+${V}$`,"i"),SCROLL:/^(?:scroll|swipe)\s+(down|up)$/i,DIRECTIONAL_SWIPE:new RegExp(`^(?:swipe|scroll)\\s+(left|right|up|down)(?:\\s+on\\s+${V})?$`,"i"),TOGGLE:new RegExp(`^(?:toggle|turn\\s+(on|off)|check|uncheck)\\s+(?:checkbox\\s+|switch\\s+)?${V}$`,"i"),SELECT_DROPDOWN:new RegExp(`^(?:select|choose)\\s+${V}\\s+from\\s+${V}$`,"i"),LAUNCH_APP:new RegExp(`^(?:launch|open)\\s+(?:app\\s+)?${V}$`,"i"),RESTART_APP:new RegExp(`^(?:restart|relaunch)\\s+(?:app\\s+)?${V}$`,"i"),TERMINATE_APP:new RegExp(`^(?:terminate|close|force\\s*stop)\\s+(?:app\\s+)?(?:${V})?$`,"i"),CLEAR_DATA:new RegExp(`^(?:clear\\s+(?:app\\s+)?data\\s+(?:for\\s+)?|reset\\s+app\\s+)${V}$`,"i"),GRANT_PERMISSION:new RegExp(`^grant\\s+permissions?\\s+${V}(?:\\s+(?:to|for)\\s+${V})?$`,"i"),DEEP_LINK:new RegExp(`^(?:open\\s+deep\\s*link|navigate\\s+to\\s+url)\\s+${V}$`,"i"),WAIT:/^wait\s+(\d+)\s*(?:s|sec|seconds|ms)?$/i,WAIT_UNTIL:new RegExp(`^(?:wait\\s+until|wait\\s+for)\\s+${V}\\s+(?:(?:is\\s+)?(visible|appears?|enabled|exists))?(?:\\s+(?:up\\s+to|for)\\s+(\\d+)\\s*(s|sec|seconds|ms)?)?$`,"i"),IF_VISIBLE:new RegExp(`^if\\s+(?:(?:that\\s+)?${V}\\s+(?:is\\s+)?(visible|present|exists|appears?)|element\\s+${V}\\s+(?:is\\s+)?visible)$`,"i"),ELSE:/^else$/i,END:/^(?:end|end\s*if|end\s*loop|fi|done)$/i,LOOP:/^(?:loop|repeat)\s+(\d+)\s*(?:times)?$/i,FOR_EACH:new RegExp(`^for\\s+each\\s+(?:item\\s+)?(?:in\\s+)?${V}$`,"i"),SET_VARIABLE:/^set\s+\$([a-zA-Z0-9_]+)\s*=\s*(.+)$/i,DEVICE_WIFI:/^(?:turn\s+|set\s+)?wifi\s+(on|off|enable|disable)$/i,DEVICE_AIRPLANE:/^(?:turn\s+|set\\s+)?airplane\s*(?:mode)?\s+(on|off|enable|disable)$/i,DEVICE_ORIENTATION:/^(?:set\s+)?orientation\s+(?:to\s+)?(portrait|landscape)$/i,DEVICE_NOTIFICATIONS:/^(open|expand|pull\s+down|collapse|close)\s+notification(?:s|\s+shade)?$/i,INCLUDE_SPEC:new RegExp(`^(?:include|run_spec|subflow)\\s+${V}$`,"i"),RUN_JS:/^(?:run\s+js|eval\s+js|js)(?::|\s+)(.+)$/i,ENTER_OTP:new RegExp(`^(?:enter|input|type)\\s+(?:the\\s+)?(?:verification\\s+code|otp|2fa\\s+code|sms\\s+code)(?:\\s+just\\s+sent)?(?:\\s+(?:in|into)\\s+${V})?$`,"i"),API_REQUEST:new RegExp(`^(?:api|call\\s+api)\\s+(GET|POST|PUT|DELETE|PATCH)\\s+${V}(?:\\s+with\\s+body\\s+${V})?(?:\\s+store\\s+${V}\\s+as\\s+\\$([a-zA-Z0-9_]+))?(?:\\s+assert\\s+status\\s+(\\d+))?$`,"i"),EMAIL_ASSERT:new RegExp(`^(?:assert|verify)\\s+email\\s+to\\s+${V}\\s+with\\s+subject\\s+${V}(?:\\s+contains\\s+${V})?$`,"i"),OPTIONAL_SUFFIX:/\s*\((?:optional|if\s+present|maybe)\)\s*$/i,FALLBACK_QUOTED_TAP:/^(?:tap|click)\s+.*?['"]([^'"]+)['"]/i},ft={HIERARCHY_ROOT:/<hierarchy[^>]*>([\s\S]*?)<\/hierarchy>/i,NODE_TAG:/<node\b([^>]*?)(\/?)>|<\/node>/gi,ATTR_KEY_VALUE:/([a-zA-Z0-9_-]+)="([^"]*)"/g,BOUNDS_RECT:/\[(\d+),(\d+)\]\[(\d+),(\d+)\]/};function st(p){return(p||"").replace(/&#(\d+);/g,(e,t)=>{try{return String.fromCodePoint(parseInt(t,10))}catch{return""}}).replace(/&#x([0-9a-fA-F]+);/g,(e,t)=>{try{return String.fromCodePoint(parseInt(t,16))}catch{return""}}).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,'"').replace(/'/g,"'")}function X(p){return st(p||"").trim().toLowerCase().replace(/[\u2018\u2019]/g,"'").replace(/[\u201C\u201D]/g,'"').replace(/\uFFFD/g,"").replace(new RegExp("\\p{Extended_Pictographic}","gu"),"").replace(/[\uE000-\uF8FF\uFFF0-\uFFFF]/g,"").replace(/\s+/g," ").trim()}var Is=new Map;function z(p,e){if(!p||!e)return!1;let t=X(p),s=X(e);if(t.length<=3){let i=Is.get(t);if(!i){let n=t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");i=new RegExp(`(?:^|\\b)${n}(?:\\b|$)`,"i"),Is.set(t,i)}return i.test(s)}return s.includes(t)}var Ns=["sign in","signin","login","log in","submit","save","continue","next","done","send","confirm","proceed","post","rate","enter","join","apply","update"],Ms=["sign in","signin","login","log in","submit","save","next","continue","done","like","fav","favorite","star","bookmark","add","post","confirm","apply","explore","search","filter"];function yi(p,e){let t=X(p),s=X(e),i=t.length,n=s.length;if(i===0)return n;if(n===0)return i;let r=Array.from({length:n+1},(a,d)=>d),o=new Array(n+1);for(let a=1;a<=i;a++){o[0]=a;for(let c=1;c<=n;c++){let l=t[a-1]===s[c-1]?0:1;o[c]=Math.min(r[c]+1,o[c-1]+1,r[c-1]+l)}let d=r;r=o,o=d}return r[n]}function Si(p,e){let t=X(p),s=X(e);if(t===s)return 1;if(!t||!s)return 0;let i=Math.max(t.length,s.length);return 1-yi(t,s)/i}function We(p,e,t=3,s=.35){return Array.from(new Set(e.map(r=>r.trim()).filter(r=>r.length>0))).map(r=>({text:r,score:Si(p,r)})).filter(r=>r.score>=s).sort((r,o)=>o.score-r.score).slice(0,t)}function Fs(p){let e=p.match(ft.BOUNDS_RECT);if(!e)return{x1:0,y1:0,x2:0,y2:0,width:0,height:0,centerX:0,centerY:0};let t=parseInt(e[1],10),s=parseInt(e[2],10),i=parseInt(e[3],10),n=parseInt(e[4],10);return{x1:t,y1:s,x2:i,y2:n,width:i-t,height:n-s,centerX:Math.round((t+i)/2),centerY:Math.round((s+n)/2)}}function wi(p){let e=p.match(ft.HIERARCHY_ROOT);if(!e)return{root:[],flat:[]};let t=e[1],s=new RegExp(ft.NODE_TAG.source,"gi"),i=new RegExp(ft.ATTR_KEY_VALUE.source,"g"),n=[],r=[],o=[],a;for(;(a=s.exec(t))!==null;){let d=a[0],c=a[1],l=a[2]==="/";if(d.startsWith("</node>")){o.pop();continue}let h={};i.lastIndex=0;let u;for(;(u=i.exec(c))!==null;)h[u[1]]=u[2];let g={index:parseInt(h.index||"0",10),text:st(h.text||""),resourceId:h["resource-id"]||"",className:h.class||"",packageName:h.package||"",contentDesc:st(h["content-desc"]||""),bounds:Fs(h.bounds||""),clickable:h.clickable==="true",enabled:h.enabled==="true",focused:h.focused==="true",scrollable:h.scrollable==="true",password:h.password==="true",selected:h.selected==="true",checked:h.checked!==void 0?h.checked==="true":void 0,checkable:h.checkable!==void 0?h.checkable==="true":void 0,children:[]};r.push(g),o.length===0?n.push(g):o[o.length-1].children.push(g),l||o.push(g)}return{root:n,flat:r}}function vi(p){let e=[],t=[],s=[],i=p.length,n=0;for(;n<i;){let r=p.indexOf("<",n);if(r===-1)break;if(p.startsWith("<!--",r)){let S=p.indexOf("-->",r+4);n=S===-1?i:S+3;continue}if(p.startsWith("<?",r)){let S=p.indexOf("?>",r+2);n=S===-1?i:S+2;continue}if(p.startsWith("</",r)){let S=p.indexOf(">",r+2);if(S===-1)break;p.slice(r+2,S).trim()==="node"&&s.pop(),n=S+1;continue}let o=-1,a=null,d=!1;for(let S=r+1;S<i;S++){let b=p[S];if(a)b===a&&(a=null);else if(b==='"'||b==="'")a=b;else if(b===">"){o=S,p[S-1]==="/"&&(d=!0);break}}if(o===-1)break;let c=p.slice(r+1,d?o-1:o).trim();n=o+1;let l=0;for(;l<c.length&&!/\s/.test(c[l]);)l++;if(c.slice(0,l)!=="node")continue;let u={},g=l,f=c.length;for(;g<f;){for(;g<f&&/\s/.test(c[g]);)g++;if(g>=f)break;let S=g;for(;g<f&&c[g]!=="="&&!/\s/.test(c[g]);)g++;let b=c.slice(S,g);for(;g<f&&/\s/.test(c[g]);)g++;if(g>=f||c[g]!=="=")break;for(g++;g<f&&/\s/.test(c[g]);)g++;if(g>=f)break;let m=c[g];if(m==='"'||m==="'"){g++;let v=g;for(;g<f&&c[g]!==m;)g++;u[b]=c.slice(v,g),g<f&&g++}else{let v=g;for(;g<f&&!/\s/.test(c[g]);)g++;u[b]=c.slice(v,g)}}let y={index:parseInt(u.index||"0",10),text:st(u.text||""),resourceId:u["resource-id"]||"",className:u.class||"",packageName:u.package||"",contentDesc:st(u["content-desc"]||""),bounds:Fs(u.bounds||""),clickable:u.clickable==="true",enabled:u.enabled==="true",focused:u.focused==="true",scrollable:u.scrollable==="true",password:u.password==="true",selected:u.selected==="true",checked:u.checked!==void 0?u.checked==="true":void 0,checkable:u.checkable!==void 0?u.checkable==="true":void 0,children:[]};t.push(y),s.length===0?e.push(y):s[s.length-1].children.push(y),d||s.push(y)}return{root:e,flat:t}}function ue(p,e){if((e||E.parser?.mode||"regex")==="streaming")try{return vi(p)}catch{}return wi(p)}function ir(p,e){return p.filter(t=>{if(e.clickable!==void 0&&t.clickable!==e.clickable||e.className&&!t.className.includes(e.className))return!1;if(e.text){if(typeof e.text=="string"){if(!t.text.toLowerCase().includes(e.text.toLowerCase()))return!1}else if(!e.text.test(t.text))return!1}if(e.contentDesc){if(typeof e.contentDesc=="string"){if(!t.contentDesc.toLowerCase().includes(e.contentDesc.toLowerCase()))return!1}else if(!e.contentDesc.test(t.contentDesc))return!1}if(e.resourceId){if(typeof e.resourceId=="string"){if(!t.resourceId.toLowerCase().includes(e.resourceId.toLowerCase()))return!1}else if(!e.resourceId.test(t.resourceId))return!1}return!0})}var xi=[/logbox/i,/^Log \d+ of \d+/i,/Console (?:Error|Warning|Log|Info)/i,/^Call Stack$/i,/^Source Map$/i,/^(?:Dismiss|Minimize|Copy|View error log)$/i,/\b[\w-]+\.(?:tsx?|jsx?|js|ts):\d+:\d+\b/i,/Unhandled Promise Rejection/i,/SyntaxError/i,/TypeError:/i,/ReferenceError:/i,/Warning:/i,/LogBoxNotificationOverlay/i];function Se(p){if(p.resourceId&&/logbox/i.test(p.resourceId)||p.className&&/logbox/i.test(p.className))return!0;let e=`${p.text} ${p.contentDesc}`.trim();return!!xi.some(t=>t.test(e))}function _e(p){return p.filter(e=>Se(e)?!1:(e.className.includes("EditText")||e.className.includes("TextInputEditText")||e.focused&&e.clickable)&&e.bounds.width>0&&e.bounds.height>0)}function Ht(p){return p.className.includes("EditText")||p.className.includes("TextInputEditText")?!0:(p.children||[]).some(Ht)}function bt(p,e){let t=e?.width||E.screen.fallbackWidth,s=e?.height||E.screen.fallbackHeight,i=s*.08,n=s*.88,r=p.filter(a=>!Se(a)&&(a.className.includes("RecyclerView")||a.className.includes("ListView")||a.className.includes("ScrollView"))),o=[];for(let a of r)for(let d of a.children){if(Se(d)||d.bounds.centerY<i||d.bounds.centerY>n)continue;(d.clickable||d.children.some(l=>!Se(l)&&l.clickable&&l.bounds.width>t*.1))&&d.bounds.width>t*.08&&d.bounds.height>s*.02&&!Ht(d)&&o.push(d)}return o.length===0?p.filter(a=>{if(Se(a))return!1;let d=(a.text||a.contentDesc||"").trim();return d.length<=1||/^(remember me|forgot password|sign in|sign up|login|submit|cancel)$/i.test(d)?!1:a.clickable&&a.bounds.width>t*.14&&a.bounds.height>s*.033&&a.bounds.centerY>i&&a.bounds.centerY<n&&!a.className.includes("Button")&&!a.className.includes("EditText")&&!Ht(a)}):o}function Ge(p){return p.filter(e=>{if(Se(e)||!e.clickable||e.bounds.width===0||e.className.includes("EditText"))return!1;let t=e.className.includes("Button")||e.className.includes("ImageButton"),s=Ms.some(i=>z(i,e.text)||z(i,e.contentDesc)||z(i,e.resourceId));return t||s})}function Ut(p,e=E.screen.fallbackHeight,t){if(!p||p.length===0)return[];let s=p.find(u=>u.bounds.height>=e*.6&&u.bounds.y1<=e*.15),i=s?.bounds?.y1!==void 0&&s.bounds.y1>=0?s.bounds.y1:0,n=s?.bounds?.y2&&s.bounds.y2>i?s.bounds.y2:e,r=n-i,o=s?.bounds?.x2&&s.bounds.x2>0?s.bounds.x2-(s.bounds.x1||0):E.screen.fallbackWidth,a=i+r*.78,d=p.filter(u=>Se(u)||t&&u.packageName&&!u.packageName.includes(t)||!u.clickable||u.bounds.width<=0||u.bounds.height<=0||!(u.bounds.centerY>=a||u.bounds.y2>=n-20)?!1:u.bounds.width>=o*.12&&u.bounds.width<=o*.55);if(d.length<=1)return d;let c=[];for(let u of d){let g=c.find(f=>Math.abs(f[0].bounds.centerY-u.bounds.centerY)<=30||Math.abs(f[0].bounds.y2-u.bounds.y2)<=20);g||(g=[],c.push(g)),g.push(u)}let l=c.map(u=>{u.sort((f,y)=>f.bounds.centerX-y.bounds.centerX);let g=[];for(let f of u)g.some(y=>Math.abs(y.bounds.centerX-f.bounds.centerX)<o*.08)||g.push(f);return g}).filter(u=>u.length<2||u.length>6?!1:u[u.length-1].bounds.x2-u[0].bounds.x1>=o*.6);return l.length===0?d.slice(0,5):(l.sort((u,g)=>g[0].bounds.centerY-u[0].bounds.centerY),l[0].slice(0,5))}function _s(p){let e=[/fatal\s+exception/i,/unfortunately.*stopped/i,/(?:app|system|process).*(?:isn't|is not|not)\s+responding/i,/(?:isn't|is not|not)\s+responding/i,/force\s+close/i,/network\s+connection\s+(?:failed|lost|error)/i,/something went wrong/i,/(?:failed|unable)\s+to\s+(?:connect|load|fetch)/i];return p.filter(t=>{let s=`${t.text} ${t.contentDesc}`;return e.some(i=>i.test(s))})}var Ti=[/(?:\b|[_\s-])(?:log[_\s-]*out|sign[_\s-]*out)(?:\b|[_\s-])/i,/(?:\b|[_\s-])(?:delete|remove)(?:[_\s-]+(?:account|profile|all|data|item|cart|user))?(?:\b|[_\s-])/i,/(?:\b|[_\s-])(?:clear[_\s-]+data|wipe|force[_\s-]+stop|factory[_\s-]+reset|uninstall)(?:\b|[_\s-])/i,/(?:\b|[_\s-])(?:pay|purchase|checkout|buy[_\s-]+now|order[_\s-]+now)(?:\b|[_\s-])/i,/(?:\b|[_\s-])(?:exit[_\s-]*app|close[_\s-]*app|^exit$|^quit$)(?:\b|[_\s-])/i],Ei=[/(?:\b|[_\s-])(?:log[_\s-]*out|sign[_\s-]*out)(?:\b|[_\s-])/i,/(?:\b|[_\s-])(?:delete|remove)[_\s-]+(?:account|profile|user)(?:\b|[_\s-])/i,/(?:\b|[_\s-])(?:clear[_\s-]+data|wipe|force[_\s-]+stop|factory[_\s-]+reset|uninstall)(?:\b|[_\s-])/i],Pi=[/(?:\b|[_\s-])(?:rate[_\s-]*us|review[_\s-]*(?:on|in)[_\s-]*(?:play|store)|play[_\s-]*store)(?:\b|[_\s-])/i,/(?:\b|[_\s-])(?:visit[_\s-]*(?:our)?[_\s-]*website|privacy[_\s-]*policy|terms[_\s-]*of[_\s-]*service|terms[_\s-]*&[_\s-]*conditions)(?:\b|[_\s-])/i,/(?:\b|[_\s-])(?:follow[_\s-]*us|share[_\s-]*via|share[_\s-]*app|open[_\s-]*in[_\s-]*browser)(?:\b|[_\s-])/i,/^https?:\/\//i,/(?:www\.[a-z0-9-]+\.[a-z]{2,})/i];function Ee(p,e,t=E.safety?.mode||"strict"){if(t==="disabled")return!1;let s=`${p.text} ${p.contentDesc} ${p.resourceId}`.trim();if((t==="moderate"?Ei:Ti).some(r=>r.test(s)))return!0;let n=e||E.safety?.customBlacklist||[];for(let r of n)if(typeof r=="string"&&r.length>0){if(s.toLowerCase().includes(r.toLowerCase()))return!0}else if(r instanceof RegExp&&r.test(s))return!0;return!1}function Pe(p){let e=`${p.text} ${p.contentDesc} ${p.resourceId}`.trim();return Pi.some(t=>t.test(e))}function yt(p){for(let e of p){let t=`${e.text} ${e.contentDesc} ${e.resourceId}`;if(e.resourceId.toLowerCase().includes("logbox")||t.includes("Unhandled Promise Rejection")||t.includes("SyntaxError")||t.includes("Encountered two children with the same key")||t.includes("Warning:")||t.includes("Console Error")||t.includes("Console Warning")||t.includes("Console Info")||t.includes("TypeError:")||t.includes("ReferenceError:")||/Log \d+ of \d+/i.test(t)||t.includes("LogBox")&&(e.className.includes("Modal")||e.className.includes("ViewGroup"))){let i="error";/warning/i.test(t)?i="warning":/info/i.test(t)&&(i="info");let n=e.resourceId.includes("notification")||e.bounds.height>0&&e.bounds.height<250,r,o=[];for(let c of p){let l=`${c.text} ${c.contentDesc}`.trim();if(!r){let h=l.match(/\b([a-zA-Z0-9_.-]+\.(?:tsx?|jsx?|js|ts):\d+:\d+)\b/);h&&(r=h[1])}/\b(?:at\s+|\()?[a-zA-Z0-9_.-]+\.(?:tsx?|jsx?|js|ts):\d+/i.test(l)&&!o.includes(l)&&l.length<200&&o.push(l)}let a=p.find(c=>c.clickable&&c.bounds.width>0&&(/^(?:dismiss|close)$/i.test((c.text||"").trim())||/^(?:dismiss|close)$/i.test((c.contentDesc||"").trim())));if(!a&&!n){let l=p.filter(h=>h.clickable&&h.bounds.width>0&&h.bounds.height>0&&!/minimize|copy/i.test(`${h.text} ${h.contentDesc}`)).filter(h=>h.bounds.centerY>E.screen.fallbackHeight*.8);l.length>0&&(l.sort((h,u)=>h.bounds.x1-u.bounds.x1),a=l[0])}!a&&n&&(a=p.find(c=>c.clickable&&c.bounds.y1>=e.bounds.y1-20&&c.bounds.y2<=e.bounds.y2+20&&c.bounds.x1>=e.bounds.x2-120));let d=e.text||e.contentDesc||"React Native LogBox Console Overlay";return{type:"logbox",severity:i,node:e,errorText:d,sourceLocation:r,stackTrace:o.length>0?o:void 0,dismissButton:a,isToast:n}}if(e.resourceId.includes("alertTitle")&&(t.includes("isn't responding")||t.includes("has stopped"))){let i=p.find(n=>n.text.toLowerCase().includes("close app")||n.text.toLowerCase().includes("ok"));return{type:"anr",severity:"error",node:e,errorText:e.text||"Application Not Responding (ANR)",dismissButton:i}}}return null}function it(p){return p.filter(e=>Se(e)?!1:(e.checkable||e.className.includes("CheckBox")||e.className.includes("Switch")||e.className.includes("RadioButton")||e.className.includes("SwitchCompat"))&&e.bounds.width>0&&e.bounds.height>0)}function Oe(p,e=E.screen.fallbackHeight,t=E.screen.fallbackWidth){let s=e*.22,i=/(?:menu|hamburger|drawer|navigation|\u2630|open[_\s-]*drawer)/i,n=p.find(r=>{if(!r.clickable||r.bounds.width===0||r.bounds.centerY>s)return!1;let o=`${r.contentDesc} ${r.text} ${r.resourceId}`;return i.test(o)});return n||p.find(r=>{if(!r.clickable||r.bounds.width===0||r.bounds.height===0||r.bounds.centerY>s||(r.text||r.contentDesc||"").trim().length>0)return!1;let a=r.bounds.x1<=t*.25,d=r.bounds.width<=250&&r.bounds.height<=250;return a&&d})||null}function Os(p,e=E.screen.fallbackHeight,t=E.screen.fallbackWidth){let s=e*.22;return p.filter(i=>{if(!i.clickable||i.bounds.width===0||i.bounds.height===0||i.bounds.centerY>s)return!1;let n=i.bounds.x2>=t*.75,r=i.bounds.width<=250&&i.bounds.height<=250;return n&&r})}function Bs(p){return p.filter(e=>{if(e.bounds.width===0||e.bounds.height===0)return!1;let t=e.className.includes("ScrollView")||e.className.includes("RecyclerView")||e.className.includes("ListView")||e.className.includes("NestedScrollView");return e.scrollable||t})}function Hs(p,e=E.screen.fallbackWidth,t=E.screen.fallbackHeight,s=E.visual.overlapRatio){let i=[];for(let h of p)(h.text||h.clickable)&&h.bounds.width>0&&(h.bounds.x2>e+10&&h.bounds.x1<e&&i.push({type:"OVERFLOW",severity:"WARN",description:`Element "${h.text||h.contentDesc||h.className}" overflows right screen boundary by ${h.bounds.x2-e}px`,elements:[h.text||h.contentDesc||h.className]}),h.bounds.y2>t+20&&h.bounds.y1<t&&i.push({type:"OVERFLOW",severity:"WARN",description:`Element "${h.text||h.contentDesc||h.className}" overflows bottom screen boundary by ${h.bounds.y2-t}px`,elements:[h.text||h.contentDesc||h.className]}));let n=p.filter(h=>h.text&&h.text.trim().length>1&&h.bounds.width>0&&h.bounds.height>0),r=200,o=new Map;for(let h of n){let u=Math.floor(h.bounds.y1/r),g=Math.floor(h.bounds.y2/r);for(let f=u;f<=g;f++)o.has(f)||o.set(f,[]),o.get(f).push(h)}let a=new Set;for(let[,h]of o)for(let u=0;u<h.length;u++)for(let g=u+1;g<h.length;g++){let f=h[u],y=h[g];if(f===y||f.text===y.text)continue;let S=f.bounds.x1<y.bounds.x1?`${f.bounds.x1},${f.bounds.y1}:${y.bounds.x1},${y.bounds.y1}`:`${y.bounds.x1},${y.bounds.y1}:${f.bounds.x1},${f.bounds.y1}`;if(a.has(S))continue;a.add(S);let b=f.bounds,m=y.bounds;if(Math.abs(b.centerY-m.centerY)>(b.height+m.height)/2)continue;let v=Math.max(0,Math.min(b.x2,m.x2)-Math.max(b.x1,m.x1)),x=Math.max(0,Math.min(b.y2,m.y2)-Math.max(b.y1,m.y1)),D=v*x;if(D>0){let w=b.width*b.height,A=m.width*m.height,B=Math.min(w,A);B>0&&D/B>s&&i.push({type:"OVERLAP",severity:"WARN",description:`Visual text collision: "${f.text}" overlaps with "${y.text}" (${Math.round(D)}px\xB2 collision)`,elements:[f.text,y.text]})}}let d=p.filter(h=>(h.className.includes("ProgressBar")||h.className.includes("ActivityIndicator"))&&h.bounds.width>0);d.length>0&&i.push({type:"STALLED_LOADER",severity:"WARN",description:`Active loading indicator (${d[0].className}) detected on screen`,elements:d.map(h=>h.className)});let c=[],l=new Set;for(let h of i){let u=`${h.type}::${[...h.elements].sort().join("::")}`;l.has(u)||(l.add(u),c.push(h))}return c}function Yt(p,e,t){let s=e?.height||E.screen.fallbackHeight,i=e?.width||E.screen.fallbackWidth,n=Ut(p,s,t);if(n.length>1&&n[0].bounds.centerY>=s*.75)return{paradigm:"bottom_tabs",roots:n};let r=s*.05,o=s*.22,a=p.filter(c=>{if(Se(c)||t&&c.packageName&&!c.packageName.includes(t))return!1;let l=c.className.includes("Tab")||c.resourceId.toLowerCase().includes("tab")||c.resourceId.toLowerCase().includes("header_tab")||/(?:tab\s*\d|\bphotos\b|\bvideos\b|\baudios\b)/i.test(`${c.text} ${c.contentDesc}`),h=c.bounds.centerY>=r&&c.bounds.centerY<=o,u=c.bounds.height>25&&c.bounds.height<=220;return(l||h)&&c.clickable&&c.bounds.width>50&&u});if(a.length>=2){let c=[];for(let h of a){let u=c.find(g=>Math.abs(g[0].bounds.centerY-h.bounds.centerY)<=20);u||(u=[],c.push(u)),u.push(h)}let l=c.find(h=>h.length>=2);if(l){l.sort((g,f)=>g.bounds.centerX-f.bounds.centerX);let h=[];for(let g of l)h.some(f=>Math.abs(f.bounds.centerX-g.bounds.centerX)<40)||h.push(g);if(!(h.length===2&&h[0].bounds.x1<=i*.25&&h[1].bounds.x2>=i*.75&&!h.some(g=>g.className.includes("Tab")||g.resourceId.toLowerCase().includes("tab")))&&h.length>=2)return{paradigm:"top_tabs",roots:h.slice(0,5)}}}let d=Oe(p,s,i);return d?{paradigm:"drawer",roots:[d]}:{paradigm:"single_hub",roots:[]}}function Us(p,e=2){if(p.length<=e)return p;let t=[];for(let i of p){let n=t.find(r=>{let o=r[0],a=o.className===i.className,d=Math.abs(o.bounds.width-i.bounds.width)<=30,c=Math.abs(o.bounds.height-i.bounds.height)<=30,l=Math.abs(o.bounds.x1-i.bounds.x1)<=30;return a&&d&&c&&l});n||(n=[],t.push(n)),n.push(i)}let s=[];for(let i of t)s.push(...i.slice(0,e));return s}function we(p,e=E.screen.fallbackHeight,t=E.screen.fallbackWidth,s=!1){let i=e*.22,n=t*.28,r=/(?:navigate[_\s-]*up|go[_\s-]*back|^back$|\bback\b|arrow_back|ic_arrow_back|chevron[_\s-]*left|ic_back|btn_back|action_bar_up|header[_\s-]*left|prev(?:ious)?|^close$|\bclose\b|^cancel$|^dismiss$|^done$|^exit$|[<←«‹❮⟨◁◀✕✖×]|\u2190|\u2196|\u2039|\u27E8|\u276E|\u25C1|\u25C0|\u00AB|\u2715|\u2716|\u00D7|[\uF060\uF104\uF053\uF2EA\uF00D\uE5C4\uE5CD])/i,o=a=>a.bounds.width>0&&a.bounds.height>0&&a.bounds.centerY<=i;for(let a of p){if(Se(a)||!o(a))continue;let d=`${a.text||""} ${a.contentDesc||""} ${a.resourceId||""}`.trim();if(r.test(d)||d.length<=2&&/^[<‹«←x✕×\-]$/i.test(d))return a.clickable?a:p.find(l=>l.clickable&&l.bounds.x1<=a.bounds.x1&&l.bounds.x2>=a.bounds.x2&&l.bounds.y1<=a.bounds.y1&&l.bounds.y2>=a.bounds.y2&&l.bounds.width<=300&&l.bounds.height<=300)||a}for(let a of p){if(Se(a)||!o(a)||a.bounds.x1>n)continue;if(a.className.includes("ImageButton")||a.className.includes("ImageView")||a.className.includes("ViewGroup")&&a.bounds.width<=250&&a.bounds.height<=250){let c=`${a.text||""} ${a.contentDesc||""} ${a.resourceId||""}`.toLowerCase();if(c.includes("back")||c.includes("up")||c.includes("arrow")||c.includes("chevron")||c.includes("nav")||c.includes("close")||c.includes("cancel"))return a.clickable?a:p.find(h=>h.clickable&&h.bounds.x1<=a.bounds.x1&&h.bounds.x2>=a.bounds.x2&&h.bounds.y1<=a.bounds.y1&&h.bounds.y2>=a.bounds.y2)||a}}if(s){let a=p.find(d=>{if(Se(d)||!o(d)||!d.clickable)return!1;let c=d.bounds.x1<=n,l=d.bounds.width<=250&&d.bounds.height<=250;return c&&l&&d.bounds.width<t*.4});if(a)return a}return null}import Le from"fs";import Be from"path";var rt=class{outputDir;steps=[];errorsDetected=[];startTime=new Date;packageName="Unknown";deviceId="Unknown";pendingWrites=[];formats=["json","md","junit","html"];videoPath;embedScreenshots=!1;screenshotBuffers=new Map;stepListeners=[];completeListeners=[];errorListeners=[];constructor(e,t){typeof e=="object"&&e!==null?(this.outputDir=e.outputDir||Be.resolve(process.cwd(),"output"),e.formats&&e.formats.length>0&&(this.formats=e.formats),this.embedScreenshots=!!e.embedScreenshots):(this.outputDir=e||Be.resolve(process.cwd(),"output"),t&&t.length>0&&(this.formats=t)),Le.existsSync(this.outputDir)||Le.mkdirSync(this.outputDir,{recursive:!0})}setEmbedScreenshots(e){this.embedScreenshots=e}onStep(e){return this.stepListeners.push(e),()=>{let t=this.stepListeners.indexOf(e);t!==-1&&this.stepListeners.splice(t,1)}}onComplete(e){return this.completeListeners.push(e),()=>{let t=this.completeListeners.indexOf(e);t!==-1&&this.completeListeners.splice(t,1)}}onError(e){return this.errorListeners.push(e),()=>{let t=this.errorListeners.indexOf(e);t!==-1&&this.errorListeners.splice(t,1)}}setFormats(e){this.formats=e}setVideoPath(e){this.videoPath=e}init(e,t){this.packageName=e,this.deviceId=t,this.startTime=new Date,this.steps=[],this.errorsDetected=[],this.pendingWrites=[],this.videoPath=void 0,this.screenshotBuffers.clear()}clearScreenshotBuffers(){this.screenshotBuffers.clear()}logStep(e,t,s,i,n,r,o,a,d){let c=this.steps.length+1,l;if(r){this.embedScreenshots&&this.screenshotBuffers.set(c,r);let u=`step_${c}_${e.toLowerCase().replace(/[^a-z0-9]/g,"_")}.png`,g=Be.join(this.outputDir,u),f=Le.promises.writeFile(g,r).catch(y=>{console.error(`Warning: Failed to write screenshot ${u}:`,y)}).finally(()=>{let y=this.pendingWrites.indexOf(f);y!==-1&&this.pendingWrites.splice(y,1)});this.pendingWrites.push(f),l=u}let h={stepIndex:c,timestamp:new Date().toISOString(),phase:e,description:t,action:s,target:n,status:i,screenshotPath:l,durationMs:a,triageBundle:d,details:o};this.steps.push(h);for(let u of this.stepListeners)try{u(h)}catch{}return h}logError(e){this.errorsDetected.push(e);for(let t of this.errorListeners)try{t(e)}catch{}}async generateReport(e){this.pendingWrites.length>0&&await Promise.all(this.pendingWrites);let t=new Date,s=Math.round((t.getTime()-this.startTime.getTime())/1e3),i={packageName:this.packageName,deviceId:this.deviceId,startTime:this.startTime.toISOString(),endTime:t.toISOString(),durationSeconds:s,totalSteps:this.steps.length,passedSteps:this.steps.filter(r=>r.status==="PASS").length,failedSteps:this.steps.filter(r=>r.status==="FAIL").length,skippedSteps:this.steps.filter(r=>r.status==="SKIPPED").length,warnSteps:this.steps.filter(r=>r.status==="WARN").length,errorsDetected:this.errorsDetected,videoPath:this.videoPath,steps:this.steps},n=e||"prompttest";if(this.formats.includes("json")){let r=Be.join(this.outputDir,`${n}-results.json`);await Le.promises.writeFile(r,JSON.stringify(i,null,2),"utf-8")}if(this.formats.includes("md")){let r=Be.join(this.outputDir,`${n}-report.md`),o=this.formatMarkdownReport(i);await Le.promises.writeFile(r,o,"utf-8")}if(this.formats.includes("junit")){let r=Be.join(this.outputDir,`${n}-junit.xml`),o=this.formatJunitXml(i,n);await Le.promises.writeFile(r,o,"utf-8")}if(this.formats.includes("html")){let r=Be.join(this.outputDir,`${n}-report.html`),o=this.formatHtmlReport(i,n);await Le.promises.writeFile(r,o,"utf-8")}for(let r of this.completeListeners)try{r(i)}catch{}return this.clearScreenshotBuffers(),i}escapeXml(e){return(e||"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}escapeHtml(e){return(e||"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}formatJunitXml(e,t){let s=e.durationSeconds,i=e.failedSteps+(e.errorsDetected.length>0&&e.failedSteps===0?1:0),n=['<?xml version="1.0" encoding="UTF-8"?>',`<testsuites name="PromptTest" tests="${e.totalSteps}" failures="${i}" skipped="${e.skippedSteps}" time="${s}">`,` <testsuite name="${this.escapeXml(t)}" tests="${e.totalSteps}" failures="${i}" skipped="${e.skippedSteps}" time="${s}">`];for(let r of e.steps){let o=`Step ${r.stepIndex}: ${this.escapeXml(r.action)} - ${this.escapeXml(r.description)}`,a=r.durationMs!==void 0?(r.durationMs/1e3).toFixed(3):"1.000";n.push(` <testcase classname="${this.escapeXml(t)}" name="${o}" time="${a}">`),r.status==="FAIL"?n.push(` <failure message="${this.escapeXml(r.description)}">${this.escapeXml(r.description)}</failure>`):r.status==="SKIPPED"&&n.push(` <skipped message="${this.escapeXml(r.description)}"/>`),n.push(" </testcase>")}return e.failedSteps===0&&e.errorsDetected.length>0&&n.push(` <testcase classname="${this.escapeXml(t)}" name="Error Sentinel Crash Detection" time="0.000">`,` <failure message="${this.escapeXml(e.errorsDetected[0])}">${this.escapeXml(e.errorsDetected.join(`
|
|
6
6
|
`))}</failure>`," </testcase>"),n.push(" </testsuite>"),n.push("</testsuites>"),n.join(`
|
|
7
7
|
`)}formatMarkdownReport(e){let i=["# \u{1F9EA} PromptTest Autonomous Mobile QA Audit Report","",`**Status:** ${e.failedSteps===0&&e.errorsDetected.length===0?"\u2705 ALL PASSED":"\u26A0\uFE0F ISSUES DETECTED"} `,`**Target Package:** \`${e.packageName}\` `,`**Device:** \`${e.deviceId}\` `,`**Execution Time:** ${e.durationSeconds}s (${e.startTime} to ${e.endTime}) `];if(e.videoPath&&i.push(`**Screen Recording:** [Download Video](./${e.videoPath}) `),i.push("","---","","## \u{1F4CA} Executive Summary","","| Metric | Count |","|---|---|",`| **Total Steps** | ${e.totalSteps} |`,`| **Passed** | \u{1F7E2} ${e.passedSteps} |`,`| **Warnings** | \u{1F7E1} ${e.warnSteps} |`,`| **Failures** | \u{1F534} ${e.failedSteps} |`,`| **Skipped** | \u26AA ${e.skippedSteps} |`,`| **Errors Detected** | ${e.errorsDetected.length} |`,""),e.errorsDetected.length>0){i.push("## \u26A0\uFE0F Errors & Sentinels Flagged");for(let r of e.errorsDetected)i.push(`- \u{1F534} \`${r}\``);i.push("")}i.push("## \u{1F50D} Detailed Execution Timeline"),i.push(""),i.push("| Step | Phase | Action | Target / Details | Status | Duration | Screenshot / Triage |"),i.push("|---|---|---|---|---|---|---|");let n=r=>(r||"").replace(/\|/g,"\\|").replace(/\r?\n/g," ");for(let r of e.steps){let o="\u2014";r.triageBundle?o=`[\u{1F50D} Triage Bundle](./${r.triageBundle})`:r.screenshotPath&&(o=`[\u{1F4F8} Screenshot](./${r.screenshotPath})`);let a=r.durationMs!==void 0?`${r.durationMs}ms`:"\u2014",d=r.status==="PASS"?"\u{1F7E2} PASS":r.status==="WARN"?"\u{1F7E1} WARN":r.status==="SKIPPED"?"\u26AA SKIPPED":"\u{1F534} FAIL";i.push(`| ${r.stepIndex} | ${n(r.phase)} | ${n(r.action)} | ${n(r.description)} | ${d} | ${a} | ${o} |`)}return i.push(""),i.push("---"),i.push("*Generated autonomously by PromptTest Mobile QA Brain.*"),i.join(`
|
|
8
8
|
`)}resolveScreenshotData(e){if(e.screenshotPath){if(this.embedScreenshots){if(this.screenshotBuffers.has(e.stepIndex))return`data:image/png;base64,${this.screenshotBuffers.get(e.stepIndex).toString("base64")}`;let t=Be.join(this.outputDir,e.screenshotPath);if(Le.existsSync(t))try{return`data:image/png;base64,${Le.readFileSync(t).toString("base64")}`}catch{}}return`./${e.screenshotPath}`}}formatHtmlReport(e,t){return jt(e,{suiteName:t,embedScreenshots:this.embedScreenshots,screenshotResolver:s=>this.resolveScreenshotData(s)})}};function jt(p,e){let t=e?.suiteName||"PromptTest Suite",s=p.failedSteps===0&&p.errorsDetected.length===0,i=s?"ALL PASSED":"ISSUES DETECTED",n=s?"#10b981":"#ef4444",r=p.totalSteps,o=r>0?Math.round(p.passedSteps/r*100):100,a=u=>(u||"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'"),d=[],c=p.steps.map(u=>{let g=u.status.toLowerCase(),f=u.durationMs!==void 0?`${u.durationMs}ms`:"\u2014",y=e?.screenshotResolver?e.screenshotResolver(u):u.screenshotPath?`./${u.screenshotPath}`:void 0;y&&d.push({stepIndex:u.stepIndex,action:u.action,description:u.description,status:u.status,src:y,durationMs:u.durationMs});let S="\u2014";if(u.triageBundle){let x=a(u.triageBundle);S=`
|
|
@@ -739,11 +739,11 @@ data: ${JSON.stringify(g)}
|
|
|
739
739
|
\u{1F6D1} Aborting dependent flow due to upstream failure at Step ${U}.`)),console.log(R.gray(`Cascading skip applied to ${k.length} downstream step(s):`));for(let _=0;_<k.length;_++){let L=k[_],oe=U+1+_;this.reporter.logStep(te,L.originalText,L.type,"SKIPPED",`Cascading skip: Precondition failed at Step ${U} (${N.originalText})`,void 0,void 0,0),console.log(R.gray(` [Step ${oe}/${le.length}] \u26AA SKIPPED: ${L.originalText}`))}}break}}}}try{let B=this.lastHierarchy;if(B?.flat){let Z=Hs(B.flat,this.screenDimensions.width,this.screenDimensions.height);if(Z.length>0){console.log(R.bold.yellow(`
|
|
740
740
|
\u{1F50D} Visual QA Sentinel Warnings (${Z.length} detected):`));for(let j of Z)console.log(R.yellow(` \u2022 [${j.type}] ${j.description}`)),this.reporter.logError(`Visual Defect (${j.type}): ${j.description}`)}}}catch{}await this.memory.save(),i.resume&&v===0&&await this.checkpoints.clear(c);let x=this.memory.getStats();if(x.totalMappings>0&&console.log(R.cyan(`\u{1F9E0} Knowledge Engine: ${x.totalMappings} active learned mapping(s) for ${x.package}`)),S){console.log(R.cyan("\u{1F3AC} Finalizing video recording..."));let B=me.resolve(process.cwd(),"output"),Z=me.join(B,`${c}-recording.mp4`),j=await this.driver.stopScreenRecording(S,Z,i.serial);j&&Ce.existsSync(j)&&(this.reporter.setVideoPath?.(`${c}-recording.mp4`),console.log(R.green(`\u2714 Screen recording saved to: output/${c}-recording.mp4`))),S=null}let D=((Date.now()-m)/1e3).toFixed(1),w=await this.reporter.generateReport(c);f&&(f.broadcastComplete(w),y&&y()),console.log(R.bold.green(`
|
|
741
741
|
\u2728 PromptTest Script Execution Finished in ${D}s!`));let A=w.failedSteps===0?R.bold.green:R.bold.yellow;return console.log(A(`Result: ${w.passedSteps}/${w.totalSteps} passed (${w.failedSteps} failed, ${w.skippedSteps} skipped).`)),console.log(R.cyan(`Report saved to output/${c}-report.html
|
|
742
|
-
`)),i.json&&console.log(JSON.stringify(w,null,2)),w}finally{if(S)try{await this.driver.stopScreenRecording(S,me.join("output",`${c}-recording.mp4`),i.serial)}catch{}g&&ze(g)}}async getCleanHierarchy(e){let t=await this.driver.getUiHierarchy(e),{flat:s}=ue(t),i=s.find(o=>(o.text==="Dismiss"||o.contentDesc==="Dismiss")&&o.bounds.width>0);i&&(await this.driver.tap(i.bounds.centerX,i.bounds.centerY,e),await new Promise(o=>setTimeout(o,400)),t=await this.driver.getUiHierarchy(e),s=ue(t).flat);let n=s.find(o=>o.contentDesc&&(o.contentDesc.startsWith("!, ")||o.contentDesc.includes("react.dev/link")));if(n){let o=n.bounds.x2-45,a=n.bounds.centerY;await this.driver.tap(o,a,e),await new Promise(d=>setTimeout(d,350)),t=await this.driver.getUiHierarchy(e),s=ue(t).flat}let r=ce(s);return this.lastHierarchy={xml:t,flat:s,timestamp:Date.now(),hash:r},{xml:t,flat:s}}async ensureInViewport(e,t){let s=this.screenDimensions.width,i=this.screenDimensions.height,n=Math.round(s*.5),r=this.navBarHeight>0?i-this.navBarHeight:i,o=this.navBarHeight>0?r*.94:i*E.gestures.bottomNavThresholdRatio;if(e.bounds.centerY>o||e.bounds.y1>o)return!1;let d=e.bounds.y2>r,c=e.bounds.y1<Math.round(i*.04)&&e.bounds.centerY<Math.round(i*.06);return d?(await this.driver.swipe(n,Math.round(i*.75),n,Math.round(i*.45),250,t),await new Promise(l=>setTimeout(l,200)),this.lastHierarchy=null,!0):c?(await this.driver.swipe(n,Math.round(i*.35),n,Math.round(i*.65),250,t),await new Promise(l=>setTimeout(l,200)),this.lastHierarchy=null,!0):!1}async waitForTarget(e,t,s=3e3,i){if(this.lastHierarchy&&Date.now()-this.lastHierarchy.timestamp<E.polling.hierarchyCacheTtlMs){let a=this.matchNodes(this.lastHierarchy.flat,e,i);if(a.length>0)return{flat:this.lastHierarchy.flat,matched:a}}let n=Date.now();for(;Date.now()-n<s;){let{flat:a}=await this.getCleanHierarchy(t),d=this.matchNodes(a,e,i);if(d.length>0)return{flat:a,matched:d};await new Promise(c=>setTimeout(c,ks()))}let{flat:r}=await this.getCleanHierarchy(t),o=this.matchNodes(r,e,i);if(o.length===0&&(this.healEnabled||E.ai.selfHealing))try{let a=await this.semanticResolver.findSuggestions(e,r,{locale:this.activeLocale}),d=E.ai.confidenceThreshold||.75,c=a.find(l=>l.score>=d);if(c){let l=c.node||r.find(h=>(z(c.text,h.text)||z(c.text,h.contentDesc))&&h.bounds.width>0);if(l){console.log(R.yellow(` \u{1F4A1} [Self-Healing] Auto-healed "${e}" \u2192 "${c.text}" (${Math.round(c.score*100)}% confidence, ${c.strategy})`)),i&&(i.healedFrom=e);let h=l.text?"text":l.contentDesc?"contentDesc":"resourceId",u=this.lastHierarchy?.hash,g=Math.min(E.memory?.maxHealedConfidence??.65,c.score);this.memory.recordSuccess(i?.type||"TAP",e,c.text,h,g,e,u),o=[l]}}}catch(a){ke.warn(`Self-healing resolution failed: ${a.message}`)}return{flat:r,matched:o}}buildDiagnosticError(e,t,s){return qs(e,t,s)}async executeStep(e,t,s){let i=Gs.get(e.type);if(!i)throw new Error(`No handler registered for step type: ${e.type}`);e.target&&e.target.includes("$")&&(e.target=Xe(e.target,Object.fromEntries(this.runtimeVariables))),e.value&&e.value.includes("$")&&(e.value=Xe(e.value,Object.fromEntries(this.runtimeVariables)));let n={driver:this.driver,memory:this.memory,serial:t,screenDimensions:this.screenDimensions,navBarHeight:this.navBarHeight,lastHierarchy:this.lastHierarchy,setLastHierarchy:r=>{this.lastHierarchy=r},getCleanHierarchy:r=>this.getCleanHierarchy(r),waitForTarget:(r,o,a,d)=>this.waitForTarget(r,o,a,d),matchNodes:(r,o,a)=>this.matchNodes(r,o,a),ensureInViewport:(r,o)=>this.ensureInViewport(r,o),buildDiagnosticError:(r,o,a)=>this.buildDiagnosticError(r,o,a),findTargetInputField:(r,o,a)=>this.findTargetInputField(r,o,a),upcomingSteps:s,variables:this.runtimeVariables,loopStack:this.loopStack,executeChildSteps:async(r,o)=>{for(let a=0;a<r.length;a++){let d=r[a];await this.executeStep(d,o.serial,r.slice(a+1))}}};return i.execute(e,n)}findTargetInputField(e,t,s){return Zs(e,t,s,this.memory)}matchNodes(e,t,s){return It(e,t,this.memory,this.activeLocale,s,this.lastHierarchy?.hash)}},Jn=new Ye;import P from"chalk";import ii from"fs";import Fi from"path";var je=class{testData={};dataPath;isLoaded=!1;driver;constructor(e,t){this.dataPath=e||Fi.resolve(process.cwd(),"test-data.json"),this.driver=t||he}setTestData(e){this.testData=e,this.isLoaded=!0}async ensureDataLoaded(){if(!this.isLoaded){if(ii.existsSync(this.dataPath))try{let e=await ii.promises.readFile(this.dataPath,"utf-8");this.testData=JSON.parse(e)}catch{this.testData={}}this.isLoaded=!0}}async loadTestData(e){return e&&(this.dataPath=e),this.isLoaded=!1,await this.ensureDataLoaded(),this.testData}inferValueForNode(e,t){let s=[e.text,e.contentDesc,e.resourceId].join(" ").toLowerCase(),i="",n=t.filter(o=>o.text&&!o.clickable&&o.bounds.centerY<e.bounds.centerY&&e.bounds.centerY-o.bounds.centerY<250&&Math.abs(o.bounds.centerX-e.bounds.centerX)<400);n.length>0&&(i=n[n.length-1].text.toLowerCase());let r=`${s} ${i}`.toLowerCase();if(r.includes("review")||r.includes("feedback")||r.includes("comment")||r.includes("write a review")||r.includes("experience")){if(!this.testData.review?.text)throw new Error("Missing test-data.json field: review.text");return this.testData.review.text}if(r.includes("card")||r.includes("credit")||r.includes("debit")||r.includes("card number")){let o=this.testData.payment?.card;if(o)return String(o);let a="4532";for(let d=0;d<12;d++)a+=Math.floor(Math.random()*10);return a}if(r.includes("phone")||r.includes("mobile")||!r.includes("card")&&r.includes("number")||r.includes("contact")){let o=this.testData.auth?.phone||this.testData.profile?.phone;if(!o)throw new Error("Missing test-data.json field: auth.phone or profile.phone");return o}if(r.includes("email")||r.includes("e-mail")||r.includes("mail")){let o=this.testData.profile?.email||this.testData.auth?.email;if(!o)throw new Error("Missing test-data.json field: profile.email or auth.email");return o}if(r.includes("cvv")||r.includes("cvc")||r.includes("security code"))return"123";if(r.includes("expiry")||r.includes("exp date")||r.includes("expiration"))return"12/28";if(r.includes("pincode")||r.includes("zip")||r.includes("postal")){if(!this.testData.profile?.pincode)throw new Error("Missing test-data.json field: profile.pincode");return this.testData.profile.pincode}if(e.password||r.includes("password")||/\bpin\b/.test(r)&&!r.includes("pincode")||r.includes("passcode")){if(!this.testData.auth?.password)throw new Error("Missing test-data.json field: auth.password");return this.testData.auth.password}if(r.includes("otp")||r.includes("verification")||r.includes("code")&&!r.includes("postal")&&!r.includes("zip")){if(!this.testData.auth?.otp)throw new Error("Missing test-data.json field: auth.otp");return this.testData.auth.otp}if(r.includes("address")||r.includes("street")||r.includes("location")){if(!this.testData.profile?.address)throw new Error("Missing test-data.json field: profile.address");return this.testData.profile.address}if(r.includes("city")||r.includes("state")){if(!this.testData.profile?.city)throw new Error("Missing test-data.json field: profile.city");return this.testData.profile.city}if(r.includes("name")||r.includes("full name")||r.includes("first name")){if(!this.testData.profile?.name)throw new Error("Missing test-data.json field: profile.name");return this.testData.profile.name}if(r.includes("search")||r.includes("find")||r.includes("query")){if(!this.testData.search?.query)throw new Error("Missing test-data.json field: search.query");return this.testData.search.query}return this.testData.review?.text||"PromptTest Auto QA Test Data"}async fillAndSubmitForm(e,t,s=!0,i=!1){await this.ensureDataLoaded();let n=_e(e),r=[];if(n.length===0)return{filledFields:[],submitted:!1};let o=!1;for(let c of n){let l=c.resourceId||c.text||`input_${c.bounds.centerX}_${c.bounds.centerY}`,h=(c.text||"").trim(),u=!h||/^enter\s+/i.test(h)||/^type\s+/i.test(h)||/^e\.g\./i.test(h)||/^(email|email address|password|phone|name|search)$/i.test(h);if(!i&&!u){r.push({fieldId:l,fieldLabel:c.text||c.contentDesc||c.resourceId,valueEntered:h});continue}let g=this.inferValueForNode(c,e);await this.driver.tap(c.bounds.centerX,c.bounds.centerY,t),await new Promise(f=>setTimeout(f,400)),await this.driver.clearField(t,g.length+10),await new Promise(f=>setTimeout(f,200)),await this.driver.inputText(g,t),await new Promise(f=>setTimeout(f,300)),o=!0,r.push({fieldId:l,fieldLabel:c.text||c.contentDesc||c.resourceId,valueEntered:g})}o&&(await this.driver.pressKey(4,t),await new Promise(c=>setTimeout(c,500)));let a=!1,d;if(s){let c=e.filter(l=>!l.clickable||l.bounds.width===0?!1:Ns.some(h=>z(h,l.text)||z(h,l.contentDesc)||z(h,l.resourceId)));if(c.length>0){let l=["submit","sign in","signin","login","save","confirm","proceed","continue"];c.sort((u,g)=>{let f=(u.text||u.contentDesc||u.resourceId||"").toLowerCase(),y=(g.text||g.contentDesc||g.resourceId||"").toLowerCase(),S=l.findIndex(x=>f.includes(x)),b=l.findIndex(x=>y.includes(x)),m=S>=0?S:99,v=b>=0?b:99;return m!==v?m-v:g.bounds.centerY-u.bounds.centerY});let h=c[0];d=h.text||h.contentDesc||h.resourceId,await this.driver.tap(h.bounds.centerX,h.bounds.centerY,t),await new Promise(u=>setTimeout(u,E.settle.submitWaitMs));try{let u=await this.driver.getUiHierarchy(t),{flat:g}=ue(u),f=_e(g);a=!(f.length>0&&n.every(S=>f.some(b=>b.resourceId&&b.resourceId===S.resourceId)))}catch{a=!0}}}return{filledFields:r,submitted:a,submitButtonText:d}}},ri=new je;import H from"chalk";var Rt=class{driver;reporter;memory;formFiller;screenLedger=new Map;callStack=[];executedStepCount=0;visitedScreenHashes=new Set;devDefectsList=[];safetyHitList=[];externalSkippedList=[];constructor(e=he,t=nt,s=at){this.driver=e,this.reporter=t,this.memory=s,this.formFiller=new je(void 0,e)}async getCleanUiHierarchy(e,t,s=5){let i=await this.driver.getUiHierarchy(t),{flat:n}=ue(i),r=0;for(;r<s;){let o=yt(n);if(!o)break;r++;let a=`[${(o.severity||"error").toUpperCase()} - ${o.type.toUpperCase()}]`,d=o.sourceLocation?` [${o.sourceLocation}]`:"";console.log(H.red(` \u{1F6A8} Global Dev Console Intercepted: ${a} "${o.errorText.slice(0,80)}"${d}`)),this.devDefectsList.push(o.sourceLocation?`${o.errorText} (${o.sourceLocation})`:o.errorText);let c=await this.driver.captureScreenshot(t);this.reporter.logStep("Dev Overlay Triage",`Intercepted and triaged ${a}: "${o.errorText}"${d}`,"Verify","FAIL",o.errorText,c,{errors:[o.sourceLocation?`${o.errorText} at ${o.sourceLocation}`:o.errorText]}),o.dismissButton&&o.dismissButton.bounds.width>0?(console.log(H.gray(" Dismissing overlay via dismiss button...")),await this.driver.tap(o.dismissButton.bounds.centerX,o.dismissButton.bounds.centerY,t)):o.isToast&&o.node&&o.node.bounds.width>0?(console.log(H.gray(" Expanding toast notification to access Dismiss button...")),await this.driver.tap(o.node.bounds.centerX,o.node.bounds.centerY,t)):(console.log(H.gray(" Pressing Back to dismiss overlay...")),await this.driver.pressBack(t)),await new Promise(l=>setTimeout(l,800)),i=await this.driver.getUiHierarchy(t),n=ue(i).flat}return{xml:i,flat:n}}async dismissSoftKeyboard(e){try{await this.driver.pressKey(111,e),await new Promise(t=>setTimeout(t,150))}catch{}}async packageJailGuard(e,t){let s=await this.driver.getForegroundPackage(t);if(!s||!s.includes(e)){console.log(H.yellow(` \u{1F6A8} External App Jailbreak Detected [${s||"Unknown"}]. Recovering...`)),await this.driver.pressBack(t),await new Promise(n=>setTimeout(n,800));let i=await this.driver.getForegroundPackage(t);return(!i||!i.includes(e))&&(console.log(H.red(` Force restoring ${e} foreground...`)),await this.driver.launchApp(e,t),await new Promise(n=>setTimeout(n,1500)),this.callStack=this.callStack.slice(0,1)),!0}return!1}async handleExternalMediaChooser(e,t){let s=await this.driver.getForegroundPackage(t);if(s==="android"||s?.includes("resolver")||s?.includes("intentresolver")||s&&!s.includes(e)){console.log(H.yellow(` \u{1F6E1}\uFE0F External Media Chooser / Third-Party App Intercepted [${s||"Unknown"}]. Dismissing to stay in-app...`)),await this.driver.pressBack(t),await new Promise(r=>setTimeout(r,800));let n=await this.driver.getForegroundPackage(t);return(!n||!n.includes(e))&&(await this.driver.launchApp(e,t),await new Promise(r=>setTimeout(r,1200)),this.callStack=this.callStack.slice(0,1)),!0}return!1}catalogScreen(e,t,s=[]){let i=[],n=t.height,r=n*.06,o=n*.88,a=m=>s.some(v=>Math.abs(v.bounds.centerX-m.bounds.centerX)<30&&Math.abs(v.bounds.centerY-m.bounds.centerY)<30),d=_e(e);for(let m of d){if(a(m))continue;let v=m.text||m.contentDesc||m.resourceId||"Text Input";i.push({node:m,type:"input",label:v,tested:!1})}let c=it(e);for(let m of c){if(a(m))continue;let v=m.text||m.contentDesc||m.resourceId||"Toggle Control";i.push({node:m,type:"toggle",label:v,tested:!1})}let l=Ge(e);for(let m of l){if(a(m)||Oe([m],n,t.width)||we([m],n,t.width,!0))continue;let v=m.text||m.contentDesc||m.resourceId||"Action Button";if(Pe(m)){this.externalSkippedList.push(v);continue}let x=m.selected!==void 0||m.checkable||/(?:tab|chip|pill|filter|segment|toggle)/i.test(`${m.resourceId} ${m.className} ${m.contentDesc}`)||/(?:today|yesterday|all|active|archived|pending|completed|present|absent)/i.test(v);i.push({node:m,type:x?"toggle":"transition",label:v,tested:!1})}let h=Os(e,n,t.width);for(let m of h){if(a(m)||we([m],n,t.width,!0)||i.some(x=>Math.abs(x.node.bounds.centerX-m.bounds.centerX)<25&&Math.abs(x.node.bounds.centerY-m.bounds.centerY)<25))continue;let v=m.text||m.contentDesc||m.resourceId||"Notification / Header Action";if(Pe(m)){this.externalSkippedList.push(v);continue}i.push({node:m,type:"transition",label:v,tested:!1})}let u=e.filter(m=>{if(!m.clickable||m.bounds.width===0)return!1;let v=`${m.text} ${m.contentDesc} ${m.resourceId}`.toLowerCase();return v.includes("now playing")||v.includes("play")||v.includes("pause")});for(let m of u){if(a(m)||i.some(x=>Math.abs(x.node.bounds.centerX-m.bounds.centerX)<25&&Math.abs(x.node.bounds.centerY-m.bounds.centerY)<25))continue;let v=m.text||m.contentDesc||m.resourceId||"Media Control";i.push({node:m,type:"toggle",label:v,tested:!1})}let g=bt(e,t).filter(m=>!a(m)&&m.bounds.centerY>r&&m.bounds.centerY<o&&!we([m],n,t.width,!0)&&!Pe(m)),f=Us(g,2);for(let m of f){let v=m.text||m.contentDesc||m.resourceId||`Item (${m.bounds.centerX},${m.bounds.centerY})`;i.push({node:m,type:"transition",label:v,tested:!1})}let y=0,S=0,b=[];for(let m of i){let v=/\.(?:mp4|mkv|mov|webm|flv|avi)\b/i.test(m.label),x=/\.(?:mp3|wav|m4a|aac|ogg)\b/i.test(m.label);if(v){if(y++,y>1)continue}else if(x&&(S++,S>1))continue;b.push(m)}return b}async safeBacktrack(e,t,s,i,n=!0){let{flat:r}=await this.getCleanUiHierarchy(t,s),{height:o,width:a}=await this.driver.getScreenDimensions(s),d=Ne(r,{width:a,height:o});if(d===e)return!0;let c=we(r,o,a,!0);if(c&&c.bounds.width>0){console.log(H.gray(` [Backtrack] Tapping in-app back/close button "${c.text||c.contentDesc||c.className}"...`)),await this.driver.tap(c.bounds.centerX,c.bounds.centerY,s),await new Promise(u=>setTimeout(u,1e3)),await this.packageJailGuard(t,s);let l=await this.getCleanUiHierarchy(t,s),h=Ne(l.flat,{width:a,height:o});if(h===e||h!==d)return!0}if(i&&i.bounds.width>0){console.log(H.gray(` [Backtrack] Restoring root domain via tab "${i.text||i.contentDesc||"Root Tab"}"...`)),await this.driver.tap(i.bounds.centerX,i.bounds.centerY,s),await new Promise(u=>setTimeout(u,1e3)),await this.packageJailGuard(t,s);let l=await this.getCleanUiHierarchy(t,s),h=Ne(l.flat,{width:a,height:o});if(h===e||h!==d)return!0}if(n){if(console.log(H.gray(" [Backtrack] Sending fallback KEYCODE_BACK...")),await this.driver.pressKey(4,s),await new Promise(g=>setTimeout(g,1e3)),await this.packageJailGuard(t,s))return console.log(H.yellow(" \u26A0\uFE0F Backtrack via KEYCODE_BACK caused app jailbreak. Aborting branch restoration.")),!1;let h=await this.getCleanUiHierarchy(t,s),u=Ne(h.flat,{width:a,height:o});if(u===e||u!==d)return!0}return!1}async explore(e){let{packageName:t,serial:s,maxScreens:i=30,maxDepth:n=3,stepBudget:r=100,maxInteractionsPerScreen:o=6,safetyMode:a="strict",safetyBlacklist:d=[]}=e;console.log(H.bold.cyan(`
|
|
742
|
+
`)),i.json&&console.log(JSON.stringify(w,null,2)),w}finally{if(S)try{await this.driver.stopScreenRecording(S,me.join("output",`${c}-recording.mp4`),i.serial)}catch{}g&&ze(g)}}async getCleanHierarchy(e){let t=await this.driver.getUiHierarchy(e),{flat:s}=ue(t),i=s.find(o=>(o.text==="Dismiss"||o.contentDesc==="Dismiss")&&o.bounds.width>0);i&&(await this.driver.tap(i.bounds.centerX,i.bounds.centerY,e),await new Promise(o=>setTimeout(o,400)),t=await this.driver.getUiHierarchy(e),s=ue(t).flat);let n=s.find(o=>o.contentDesc&&(o.contentDesc.startsWith("!, ")||o.contentDesc.includes("react.dev/link")));if(n){let o=n.bounds.x2-45,a=n.bounds.centerY;await this.driver.tap(o,a,e),await new Promise(d=>setTimeout(d,350)),t=await this.driver.getUiHierarchy(e),s=ue(t).flat}let r=ce(s);return this.lastHierarchy={xml:t,flat:s,timestamp:Date.now(),hash:r},{xml:t,flat:s}}async ensureInViewport(e,t){let s=this.screenDimensions.width,i=this.screenDimensions.height,n=Math.round(s*.5),r=this.navBarHeight>0?i-this.navBarHeight:i,o=this.navBarHeight>0?r*.94:i*E.gestures.bottomNavThresholdRatio;if(e.bounds.centerY>o||e.bounds.y1>o)return!1;let d=e.bounds.y2>r,c=e.bounds.y1<Math.round(i*.04)&&e.bounds.centerY<Math.round(i*.06);return d?(await this.driver.swipe(n,Math.round(i*.75),n,Math.round(i*.45),250,t),await new Promise(l=>setTimeout(l,200)),this.lastHierarchy=null,!0):c?(await this.driver.swipe(n,Math.round(i*.35),n,Math.round(i*.65),250,t),await new Promise(l=>setTimeout(l,200)),this.lastHierarchy=null,!0):!1}async waitForTarget(e,t,s=3e3,i){if(this.lastHierarchy&&Date.now()-this.lastHierarchy.timestamp<E.polling.hierarchyCacheTtlMs){let a=this.matchNodes(this.lastHierarchy.flat,e,i);if(a.length>0)return{flat:this.lastHierarchy.flat,matched:a}}let n=Date.now();for(;Date.now()-n<s;){let{flat:a}=await this.getCleanHierarchy(t),d=this.matchNodes(a,e,i);if(d.length>0)return{flat:a,matched:d};await new Promise(c=>setTimeout(c,ks()))}let{flat:r}=await this.getCleanHierarchy(t),o=this.matchNodes(r,e,i);if(o.length===0&&(this.healEnabled||E.ai.selfHealing))try{let a=await this.semanticResolver.findSuggestions(e,r,{locale:this.activeLocale}),d=E.ai.confidenceThreshold||.75,c=a.find(l=>l.score>=d);if(c){let l=c.node||r.find(h=>(z(c.text,h.text)||z(c.text,h.contentDesc))&&h.bounds.width>0);if(l){console.log(R.yellow(` \u{1F4A1} [Self-Healing] Auto-healed "${e}" \u2192 "${c.text}" (${Math.round(c.score*100)}% confidence, ${c.strategy})`)),i&&(i.healedFrom=e);let h=l.text?"text":l.contentDesc?"contentDesc":"resourceId",u=this.lastHierarchy?.hash,g=Math.min(E.memory?.maxHealedConfidence??.65,c.score);this.memory.recordSuccess(i?.type||"TAP",e,c.text,h,g,e,u),o=[l]}}}catch(a){ke.warn(`Self-healing resolution failed: ${a.message}`)}return{flat:r,matched:o}}buildDiagnosticError(e,t,s){return qs(e,t,s)}async executeStep(e,t,s){let i=Gs.get(e.type);if(!i)throw new Error(`No handler registered for step type: ${e.type}`);e.target&&e.target.includes("$")&&(e.target=Xe(e.target,Object.fromEntries(this.runtimeVariables))),e.value&&e.value.includes("$")&&(e.value=Xe(e.value,Object.fromEntries(this.runtimeVariables)));let n={driver:this.driver,memory:this.memory,serial:t,screenDimensions:this.screenDimensions,navBarHeight:this.navBarHeight,lastHierarchy:this.lastHierarchy,setLastHierarchy:r=>{this.lastHierarchy=r},getCleanHierarchy:r=>this.getCleanHierarchy(r),waitForTarget:(r,o,a,d)=>this.waitForTarget(r,o,a,d),matchNodes:(r,o,a)=>this.matchNodes(r,o,a),ensureInViewport:(r,o)=>this.ensureInViewport(r,o),buildDiagnosticError:(r,o,a)=>this.buildDiagnosticError(r,o,a),findTargetInputField:(r,o,a)=>this.findTargetInputField(r,o,a),upcomingSteps:s,variables:this.runtimeVariables,loopStack:this.loopStack,executeChildSteps:async(r,o)=>{for(let a=0;a<r.length;a++){let d=r[a];await this.executeStep(d,o.serial,r.slice(a+1))}}};return i.execute(e,n)}findTargetInputField(e,t,s){return Zs(e,t,s,this.memory)}matchNodes(e,t,s){return It(e,t,this.memory,this.activeLocale,s,this.lastHierarchy?.hash)}},Jn=new Ye;import P from"chalk";import ii from"fs";import Fi from"path";var je=class{testData={};dataPath;isLoaded=!1;driver;constructor(e,t){this.dataPath=e||Fi.resolve(process.cwd(),"test-data.json"),this.driver=t||he}setTestData(e){this.testData=e,this.isLoaded=!0}async ensureDataLoaded(){if(!this.isLoaded){if(ii.existsSync(this.dataPath))try{let e=await ii.promises.readFile(this.dataPath,"utf-8");this.testData=JSON.parse(e)}catch{this.testData={}}this.isLoaded=!0}}async loadTestData(e){return e&&(this.dataPath=e),this.isLoaded=!1,await this.ensureDataLoaded(),this.testData}inferValueForNode(e,t){let s=[e.text,e.contentDesc,e.resourceId].join(" ").toLowerCase(),i="",n=t.filter(o=>o.text&&!o.clickable&&o.bounds.centerY<e.bounds.centerY&&e.bounds.centerY-o.bounds.centerY<250&&Math.abs(o.bounds.centerX-e.bounds.centerX)<400);n.length>0&&(i=n[n.length-1].text.toLowerCase());let r=`${s} ${i}`.toLowerCase();if(r.includes("review")||r.includes("feedback")||r.includes("comment")||r.includes("write a review")||r.includes("experience")){if(!this.testData.review?.text)throw new Error("Missing test-data.json field: review.text");return this.testData.review.text}if(r.includes("card")||r.includes("credit")||r.includes("debit")||r.includes("card number")){let o=this.testData.payment?.card;if(o)return String(o);let a="4532";for(let d=0;d<12;d++)a+=Math.floor(Math.random()*10);return a}if(r.includes("phone")||r.includes("mobile")||!r.includes("card")&&r.includes("number")||r.includes("contact")){let o=this.testData.auth?.phone||this.testData.profile?.phone;if(!o)throw new Error("Missing test-data.json field: auth.phone or profile.phone");return o}if(r.includes("email")||r.includes("e-mail")||r.includes("mail")){let o=this.testData.profile?.email||this.testData.auth?.email;if(!o)throw new Error("Missing test-data.json field: profile.email or auth.email");return o}if(r.includes("cvv")||r.includes("cvc")||r.includes("security code"))return"123";if(r.includes("expiry")||r.includes("exp date")||r.includes("expiration"))return"12/28";if(r.includes("pincode")||r.includes("zip")||r.includes("postal")){if(!this.testData.profile?.pincode)throw new Error("Missing test-data.json field: profile.pincode");return this.testData.profile.pincode}if(e.password||r.includes("password")||/\bpin\b/.test(r)&&!r.includes("pincode")||r.includes("passcode")){if(!this.testData.auth?.password)throw new Error("Missing test-data.json field: auth.password");return this.testData.auth.password}if(r.includes("otp")||r.includes("verification")||r.includes("code")&&!r.includes("postal")&&!r.includes("zip")){if(!this.testData.auth?.otp)throw new Error("Missing test-data.json field: auth.otp");return this.testData.auth.otp}if(r.includes("address")||r.includes("street")||r.includes("location")){if(!this.testData.profile?.address)throw new Error("Missing test-data.json field: profile.address");return this.testData.profile.address}if(r.includes("city")||r.includes("state")){if(!this.testData.profile?.city)throw new Error("Missing test-data.json field: profile.city");return this.testData.profile.city}if(r.includes("name")||r.includes("full name")||r.includes("first name")){if(!this.testData.profile?.name)throw new Error("Missing test-data.json field: profile.name");return this.testData.profile.name}if(r.includes("search")||r.includes("find")||r.includes("query")){if(!this.testData.search?.query)throw new Error("Missing test-data.json field: search.query");return this.testData.search.query}return this.testData.review?.text||"PromptTest Auto QA Test Data"}async fillAndSubmitForm(e,t,s=!0,i=!1){await this.ensureDataLoaded();let n=_e(e),r=[];if(n.length===0)return{filledFields:[],submitted:!1};let o=!1;for(let c of n){let l=c.resourceId||c.text||`input_${c.bounds.centerX}_${c.bounds.centerY}`,h=(c.text||"").trim(),u=!h||/^enter\s+/i.test(h)||/^type\s+/i.test(h)||/^e\.g\./i.test(h)||/^(email|email address|password|phone|name|search)$/i.test(h);if(!i&&!u){r.push({fieldId:l,fieldLabel:c.text||c.contentDesc||c.resourceId,valueEntered:h});continue}let g=this.inferValueForNode(c,e);await this.driver.tap(c.bounds.centerX,c.bounds.centerY,t),await new Promise(f=>setTimeout(f,400)),await this.driver.clearField(t,g.length+10),await new Promise(f=>setTimeout(f,200)),await this.driver.inputText(g,t),await new Promise(f=>setTimeout(f,300)),o=!0,r.push({fieldId:l,fieldLabel:c.text||c.contentDesc||c.resourceId,valueEntered:g})}o&&(await this.driver.pressKey(4,t),await new Promise(c=>setTimeout(c,500)));let a=!1,d;if(s){let c=e.filter(l=>!l.clickable||l.bounds.width===0?!1:Ns.some(h=>z(h,l.text)||z(h,l.contentDesc)||z(h,l.resourceId)));if(c.length>0){let l=["submit","sign in","signin","login","save","confirm","proceed","continue"];c.sort((u,g)=>{let f=(u.text||u.contentDesc||u.resourceId||"").toLowerCase(),y=(g.text||g.contentDesc||g.resourceId||"").toLowerCase(),S=l.findIndex(x=>f.includes(x)),b=l.findIndex(x=>y.includes(x)),m=S>=0?S:99,v=b>=0?b:99;return m!==v?m-v:g.bounds.centerY-u.bounds.centerY});let h=c[0];d=h.text||h.contentDesc||h.resourceId,await this.driver.tap(h.bounds.centerX,h.bounds.centerY,t),await new Promise(u=>setTimeout(u,E.settle.submitWaitMs));try{let u=await this.driver.getUiHierarchy(t),{flat:g}=ue(u),f=_e(g);a=!(f.length>0&&n.every(S=>f.some(b=>b.resourceId&&b.resourceId===S.resourceId)))}catch{a=!0}}}return{filledFields:r,submitted:a,submitButtonText:d}}},ri=new je;import H from"chalk";var Rt=class{driver;reporter;memory;formFiller;screenLedger=new Map;callStack=[];executedStepCount=0;visitedScreenHashes=new Set;devDefectsList=[];safetyHitList=[];externalSkippedList=[];constructor(e=he,t=nt,s=at){this.driver=e,this.reporter=t,this.memory=s,this.formFiller=new je(void 0,e)}async getCleanUiHierarchy(e,t,s=5){let i=await this.driver.getUiHierarchy(t),{flat:n}=ue(i),r=0;for(;r<s;){let o=yt(n);if(!o)break;r++;let a=`[${(o.severity||"error").toUpperCase()} - ${o.type.toUpperCase()}]`,d=o.sourceLocation?` [${o.sourceLocation}]`:"";console.log(H.red(` \u{1F6A8} Global Dev Console Intercepted: ${a} "${o.errorText.slice(0,80)}"${d}`)),this.devDefectsList.push(o.sourceLocation?`${o.errorText} (${o.sourceLocation})`:o.errorText);let c=await this.driver.captureScreenshot(t);this.reporter.logStep("Dev Overlay Triage",`Intercepted and triaged ${a}: "${o.errorText}"${d}`,"Verify","FAIL",o.errorText,c,{errors:[o.sourceLocation?`${o.errorText} at ${o.sourceLocation}`:o.errorText]}),o.dismissButton&&o.dismissButton.bounds.width>0?(console.log(H.gray(" Dismissing overlay via dismiss button...")),await this.driver.tap(o.dismissButton.bounds.centerX,o.dismissButton.bounds.centerY,t)):o.isToast&&o.node&&o.node.bounds.width>0?(console.log(H.gray(" Expanding toast notification to access Dismiss button...")),await this.driver.tap(o.node.bounds.centerX,o.node.bounds.centerY,t)):(console.log(H.gray(" Pressing Back to dismiss overlay...")),await this.driver.pressBack(t)),await new Promise(l=>setTimeout(l,800)),i=await this.driver.getUiHierarchy(t),n=ue(i).flat}return{xml:i,flat:n}}async dismissSoftKeyboard(e){try{await this.driver.pressKey(111,e),await new Promise(t=>setTimeout(t,150))}catch{}}async packageJailGuard(e,t){let s=await this.driver.getForegroundPackage(t);if(!s)return!1;if(!s.includes(e)){let i=s.includes("launcher")||s.includes("home")||s.includes("trebuchet");console.log(H.yellow(` \u{1F6A8} External App Jailbreak Detected [${s}]. Recovering...`)),i||(await this.driver.pressBack(t),await new Promise(r=>setTimeout(r,800)));let n=await this.driver.getForegroundPackage(t);return(!n||!n.includes(e))&&(console.log(H.red(` Force restoring ${e} foreground...`)),await this.driver.launchApp(e,t),await new Promise(r=>setTimeout(r,1500)),this.callStack=this.callStack.slice(0,1)),!0}return!1}async handleExternalMediaChooser(e,t){let s=await this.driver.getForegroundPackage(t);if(!s)return!1;let i=s==="android"||s.includes("resolver")||s.includes("intentresolver");if(i){console.log(H.yellow(` \u{1F6E1}\uFE0F External Media Chooser Intercepted [${s}]. Dismissing to stay in-app...`)),await this.driver.pressBack(t),await new Promise(r=>setTimeout(r,800));let n=await this.driver.getForegroundPackage(t);return n&&!n.includes(e)&&i&&(await this.driver.launchApp(e,t),await new Promise(r=>setTimeout(r,1200)),this.callStack=this.callStack.slice(0,1)),!0}return!1}catalogScreen(e,t,s=[]){let i=[],n=t.height,r=n*.06,o=n*.88,a=m=>s.some(v=>Math.abs(v.bounds.centerX-m.bounds.centerX)<30&&Math.abs(v.bounds.centerY-m.bounds.centerY)<30),d=_e(e);for(let m of d){if(a(m))continue;let v=m.text||m.contentDesc||m.resourceId||"Text Input";i.push({node:m,type:"input",label:v,tested:!1})}let c=it(e);for(let m of c){if(a(m))continue;let v=m.text||m.contentDesc||m.resourceId||"Toggle Control";i.push({node:m,type:"toggle",label:v,tested:!1})}let l=Ge(e);for(let m of l){if(a(m)||Oe([m],n,t.width)||we([m],n,t.width,!0))continue;let v=m.text||m.contentDesc||m.resourceId||"Action Button";if(Pe(m)){this.externalSkippedList.push(v);continue}let x=m.selected!==void 0||m.checkable||/(?:tab|chip|pill|filter|segment|toggle)/i.test(`${m.resourceId} ${m.className} ${m.contentDesc}`)||/(?:today|yesterday|all|active|archived|pending|completed|present|absent)/i.test(v);i.push({node:m,type:x?"toggle":"transition",label:v,tested:!1})}let h=Os(e,n,t.width);for(let m of h){if(a(m)||we([m],n,t.width,!0)||i.some(x=>Math.abs(x.node.bounds.centerX-m.bounds.centerX)<25&&Math.abs(x.node.bounds.centerY-m.bounds.centerY)<25))continue;let v=m.text||m.contentDesc||m.resourceId||"Notification / Header Action";if(Pe(m)){this.externalSkippedList.push(v);continue}i.push({node:m,type:"transition",label:v,tested:!1})}let u=e.filter(m=>{if(!m.clickable||m.bounds.width===0)return!1;let v=`${m.text} ${m.contentDesc} ${m.resourceId}`.toLowerCase();return v.includes("now playing")||v.includes("play")||v.includes("pause")});for(let m of u){if(a(m)||i.some(x=>Math.abs(x.node.bounds.centerX-m.bounds.centerX)<25&&Math.abs(x.node.bounds.centerY-m.bounds.centerY)<25))continue;let v=m.text||m.contentDesc||m.resourceId||"Media Control";i.push({node:m,type:"toggle",label:v,tested:!1})}let g=bt(e,t).filter(m=>!a(m)&&m.bounds.centerY>r&&m.bounds.centerY<o&&!we([m],n,t.width,!0)&&!Pe(m)),f=Us(g,2);for(let m of f){let v=m.text||m.contentDesc||m.resourceId||`Item (${m.bounds.centerX},${m.bounds.centerY})`;i.push({node:m,type:"transition",label:v,tested:!1})}let y=0,S=0,b=[];for(let m of i){let v=/\.(?:mp4|mkv|mov|webm|flv|avi)\b/i.test(m.label),x=/\.(?:mp3|wav|m4a|aac|ogg)\b/i.test(m.label);if(v){if(y++,y>1)continue}else if(x&&(S++,S>1))continue;b.push(m)}return b}async safeBacktrack(e,t,s,i,n=!0){let{flat:r}=await this.getCleanUiHierarchy(t,s),{height:o,width:a}=await this.driver.getScreenDimensions(s),d=Ne(r,{width:a,height:o});if(d===e)return!0;let c=we(r,o,a,!0);if(c&&c.bounds.width>0){console.log(H.gray(` [Backtrack] Tapping in-app back/close button "${c.text||c.contentDesc||c.className}"...`)),await this.driver.tap(c.bounds.centerX,c.bounds.centerY,s),await new Promise(u=>setTimeout(u,1e3)),await this.packageJailGuard(t,s);let l=await this.getCleanUiHierarchy(t,s),h=Ne(l.flat,{width:a,height:o});if(h===e||h!==d)return!0}if(i&&i.bounds.width>0){console.log(H.gray(` [Backtrack] Restoring root domain via tab "${i.text||i.contentDesc||"Root Tab"}"...`)),await this.driver.tap(i.bounds.centerX,i.bounds.centerY,s),await new Promise(u=>setTimeout(u,1e3)),await this.packageJailGuard(t,s);let l=await this.getCleanUiHierarchy(t,s),h=Ne(l.flat,{width:a,height:o});if(h===e||h!==d)return!0}if(n){if(console.log(H.gray(" [Backtrack] Sending fallback KEYCODE_BACK...")),await this.driver.pressKey(4,s),await new Promise(g=>setTimeout(g,1e3)),await this.packageJailGuard(t,s))return console.log(H.yellow(" \u26A0\uFE0F Backtrack via KEYCODE_BACK caused app jailbreak. Aborting branch restoration.")),!1;let h=await this.getCleanUiHierarchy(t,s),u=Ne(h.flat,{width:a,height:o});if(u===e||u!==d)return!0}return!1}async explore(e){let{packageName:t,serial:s,maxScreens:i=30,maxDepth:n=3,stepBudget:r=100,maxInteractionsPerScreen:o=6,safetyMode:a="strict",safetyBlacklist:d=[]}=e;console.log(H.bold.cyan(`
|
|
743
743
|
\u{1F916} PromptTest Universal State-Graph DFS Engine`)),console.log(H.gray(`Target Package: ${t}`)),console.log(H.gray(`Safety Policy: ${a.toUpperCase()}`)),console.log(H.gray(`Limits: Max Screens = ${i}, Step Budget = ${r}, Max Depth = ${n}`)),await this.driver.clearCrashLogs(s);let c=await this.driver.getScreenDimensions(s),l=await this.driver.getForegroundPackage(s);(!l||!l.includes(t))&&(console.log(H.gray(`Launching ${t}...`)),await this.driver.launchApp(t,s),await new Promise(S=>setTimeout(S,2500)));let{flat:h}=await this.getCleanUiHierarchy(t,s);await this.dismissSoftKeyboard(s);let u=Yt(h,c,t);console.log(H.magenta(`\u26A1 Universal Paradigm Detected: [${u.paradigm.toUpperCase()}] with ${u.roots.length} root node(s).`));let g=[],f;if(u.paradigm==="drawer"&&u.roots.length>0){f=u.roots[0];let S={...h[0]||{},text:"Home Screen / Main Hub",contentDesc:"Home Screen / Main Hub",clickable:!0,bounds:{x1:0,y1:0,x2:c.width,y2:c.height,centerX:c.width/2,centerY:c.height/2,width:c.width,height:c.height}};g.push(S),console.log(H.gray(" Opening drawer to catalog root destinations...")),await this.driver.tap(f.bounds.centerX,f.bounds.centerY,s),await new Promise(w=>setTimeout(w,1200));let b=await this.getCleanUiHierarchy(t,s),m=b.flat.filter(w=>{if(!w.clickable||w.bounds.width===0||w.bounds.height===0||w.bounds.width>=c.width*.95&&w.bounds.height>=c.height*.95||we([w],c.height,c.width,!0))return!1;let A=`${w.contentDesc} ${w.text}`.trim(),B=w.bounds.width>c.width*.15&&w.bounds.height>c.height*.05;return A.toLowerCase().includes("developed by")||w.bounds.centerY>c.height*.85?!1:A.length>=2||B});for(let w of m)g.some(A=>Math.abs(A.bounds.centerX-w.bounds.centerX)<30&&Math.abs(A.bounds.centerY-w.bounds.centerY)<30)||g.push(w);let v=we(b.flat,c.height,c.width,!0);v?await this.driver.tap(v.bounds.centerX,v.bounds.centerY,s):await this.driver.tap(f.bounds.centerX,f.bounds.centerY,s),await new Promise(w=>setTimeout(w,1200));let x=await this.getCleanUiHierarchy(t,s);if(x.flat.some(w=>`${w.contentDesc} ${w.text}`.includes("Gallery")&&`${w.contentDesc} ${w.text}`.includes("Planner"))){let w=we(x.flat,c.height,c.width,!0);w?await this.driver.tap(w.bounds.centerX,w.bounds.centerY,s):await this.driver.tap(c.width-50,c.height/2,s),await new Promise(A=>setTimeout(A,1e3))}console.log(H.magenta(` Discovered ${g.length} destinations (1 Main Hub + ${g.length-1} Drawer destinations).`))}else u.roots.length>0?g=u.roots:g=[h[0]||{text:"Main Hub",clickable:!0}];let y=1;for(let S of g){if(this.executedStepCount>=r||this.visitedScreenHashes.size>=i)break;let b=S.text||S.contentDesc||`Root #${y}`;if(console.log(H.yellow(`
|
|
744
744
|
[Domain ${y}/${g.length}] Entering Root Tree: "${b}"...`)),u.paradigm==="bottom_tabs"||u.paradigm==="top_tabs"){let x=S.bounds.height<25?Math.max(S.bounds.y1-10,S.bounds.centerY):Math.min(S.bounds.centerY,S.bounds.y2-4);await this.driver.tap(S.bounds.centerX,x,s),this.executedStepCount++,await new Promise(D=>setTimeout(D,1200))}else u.paradigm==="drawer"&&y>1&&f&&(await this.dismissSoftKeyboard(s),console.log(H.gray(` Opening drawer to select "${b}"...`)),await this.driver.tap(f.bounds.centerX,f.bounds.centerY,s),await new Promise(x=>setTimeout(x,1e3)),await this.driver.tap(S.bounds.centerX,S.bounds.centerY,s),this.executedStepCount++,await new Promise(x=>setTimeout(x,1500)));h=(await this.getCleanUiHierarchy(t,s)).flat,await this.dismissSoftKeyboard(s);let v=y===1?Math.max(25,Math.ceil(r*.45)):Math.max(8,Math.ceil(r*.55/Math.max(1,g.length-1)));if(await this.exploreDfsNode({packageName:t,serial:s,screenDims:c,rootNodes:u.roots,activeRootNode:S,currentDepth:0,maxDepth:n,maxScreens:i,stepBudget:r,domainStepLimit:this.executedStepCount+v,maxInteractionsPerScreen:o,safetyMode:a,safetyBlacklist:d,domainLabel:b}),u.paradigm==="drawer"&&y>1){let x=0;for(;x<4;){let D=await this.getCleanUiHierarchy(t,s),w=Oe(D.flat,c.height,c.width),A=D.flat.some(Z=>`${Z.contentDesc} ${Z.text}`.includes("Gallery")&&`${Z.contentDesc} ${Z.text}`.includes("Planner"));if(w&&!A)break;let B=we(D.flat,c.height,c.width,!0);if(B)console.log(H.gray(" [Domain Wrap-Up] Returning to Home Screen via in-app back...")),await this.driver.tap(B.bounds.centerX,B.bounds.centerY,s),await new Promise(Z=>setTimeout(Z,1e3)),x++;else break}}if(y===1&&g.length===1){let x=await this.getCleanUiHierarchy(t,s),D=Yt(x.flat,c,t);if((D.paradigm==="bottom_tabs"||D.paradigm==="top_tabs")&&D.roots.length>1){console.log(H.magenta(`
|
|
745
745
|
\u26A1 Post-Auth Navigation Discovered: [${D.paradigm.toUpperCase()}] with ${D.roots.length} root domain(s)!`)),u.paradigm=D.paradigm,u.roots=D.roots;let w=D.roots.slice(1);g.push(...w.length>0?w:D.roots)}}y++}for(let S of this.visitedScreenHashes)this.memory.markScreenCrawled(S);await this.memory.save(),console.log(H.green(`
|
|
746
|
-
\u2714 Universal DFS Traversal Complete!`)),console.log(H.cyan(` Visited Screens: ${this.visitedScreenHashes.size}`)),console.log(H.cyan(` Total Steps Executed: ${this.executedStepCount}/${r}`)),console.log(H.cyan(` Defects Triaged: ${this.devDefectsList.length}`))}async exploreDfsNode(e){let{packageName:t,serial:s,screenDims:i,rootNodes:n,activeRootNode:r,currentDepth:o,maxDepth:a,maxScreens:d,stepBudget:c,domainStepLimit:l,maxInteractionsPerScreen:h,safetyMode:u,safetyBlacklist:g,domainLabel:f}=e;if(this.executedStepCount>=c||this.visitedScreenHashes.size>=d||l&&this.executedStepCount>=l)return;let{flat:y}=await this.getCleanUiHierarchy(t,s),S=ce(y),b=Ne(y,i);this.visitedScreenHashes.add(S);let m=this.screenLedger.get(b);if(m){if(m.status==="FULLY_EXPLORED"){console.log(H.gray(` \u26A1 Cycle Detected: Screen [${b.slice(0,8)}] already fully explored. Pruning branch.`));return}}else{let w=this.catalogScreen(y,i,n);m={hash:S,semanticHash:b,title:`${f} (depth ${o})`,elements:w,status:"IN_PROGRESS",depth:o,interactionCount:0},this.screenLedger.set(b,m),console.log(H.cyan(` \u{1F50E} Discovered Screen [${b.slice(0,8)}] at depth ${o} with ${w.length} candidate element(s).`))}this.callStack.push({screenHash:S,semanticHash:b,title:m.title,timestamp:Date.now()});let v=m.elements.filter(w=>w.type==="input"&&!w.tested);if(v.length>0&&m.interactionCount<h){console.log(H.magenta(` Filling in-place form fields (${v.length} inputs)...`));let w=await this.formFiller.fillAndSubmitForm(y,s,!0,!1);this.executedStepCount++,m.interactionCount++;for(let A of v)A.tested=!0;await this.dismissSoftKeyboard(s),await this.packageJailGuard(t,s),this.reporter.logStep("In-Place Form Fill",`Populated ${w.filledFields.length} field(s) on [${b.slice(0,8)}]`,"FormSubmit","PASS",w.submitButtonText||"Auto")}let x=m.elements.filter(w=>w.type==="toggle"&&!w.tested);for(let w of x){if(this.executedStepCount>=c||m.interactionCount>=h)break;if(w.tested=!0,Ee(w.node,g,u)){this.safetyHitList.push(w.label);continue}console.log(H.gray(` Exercising toggle: "${w.label.slice(0,30)}"...`)),this.executedStepCount++,m.interactionCount++,await this.driver.tap(w.node.bounds.centerX,w.node.bounds.centerY,s),await new Promise(A=>setTimeout(A,600)),this.reporter.logStep("In-Place Toggle",`Toggled control "${w.label}"`,"Tap","PASS",w.label)}let D=m.elements.filter(w=>w.type==="transition"&&!w.tested);for(let w of D){if(this.executedStepCount>=c||this.visitedScreenHashes.size>=d||l&&this.executedStepCount>=l&&!(o===0&&f.includes("Home Screen"))||m.interactionCount>=h)break;if(w.tested=!0,Pe(w.node)){this.externalSkippedList.push(w.label);continue}if(Ee(w.node,g,u)){this.safetyHitList.push(w.label);continue}console.log(H.magenta(` \u26A1 [DFS Depth ${o}] Testing transition: "${w.label.slice(0,40)}"...`)),this.executedStepCount++,m.interactionCount++,await this.driver.tap(w.node.bounds.centerX,w.node.bounds.centerY,s),await new Promise(j=>setTimeout(j,1500)),await this.dismissSoftKeyboard(s),await this.handleExternalMediaChooser(t,s),await this.packageJailGuard(t,s);let A=await this.getCleanUiHierarchy(t,s),Z=Ne(A.flat,i)!==b;if(this.reporter.logStep("DFS Transition",`Tapped "${w.label}" [${Z?"Navigated":"In-Place"}]`,"Tap","PASS",w.label),Z){if(o<a){let te=o===0&&f.includes("Home Screen")?Math.min(this.executedStepCount+3,l||this.executedStepCount+3):l;await this.exploreDfsNode({packageName:t,serial:s,screenDims:i,rootNodes:n,activeRootNode:r,domainStepLimit:te,currentDepth:o+1,maxDepth:a,maxScreens:d,stepBudget:c,maxInteractionsPerScreen:h,safetyMode:u,safetyBlacklist:g,domainLabel:`${f} > ${w.label.slice(0,20)}`})}console.log(H.gray(` [DFS Backtrack] Returning to [${b.slice(0,8)}]...`)),await this.safeBacktrack(b,t,s,o===0?r:void 0,o>0),await this.dismissSoftKeyboard(s);let j=await this.getCleanUiHierarchy(t,s);if(Ne(j.flat,i)!==b){let te=we(j.flat,i.height,i.width,!0);te&&(console.log(H.gray(" [Backtrack Verification] Re-tapping in-app back/close button...")),await this.driver.tap(te.bounds.centerX,te.bounds.centerY,s),await new Promise(be=>setTimeout(be,1e3)),await this.packageJailGuard(t,s));let le=await this.getCleanUiHierarchy(t,s);if(Ne(le.flat,i)!==b){console.log(H.yellow(` \u26A0\uFE0F Unable to restore parent screen [${b.slice(0,8)}]. Unwinding branch to prevent jailbreak loop.`));break}}}}m.status="FULLY_EXPLORED",this.callStack.pop()}};var ut=class{driver;formFiller;reporter;memory;safetyHitList=[];externalSkippedList=[];devDefectsList=[];visitedScreenHashes=new Set;executedStepCount=0;constructor(e,t,s,i){this.driver=e||he,this.formFiller=t||ri,this.reporter=s||nt,this.memory=i||at}getMemory(){return this.memory}getSafetyHitList(){return this.safetyHitList}getExternalSkippedList(){return this.externalSkippedList}getDevDefectsList(){return this.devDefectsList}getVisitedScreensCount(){return this.visitedScreenHashes.size}async packageJailGuard(e,t){let s=await this.driver.getForegroundPackage(t);if(s&&!s.includes(e)){console.log(P.yellow(` \u{1F6E1}\uFE0F Jail Containment: App escaped to "${s}". Executing containment recovery...`)),await this.driver.pressBack(t),await new Promise(
|
|
746
|
+
\u2714 Universal DFS Traversal Complete!`)),console.log(H.cyan(` Visited Screens: ${this.visitedScreenHashes.size}`)),console.log(H.cyan(` Total Steps Executed: ${this.executedStepCount}/${r}`)),console.log(H.cyan(` Defects Triaged: ${this.devDefectsList.length}`))}async exploreDfsNode(e){let{packageName:t,serial:s,screenDims:i,rootNodes:n,activeRootNode:r,currentDepth:o,maxDepth:a,maxScreens:d,stepBudget:c,domainStepLimit:l,maxInteractionsPerScreen:h,safetyMode:u,safetyBlacklist:g,domainLabel:f}=e;if(this.executedStepCount>=c||this.visitedScreenHashes.size>=d||l&&this.executedStepCount>=l)return;let{flat:y}=await this.getCleanUiHierarchy(t,s),S=ce(y),b=Ne(y,i);this.visitedScreenHashes.add(S);let m=this.screenLedger.get(b);if(m){if(m.status==="FULLY_EXPLORED"){console.log(H.gray(` \u26A1 Cycle Detected: Screen [${b.slice(0,8)}] already fully explored. Pruning branch.`));return}}else{let w=this.catalogScreen(y,i,n);m={hash:S,semanticHash:b,title:`${f} (depth ${o})`,elements:w,status:"IN_PROGRESS",depth:o,interactionCount:0},this.screenLedger.set(b,m),console.log(H.cyan(` \u{1F50E} Discovered Screen [${b.slice(0,8)}] at depth ${o} with ${w.length} candidate element(s).`))}this.callStack.push({screenHash:S,semanticHash:b,title:m.title,timestamp:Date.now()});let v=m.elements.filter(w=>w.type==="input"&&!w.tested);if(v.length>0&&m.interactionCount<h){console.log(H.magenta(` Filling in-place form fields (${v.length} inputs)...`));let w=await this.formFiller.fillAndSubmitForm(y,s,!0,!1);this.executedStepCount++,m.interactionCount++;for(let A of v)A.tested=!0;await this.dismissSoftKeyboard(s),await this.packageJailGuard(t,s),this.reporter.logStep("In-Place Form Fill",`Populated ${w.filledFields.length} field(s) on [${b.slice(0,8)}]`,"FormSubmit","PASS",w.submitButtonText||"Auto")}let x=m.elements.filter(w=>w.type==="toggle"&&!w.tested);for(let w of x){if(this.executedStepCount>=c||m.interactionCount>=h)break;if(w.tested=!0,Ee(w.node,g,u)){this.safetyHitList.push(w.label);continue}console.log(H.gray(` Exercising toggle: "${w.label.slice(0,30)}"...`)),this.executedStepCount++,m.interactionCount++,await this.driver.tap(w.node.bounds.centerX,w.node.bounds.centerY,s),await new Promise(A=>setTimeout(A,600)),this.reporter.logStep("In-Place Toggle",`Toggled control "${w.label}"`,"Tap","PASS",w.label)}let D=m.elements.filter(w=>w.type==="transition"&&!w.tested);for(let w of D){if(this.executedStepCount>=c||this.visitedScreenHashes.size>=d||l&&this.executedStepCount>=l&&!(o===0&&f.includes("Home Screen"))||m.interactionCount>=h)break;if(w.tested=!0,Pe(w.node)){this.externalSkippedList.push(w.label);continue}if(Ee(w.node,g,u)){this.safetyHitList.push(w.label);continue}console.log(H.magenta(` \u26A1 [DFS Depth ${o}] Testing transition: "${w.label.slice(0,40)}"...`)),this.executedStepCount++,m.interactionCount++,await this.driver.tap(w.node.bounds.centerX,w.node.bounds.centerY,s),await new Promise(j=>setTimeout(j,1500)),await this.dismissSoftKeyboard(s),await this.handleExternalMediaChooser(t,s),await this.packageJailGuard(t,s);let A=await this.getCleanUiHierarchy(t,s),Z=Ne(A.flat,i)!==b;if(this.reporter.logStep("DFS Transition",`Tapped "${w.label}" [${Z?"Navigated":"In-Place"}]`,"Tap","PASS",w.label),Z){if(o<a){let te=o===0&&f.includes("Home Screen")?Math.min(this.executedStepCount+3,l||this.executedStepCount+3):l;await this.exploreDfsNode({packageName:t,serial:s,screenDims:i,rootNodes:n,activeRootNode:r,domainStepLimit:te,currentDepth:o+1,maxDepth:a,maxScreens:d,stepBudget:c,maxInteractionsPerScreen:h,safetyMode:u,safetyBlacklist:g,domainLabel:`${f} > ${w.label.slice(0,20)}`})}console.log(H.gray(` [DFS Backtrack] Returning to [${b.slice(0,8)}]...`)),await this.safeBacktrack(b,t,s,o===0?r:void 0,o>0),await this.dismissSoftKeyboard(s);let j=await this.getCleanUiHierarchy(t,s);if(Ne(j.flat,i)!==b){let te=we(j.flat,i.height,i.width,!0);te&&(console.log(H.gray(" [Backtrack Verification] Re-tapping in-app back/close button...")),await this.driver.tap(te.bounds.centerX,te.bounds.centerY,s),await new Promise(be=>setTimeout(be,1e3)),await this.packageJailGuard(t,s));let le=await this.getCleanUiHierarchy(t,s);if(Ne(le.flat,i)!==b){console.log(H.yellow(` \u26A0\uFE0F Unable to restore parent screen [${b.slice(0,8)}]. Unwinding branch to prevent jailbreak loop.`));break}}}}m.status="FULLY_EXPLORED",this.callStack.pop()}};var ut=class{driver;formFiller;reporter;memory;safetyHitList=[];externalSkippedList=[];devDefectsList=[];visitedScreenHashes=new Set;executedStepCount=0;constructor(e,t,s,i){this.driver=e||he,this.formFiller=t||ri,this.reporter=s||nt,this.memory=i||at}getMemory(){return this.memory}getSafetyHitList(){return this.safetyHitList}getExternalSkippedList(){return this.externalSkippedList}getDevDefectsList(){return this.devDefectsList}getVisitedScreensCount(){return this.visitedScreenHashes.size}async packageJailGuard(e,t){let s=await this.driver.getForegroundPackage(t);if(s&&!s.includes(e)){let i=s.includes("launcher")||s.includes("home")||s.includes("trebuchet");console.log(P.yellow(` \u{1F6E1}\uFE0F Jail Containment: App escaped to "${s}". Executing containment recovery...`)),i||(await this.driver.pressBack(t),await new Promise(r=>setTimeout(r,600)));let n=await this.driver.getForegroundPackage(t);return(!n||!n.includes(e))&&(console.log(P.gray(` Force re-launching ${e} to restore foreground...`)),await this.driver.launchApp(e,t),await new Promise(r=>setTimeout(r,1200))),this.reporter.logStep("Jail Containment",`App escaped to "${s}"; successfully contained and restored "${e}"`,"Back","PASS"),!0}return!1}async getCleanUiHierarchy(e,t,s=5){let i=await this.driver.getUiHierarchy(t),{flat:n}=ue(i),r=0;for(;r<s;){let o=yt(n);if(!o)break;r++;let a=`[${(o.severity||"error").toUpperCase()} - ${o.type.toUpperCase()}]`,d=o.sourceLocation?` [${o.sourceLocation}]`:"";console.log(P.red(` \u{1F6A8} Global Dev Console Intercepted: ${a} "${o.errorText.slice(0,80)}"${d}`)),this.devDefectsList.push(o.sourceLocation?`${o.errorText} (${o.sourceLocation})`:o.errorText);let c=await this.driver.captureScreenshot(t);this.reporter.logStep("Dev Overlay Triage",`Intercepted and triaged ${a}: "${o.errorText}"${d}`,"Verify","FAIL",o.errorText,c,{errors:[o.sourceLocation?`${o.errorText} at ${o.sourceLocation}`:o.errorText]}),o.dismissButton&&o.dismissButton.bounds.width>0?(console.log(P.gray(" Dismissing overlay via dismiss button...")),await this.driver.tap(o.dismissButton.bounds.centerX,o.dismissButton.bounds.centerY,t)):o.isToast&&o.node&&o.node.bounds.width>0?(console.log(P.gray(" Expanding toast notification to access Dismiss button...")),await this.driver.tap(o.node.bounds.centerX,o.node.bounds.centerY,t)):(console.log(P.gray(" Pressing Back to dismiss overlay...")),await this.driver.pressBack(t)),await new Promise(l=>setTimeout(l,800)),i=await this.driver.getUiHierarchy(t),n=ue(i).flat}return{xml:i,flat:n}}async handleDevOverlays(e,t,s){let i=yt(e);if(!i)return!1;let n=`[${(i.severity||"error").toUpperCase()} - ${i.type.toUpperCase()}]`,r=i.sourceLocation?` [${i.sourceLocation}]`:"";console.log(P.red(` \u{1F6A8} Dev Overlay Detected: ${n} "${i.errorText.slice(0,80)}"${r}`)),this.devDefectsList.push(i.sourceLocation?`${i.errorText} (${i.sourceLocation})`:i.errorText);let o=await this.driver.captureScreenshot(s);return this.reporter.logStep("Dev Overlay Triage",`Detected and triaged ${n} error: "${i.errorText}"${r}`,"Verify","FAIL",i.errorText,o,{errors:[i.sourceLocation?`${i.errorText} (${i.sourceLocation})`:i.errorText]}),i.dismissButton&&i.dismissButton.bounds.width>0?(console.log(P.gray(" Dismissing error overlay via dismiss button...")),await this.driver.tap(i.dismissButton.bounds.centerX,i.dismissButton.bounds.centerY,s)):i.isToast&&i.node&&i.node.bounds.width>0?(console.log(P.gray(" Expanding toast notification to access Dismiss button...")),await this.driver.tap(i.node.bounds.centerX,i.node.bounds.centerY,s)):(console.log(P.gray(" Pressing Back to dismiss error overlay...")),await this.driver.pressBack(s)),await new Promise(a=>setTimeout(a,800)),!0}async dismissSoftKeyboard(e){try{await this.driver.pressKey(111,e),await new Promise(t=>setTimeout(t,200))}catch{}}async explore(e){let{packageName:t,serial:s,maxTabs:i=E.explorer?.maxTabs||15,exploreLists:n=!0,skipAlreadyCrawled:r=!1,safetyMode:o=E.safety?.mode||"strict",safetyBlacklist:a=E.safety?.customBlacklist?[...E.safety.customBlacklist]:[],maxScreens:d=E.explorer?.maxScreens||30,stepBudget:c=E.explorer?.stepBudget||100,screenshots:l=E.explorer?.screenshots||"state-change"}=e;console.log(P.cyan(`
|
|
747
747
|
\u{1F916} PromptTest Autonomous Exploration Brain v3`)),console.log(P.gray("Target Package: ")+P.bold.white(t)),console.log(P.gray("Safety Policy: ")+P.bold.yellow(o.toUpperCase())+(a.length>0?P.gray(` (+${a.length} custom blacklist)`):"")),console.log(P.gray("Screenshots Policy: ")+P.bold.white(l));let h=async(y,S=!0)=>{if(!(l==="failure-only"&&y!=="FAIL")&&!(l==="state-change"&&!S&&y!=="FAIL"))try{return await this.driver.captureScreenshot(s)}catch{return}};await this.memory.init(t);let u=s||this.driver.getDefaultDevice(),g=u||"Default Device";this.reporter.init(t,g);let f=null;u&&(f=Ke(u)),this.safetyHitList=[],this.externalSkippedList=[],this.devDefectsList=[],this.visitedScreenHashes.clear(),this.executedStepCount=0;try{if(await this.driver.clearCrashLogs(s),(e.strategy||E.explorer?.strategy||"heuristic")==="dfs"){let ne=new Rt(this.driver,this.reporter,this.memory);await ne.explore({packageName:t,serial:s,maxScreens:d,maxDepth:e.maxDepth||E.explorer?.maxDepth||5,stepBudget:c,safetyMode:o,safetyBlacklist:a,screenshotPolicy:l,skipAlreadyCrawled:r}),this.safetyHitList=ne.safetyHitList,this.externalSkippedList=ne.externalSkippedList,this.devDefectsList=ne.devDefectsList,this.visitedScreenHashes=ne.visitedScreenHashes,this.executedStepCount=ne.executedStepCount;let J=this.memory.getStats();this.reporter.logStep("Coverage Audit",`State Graph Coverage: Visited ${this.visitedScreenHashes.size} unique screen state(s). Memory Engine cached ${J.totalScreens} crawled screen(s).`,"Verify","PASS"),await this.reporter.generateReport(),console.log(P.bold.green(`
|
|
748
748
|
\u2728 Full Audit Report generated at: output/prompttest-report.md`));return}let{width:S,height:b}=await this.driver.getScreenDimensions(s);console.log(P.yellow(`
|
|
749
749
|
[1/5] Verifying Foreground State...`));let m=await this.driver.getForegroundPackage(s);(!m||!m.includes(t))&&(console.log(P.gray(`Launching ${t}...`)),await this.driver.launchApp(t,s),await new Promise(ne=>setTimeout(ne,2500)));let v=await this.runErrorSentinel(t,s,"Post-Launch"),x=v.hasError?"FAIL":"PASS",D=await h(x,!0);this.reporter.logStep("Init",`Launched and verified foreground for ${t}`,"Launch",x,t,D,v.hasError?{errors:v.errors}:void 0),console.log(x==="PASS"?P.green("\u2714 App is active in foreground."):P.red(`\u2716 Issues detected during launch for ${t}.`)),console.log(P.yellow(`
|
|
@@ -751,8 +751,8 @@ data: ${JSON.stringify(g)}
|
|
|
751
751
|
[3/5] Inspecting Interactive Action Controls...`));let J=Ge(A).filter(C=>{let M=(C.text+" "+C.contentDesc).toLowerCase();if(M.includes("submit")||M.includes("cancel")||Oe([C],b))return!1;if(Pe(C)){let I=C.text||C.contentDesc||C.resourceId||"External Link";return console.log(P.gray(` \u{1F517} External Link Bypassed: "${I}"`)),this.externalSkippedList.push(I),!1}if(Ee(C,a,o)){let I=C.text||C.contentDesc||C.resourceId||"Action";return console.log(P.yellow(` \u{1F6E1}\uFE0F Safety Guard: Blocked destructive action "${I}"`)),this.safetyHitList.push(I),!1}return!0});if(J.length>0){let C=J[0],M=C.text||C.contentDesc||C.resourceId;console.log(P.magenta(`\u26A1 Found action button: "${M}". Testing interaction...`));let I=ce(A);this.executedStepCount++,await this.driver.tap(C.bounds.centerX,C.bounds.centerY,s),await new Promise(ee=>setTimeout(ee,1200)),await this.packageJailGuard(t,s);let F=await this.getCleanUiHierarchy(t,s);w=F.xml,A=F.flat;let K=ce(A);this.visitedScreenHashes.add(K);let T=await this.runErrorSentinel(t,s,"Post-Action-Tap"),k=I!==K,_="PASS",L="";T.hasError?(_="FAIL",L="Action triggered error or crash"):k?(_="PASS",L="Screen transitioned successfully"):(_="WARN",L="No visible screen transition or state change observed (No-Op)",console.log(P.yellow(` \u26A0\uFE0F Outcome Warning: Action "${M}" produced no UI state change.`)));let oe=await h(_,k);this.reporter.logStep("Action Testing",`Tapped action button "${M}" [${L}]`,"Tap",_,M,oe,T.hasError?{errors:T.errors}:void 0),console.log(_==="PASS"?P.green(`\u2714 Action interaction complete (${L}).`):_==="WARN"?P.yellow(`\u2714 Action interaction recorded with warning (${L}).`):P.red("\u2716 Action interaction triggered an error.")),_!=="FAIL"&&(this.memory.recordSuccess("TAP",M,C.resourceId||C.text||C.contentDesc||M,"text",1,void 0,I),this.memory.markScreenCrawled(I,[M]))}else console.log(P.gray("No standalone action buttons to test."));let N=it(A).filter(C=>!Ee(C,a,o));if(N.length>0){console.log(P.yellow(`
|
|
752
752
|
[3b/5] Inspecting Toggle & Checkable Controls...`));let C=N[0],M=C.text||C.contentDesc||C.resourceId||"Switch/Checkbox",I=C.checked;console.log(P.magenta(`\u26A1 Found toggle: "${M}" (checked: ${I}). Testing toggle interaction...`)),this.executedStepCount++,await this.driver.tap(C.bounds.centerX,C.bounds.centerY,s),await new Promise(ee=>setTimeout(ee,1e3)),w=await this.driver.getUiHierarchy(s),A=ue(w).flat;let F=ce(A);this.visitedScreenHashes.add(F);let T=it(A).find(ee=>Math.abs(ee.bounds.centerX-C.bounds.centerX)<25&&Math.abs(ee.bounds.centerY-C.bounds.centerY)<25),k=T?T.checked!==I:!0,_=await this.runErrorSentinel(t,s,"Post-Toggle"),L=_.hasError?"FAIL":k?"PASS":"WARN",oe=await h(L,k);this.reporter.logStep("Toggle Testing",`Toggled "${M}" (Checked: ${I} -> ${T?T.checked:"flipped"})`,"Toggle",L,M,oe,_.hasError?{errors:_.errors}:void 0),console.log(L==="PASS"?P.green("\u2714 Toggle control flipped successfully!"):L==="WARN"?P.yellow("\u26A0\uFE0F Toggle tapped but checked state did not flip."):P.red("\u2716 Toggle control triggered an error.")),L!=="FAIL"&&this.memory.markScreenCrawled(F,[M])}let Q=Oe(A,b);if(Q&&!Ee(Q,a,o)){let C=Q.contentDesc||Q.text||Q.resourceId||"Drawer Menu";console.log(P.yellow(`
|
|
753
753
|
[3c/5] Inspecting Side Navigation Drawer...`)),console.log(P.magenta(`\u26A1 Found drawer trigger: "${C}". Opening side drawer...`));let M=ce(A);this.executedStepCount++,await this.driver.tap(Q.bounds.centerX,Q.bounds.centerY,s),await new Promise(L=>setTimeout(L,1200)),await this.packageJailGuard(t,s);let I=await this.getCleanUiHierarchy(t,s);w=I.xml,A=I.flat;let F=ce(A);this.visitedScreenHashes.add(F);let K=M!==F,T=await this.runErrorSentinel(t,s,"Drawer-Open"),k=T.hasError?"FAIL":K?"PASS":"WARN",_=await h(k,K);if(this.reporter.logStep("Drawer Navigation",`Opened side drawer via "${C}"`,"Tap",k,C,_,T.hasError?{errors:T.errors}:void 0),k==="PASS"){console.log(P.green("\u2714 Side drawer opened successfully!"));let L=Ge(A).filter(ee=>!Ee(ee,a,o)&&!Pe(ee)&&ee.bounds.centerY>Q.bounds.y2);if(L.length>0){let ee=L[0],fe=ee.text||ee.contentDesc||ee.resourceId||"Drawer Item";console.log(P.gray(` - Testing drawer item: "${fe}"...`)),this.executedStepCount++,await this.driver.tap(ee.bounds.centerX,ee.bounds.centerY,s),await new Promise(q=>setTimeout(q,1200)),await this.packageJailGuard(t,s);let Ie=await this.getCleanUiHierarchy(t,s);w=Ie.xml,A=Ie.flat,this.visitedScreenHashes.add(ce(A))}console.log(P.gray(" Closing side drawer to restore main view...")),await this.driver.pressBack(s),await new Promise(ee=>setTimeout(ee,1e3));let oe=await this.getCleanUiHierarchy(t,s);w=oe.xml,A=oe.flat,this.visitedScreenHashes.add(ce(A)),console.log(P.green("\u2714 Main view restored from side drawer."))}else k==="WARN"&&console.log(P.yellow("\u26A0\uFE0F Drawer button tapped but no visible screen change occurred."));k!=="FAIL"&&this.memory.markScreenCrawled(M,[C])}if(n){console.log(P.yellow(`
|
|
754
|
-
[4/5] Inspecting Lists & Master-Detail Flows...`));let C=Bs(A);if(C.length>0){console.log(P.magenta(`\u26A1 Found ${C.length} scrollable container(s). Performing vertical scroll exploration...`));let I=ce(A),F=Math.round(S*.5);await this.driver.swipe(F,Math.round(b*.7),F,Math.round(b*.3),300,s),await new Promise(L=>setTimeout(L,1200));let K=await this.getCleanUiHierarchy(t,s);w=K.xml,A=K.flat;let T=ce(A);this.visitedScreenHashes.add(T);let k=I!==T,_=await h("PASS",k);this.reporter.logStep("Scroll Exploration",k?"Scrolled vertically to discover off-screen elements":"Scrolled but content remained at edge","Swipe","PASS","SwipeUp",_),k&&console.log(P.green("\u2714 Vertical scroll succeeded; discovered off-screen content below fold."))}let M=bt(A).filter(I=>{if(Pe(I)){let F=I.text||I.contentDesc||"External Link";return this.externalSkippedList.push(F),!1}if(Ee(I,a,o)){let F=I.text||I.contentDesc||"List Item";return console.log(P.yellow(` \u{1F6E1}\uFE0F Safety Guard: Blocked destructive list item "${F}"`)),this.safetyHitList.push(F),!1}return!0});if(M.length>0){let I=M[0],F=I.text||I.contentDesc||`Item at (${I.bounds.centerX}, ${I.bounds.centerY})`;console.log(P.magenta(`\u26A1 Found list item: "${F}". Diving into detail view...`));let K=ce(A);await this.driver.tap(I.bounds.centerX,I.bounds.centerY,s),await new Promise(Ae=>setTimeout(Ae,1800));let T=await this.getCleanUiHierarchy(t,s);w=T.xml,A=T.flat;let k=ce(A);this.visitedScreenHashes.add(k);let _=await this.runErrorSentinel(t,s,"Detail-View"),L=K!==k,oe=_.hasError?"FAIL":L?"PASS":"WARN",ee=await h(oe,L);this.reporter.logStep("List Dive",`Dived into detail view for "${F}" [${L?"Navigated":"Unchanged"}]`,"Tap",oe,F,ee,_.hasError?{errors:_.errors}:void 0),console.log(oe==="PASS"?P.green("\u2714 Detail screen loaded."):oe==="WARN"?P.yellow("\u26A0\uFE0F Detail view tapped but no navigation detected."):P.red("\u2716 Detail view triggered an error."));let fe=we(T.flat,E.screen.fallbackHeight,E.screen.fallbackWidth,!0);fe&&fe.bounds.width>0?(console.log(P.gray(`Navigating back via in-app back button "${fe.text||fe.contentDesc||fe.className}"...`)),await this.driver.tap(fe.bounds.centerX,fe.bounds.centerY,s)):(console.log(P.gray("Navigating back to main view...")),await this.driver.pressKey(4,s)),await new Promise(Ae=>setTimeout(Ae,1200));let Ie=await this.driver.getForegroundPackage(s);
|
|
755
|
-
[5/5] Inspecting Navigation Tabs...`));let j=await this.driver.getForegroundPackage(s);
|
|
754
|
+
[4/5] Inspecting Lists & Master-Detail Flows...`));let C=Bs(A);if(C.length>0){console.log(P.magenta(`\u26A1 Found ${C.length} scrollable container(s). Performing vertical scroll exploration...`));let I=ce(A),F=Math.round(S*.5);await this.driver.swipe(F,Math.round(b*.7),F,Math.round(b*.3),300,s),await new Promise(L=>setTimeout(L,1200));let K=await this.getCleanUiHierarchy(t,s);w=K.xml,A=K.flat;let T=ce(A);this.visitedScreenHashes.add(T);let k=I!==T,_=await h("PASS",k);this.reporter.logStep("Scroll Exploration",k?"Scrolled vertically to discover off-screen elements":"Scrolled but content remained at edge","Swipe","PASS","SwipeUp",_),k&&console.log(P.green("\u2714 Vertical scroll succeeded; discovered off-screen content below fold."))}let M=bt(A).filter(I=>{if(Pe(I)){let F=I.text||I.contentDesc||"External Link";return this.externalSkippedList.push(F),!1}if(Ee(I,a,o)){let F=I.text||I.contentDesc||"List Item";return console.log(P.yellow(` \u{1F6E1}\uFE0F Safety Guard: Blocked destructive list item "${F}"`)),this.safetyHitList.push(F),!1}return!0});if(M.length>0){let I=M[0],F=I.text||I.contentDesc||`Item at (${I.bounds.centerX}, ${I.bounds.centerY})`;console.log(P.magenta(`\u26A1 Found list item: "${F}". Diving into detail view...`));let K=ce(A);await this.driver.tap(I.bounds.centerX,I.bounds.centerY,s),await new Promise(Ae=>setTimeout(Ae,1800));let T=await this.getCleanUiHierarchy(t,s);w=T.xml,A=T.flat;let k=ce(A);this.visitedScreenHashes.add(k);let _=await this.runErrorSentinel(t,s,"Detail-View"),L=K!==k,oe=_.hasError?"FAIL":L?"PASS":"WARN",ee=await h(oe,L);this.reporter.logStep("List Dive",`Dived into detail view for "${F}" [${L?"Navigated":"Unchanged"}]`,"Tap",oe,F,ee,_.hasError?{errors:_.errors}:void 0),console.log(oe==="PASS"?P.green("\u2714 Detail screen loaded."):oe==="WARN"?P.yellow("\u26A0\uFE0F Detail view tapped but no navigation detected."):P.red("\u2716 Detail view triggered an error."));let fe=we(T.flat,E.screen.fallbackHeight,E.screen.fallbackWidth,!0);fe&&fe.bounds.width>0?(console.log(P.gray(`Navigating back via in-app back button "${fe.text||fe.contentDesc||fe.className}"...`)),await this.driver.tap(fe.bounds.centerX,fe.bounds.centerY,s)):(console.log(P.gray("Navigating back to main view...")),await this.driver.pressKey(4,s)),await new Promise(Ae=>setTimeout(Ae,1200));let Ie=await this.driver.getForegroundPackage(s);Ie&&!Ie.includes(t)&&(console.log(P.yellow(`Restoring ${t} foreground after back navigation...`)),await this.driver.launchApp(t,s),await new Promise(Ae=>setTimeout(Ae,2e3)));let q=await this.getCleanUiHierarchy(t,s);w=q.xml,A=q.flat,this.visitedScreenHashes.add(ce(A));let pe=await this.runErrorSentinel(t,s,"Post-Back-Restore"),Me=pe.hasError?"FAIL":"PASS",Tt=await h(Me,!0);this.reporter.logStep("List Dive","Navigated back to master screen","Back",Me,"KEYCODE_BACK",Tt,pe.hasError?{errors:pe.errors}:void 0),Me==="PASS"&&console.log(P.green("\u2714 Master view restored."))}else console.log(P.gray("No list items found for master-detail exploration."))}}console.log(P.yellow(`
|
|
755
|
+
[5/5] Inspecting Navigation Tabs...`));let j=await this.driver.getForegroundPackage(s);j&&!j.includes(t)&&(console.log(P.gray(`Restoring ${t} foreground for tab traversal...`)),await this.driver.launchApp(t,s),await new Promise(ne=>setTimeout(ne,2e3))),await this.dismissSoftKeyboard(s);let Te=await this.getCleanUiHierarchy(t,s);w=Te.xml,A=Te.flat;let te=Ut(A,void 0,t);if(te.length>1){console.log(P.magenta(`\u26A1 Found ${te.length} navigation tab(s). Traversal sequence initiated...`));let ne=te.slice(0,i),J=1;for(let N of ne){let Q=N.text||N.contentDesc||`Tab #${J}`;console.log(P.gray(` - Visiting ${Q}...`)),await this.dismissSoftKeyboard(s);let C=Math.min(N.bounds.centerY,N.bounds.y2-4);this.executedStepCount++,await this.driver.tap(N.bounds.centerX,C,s),await new Promise(k=>setTimeout(k,1500)),await this.packageJailGuard(t,s);let M=await this.getCleanUiHierarchy(t,s);w=M.xml,A=M.flat;let I=ce(A);this.visitedScreenHashes.add(I),this.memory.markScreenCrawled(I,[Q]);let F=await this.runErrorSentinel(t,s,`Tab-${Q}`),K=F.hasError?"FAIL":"PASS",T=await h(K,!0);if(this.reporter.logStep("Tab Traversal",`Navigated to tab "${Q}"`,"Tap",K,Q,T,F.hasError?{errors:F.errors}:void 0),console.log(K==="PASS"?P.green(` \u2714 Tab "${Q}" loaded cleanly.`):P.red(` \u2716 Tab "${Q}" triggered errors.`)),this.executedStepCount<c&&this.visitedScreenHashes.size<d){let k=[],_=b*.08,L=b*.88,oe=q=>q.bounds.centerY>_&&q.bounds.centerY<L&&q.clickable&&q.bounds.width>30&&q.bounds.height>20&&te.every(pe=>Math.abs(pe.bounds.centerX-q.bounds.centerX)>40||Math.abs(pe.bounds.centerY-q.bounds.centerY)>40),ee=bt(A,{width:S,height:b}).filter(oe);for(let q of ee.slice(0,3))k.push({node:q,type:"list"});let fe=Ge(A).filter(q=>!(!oe(q)||Oe([q],b)));for(let q of fe.slice(0,4))k.push({node:q,type:"button"});let Ie=it(A).filter(oe);for(let q of Ie.slice(0,2))k.push({node:q,type:"toggle"});for(let q of k){if(this.executedStepCount>=c||this.visitedScreenHashes.size>=d)break;let pe=q.node;if(Pe(pe)){this.externalSkippedList.push(pe.text||pe.contentDesc||"External Link");continue}if(Ee(pe,a,o)){let Ze=pe.text||pe.contentDesc||"Destructive Action";this.safetyHitList.push(Ze);continue}let Me=pe.text||pe.contentDesc||pe.resourceId||`${q.type} element`;console.log(P.gray(` Sub-crawl [${Q}]: Exercising ${q.type} "${Me.slice(0,40)}"...`)),this.executedStepCount++,await this.driver.tap(pe.bounds.centerX,pe.bounds.centerY,s),await new Promise(Ze=>setTimeout(Ze,1200)),await this.dismissSoftKeyboard(s),await this.packageJailGuard(t,s);let Tt=await this.getCleanUiHierarchy(t,s);w=Tt.xml,A=Tt.flat;let Ae=ce(A),hi=!this.visitedScreenHashes.has(Ae);this.visitedScreenHashes.add(Ae);let Mt=await this.runErrorSentinel(t,s,`Sub-${Me.slice(0,30)}`),As=Mt.hasError?"FAIL":"PASS",gi=await h(As,hi);if(this.reporter.logStep("Tab Sub-Crawl",`Tested ${q.type} "${Me}" under tab "${Q}"`,"Tap",As,Me,gi,Mt.hasError?{errors:Mt.errors}:void 0),Ae!==I){console.log(P.cyan(` \u{1F50E} Screen transitioned. Discovered new screen state [${Ae.slice(0,8)}]`));let Ze=Ge(A).filter($e=>oe($e)&&!Ee($e,a,o)&&!Pe($e));if(Ze.length>0&&this.executedStepCount<c){let $e=Ze[0],mi=$e.text||$e.contentDesc||"Child Action";console.log(P.gray(` Exercising child action "${mi.slice(0,30)}"...`)),this.executedStepCount++,await this.driver.tap($e.bounds.centerX,$e.bounds.centerY,s),await new Promise(fi=>setTimeout(fi,1e3)),await this.dismissSoftKeyboard(s)}console.log(P.gray(` Restoring ${Q} master view via Back...`)),await this.driver.pressBack(s),await new Promise($e=>setTimeout($e,1e3)),await this.dismissSoftKeyboard(s);let $s=await this.getCleanUiHierarchy(t,s);w=$s.xml,A=$s.flat}}}J++}console.log(P.green("\u2714 Navigation tabs traversed successfully!"))}else console.log(P.gray("Single-screen or tabs not detected at screen footer."));for(let ne of this.visitedScreenHashes)this.memory.markScreenCrawled(ne);await this.memory.save();let le=this.memory.getStats();this.reporter.logStep("Coverage Audit",`State Graph Coverage: Visited ${this.visitedScreenHashes.size} unique screen state(s). Memory Engine cached ${le.crawledScreens} crawled screen(s).`,"Verify","PASS"),await this.runErrorSentinel(t,s,"Final-State");let U=await this.reporter.generateReport(),be=U.failedSteps===0&&U.errorsDetected.length===0;console.log(be?P.bold.green(`
|
|
756
756
|
\u2728 PromptTest Exploration Complete \u2014 All Steps Passed!`):P.bold.red(`
|
|
757
757
|
\u26A0\uFE0F PromptTest Exploration Complete \u2014 Issues Detected!`)),console.log(P.white(`Summary: ${U.passedSteps} passed, ${U.warnSteps} warnings, ${U.failedSteps} failed, ${U.errorsDetected.length} errors detected.`)),this.safetyHitList.length>0&&console.log(P.cyan(`\u{1F6E1}\uFE0F Safety Shield: Blocked ${this.safetyHitList.length} destructive action(s) [${this.safetyHitList.join(", ")}].`)),console.log(P.cyan(`\u{1F5FA}\uFE0F State Graph Coverage: Visited ${this.visitedScreenHashes.size} unique screen state(s).`)),console.log(P.cyan(`\u{1F9E0} Memory Engine Coverage: ${le.crawledScreens} crawled screen(s) indexed offline.`)),console.log(P.cyan(`Full Audit Report generated at: output/prompttest-report.md
|
|
758
758
|
`))}finally{f&&ze(f)}}async runErrorSentinel(e,t,s){let i=[];try{let[n,r]=await Promise.all([this.driver.checkCrashes(e,t),this.driver.getUiHierarchy(t)]);if(n){let d=`Crash in ${s}: ${n.slice(0,300)}`;console.log(P.bold.red(`\u{1F6A8} FATAL EXCEPTION DETECTED in ${e} [${s}]!`)),this.reporter.logError(d),i.push(d)}let{flat:o}=ue(r),a=_s(o);if(a.length>0)for(let d of a){let c=d.text||d.contentDesc,l=`UI Error dialog in ${s}: "${c}"`;console.log(P.bold.yellow(`\u26A0\uFE0F Error Dialog/Banner on screen [${s}]: "${c}"`)),this.reporter.logError(l),i.push(l)}}catch{}return{hasError:i.length>0,errors:i}}},No=new ut;import de from"fs";import re from"path";import $ from"chalk";import{spawn as _i}from"child_process";import ni from"readline";import{createRequire as Oi}from"module";var Bi=Oi(import.meta.url),Es="1.1.0";try{Es=Bi("../../package.json").version||"1.1.0"}catch{}var pt=class{driver;deviceLock=null;isRecording=!1;recordedSteps=[];existingSteps=[];existingFileHeader=[];lastHierarchyFlat=[];lastScreenTitle="";visitedScreens=[];targetPackage=null;isAppendMode=!1;resolvedOutputPath="";resolvedReportPath="";activeInput=null;geteventProcess=null;isRefreshingHierarchy=!1;targetSerial="";touchMaxX=E.screen.fallbackWidth;touchMaxY=E.screen.fallbackHeight;displayWidth=E.screen.fallbackWidth;displayHeight=E.screen.fallbackHeight;activeTouchX=-1;activeTouchY=-1;touchDownTime=0;isTouchActive=!1;isSwipeGesture=!1;lastHierarchyTimestamp=0;lastTapTimestamp=0;lastRecordedTapTarget="";inFlightTapQueue=[];inputDebounceTimer=null;logcatEventProcess=null;logcatCrashProcess=null;hierarchyCacheTime=0;hierarchyStale=!0;lastScrollTimestamp=0;lastScrollDirection="";currentScreenName="";sessionScreenSections=[];sessionStartTime=0;screenshotDir="";screenshotCounter=0;crashDetected=!1;lastScreenTransitionTime=0;constructor(e){this.driver=e||he}flushToDisk(){if(this.resolvedOutputPath)try{this.saveSpec(this.resolvedOutputPath)}catch{}if(this.resolvedReportPath)try{this.saveReport(this.resolvedReportPath)}catch{}}appendStep(e,t){this.recordedSteps[this.recordedSteps.length-1]!==e&&(this.recordedSteps.push(e),this.flushToDisk(),t&&console.log(t))}extractBrandSlug(e){if(!e)return"app";let t=new Set(["com","org","net","io","in","co","uk","de","fr","us","ai","dev","android","mobile","app","apps","client","release","debug","main","browser","ui","view","experience","portal","phone","tablet","service"]),s=e.split("."),i=s.filter(r=>!t.has(r.toLowerCase())&&!/^\d+$/.test(r));return((i.length>0?i[i.length-1]:s[s.length-1])||"app").toLowerCase().replace(/[^a-z0-9_]/g,"")}async startRecording(e,t,s){let i=await this.driver.ensureDefaultDevice(t);if(!i)throw new Error("No Android device connected for recording.");this.deviceLock=Ke(i),this.targetSerial=i,this.isRecording=!0,this.recordedSteps=[],this.existingSteps=[],this.existingFileHeader=[],this.visitedScreens=[],this.targetPackage=null,this.activeInput=null,this.isAppendMode=!!s?.append;try{let v=await this.driver.getForegroundPackage(i);v&&(this.targetPackage=v)}catch{}let n=this.extractBrandSlug(this.targetPackage),r;if(e&&e!=="specs/recorded_suite.txt")r=re.resolve(e);else{let v=`${n}_flow`,x=re.resolve(`specs/${v}.txt`);if(!this.isAppendMode&&de.existsSync(x)){let D=1;for(x=re.resolve(`specs/${v}_${D}.txt`);de.existsSync(x);)D++,x=re.resolve(`specs/${v}_${D}.txt`)}r=x}if(this.resolvedOutputPath=r,de.existsSync(r))if(this.isAppendMode){let v=de.readFileSync(r,"utf-8"),x=v.split(`
|
package/package.json
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "prompttest",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.5",
|
|
4
4
|
"description": "Ultra-fast, zero-code autonomous mobile testing & visual QA brain for Android",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
|
+
"homepage": "https://github.com/shriramsingh/prompttest-community#readme",
|
|
7
|
+
"bugs": {
|
|
8
|
+
"url": "https://github.com/shriramsingh/prompttest-community/issues"
|
|
9
|
+
},
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "git+https://github.com/shriramsingh/prompttest-community.git"
|
|
13
|
+
},
|
|
6
14
|
"type": "module",
|
|
7
15
|
"main": "dist/index.js",
|
|
8
16
|
"types": "dist/index.d.ts",
|