@@ -391,6 +391,29 @@ class Appium extends Webdriver {
391391 return `${ protocol } ://${ hostname } :${ port } ${ normalizedPath } /session/${ this . browser . sessionId } `
392392 }
393393
394+ /**
395+ * Helper method to safely call isDisplayed() on mobile elements.
396+ * Handles the case where webdriverio tries to use execute/sync which isn't supported in Appium.
397+ * @private
398+ */
399+ async _isDisplayedSafe ( element ) {
400+ if ( this . isWeb ) {
401+ // For web contexts, use the normal isDisplayed
402+ return element . isDisplayed ( )
403+ }
404+
405+ try {
406+ return await element . isDisplayed ( )
407+ } catch ( err ) {
408+ // If isDisplayed fails due to execute/sync not being supported in native mobile contexts,
409+ // fall back to assuming the element is displayed (since we found it)
410+ if ( err . message && err . message . includes ( 'Method is not implemented' ) ) {
411+ return true
412+ }
413+ throw err
414+ }
415+ }
416+
394417 /**
395418 * Execute code only on iOS
396419 *
@@ -1523,34 +1546,23 @@ class Appium extends Webdriver {
15231546 */
15241547 async dontSeeElement ( locator ) {
15251548 if ( this . isWeb ) return super . dontSeeElement ( locator )
1526-
1527- // For mobile native apps, handle isDisplayed error gracefully
1549+
1550+ // For mobile native apps, use safe isDisplayed wrapper
15281551 const parsedLocator = parseLocator . call ( this , locator )
15291552 const res = await this . _locate ( parsedLocator , false )
15301553 const { truth } = require ( '../assert/truth' )
15311554 const Locator = require ( '../locator' )
1532-
1555+
15331556 if ( ! res || res . length === 0 ) {
15341557 return truth ( `elements of ${ new Locator ( parsedLocator ) } ` , 'to be seen' ) . negate ( false )
15351558 }
1536-
1559+
15371560 const selected = [ ]
15381561 for ( const el of res ) {
1539- try {
1540- const displayed = await el . isDisplayed ( )
1541- if ( displayed ) selected . push ( true )
1542- } catch ( err ) {
1543- // If isDisplayed fails due to execute/sync not being supported,
1544- // fall back to checking if element exists (which we already verified)
1545- if ( err . message && err . message . includes ( 'Method is not implemented' ) ) {
1546- // Element exists, assume it's displayed
1547- selected . push ( true )
1548- } else {
1549- throw err
1550- }
1551- }
1562+ const displayed = await this . _isDisplayedSafe ( el )
1563+ if ( displayed ) selected . push ( true )
15521564 }
1553-
1565+
15541566 try {
15551567 return truth ( `elements of ${ new Locator ( parsedLocator ) } ` , 'to be seen' ) . negate ( selected )
15561568 } catch ( err ) {
@@ -1609,7 +1621,18 @@ class Appium extends Webdriver {
16091621 */
16101622 async grabNumberOfVisibleElements ( locator ) {
16111623 if ( this . isWeb ) return super . grabNumberOfVisibleElements ( locator )
1612- return super . grabNumberOfVisibleElements ( parseLocator . call ( this , locator ) )
1624+
1625+ // For mobile native apps, use safe isDisplayed wrapper
1626+ const parsedLocator = parseLocator . call ( this , locator )
1627+ const res = await this . _locate ( parsedLocator )
1628+
1629+ const selected = [ ]
1630+ for ( const el of res ) {
1631+ const displayed = await this . _isDisplayedSafe ( el )
1632+ if ( displayed ) selected . push ( true )
1633+ }
1634+
1635+ return selected . length
16131636 }
16141637
16151638 /**
@@ -1688,38 +1711,25 @@ class Appium extends Webdriver {
16881711 */
16891712 async seeElement ( locator ) {
16901713 if ( this . isWeb ) return super . seeElement ( locator )
1691-
1692- // For mobile native apps, we need to handle isDisplayed differently
1693- // because webdriverio's isDisplayed() tries to execute JavaScript which isn't supported
1714+
1715+ // For mobile native apps, use safe isDisplayed wrapper
16941716 const parsedLocator = parseLocator . call ( this , locator )
16951717 const res = await this . _locate ( parsedLocator , true )
16961718 const ElementNotFound = require ( './errors/ElementNotFound' )
16971719 const { truth } = require ( '../assert/truth' )
16981720 const { dontSeeElementError } = require ( './errors/ElementAssertion' )
16991721 const Locator = require ( '../locator' )
1700-
1722+
17011723 if ( ! res || res . length === 0 ) {
17021724 throw new ElementNotFound ( parsedLocator )
17031725 }
1704-
1705- // Use a safer approach for mobile: check if element exists and try isDisplayed with error handling
1726+
17061727 const selected = [ ]
17071728 for ( const el of res ) {
1708- try {
1709- const displayed = await el . isDisplayed ( )
1710- if ( displayed ) selected . push ( true )
1711- } catch ( err ) {
1712- // If isDisplayed fails due to execute/sync not being supported,
1713- // fall back to checking if element exists (which we already verified)
1714- if ( err . message && err . message . includes ( 'Method is not implemented' ) ) {
1715- // Element exists, assume it's displayed since we found it
1716- selected . push ( true )
1717- } else {
1718- throw err
1719- }
1720- }
1729+ const displayed = await this . _isDisplayedSafe ( el )
1730+ if ( displayed ) selected . push ( true )
17211731 }
1722-
1732+
17231733 try {
17241734 return truth ( `elements of ${ new Locator ( parsedLocator ) } ` , 'to be seen' ) . assert ( selected )
17251735 } catch ( e ) {
@@ -1771,7 +1781,30 @@ class Appium extends Webdriver {
17711781 */
17721782 async waitForVisible ( locator , sec = null ) {
17731783 if ( this . isWeb ) return super . waitForVisible ( locator , sec )
1774- return super . waitForVisible ( parseLocator . call ( this , locator ) , sec )
1784+
1785+ // For mobile native apps, use safe isDisplayed wrapper
1786+ const parsedLocator = parseLocator . call ( this , locator )
1787+ const aSec = sec || this . options . waitForTimeoutInSeconds
1788+ const Locator = require ( '../locator' )
1789+
1790+ return this . browser . waitUntil (
1791+ async ( ) => {
1792+ const res = await this . _res ( parsedLocator )
1793+ if ( ! res || res . length === 0 ) return false
1794+
1795+ const selected = [ ]
1796+ for ( const el of res ) {
1797+ const displayed = await this . _isDisplayedSafe ( el )
1798+ if ( displayed ) selected . push ( true )
1799+ }
1800+
1801+ return selected . length > 0
1802+ } ,
1803+ {
1804+ timeout : aSec * 1000 ,
1805+ timeoutMsg : `element (${ new Locator ( parsedLocator ) } ) still not visible after ${ aSec } sec` ,
1806+ } ,
1807+ )
17751808 }
17761809
17771810 /**
@@ -1780,7 +1813,27 @@ class Appium extends Webdriver {
17801813 */
17811814 async waitForInvisible ( locator , sec = null ) {
17821815 if ( this . isWeb ) return super . waitForInvisible ( locator , sec )
1783- return super . waitForInvisible ( parseLocator . call ( this , locator ) , sec )
1816+
1817+ // For mobile native apps, use safe isDisplayed wrapper
1818+ const parsedLocator = parseLocator . call ( this , locator )
1819+ const aSec = sec || this . options . waitForTimeoutInSeconds
1820+ const Locator = require ( '../locator' )
1821+
1822+ return this . browser . waitUntil (
1823+ async ( ) => {
1824+ const res = await this . _res ( parsedLocator )
1825+ if ( ! res || res . length === 0 ) return true
1826+
1827+ const selected = [ ]
1828+ for ( const el of res ) {
1829+ const displayed = await this . _isDisplayedSafe ( el )
1830+ if ( displayed ) selected . push ( true )
1831+ }
1832+
1833+ return selected . length === 0
1834+ } ,
1835+ { timeout : aSec * 1000 , timeoutMsg : `element (${ new Locator ( parsedLocator ) } ) still visible after ${ aSec } sec` } ,
1836+ )
17841837 }
17851838
17861839 /**
0 commit comments