@@ -895,6 +895,12 @@ class WebDriver extends Helper {
895895 return this . _locateByRole ( locator )
896896 }
897897
898+ // Handle role locators passed as Locator instances
899+ const matchedLocator = new Locator ( locator )
900+ if ( matchedLocator . isRole ( ) ) {
901+ return this . _locateByRole ( matchedLocator . locator )
902+ }
903+
898904 if ( ! this . options . smartWait || ! smartWait ) {
899905 if ( this . _isCustomLocator ( locator ) ) {
900906 const locatorObj = new Locator ( locator )
@@ -977,34 +983,15 @@ class WebDriver extends Helper {
977983 return this . browser . $$ ( `[role="${ role } "]` )
978984 }
979985
980- if ( locator . exact === true ) {
981- // Use WebdriverIO's aria selector for exact accessible name matching
982- const elements = await this . browser . $$ ( `[role="${ role } "]` )
983- const filteredElements = [ ]
984-
985- for ( const element of elements ) {
986- try {
987- const match = await element . $ ( `aria/${ locator . text } ` )
988- if ( match ) filteredElements . push ( element )
989- } catch ( e ) {
990- // Element doesn't have this accessible name
991- }
992- }
993-
994- return filteredElements
995- }
996-
997- // For partial match, manually filter by accessible name, placeholder, and innerText
998986 const elements = await this . browser . $$ ( `[role="${ role } "]` )
999987 const filteredElements = [ ]
988+ const matchFn = locator . exact === true
989+ ? t => t === locator . text
990+ : t => t && t . includes ( locator . text )
1000991
1001992 for ( const element of elements ) {
1002- const texts = await element . execute ( e => {
1003- const accessibleName = e . hasAttribute ( 'aria-label' ) ? e . getAttribute ( 'aria-label' ) : ( e . id && document . querySelector ( `label[for="${ e . id } "]` ) ?. textContent . trim ( ) ) || ''
1004- return [ accessibleName , e . getAttribute ( 'placeholder' ) || '' , e . innerText ? e . innerText . trim ( ) : '' ]
1005- } )
1006-
1007- if ( texts . some ( t => t && t . includes ( locator . text ) ) ) {
993+ const texts = await getElementTextAttributes . call ( this , element )
994+ if ( texts . some ( matchFn ) ) {
1008995 filteredElements . push ( element )
1009996 }
1010997 }
@@ -1291,7 +1278,8 @@ class WebDriver extends Helper {
12911278 const elementId = getElementId ( elem )
12921279 highlightActiveElement . call ( this , elem )
12931280
1294- const isSelected = await this . browser . isElementSelected ( elementId )
1281+ const isSelected = await isElementChecked ( this . browser , elementId )
1282+
12951283 if ( isSelected ) return Promise . resolve ( true )
12961284 return this . browser [ clickMethod ] ( elementId )
12971285 }
@@ -1311,7 +1299,8 @@ class WebDriver extends Helper {
13111299 const elementId = getElementId ( elem )
13121300 highlightActiveElement . call ( this , elem )
13131301
1314- const isSelected = await this . browser . isElementSelected ( elementId )
1302+ const isSelected = await isElementChecked ( this . browser , elementId )
1303+
13151304 if ( ! isSelected ) return Promise . resolve ( true )
13161305 return this . browser [ clickMethod ] ( elementId )
13171306 }
@@ -2963,13 +2952,19 @@ async function proceedSeeField(assertType, field, value) {
29632952 }
29642953 }
29652954
2966- const proceedSingle = el =>
2967- el . getValue ( ) . then ( res => {
2968- if ( res === null ) {
2969- throw new Error ( `Element ${ el . selector } has no value attribute` )
2970- }
2971- stringIncludes ( `fields by ${ field } ` ) [ assertType ] ( value , res )
2972- } )
2955+ const proceedSingle = async el => {
2956+ let res = await el . getValue ( )
2957+
2958+ if ( res === null ) {
2959+ res = await el . getText ( )
2960+ }
2961+
2962+ if ( res === null || res === undefined ) {
2963+ throw new Error ( `Element ${ el . selector } has no value attribute` )
2964+ }
2965+
2966+ stringIncludes ( `fields by ${ field } ` ) [ assertType ] ( value , res )
2967+ }
29732968
29742969 const filterBySelected = async elements => filterAsync ( elements , async el => this . browser . isElementSelected ( getElementId ( el ) ) )
29752970
@@ -3031,10 +3026,31 @@ async function proceedSeeCheckbox(assertType, field) {
30313026 const res = await findFields . call ( this , field )
30323027 assertElementExists ( res , field , 'Field' )
30333028
3034- const selected = await forEachAsync ( res , async el => this . browser . isElementSelected ( getElementId ( el ) ) )
3029+ const selected = await forEachAsync ( res , async el => {
3030+ const elementId = getElementId ( el )
3031+ return isElementChecked ( this . browser , elementId )
3032+ } )
3033+
30353034 return truth ( `checkable field "${ field } "` , 'to be checked' ) [ assertType ] ( selected )
30363035}
30373036
3037+ async function getElementTextAttributes ( element ) {
3038+ const elementId = getElementId ( element )
3039+ const ariaLabel = await this . browser . getElementAttribute ( elementId , 'aria-label' ) . catch ( ( ) => '' )
3040+ const placeholder = await this . browser . getElementAttribute ( elementId , 'placeholder' ) . catch ( ( ) => '' )
3041+ const innerText = await this . browser . getElementText ( elementId ) . catch ( ( ) => '' )
3042+ return [ ariaLabel , placeholder , innerText ]
3043+ }
3044+
3045+ async function isElementChecked ( browser , elementId ) {
3046+ let isChecked = await browser . isElementSelected ( elementId )
3047+ if ( ! isChecked ) {
3048+ const ariaChecked = await browser . getElementAttribute ( elementId , 'aria-checked' )
3049+ isChecked = ariaChecked === 'true'
3050+ }
3051+ return isChecked
3052+ }
3053+
30383054async function findCheckable ( locator , locateFn ) {
30393055 let els
30403056 locator = new Locator ( locator )
0 commit comments