@@ -19,6 +19,13 @@ import {
1919 tryParseSelectorChain ,
2020} from '../selectors.ts' ;
2121import { inferFillText , uniqueStrings } from '../action-utils.ts' ;
22+ import {
23+ appendScriptSeriesFlags ,
24+ formatScriptActionSummary ,
25+ formatScriptArg ,
26+ isClickLikeCommand ,
27+ parseReplaySeriesFlags ,
28+ } from '../script-utils.ts' ;
2229
2330type ReinstallOps = {
2431 ios : ( device : DeviceInfo , app : string , appPath : string ) => Promise < { bundleId : string } > ;
@@ -649,13 +656,7 @@ function withReplayFailureContext(
649656}
650657
651658function formatReplayActionSummary ( action : SessionAction ) : string {
652- const values = ( action . positionals ?? [ ] ) . map ( ( value ) => {
653- const trimmed = value . trim ( ) ;
654- if ( / ^ - ? \d + ( \. \d + ) ? $ / . test ( trimmed ) ) return trimmed ;
655- if ( trimmed . startsWith ( '@' ) ) return trimmed ;
656- return JSON . stringify ( trimmed ) ;
657- } ) ;
658- return [ action . command , ...values ] . join ( ' ' ) ;
659+ return formatScriptActionSummary ( action ) ;
659660}
660661
661662async function healReplayAction ( params : {
@@ -666,13 +667,12 @@ async function healReplayAction(params: {
666667 dispatch : typeof dispatchCommand ;
667668} ) : Promise < SessionAction | null > {
668669 const { action, sessionName, logPath, sessionStore, dispatch } = params ;
669- if ( ! [ 'click' , 'press' , 'fill' , 'get' , 'is' , 'wait' ] . includes ( action . command ) ) return null ;
670+ if ( ! ( isClickLikeCommand ( action . command ) || [ 'fill' , 'get' , 'is' , 'wait' ] . includes ( action . command ) ) ) return null ;
670671 const session = sessionStore . get ( sessionName ) ;
671672 if ( ! session ) return null ;
672- const requiresRect = action . command === 'click' || action . command === 'press' || action . command === 'fill' ;
673+ const requiresRect = isClickLikeCommand ( action . command ) || action . command === 'fill' ;
673674 const allowDisambiguation =
674- action . command === 'click' ||
675- action . command === 'press' ||
675+ isClickLikeCommand ( action . command ) ||
676676 action . command === 'fill' ||
677677 ( action . command === 'get' && action . positionals ?. [ 0 ] === 'text' ) ;
678678 const snapshot = await captureSnapshotForReplay ( session , action , logPath , requiresRect , dispatch , sessionStore ) ;
@@ -688,15 +688,10 @@ async function healReplayAction(params: {
688688 } ) ;
689689 if ( ! resolved ) continue ;
690690 const selectorChain = buildSelectorChainForNode ( resolved . node , session . device . platform , {
691- action :
692- action . command === 'click' || action . command === 'press'
693- ? 'click'
694- : action . command === 'fill'
695- ? 'fill'
696- : 'get' ,
691+ action : isClickLikeCommand ( action . command ) ? 'click' : action . command === 'fill' ? 'fill' : 'get' ,
697692 } ) ;
698693 const selectorExpression = selectorChain . join ( ' || ' ) ;
699- if ( action . command === 'click' || action . command === 'press' ) {
694+ if ( isClickLikeCommand ( action . command ) ) {
700695 return {
701696 ...action ,
702697 positionals : [ selectorExpression ] ,
@@ -796,7 +791,7 @@ function collectReplaySelectorCandidates(action: SessionAction): string[] {
796791 : [ ] ;
797792 result . push ( ...explicitChain ) ;
798793
799- if ( action . command === 'click' || action . command === 'press' ) {
794+ if ( isClickLikeCommand ( action . command ) ) {
800795 const first = action . positionals ?. [ 0 ] ?? '' ;
801796 if ( first && ! first . startsWith ( '@' ) ) {
802797 result . push ( action . positionals . join ( ' ' ) ) ;
@@ -992,8 +987,8 @@ function parseReplayScriptLine(line: string): SessionAction | null {
992987 return action ;
993988 }
994989
995- if ( command === 'click' || command === 'press' ) {
996- const parsed = parseReplayPressFlags ( args ) ;
990+ if ( isClickLikeCommand ( command ) ) {
991+ const parsed = parseReplaySeriesFlags ( command , args ) ;
997992 Object . assign ( action . flags , parsed . flags ) ;
998993 if ( parsed . positionals . length === 0 ) return action ;
999994 const target = parsed . positionals [ 0 ] ;
@@ -1052,7 +1047,7 @@ function parseReplayScriptLine(line: string): SessionAction | null {
10521047 }
10531048
10541049 if ( command === 'swipe' ) {
1055- const parsed = parseReplaySwipeFlags ( args ) ;
1050+ const parsed = parseReplaySeriesFlags ( command , args ) ;
10561051 Object . assign ( action . flags , parsed . flags ) ;
10571052 action . positionals = parsed . positionals ;
10581053 return action ;
@@ -1062,81 +1057,6 @@ function parseReplayScriptLine(line: string): SessionAction | null {
10621057 return action ;
10631058}
10641059
1065- function parseReplayPressFlags ( args : string [ ] ) : { positionals : string [ ] ; flags : SessionAction [ 'flags' ] } {
1066- const positionals : string [ ] = [ ] ;
1067- const flags : SessionAction [ 'flags' ] = { } ;
1068- for ( let index = 0 ; index < args . length ; index += 1 ) {
1069- const token = args [ index ] ;
1070- if ( token === '--tap-batch' ) {
1071- flags . tapBatch = true ;
1072- continue ;
1073- }
1074- if ( token === '--count' && index + 1 < args . length ) {
1075- const parsed = parseNonNegativeIntToken ( args [ index + 1 ] ) ;
1076- if ( parsed !== null ) flags . count = parsed ;
1077- index += 1 ;
1078- continue ;
1079- }
1080- if ( token === '--interval-ms' && index + 1 < args . length ) {
1081- const parsed = parseNonNegativeIntToken ( args [ index + 1 ] ) ;
1082- if ( parsed !== null ) flags . intervalMs = parsed ;
1083- index += 1 ;
1084- continue ;
1085- }
1086- if ( token === '--hold-ms' && index + 1 < args . length ) {
1087- const parsed = parseNonNegativeIntToken ( args [ index + 1 ] ) ;
1088- if ( parsed !== null ) flags . holdMs = parsed ;
1089- index += 1 ;
1090- continue ;
1091- }
1092- if ( token === '--jitter-px' && index + 1 < args . length ) {
1093- const parsed = parseNonNegativeIntToken ( args [ index + 1 ] ) ;
1094- if ( parsed !== null ) flags . jitterPx = parsed ;
1095- index += 1 ;
1096- continue ;
1097- }
1098- positionals . push ( token ) ;
1099- }
1100- return { positionals, flags } ;
1101- }
1102-
1103- function parseReplaySwipeFlags ( args : string [ ] ) : { positionals : string [ ] ; flags : SessionAction [ 'flags' ] } {
1104- const positionals : string [ ] = [ ] ;
1105- const flags : SessionAction [ 'flags' ] = { } ;
1106- for ( let index = 0 ; index < args . length ; index += 1 ) {
1107- const token = args [ index ] ;
1108- if ( token === '--count' && index + 1 < args . length ) {
1109- const parsed = parseNonNegativeIntToken ( args [ index + 1 ] ) ;
1110- if ( parsed !== null ) flags . count = parsed ;
1111- index += 1 ;
1112- continue ;
1113- }
1114- if ( token === '--pause-ms' && index + 1 < args . length ) {
1115- const parsed = parseNonNegativeIntToken ( args [ index + 1 ] ) ;
1116- if ( parsed !== null ) flags . pauseMs = parsed ;
1117- index += 1 ;
1118- continue ;
1119- }
1120- if ( token === '--pattern' && index + 1 < args . length ) {
1121- const pattern = args [ index + 1 ] ;
1122- if ( pattern === 'one-way' || pattern === 'ping-pong' ) {
1123- flags . pattern = pattern ;
1124- }
1125- index += 1 ;
1126- continue ;
1127- }
1128- positionals . push ( token ) ;
1129- }
1130- return { positionals, flags } ;
1131- }
1132-
1133- function parseNonNegativeIntToken ( token : string | undefined ) : number | null {
1134- if ( ! token ) return null ;
1135- const value = Number ( token ) ;
1136- if ( ! Number . isFinite ( value ) || value < 0 ) return null ;
1137- return Math . floor ( value ) ;
1138- }
1139-
11401060function isNumericToken ( token : string | undefined ) : token is string {
11411061 if ( ! token ) return false ;
11421062 return ! Number . isNaN ( Number ( token ) ) ;
@@ -1205,49 +1125,23 @@ function formatReplayActionLine(action: SessionAction): string {
12051125 parts . push ( '-d' , String ( action . flags . snapshotDepth ) ) ;
12061126 }
12071127 if ( action . flags ?. snapshotScope ) {
1208- parts . push ( '-s' , formatReplayArg ( action . flags . snapshotScope ) ) ;
1128+ parts . push ( '-s' , formatScriptArg ( action . flags . snapshotScope ) ) ;
12091129 }
12101130 if ( action . flags ?. snapshotRaw ) parts . push ( '--raw' ) ;
12111131 return parts . join ( ' ' ) ;
12121132 }
12131133 if ( action . command === 'open' ) {
12141134 for ( const positional of action . positionals ?? [ ] ) {
1215- parts . push ( formatReplayArg ( positional ) ) ;
1135+ parts . push ( formatScriptArg ( positional ) ) ;
12161136 }
12171137 if ( action . flags ?. relaunch ) {
12181138 parts . push ( '--relaunch' ) ;
12191139 }
12201140 return parts . join ( ' ' ) ;
12211141 }
12221142 for ( const positional of action . positionals ?? [ ] ) {
1223- parts . push ( formatReplayArg ( positional ) ) ;
1143+ parts . push ( formatScriptArg ( positional ) ) ;
12241144 }
1225- appendReplaySeriesFlags ( parts , action ) ;
1145+ appendScriptSeriesFlags ( parts , action ) ;
12261146 return parts . join ( ' ' ) ;
12271147}
1228-
1229- function appendReplaySeriesFlags ( parts : string [ ] , action : SessionAction ) : void {
1230- const flags = action . flags ?? { } ;
1231- if ( action . command === 'click' || action . command === 'press' ) {
1232- if ( typeof flags . count === 'number' ) parts . push ( '--count' , String ( flags . count ) ) ;
1233- if ( typeof flags . intervalMs === 'number' ) parts . push ( '--interval-ms' , String ( flags . intervalMs ) ) ;
1234- if ( typeof flags . holdMs === 'number' ) parts . push ( '--hold-ms' , String ( flags . holdMs ) ) ;
1235- if ( typeof flags . jitterPx === 'number' ) parts . push ( '--jitter-px' , String ( flags . jitterPx ) ) ;
1236- if ( flags . tapBatch === true ) parts . push ( '--tap-batch' ) ;
1237- return ;
1238- }
1239- if ( action . command === 'swipe' ) {
1240- if ( typeof flags . count === 'number' ) parts . push ( '--count' , String ( flags . count ) ) ;
1241- if ( typeof flags . pauseMs === 'number' ) parts . push ( '--pause-ms' , String ( flags . pauseMs ) ) ;
1242- if ( flags . pattern === 'one-way' || flags . pattern === 'ping-pong' ) {
1243- parts . push ( '--pattern' , flags . pattern ) ;
1244- }
1245- }
1246- }
1247-
1248- function formatReplayArg ( value : string ) : string {
1249- const trimmed = value . trim ( ) ;
1250- if ( trimmed . startsWith ( '@' ) ) return trimmed ;
1251- if ( / ^ - ? \d + ( \. \d + ) ? $ / . test ( trimmed ) ) return trimmed ;
1252- return JSON . stringify ( trimmed ) ;
1253- }
0 commit comments