@@ -224,6 +224,7 @@ async function main() {
224224 await measuredStep ( "JS focus URL and type" , async ( ) => {
225225 await retryAsync (
226226 async ( ) => {
227+ await resolveKnownSystemPrompts ( "JS focus URL and type before focus" ) ;
227228 await session . tapElement (
228229 simulatorUDID ,
229230 { id : "fixture.message" } ,
@@ -234,9 +235,12 @@ async function main() {
234235 durationMs : 30 ,
235236 } ,
236237 ) ;
238+ await resolveKnownSystemPrompts ( "JS focus URL and type after tap" ) ;
237239 await session . openUrl ( simulatorUDID , fixtureFocusUrl ) ;
240+ await resolveKnownSystemPrompts ( "JS focus URL and type after open URL" ) ;
238241 await expectFixtureText ( "Message Focused" , { timeoutMs : 20_000 } ) ;
239242 await sleep ( 1_000 ) ;
243+ await resolveKnownSystemPrompts ( "JS focus URL and type before type" ) ;
240244 await session . batch ( simulatorUDID , [
241245 { action : "type" , text : "agent-ready" , delayMs : 20 } ,
242246 ] ) ;
@@ -363,6 +367,147 @@ async function expectElementContains(selector, text, options = {}) {
363367 ) ;
364368}
365369
370+ async function resolveKnownSystemPrompts ( label ) {
371+ await resolveOpenUrlPrompt ( label ) ;
372+ await resolveKeyboardTipPrompt ( label ) ;
373+ }
374+
375+ async function resolveOpenUrlPrompt ( label ) {
376+ const matches = await safeQuery ( { label : "Open" } , { maxDepth : 6 } ) ;
377+ if ( matches . length === 0 ) {
378+ return ;
379+ }
380+ console . log ( `[prompt] handling open-url prompt during ${ label } ` ) ;
381+ await session . tapElement (
382+ simulatorUDID ,
383+ { label : "Open" } ,
384+ {
385+ source : "native-ax" ,
386+ maxDepth : 6 ,
387+ waitTimeoutMs : 2_000 ,
388+ durationMs : 80 ,
389+ } ,
390+ ) ;
391+ await sleep ( 500 ) ;
392+ }
393+
394+ async function resolveKeyboardTipPrompt ( label ) {
395+ const snapshot = await safeQuery ( { } , { maxDepth : 8 } ) ;
396+ if ( ! looksLikeKeyboardTipQuery ( snapshot ) ) {
397+ return ;
398+ }
399+
400+ console . log ( `[prompt] handling keyboard-tip prompt during ${ label } ` ) ;
401+ const target = keyboardTipContinueTapTarget ( snapshot ) ;
402+ if ( target ) {
403+ await session . tap ( simulatorUDID , target . x , target . y ) ;
404+ } else {
405+ await session . tapElement (
406+ simulatorUDID ,
407+ { label : "Continue" } ,
408+ {
409+ source : "native-ax" ,
410+ maxDepth : 8 ,
411+ waitTimeoutMs : 2_000 ,
412+ durationMs : 80 ,
413+ } ,
414+ ) ;
415+ }
416+ await sleep ( 500 ) ;
417+ }
418+
419+ async function safeQuery ( selector , options = { } ) {
420+ try {
421+ return await session . query ( simulatorUDID , selector , {
422+ source : "native-ax" ,
423+ ...options ,
424+ } ) ;
425+ } catch {
426+ return [ ] ;
427+ }
428+ }
429+
430+ function looksLikeKeyboardTipQuery ( snapshot ) {
431+ const text = JSON . stringify ( snapshot ) ;
432+ return / S p e e d u p y o u r t y p i n g / i. test ( text ) && / \b C o n t i n u e \b / . test ( text ) ;
433+ }
434+
435+ function keyboardTipContinueTapTarget ( snapshot ) {
436+ const nodes = compactQueryNodes ( snapshot ) ;
437+ const rootFrame = nodes
438+ . map ( ( node ) => node . frame )
439+ . filter ( validFrame )
440+ . sort ( ( a , b ) => b . width * b . height - a . width * a . height ) [ 0 ] ;
441+ const continueButton =
442+ nodes . find (
443+ ( node ) =>
444+ node . label === "Continue" &&
445+ node . id !== "fixture.continue" &&
446+ String ( node . role ?? "" )
447+ . toLowerCase ( )
448+ . includes ( "button" ) &&
449+ validFrame ( node . frame ) ,
450+ ) ??
451+ nodes . find (
452+ ( node ) =>
453+ node . label === "Continue" &&
454+ String ( node . role ?? "" )
455+ . toLowerCase ( )
456+ . includes ( "button" ) &&
457+ validFrame ( node . frame ) ,
458+ ) ;
459+
460+ if ( ! rootFrame || ! continueButton ?. frame ) {
461+ return null ;
462+ }
463+
464+ return {
465+ x :
466+ ( continueButton . frame . x + continueButton . frame . width / 2 - rootFrame . x ) /
467+ rootFrame . width ,
468+ y :
469+ ( continueButton . frame . y + continueButton . frame . height / 2 - rootFrame . y ) /
470+ rootFrame . height ,
471+ } ;
472+ }
473+
474+ function compactQueryNodes ( snapshot ) {
475+ const nodes = [ ] ;
476+ const visit = ( node ) => {
477+ if ( ! node || typeof node !== "object" ) {
478+ return ;
479+ }
480+ if (
481+ "role" in node ||
482+ "id" in node ||
483+ "label" in node ||
484+ "value" in node ||
485+ "frame" in node
486+ ) {
487+ nodes . push ( node ) ;
488+ }
489+ for ( const child of Array . isArray ( node . children ) ? node . children : [ ] ) {
490+ visit ( child ) ;
491+ }
492+ } ;
493+ for ( const match of Array . isArray ( snapshot ) ? snapshot : [ ] ) {
494+ visit ( match ) ;
495+ }
496+ return nodes ;
497+ }
498+
499+ function validFrame ( frame ) {
500+ return (
501+ frame &&
502+ Number . isFinite ( frame . x ) &&
503+ Number . isFinite ( frame . y ) &&
504+ Number . isFinite ( frame . width ) &&
505+ Number . isFinite ( frame . height ) &&
506+ frame . width > 0 &&
507+ frame . height > 0
508+ ) ;
509+ }
510+
366511function buildFixtureApp ( ) {
367512 return buildCachedFixtureApp ( {
368513 root,
0 commit comments