@@ -153,6 +153,10 @@ class Select {
153
153
throw new Error ( `Select only works on <select> elements` )
154
154
}
155
155
} )
156
+
157
+ this . element . getAttribute ( 'multiple' ) . then ( ( multiple ) => {
158
+ this . multiple = multiple !== null && multiple !== 'false'
159
+ } )
156
160
}
157
161
158
162
/**
@@ -254,30 +258,46 @@ class Select {
254
258
async selectByVisibleText ( text ) {
255
259
text = typeof text === 'number' ? text . toString ( ) : text
256
260
257
- const normalized = text
258
- . trim ( ) // strip leading and trailing white-space characters
259
- . replace ( / \s + / , ' ' ) // replace sequences of whitespace characters by a single space
261
+ const xpath = './/option[normalize-space(.) = ' + escapeQuotes ( text ) + ']'
260
262
261
- /**
262
- * find option element using xpath
263
- */
264
- const formatted = / " / . test ( normalized )
265
- ? 'concat("' + normalized . split ( '"' ) . join ( '", \'"\', "' ) + '")'
266
- : `"${ normalized } "`
267
- const dotFormat = `[. = ${ formatted } ]`
268
- const spaceFormat = `[normalize-space(text()) = ${ formatted } ]`
263
+ const options = await this . element . findElements ( By . xpath ( xpath ) )
269
264
270
- const selections = [
271
- `./ option${ dotFormat } ` ,
272
- `./option ${ spaceFormat } ` ,
273
- `./optgroup/option ${ dotFormat } ` ,
274
- `./optgroup/option ${ spaceFormat } ` ,
275
- ]
265
+ for ( let option of options ) {
266
+ await this . setSelected ( option )
267
+ if ( ! ( await this . isMultiple ( ) ) ) {
268
+ return
269
+ }
270
+ }
276
271
277
- const optionElement = await this . element . findElement ( {
278
- xpath : selections . join ( '|' ) ,
279
- } )
280
- await this . setSelected ( optionElement )
272
+ let matched = Array . isArray ( options ) && options . length > 0
273
+
274
+ if ( ! matched && text . includes ( ' ' ) ) {
275
+ const subStringWithoutSpace = getLongestSubstringWithoutSpace ( text )
276
+ let candidates
277
+ if ( '' === subStringWithoutSpace ) {
278
+ candidates = await this . element . findElements ( By . tagName ( 'option' ) )
279
+ } else {
280
+ const xpath = './/option[contains(., ' + escapeQuotes ( subStringWithoutSpace ) + ')]'
281
+ candidates = await this . element . findElements ( By . xpath ( xpath ) )
282
+ }
283
+
284
+ const trimmed = text . trim ( )
285
+
286
+ for ( let option of candidates ) {
287
+ const optionText = await option . getText ( )
288
+ if ( trimmed === optionText . trim ( ) ) {
289
+ await this . setSelected ( option )
290
+ if ( ! ( await this . isMultiple ( ) ) ) {
291
+ return
292
+ }
293
+ matched = true
294
+ }
295
+ }
296
+ }
297
+
298
+ if ( ! matched ) {
299
+ throw new Error ( `Cannot locate option with text: ${ text } ` )
300
+ }
281
301
}
282
302
283
303
/**
@@ -293,7 +313,7 @@ class Select {
293
313
* @returns {Promise<boolean> }
294
314
*/
295
315
async isMultiple ( ) {
296
- return ( await this . element . getAttribute ( ' multiple' ) ) !== null
316
+ return this . multiple
297
317
}
298
318
299
319
/**
@@ -457,4 +477,42 @@ class Select {
457
477
}
458
478
}
459
479
460
- module . exports = { Select }
480
+ function escapeQuotes ( toEscape ) {
481
+ if ( toEscape . includes ( `"` ) && toEscape . includes ( `'` ) ) {
482
+ const quoteIsLast = toEscape . lastIndexOf ( `"` ) === toEscape . length - 1
483
+ const substrings = toEscape . split ( `"` )
484
+
485
+ // Remove the last element if it's an empty string
486
+ if ( substrings [ substrings . length - 1 ] === '' ) {
487
+ substrings . pop ( )
488
+ }
489
+
490
+ let result = 'concat('
491
+
492
+ for ( let i = 0 ; i < substrings . length ; i ++ ) {
493
+ result += `"${ substrings [ i ] } "`
494
+ result += i === substrings . length - 1 ? ( quoteIsLast ? `, '"')` : `)` ) : `, '"', `
495
+ }
496
+ return result
497
+ }
498
+
499
+ if ( toEscape . includes ( '"' ) ) {
500
+ return `'${ toEscape } '`
501
+ }
502
+
503
+ // Otherwise return the quoted string
504
+ return `"${ toEscape } "`
505
+ }
506
+
507
+ function getLongestSubstringWithoutSpace ( text ) {
508
+ let words = text . split ( ' ' )
509
+ let longestString = ''
510
+ for ( let word of words ) {
511
+ if ( word . length > longestString . length ) {
512
+ longestString = word
513
+ }
514
+ }
515
+ return longestString
516
+ }
517
+
518
+ module . exports = { Select, escapeQuotes }
0 commit comments