55
66import java .util .regex .Pattern ;
77
8+ import static com .microsoft .playwright .impl .Serialization .gson ;
89import static com .microsoft .playwright .impl .Utils .toJsRegexFlags ;
910
1011public class LocatorUtils {
@@ -25,10 +26,7 @@ static String getByLabelSelector(Object text, Locator.GetByLabelOptions options)
2526 }
2627
2728 private static String getByAttributeTextSelector (String attrName , Object value , boolean exact ) {
28- if (value instanceof Pattern ) {
29- return "internal:attr=[" + attrName + "=" + toJsRegExp ((Pattern ) value ) + "]" ;
30- }
31- return "internal:attr=[" + attrName + "=" + escapeForAttributeSelector ((String ) value , exact ) + "]" ;
29+ return "internal:attr=[" + attrName + "=" + escapeForAttributeSelector (value , exact ) + "]" ;
3230 }
3331
3432 static String getByTestIdSelector (Object testId ) {
@@ -71,14 +69,7 @@ static String getByRoleSelector(AriaRole role, Locator.GetByRoleOptions options)
7169 if (options .level != null )
7270 addAttr (result , "level" , options .level .toString ());
7371 if (options .name != null ) {
74- String name ;
75- if (options .name instanceof String ) {
76- name = escapeForAttributeSelector ((String ) options .name , options .exact != null && options .exact );
77- } else if (options .name instanceof Pattern ) {
78- name = toJsRegExp ((Pattern ) options .name );
79- } else {
80- throw new IllegalArgumentException ("options.name can be String or Pattern, found: " + options .name );
81- }
72+ String name = escapeForAttributeSelector (options .name , options .exact != null && options .exact );
8273 addAttr (result , "name" , name );
8374 }
8475 if (options .pressed != null )
@@ -87,38 +78,33 @@ static String getByRoleSelector(AriaRole role, Locator.GetByRoleOptions options)
8778 return result .toString ();
8879 }
8980
90- static String escapeForTextSelector (Object text , boolean exact ) {
91- return escapeForTextSelector (text , exact , false );
81+ private static String escapeRegexForSelector (Pattern re ) {
82+ // Even number of backslashes followed by the quote -> insert a backslash.
83+ return toJsRegExp (re ).replaceAll ("(^|[^\\ \\ ])(\\ \\ \\ \\ )*([\" '`])" , "$1$2\\ \\ $3" ).replaceAll (">>" , "\\ \\ >\\ \\ >" );
9284 }
9385
94- private static String escapeForTextSelector (Object param , boolean exact , boolean caseSensitive ) {
95- if (param instanceof Pattern ) {
96- return toJsRegExp ((Pattern ) param );
97- }
98- if (!(param instanceof String )) {
99- throw new IllegalArgumentException ("text parameter must be Pattern or String: " + param );
100- }
101- String text = (String ) param ;
102- if (exact ) {
103- return '"' + text .replace ("\" " , "\\ \" " ) + '"' ;
86+ static String escapeForTextSelector (Object value , boolean exact ) {
87+ if (value instanceof Pattern ) {
88+ return escapeRegexForSelector ((Pattern ) value );
10489 }
105-
106- if (text .contains ("\" " ) || text .contains (">>" ) || text .startsWith ("/" )) {
107- return "/" + escapeForRegex (text ).replaceAll ("\\ s+" , "\\ \\ s+" ) + "/" + (caseSensitive ? "" : "i" );
90+ if (value instanceof String ) {
91+ return gson ().toJson (value ) + (exact ? "s" : "i" );
10892 }
109- return text ;
93+ throw new IllegalArgumentException ( " text parameter must be Pattern or String: " + value ) ;
11094 }
11195
112- private static String escapeForRegex (String text ) {
113- return text .replaceAll ("[.*+?^>${}()|\\ [\\ ]\\ \\ ]" , "\\ \\ \\ \\ $0" );
114- }
115-
116- private static String escapeForAttributeSelector (String value , boolean exact ) {
117- // TODO: this should actually be
118- // cssEscape(value).replace(/\\ /g, ' ')
119- // However, our attribute selectors do not conform to CSS parsing spec,
120- // so we escape them differently.
121- return '"' + value .replaceAll ("\\ \\ " , "\\ \\ \\ \\ " ).replaceAll ("\" " , "\\ \\ \" " ) + '"' + (exact ? "" : "i" );
96+ private static String escapeForAttributeSelector (Object value , boolean exact ) {
97+ if (value instanceof Pattern ) {
98+ return escapeRegexForSelector ((Pattern ) value );
99+ }
100+ if (value instanceof String ) {
101+ // TODO: this should actually be
102+ // cssEscape(value).replace(/\\ /g, ' ')
103+ // However, our attribute selectors do not conform to CSS parsing spec,
104+ // so we escape them differently.
105+ return '"' + ((String ) value ).replaceAll ("\\ \\ " , "\\ \\ \\ \\ " ).replaceAll ("\" " , "\\ \\ \" " ) + '"' + (exact ? "" : "i" );
106+ }
107+ throw new IllegalArgumentException ("Attribute can be String or Pattern, found: " + value );
122108 }
123109
124110 private static String toJsRegExp (Pattern pattern ) {
0 commit comments