Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 4 additions & 11 deletions appium/protocols/webdriver/can_find_elements.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@

from typing import TYPE_CHECKING, Protocol, runtime_checkable

if TYPE_CHECKING:
from selenium.webdriver.common.by import By
from selenium.webdriver.common.by import By

if TYPE_CHECKING:
from appium.webdriver.webelement import WebElement


Expand All @@ -29,13 +29,6 @@ class CanFindElements(Protocol):
- find_elements(by, value): Find multiple elements
"""

if TYPE_CHECKING:

def find_element(self, by: str = By.ID, value: str | dict | None = None) -> 'WebElement': ...

def find_elements(self, by: str = By.ID, value: str | dict | None = None) -> list['WebElement']: ...
else:

def find_element(self, by: str = 'id', value: str | dict | None = None) -> 'WebElement': ...
def find_element(self, by: str | By = By.ID, value: str | dict | None = None) -> 'WebElement': ...

def find_elements(self, by: str = 'id', value: str | dict | None = None) -> list['WebElement']: ...
def find_elements(self, by: str | By = By.ID, value: str | dict | None = None) -> list['WebElement']: ...
39 changes: 19 additions & 20 deletions appium/webdriver/common/appiumby.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,6 @@

from selenium.webdriver.common.by import By


class AppiumBy(By):
IOS_PREDICATE = '-ios predicate string'
IOS_CLASS_CHAIN = '-ios class chain'
ANDROID_UIAUTOMATOR = '-android uiautomator'
ANDROID_VIEWTAG = '-android viewtag'
ANDROID_DATA_MATCHER = '-android datamatcher'
ANDROID_VIEW_MATCHER = '-android viewmatcher'
ACCESSIBILITY_ID = 'accessibility id'
IMAGE = '-image'
CUSTOM = '-custom'

# For Flutter integration usage https://github.com/AppiumTestDistribution/appium-flutter-integration-driver/tree/main
FLUTTER_INTEGRATION_SEMANTICS_LABEL = '-flutter semantics label'
FLUTTER_INTEGRATION_TYPE = '-flutter type'
FLUTTER_INTEGRATION_KEY = '-flutter key'
FLUTTER_INTEGRATION_TEXT = '-flutter text'
FLUTTER_INTEGRATION_TEXT_CONTAINING = '-flutter text containing'


ByType = Literal[
'-ios predicate string',
'-ios class chain',
Expand All @@ -52,3 +32,22 @@ class AppiumBy(By):
'-flutter text',
'-flutter text containing',
]


class AppiumBy(By):
IOS_PREDICATE: ByType = '-ios predicate string'
IOS_CLASS_CHAIN: ByType = '-ios class chain'
ANDROID_UIAUTOMATOR: ByType = '-android uiautomator'
ANDROID_VIEWTAG: ByType = '-android viewtag'
ANDROID_DATA_MATCHER: ByType = '-android datamatcher'
ANDROID_VIEW_MATCHER: ByType = '-android viewmatcher'
ACCESSIBILITY_ID: ByType = 'accessibility id'
IMAGE: ByType = '-image'
CUSTOM: ByType = '-custom'

# For Flutter integration usage https://github.com/AppiumTestDistribution/appium-flutter-integration-driver/tree/main
FLUTTER_INTEGRATION_SEMANTICS_LABEL: ByType = '-flutter semantics label'
FLUTTER_INTEGRATION_TYPE: ByType = '-flutter type'
FLUTTER_INTEGRATION_KEY: ByType = '-flutter key'
FLUTTER_INTEGRATION_TEXT: ByType = '-flutter text'
FLUTTER_INTEGRATION_TEXT_CONTAINING: ByType = '-flutter text containing'
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@
# specific language governing permissions and limitations
# under the License.

from typing import cast

from selenium.webdriver.common.by import ByType as SeleniumByType

from appium.webdriver.common.appiumby import AppiumBy
Expand All @@ -30,23 +28,23 @@ def __init__(self, using: SeleniumByType | AppiumByType, value: str) -> None:

@staticmethod
def by_key(value: str) -> 'FlutterFinder':
return FlutterFinder(cast(AppiumByType, AppiumBy.FLUTTER_INTEGRATION_KEY), value)
return FlutterFinder(AppiumBy.FLUTTER_INTEGRATION_KEY, value)

@staticmethod
def by_text(value: str) -> 'FlutterFinder':
return FlutterFinder(cast(AppiumByType, AppiumBy.FLUTTER_INTEGRATION_TEXT), value)
return FlutterFinder(AppiumBy.FLUTTER_INTEGRATION_TEXT, value)

@staticmethod
def by_semantics_label(value: str) -> 'FlutterFinder':
return FlutterFinder(cast(AppiumByType, AppiumBy.FLUTTER_INTEGRATION_SEMANTICS_LABEL), value)
return FlutterFinder(AppiumBy.FLUTTER_INTEGRATION_SEMANTICS_LABEL, value)

@staticmethod
def by_type(value: str) -> 'FlutterFinder':
return FlutterFinder(cast(AppiumByType, AppiumBy.FLUTTER_INTEGRATION_TYPE), value)
return FlutterFinder(AppiumBy.FLUTTER_INTEGRATION_TYPE, value)

@staticmethod
def by_text_containing(value: str) -> 'FlutterFinder':
return FlutterFinder(cast(AppiumByType, AppiumBy.FLUTTER_INTEGRATION_TEXT_CONTAINING), value)
return FlutterFinder(AppiumBy.FLUTTER_INTEGRATION_TEXT_CONTAINING, value)

def to_dict(self) -> dict:
return {'using': self.using, 'value': self.value}
Expand Down
10 changes: 10 additions & 0 deletions appium/webdriver/switch_to.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,23 @@
# limitations under the License.


from typing import TYPE_CHECKING

from selenium.webdriver.remote.switch_to import SwitchTo
from typing_extensions import Self

from .mobilecommand import MobileCommand

if TYPE_CHECKING:
from .webelement import WebElement as MobileWebElement


class MobileSwitchTo(SwitchTo):
if TYPE_CHECKING:

@property
def active_element(self) -> MobileWebElement: ...

def context(self, context_name: str | None) -> Self:
"""Sets the context for the current session.
Passing `None` is equal to switching to native context.
Expand Down
11 changes: 6 additions & 5 deletions appium/webdriver/webdriver.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@

if TYPE_CHECKING:
from selenium.webdriver.common.by import By
from selenium.webdriver.support.relative_locator import RelativeBy


class ExtensionBase:
Expand Down Expand Up @@ -291,11 +292,11 @@ def __init__(

if TYPE_CHECKING:

def find_element(self, by: str = By.ID, value: str | dict | None = None) -> 'MobileWebElement': # type: ignore[override]
...
def find_element(self, by: str | By | RelativeBy = By.ID, value: str | dict | None = None) -> 'MobileWebElement': ...

def find_elements(self, by: str = By.ID, value: str | dict | None = None) -> list['MobileWebElement']: # type: ignore[override]
...
def find_elements( # type: ignore[override]
self, by: str | By | RelativeBy = By.ID, value: str | dict | None = None
) -> list['MobileWebElement']: ...

def delete_extensions(self) -> None:
"""Delete extensions added in the class with 'setattr'"""
Expand Down Expand Up @@ -336,7 +337,7 @@ def _update_command_executor(self, keep_alive: bool) -> None:
client_config = copy.copy(self.command_executor.client_config)
client_config.remote_server_addr = executor
client_config.keep_alive = keep_alive
if isinstance(self.command_executor, AppiumConnection): # type: ignore
if isinstance(self.command_executor, AppiumConnection):
self.command_executor = AppiumConnection(client_config=client_config)
else:
self.command_executor = RemoteConnection(client_config=client_config)
Expand Down
5 changes: 2 additions & 3 deletions appium/webdriver/webelement.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,9 @@ class WebElement(SeleniumWebElement):

if TYPE_CHECKING:

def find_element(self, by: str = By.ID, value: str | dict | None = None) -> Self: # type: ignore[override]
...
def find_element(self, by: str | By = By.ID, value: str | dict | None = None) -> Self: ...

def find_elements(self, by: str = By.ID, value: str | dict | None = None) -> list[Self]: # type: ignore[override]
def find_elements(self, by: str | By = By.ID, value: str | dict | None = None) -> list[Self]: # type: ignore[override]
...

def get_attribute(self, name: str) -> str | dict | None: # type: ignore[override]
Expand Down
15 changes: 15 additions & 0 deletions test/unit/webdriver/context_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import httpretty

from appium.webdriver.webelement import WebElement as MobileWebElement
from test.unit.helper.test_helper import android_w3c_driver, appium_command, get_httpretty_request_body


Expand Down Expand Up @@ -50,3 +51,17 @@ def test_switch_to_context_native_app(self):
driver.switch_to.context('NATIVE_APP')

assert get_httpretty_request_body(httpretty.last_request()) == {'name': 'NATIVE_APP'}

@httpretty.activate
def test_switch_to_active_element(self):
driver = android_w3c_driver()
httpretty.register_uri(
httpretty.GET,
appium_command('/session/1234567890/element/active'),
body='{"value": {"element-6066-11e4-a52e-4f735466cecf": "active-element-id"}}',
)

el = driver.switch_to.active_element

assert isinstance(el, MobileWebElement)
assert el.id == 'active-element-id'
Loading