@@ -302,29 +302,54 @@ export class BaseMethods {
302302 isMultiple = false ,
303303 isInclude = true ,
304304 } : ElementPropertyOptions ) : Promise < void > {
305- let locator = this . resolveLocator ( selector , { parentSelector, text, index } ) ;
306-
307- if ( isMultiple ) {
308- const handles = await locator . elementHandles ( ) ;
309- for ( const handle of handles ) {
310- const actual = await this . getProperty ( handle , prop , attr ) ;
311- if ( isInclude ) {
312- expect ( actual ) . toContain ( value ) ;
313- } else {
314- expect ( actual ) . not . toContain ( value ) ;
305+ const locator = this . resolveLocator ( selector , { parentSelector, text, index } ) ;
306+ const expectationMessage =
307+ attr === 'attribute'
308+ ? `Expected attribute "${ prop } " on selector "${ selector } " to ${ isInclude ? '' : 'not ' } include "${ value } ".`
309+ : `Expected CSS property "${ prop } " on selector "${ selector } " to ${ isInclude ? '' : 'not ' } include "${ value } ".` ;
310+
311+ const doesMatch = ( actual : string ) : boolean => ( isInclude ? actual . includes ( value ) : ! actual . includes ( value ) ) ;
312+
313+ await expect
314+ . poll ( async ( ) => {
315+ if ( isMultiple ) {
316+ const handles = await locator . elementHandles ( ) ;
317+ if ( handles . length === 0 ) {
318+ return false ;
319+ }
320+
321+ try {
322+ const results = await Promise . all (
323+ handles . map ( async handle => {
324+ try {
325+ return await this . getProperty ( handle , prop , attr ) ;
326+ } finally {
327+ await handle . dispose ( ) ;
328+ }
329+ } ) ,
330+ ) ;
331+
332+ return results . every ( doesMatch ) ;
333+ } catch {
334+ return false ;
335+ }
315336 }
316- }
317337
318- return ;
319- }
320-
321- const actual = await this . getProperty ( await locator . elementHandle ( ) , prop , attr ) ;
338+ const handle = await locator . elementHandle ( { timeout : 0 } ) ;
339+ if ( ! handle ) {
340+ return false ;
341+ }
322342
323- if ( isInclude ) {
324- expect ( actual ) . toContain ( value ) ;
325- } else {
326- expect ( actual ) . not . toContain ( value ) ;
327- }
343+ try {
344+ const actual = await this . getProperty ( handle , prop , attr ) ;
345+ return doesMatch ( actual ) ;
346+ } catch {
347+ return false ;
348+ } finally {
349+ await handle . dispose ( ) ;
350+ }
351+ } , { message : expectationMessage } )
352+ . toBeTruthy ( ) ;
328353 }
329354
330355 async checkUrlText ( urlPart : string , isInclude : boolean = false ) : Promise < void > {
0 commit comments