From 3cf12d5aee5355d280099f8cb6a320808e7bfa7c Mon Sep 17 00:00:00 2001 From: Kazuaki Matsuo Date: Fri, 11 Oct 2024 22:07:19 -0700 Subject: [PATCH 1/9] use selneium --- appium/webdriver/webdriver.py | 90 +++++------- appium/webdriver/webelement.py | 130 +++++++++--------- setup.py | 2 +- .../webdriver/search_context/android_test.py | 56 ++++++++ 4 files changed, 153 insertions(+), 125 deletions(-) diff --git a/appium/webdriver/webdriver.py b/appium/webdriver/webdriver.py index d292c0ac3..2ca03fbe6 100644 --- a/appium/webdriver/webdriver.py +++ b/appium/webdriver/webdriver.py @@ -62,6 +62,11 @@ from .webelement import WebElement as MobileWebElement +class AppiumLocatorConverter: + def convert(self, by, value): + return (by, value) + + class ExtensionBase: """ Used to define an extension command as driver's methods. @@ -226,6 +231,8 @@ def __init__( super().__init__( command_executor=command_executor, options=options, + locator_converter=AppiumLocatorConverter(), + web_element_cls=MobileWebElement, ) if hasattr(self, 'command_executor'): @@ -346,71 +353,38 @@ def get_status(self) -> Dict: """ return self.execute(Command.GET_STATUS)['value'] - def find_element(self, by: str = AppiumBy.ID, value: Union[str, Dict, None] = None) -> MobileWebElement: - """ - Find an element given a AppiumBy strategy and locator + # def find_element(self, by: str = AppiumBy.ID, value: Union[str, Dict, None] = None) -> MobileWebElement: + # """ + # Find an element given a AppiumBy strategy and locator - Args: - by: The strategy - value: The locator + # Args: + # by: The strategy + # value: The locator - Usage: - driver.find_element(by=AppiumBy.ACCESSIBILITY_ID, value='accessibility_id') + # Usage: + # driver.find_element(by=AppiumBy.ACCESSIBILITY_ID, value='accessibility_id') - Returns: - `appium.webdriver.webelement.WebElement`: The found element + # Returns: + # `appium.webdriver.webelement.WebElement`: The found element - """ - # We prefer to patch locators in the client code - # Checking current context every time a locator is accessed could significantly slow down tests - # Check https://github.com/appium/python-client/pull/724 before submitting any issue - # if by == By.ID: - # by = By.CSS_SELECTOR - # value = '[id="%s"]' % value - # elif by == By.TAG_NAME: - # by = By.CSS_SELECTOR - # elif by == By.CLASS_NAME: - # by = By.CSS_SELECTOR - # value = ".%s" % value - # elif by == By.NAME: - # by = By.CSS_SELECTOR - # value = '[name="%s"]' % value - - return self.execute(RemoteCommand.FIND_ELEMENT, {'using': by, 'value': value})['value'] - - def find_elements(self, by: str = AppiumBy.ID, value: Union[str, Dict, None] = None) -> Union[List[MobileWebElement], List]: - """ - Find elements given a AppiumBy strategy and locator + # """ + # return self.execute(RemoteCommand.FIND_ELEMENT, {'using': by, 'value': value})['value'] - Args: - by: The strategy - value: The locator + # def find_elements(self, by: str = AppiumBy.ID, value: Union[str, Dict, None] = None) -> Union[List[MobileWebElement], List]: + # """ + # Find elements given a AppiumBy strategy and locator - Usage: - driver.find_elements(by=AppiumBy.ACCESSIBILITY_ID, value='accessibility_id') + # Args: + # by: The strategy + # value: The locator - Returns: - :obj:`list` of :obj:`appium.webdriver.webelement.WebElement`: The found elements - """ - # We prefer to patch locators in the client code - # Checking current context every time a locator is accessed could significantly slow down tests - # Check https://github.com/appium/python-client/pull/724 before submitting any issue - # if by == By.ID: - # by = By.CSS_SELECTOR - # value = '[id="%s"]' % value - # elif by == By.TAG_NAME: - # by = By.CSS_SELECTOR - # elif by == By.CLASS_NAME: - # by = By.CSS_SELECTOR - # value = ".%s" % value - # elif by == By.NAME: - # by = By.CSS_SELECTOR - # value = '[name="%s"]' % value - - # Return empty list if driver returns null - # See https://github.com/SeleniumHQ/selenium/issues/4555 - - return self.execute(RemoteCommand.FIND_ELEMENTS, {'using': by, 'value': value})['value'] or [] + # Usage: + # driver.find_elements(by=AppiumBy.ACCESSIBILITY_ID, value='accessibility_id') + + # Returns: + # :obj:`list` of :obj:`appium.webdriver.webelement.WebElement`: The found elements + # """ + # return self.execute(RemoteCommand.FIND_ELEMENTS, {'using': by, 'value': value})['value'] or [] def create_web_element(self, element_id: Union[int, str]) -> MobileWebElement: """Creates a web element with the specified element_id. diff --git a/appium/webdriver/webelement.py b/appium/webdriver/webelement.py index 4fb98e5d4..daec521ca 100644 --- a/appium/webdriver/webelement.py +++ b/appium/webdriver/webelement.py @@ -12,15 +12,13 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Callable, Dict, List, Optional, Union +from typing import Callable, Dict, Optional, Union from selenium.webdriver.common.utils import keys_to_typing from selenium.webdriver.remote.command import Command as RemoteCommand from selenium.webdriver.remote.webelement import WebElement as SeleniumWebElement from typing_extensions import Self -from appium.webdriver.common.appiumby import AppiumBy - from .mobilecommand import MobileCommand as Command @@ -80,69 +78,69 @@ def is_displayed(self) -> bool: """ return self._execute(Command.IS_ELEMENT_DISPLAYED)['value'] - def find_element(self, by: str = AppiumBy.ID, value: Union[str, Dict, None] = None) -> 'WebElement': - """Find an element given a AppiumBy strategy and locator - - Override for Appium - - Prefer the find_element_by_* methods when possible. - - Args: - by: The strategy - value: The locator - - Usage: - element = element.find_element(AppiumBy.ID, 'foo') - - Returns: - `appium.webdriver.webelement.WebElement` - """ - # We prefer to patch locators in the client code - # Checking current context every time a locator is accessed could significantly slow down tests - # Check https://github.com/appium/python-client/pull/724 before submitting any issue - # if by == By.ID: - # by = By.CSS_SELECTOR - # value = '[id="%s"]' % value - # elif by == By.TAG_NAME: - # by = By.CSS_SELECTOR - # elif by == By.CLASS_NAME: - # by = By.CSS_SELECTOR - # value = ".%s" % value - # elif by == By.NAME: - # by = By.CSS_SELECTOR - # value = '[name="%s"]' % value - - return self._execute(RemoteCommand.FIND_CHILD_ELEMENT, {'using': by, 'value': value})['value'] - - def find_elements(self, by: str = AppiumBy.ID, value: Union[str, Dict, None] = None) -> List['WebElement']: - """Find elements given a AppiumBy strategy and locator - - Args: - by: The strategy - value: The locator - - Usage: - element = element.find_elements(AppiumBy.CLASS_NAME, 'foo') - - Returns: - :obj:`list` of :obj:`appium.webdriver.webelement.WebElement` - """ - # We prefer to patch locators in the client code - # Checking current context every time a locator is accessed could significantly slow down tests - # Check https://github.com/appium/python-client/pull/724 before submitting any issue - # if by == By.ID: - # by = By.CSS_SELECTOR - # value = '[id="%s"]' % value - # elif by == By.TAG_NAME: - # by = By.CSS_SELECTOR - # elif by == By.CLASS_NAME: - # by = By.CSS_SELECTOR - # value = ".%s" % value - # elif by == By.NAME: - # by = By.CSS_SELECTOR - # value = '[name="%s"]' % value - - return self._execute(RemoteCommand.FIND_CHILD_ELEMENTS, {'using': by, 'value': value})['value'] + # def find_element(self, by: str = AppiumBy.ID, value: Union[str, Dict, None] = None) -> 'WebElement': + # """Find an element given a AppiumBy strategy and locator + + # Override for Appium + + # Prefer the find_element_by_* methods when possible. + + # Args: + # by: The strategy + # value: The locator + + # Usage: + # element = element.find_element(AppiumBy.ID, 'foo') + + # Returns: + # `appium.webdriver.webelement.WebElement` + # """ + # # We prefer to patch locators in the client code + # # Checking current context every time a locator is accessed could significantly slow down tests + # # Check https://github.com/appium/python-client/pull/724 before submitting any issue + # # if by == By.ID: + # # by = By.CSS_SELECTOR + # # value = '[id="%s"]' % value + # # elif by == By.TAG_NAME: + # # by = By.CSS_SELECTOR + # # elif by == By.CLASS_NAME: + # # by = By.CSS_SELECTOR + # # value = ".%s" % value + # # elif by == By.NAME: + # # by = By.CSS_SELECTOR + # # value = '[name="%s"]' % value + + # return self._execute(RemoteCommand.FIND_CHILD_ELEMENT, {'using': by, 'value': value})['value'] + + # def find_elements(self, by: str = AppiumBy.ID, value: Union[str, Dict, None] = None) -> List['WebElement']: + # """Find elements given a AppiumBy strategy and locator + + # Args: + # by: The strategy + # value: The locator + + # Usage: + # element = element.find_elements(AppiumBy.CLASS_NAME, 'foo') + + # Returns: + # :obj:`list` of :obj:`appium.webdriver.webelement.WebElement` + # """ + # # We prefer to patch locators in the client code + # # Checking current context every time a locator is accessed could significantly slow down tests + # # Check https://github.com/appium/python-client/pull/724 before submitting any issue + # # if by == By.ID: + # # by = By.CSS_SELECTOR + # # value = '[id="%s"]' % value + # # elif by == By.TAG_NAME: + # # by = By.CSS_SELECTOR + # # elif by == By.CLASS_NAME: + # # by = By.CSS_SELECTOR + # # value = ".%s" % value + # # elif by == By.NAME: + # # by = By.CSS_SELECTOR + # # value = '[name="%s"]' % value + + # return self._execute(RemoteCommand.FIND_CHILD_ELEMENTS, {'using': by, 'value': value})['value'] def clear(self) -> Self: """Clears text. diff --git a/setup.py b/setup.py index 628d08049..45132d3ff 100644 --- a/setup.py +++ b/setup.py @@ -49,5 +49,5 @@ 'Topic :: Software Development :: Quality Assurance', 'Topic :: Software Development :: Testing', ], - install_requires=['selenium ~= 4.12'], + install_requires=['selenium @ git+https://github.com/navin772/selenium.git@appium-python'], ) diff --git a/test/unit/webdriver/search_context/android_test.py b/test/unit/webdriver/search_context/android_test.py index 8076ad7fb..879d78f53 100644 --- a/test/unit/webdriver/search_context/android_test.py +++ b/test/unit/webdriver/search_context/android_test.py @@ -22,6 +22,62 @@ class TestWebDriverAndroidSearchContext(object): + @httpretty.activate + def test_find_element_by_id(self): + driver = android_w3c_driver() + httpretty.register_uri( + httpretty.POST, + appium_command('/session/1234567890/element'), + body='{"value": {"element-6066-11e4-a52e-4f735466cecf": "element-id"}}', + ) + el = driver.find_element( + by=AppiumBy.ID, + value='id data', + ) + + d = get_httpretty_request_body(httpretty.last_request()) + assert d['using'] == 'id' + assert d['value'] == 'id data' + assert isinstance(el, MobileWebElement) + + @httpretty.activate + def test_find_elements_by_id(self): + driver = android_w3c_driver() + httpretty.register_uri( + httpretty.POST, + appium_command('/session/1234567890/elements'), + body='{"value": [{"element-6066-11e4-a52e-4f735466cecf": "element-id1"}, ' + '{"element-6066-11e4-a52e-4f735466cecf": "element-id2"}]}', + ) + els = driver.find_elements( + by=AppiumBy.ID, + value='id data', + ) + + d = get_httpretty_request_body(httpretty.last_request()) + assert d['using'] == 'id' + assert d['value'] == 'id data' + assert isinstance(els[0], MobileWebElement) + + @httpretty.activate + def test_find_child_element_by_id(self): + driver = android_w3c_driver() + element = MobileWebElement(driver, 'element_id') + httpretty.register_uri( + httpretty.POST, + appium_command('/session/1234567890/element/element_id/element'), + body='{"value": {"element-6066-11e4-a52e-4f735466cecf": "child-element-id"}}', + ) + el = element.find_element( + by=AppiumBy.ID, + value='id data', + ) + + d = get_httpretty_request_body(httpretty.last_request()) + assert d['using'] == 'id' + assert d['value'] == 'id data' + assert isinstance(el, MobileWebElement) + @httpretty.activate def test_find_element_by_android_data_matcher(self): driver = android_w3c_driver() From c78f9d8204eccf86408b2bf032072e29739067a6 Mon Sep 17 00:00:00 2001 From: Kazuaki Matsuo Date: Fri, 11 Oct 2024 22:17:49 -0700 Subject: [PATCH 2/9] refer to selenium implementation a bit more --- appium/webdriver/appium_connection.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/appium/webdriver/appium_connection.py b/appium/webdriver/appium_connection.py index e05274831..0615c77fa 100644 --- a/appium/webdriver/appium_connection.py +++ b/appium/webdriver/appium_connection.py @@ -30,6 +30,8 @@ class AppiumConnection(RemoteConnection): _proxy_url: Optional[str] + user_agent = f'{PREFIX_HEADER}{library_version()} ({RemoteConnection.user_agent})' + def __init__( self, remote_server_addr: str, @@ -72,7 +74,7 @@ def get_remote_connection_headers(cls, parsed_url: 'ParseResult', keep_alive: bo """Override get_remote_connection_headers in RemoteConnection""" headers = RemoteConnection.get_remote_connection_headers(parsed_url, keep_alive=keep_alive) # e.g. appium/0.49 (selenium/3.141.0 (python linux)) - headers['User-Agent'] = f'{PREFIX_HEADER}{library_version()} ({headers["User-Agent"]})' + # headers['User-Agent'] = f'{PREFIX_HEADER}{library_version()} ({headers["User-Agent"]})' if parsed_url.path.endswith('/session'): # https://github.com/appium/appium-base-driver/pull/400 headers['X-Idempotency-Key'] = str(uuid.uuid4()) From bdf6e8676a369479e8ed83f3c2246aedcb7c2c71 Mon Sep 17 00:00:00 2001 From: Kazuaki Matsuo Date: Fri, 11 Oct 2024 22:22:19 -0700 Subject: [PATCH 3/9] revert --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 45132d3ff..628d08049 100644 --- a/setup.py +++ b/setup.py @@ -49,5 +49,5 @@ 'Topic :: Software Development :: Quality Assurance', 'Topic :: Software Development :: Testing', ], - install_requires=['selenium @ git+https://github.com/navin772/selenium.git@appium-python'], + install_requires=['selenium ~= 4.12'], ) From a7ff7b74017b1cb94101cdced880c17bf71ce4d0 Mon Sep 17 00:00:00 2001 From: Kazuaki Matsuo Date: Tue, 15 Oct 2024 22:35:17 -0700 Subject: [PATCH 4/9] more --- appium/webdriver/appium_connection.py | 42 ++------------------------- test/unit/webdriver/webdriver_test.py | 2 +- 2 files changed, 3 insertions(+), 41 deletions(-) diff --git a/appium/webdriver/appium_connection.py b/appium/webdriver/appium_connection.py index 0615c77fa..73bf57829 100644 --- a/appium/webdriver/appium_connection.py +++ b/appium/webdriver/appium_connection.py @@ -13,9 +13,8 @@ # limitations under the License. import uuid -from typing import TYPE_CHECKING, Any, Dict, Optional, Union +from typing import TYPE_CHECKING, Any, Dict, Optional -import urllib3 from selenium.webdriver.remote.remote_connection import RemoteConnection from appium.common.helper import library_version @@ -30,44 +29,7 @@ class AppiumConnection(RemoteConnection): _proxy_url: Optional[str] - user_agent = f'{PREFIX_HEADER}{library_version()} ({RemoteConnection.user_agent})' - - def __init__( - self, - remote_server_addr: str, - keep_alive: bool = False, - ignore_proxy: Optional[bool] = False, - init_args_for_pool_manager: Union[Dict[str, Any], None] = None, - ): - # Need to call before super().__init__ in order to pass arguments for the pool manager in the super. - self._init_args_for_pool_manager = init_args_for_pool_manager or {} - - super().__init__(remote_server_addr, keep_alive=keep_alive, ignore_proxy=ignore_proxy) - - def _get_connection_manager(self) -> Union[urllib3.PoolManager, urllib3.ProxyManager]: - # https://github.com/SeleniumHQ/selenium/blob/0e0194b0e52a34e7df4b841f1ed74506beea5c3e/py/selenium/webdriver/remote/remote_connection.py#L134 - pool_manager_init_args = {'timeout': self.get_timeout()} - - if self._ca_certs: - pool_manager_init_args['cert_reqs'] = 'CERT_REQUIRED' - pool_manager_init_args['ca_certs'] = self._ca_certs - else: - # This line is necessary to disable certificate verification - pool_manager_init_args['cert_reqs'] = 'CERT_NONE' - - pool_manager_init_args.update(self._init_args_for_pool_manager) - - if self._proxy_url: - if self._proxy_url.lower().startswith('sock'): - from urllib3.contrib.socks import SOCKSProxyManager - - return SOCKSProxyManager(self._proxy_url, **pool_manager_init_args) - if self._identify_http_proxy_auth(): - self._proxy_url, self._basic_proxy_auth = self._separate_http_proxy_auth() - pool_manager_init_args['proxy_headers'] = urllib3.make_headers(proxy_basic_auth=self._basic_proxy_auth) - return urllib3.ProxyManager(self._proxy_url, **pool_manager_init_args) - - return urllib3.PoolManager(**pool_manager_init_args) + RemoteConnection.user_agent = f'{PREFIX_HEADER}{library_version()} ({RemoteConnection.user_agent})' @classmethod def get_remote_connection_headers(cls, parsed_url: 'ParseResult', keep_alive: bool = True) -> Dict[str, Any]: diff --git a/test/unit/webdriver/webdriver_test.py b/test/unit/webdriver/webdriver_test.py index f9a07a04d..1e6a333b8 100644 --- a/test/unit/webdriver/webdriver_test.py +++ b/test/unit/webdriver/webdriver_test.py @@ -54,7 +54,7 @@ def test_create_session(self): request = httpretty.HTTPretty.latest_requests[0] assert request.headers['content-type'] == 'application/json;charset=UTF-8' - assert f'appium/{appium_version.version} (selenium' in request.headers['user-agent'] + assert request.headers['user-agent'] == 'appium/4.1.0 (selenium/4.25.0 (python mac)' request_json = json.loads(httpretty.HTTPretty.latest_requests[0].body.decode('utf-8')) assert request_json.get('capabilities') is not None From e44072797f8914686297bec4ee65bdfd38b4932b Mon Sep 17 00:00:00 2001 From: Kazuaki Matsuo Date: Tue, 15 Oct 2024 22:36:29 -0700 Subject: [PATCH 5/9] more --- appium/webdriver/webdriver.py | 33 ------------------ appium/webdriver/webelement.py | 64 ---------------------------------- 2 files changed, 97 deletions(-) diff --git a/appium/webdriver/webdriver.py b/appium/webdriver/webdriver.py index 2ca03fbe6..07bc274ae 100644 --- a/appium/webdriver/webdriver.py +++ b/appium/webdriver/webdriver.py @@ -353,39 +353,6 @@ def get_status(self) -> Dict: """ return self.execute(Command.GET_STATUS)['value'] - # def find_element(self, by: str = AppiumBy.ID, value: Union[str, Dict, None] = None) -> MobileWebElement: - # """ - # Find an element given a AppiumBy strategy and locator - - # Args: - # by: The strategy - # value: The locator - - # Usage: - # driver.find_element(by=AppiumBy.ACCESSIBILITY_ID, value='accessibility_id') - - # Returns: - # `appium.webdriver.webelement.WebElement`: The found element - - # """ - # return self.execute(RemoteCommand.FIND_ELEMENT, {'using': by, 'value': value})['value'] - - # def find_elements(self, by: str = AppiumBy.ID, value: Union[str, Dict, None] = None) -> Union[List[MobileWebElement], List]: - # """ - # Find elements given a AppiumBy strategy and locator - - # Args: - # by: The strategy - # value: The locator - - # Usage: - # driver.find_elements(by=AppiumBy.ACCESSIBILITY_ID, value='accessibility_id') - - # Returns: - # :obj:`list` of :obj:`appium.webdriver.webelement.WebElement`: The found elements - # """ - # return self.execute(RemoteCommand.FIND_ELEMENTS, {'using': by, 'value': value})['value'] or [] - def create_web_element(self, element_id: Union[int, str]) -> MobileWebElement: """Creates a web element with the specified element_id. diff --git a/appium/webdriver/webelement.py b/appium/webdriver/webelement.py index daec521ca..14a9c9db5 100644 --- a/appium/webdriver/webelement.py +++ b/appium/webdriver/webelement.py @@ -78,70 +78,6 @@ def is_displayed(self) -> bool: """ return self._execute(Command.IS_ELEMENT_DISPLAYED)['value'] - # def find_element(self, by: str = AppiumBy.ID, value: Union[str, Dict, None] = None) -> 'WebElement': - # """Find an element given a AppiumBy strategy and locator - - # Override for Appium - - # Prefer the find_element_by_* methods when possible. - - # Args: - # by: The strategy - # value: The locator - - # Usage: - # element = element.find_element(AppiumBy.ID, 'foo') - - # Returns: - # `appium.webdriver.webelement.WebElement` - # """ - # # We prefer to patch locators in the client code - # # Checking current context every time a locator is accessed could significantly slow down tests - # # Check https://github.com/appium/python-client/pull/724 before submitting any issue - # # if by == By.ID: - # # by = By.CSS_SELECTOR - # # value = '[id="%s"]' % value - # # elif by == By.TAG_NAME: - # # by = By.CSS_SELECTOR - # # elif by == By.CLASS_NAME: - # # by = By.CSS_SELECTOR - # # value = ".%s" % value - # # elif by == By.NAME: - # # by = By.CSS_SELECTOR - # # value = '[name="%s"]' % value - - # return self._execute(RemoteCommand.FIND_CHILD_ELEMENT, {'using': by, 'value': value})['value'] - - # def find_elements(self, by: str = AppiumBy.ID, value: Union[str, Dict, None] = None) -> List['WebElement']: - # """Find elements given a AppiumBy strategy and locator - - # Args: - # by: The strategy - # value: The locator - - # Usage: - # element = element.find_elements(AppiumBy.CLASS_NAME, 'foo') - - # Returns: - # :obj:`list` of :obj:`appium.webdriver.webelement.WebElement` - # """ - # # We prefer to patch locators in the client code - # # Checking current context every time a locator is accessed could significantly slow down tests - # # Check https://github.com/appium/python-client/pull/724 before submitting any issue - # # if by == By.ID: - # # by = By.CSS_SELECTOR - # # value = '[id="%s"]' % value - # # elif by == By.TAG_NAME: - # # by = By.CSS_SELECTOR - # # elif by == By.CLASS_NAME: - # # by = By.CSS_SELECTOR - # # value = ".%s" % value - # # elif by == By.NAME: - # # by = By.CSS_SELECTOR - # # value = '[name="%s"]' % value - - # return self._execute(RemoteCommand.FIND_CHILD_ELEMENTS, {'using': by, 'value': value})['value'] - def clear(self) -> Self: """Clears text. From 090b6e8d85b2c8398b0a1baae11d8a5538ace39b Mon Sep 17 00:00:00 2001 From: Kazuaki Matsuo Date: Tue, 15 Oct 2024 22:41:57 -0700 Subject: [PATCH 6/9] more --- appium/webdriver/appium_connection.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/appium/webdriver/appium_connection.py b/appium/webdriver/appium_connection.py index 73bf57829..d4ad6601c 100644 --- a/appium/webdriver/appium_connection.py +++ b/appium/webdriver/appium_connection.py @@ -35,10 +35,10 @@ class AppiumConnection(RemoteConnection): def get_remote_connection_headers(cls, parsed_url: 'ParseResult', keep_alive: bool = True) -> Dict[str, Any]: """Override get_remote_connection_headers in RemoteConnection""" headers = RemoteConnection.get_remote_connection_headers(parsed_url, keep_alive=keep_alive) - # e.g. appium/0.49 (selenium/3.141.0 (python linux)) - # headers['User-Agent'] = f'{PREFIX_HEADER}{library_version()} ({headers["User-Agent"]})' if parsed_url.path.endswith('/session'): # https://github.com/appium/appium-base-driver/pull/400 - headers['X-Idempotency-Key'] = str(uuid.uuid4()) + RemoteConnection.extra_headers = {'X-Idempotency-Key': str(uuid.uuid4())} + else: + RemoteConnection.extra_headers = {} return headers From 4a1069009a73c2c2e3080ddfb81e2eaea0b99d58 Mon Sep 17 00:00:00 2001 From: Kazuaki Matsuo Date: Tue, 15 Oct 2024 22:46:55 -0700 Subject: [PATCH 7/9] modify --- test/unit/webdriver/webdriver_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/webdriver/webdriver_test.py b/test/unit/webdriver/webdriver_test.py index 1e6a333b8..10a292d4e 100644 --- a/test/unit/webdriver/webdriver_test.py +++ b/test/unit/webdriver/webdriver_test.py @@ -54,7 +54,7 @@ def test_create_session(self): request = httpretty.HTTPretty.latest_requests[0] assert request.headers['content-type'] == 'application/json;charset=UTF-8' - assert request.headers['user-agent'] == 'appium/4.1.0 (selenium/4.25.0 (python mac)' + assert request.headers['user-agent'] == 'appium/4.1.0 (selenium/4.25.0 (python mac))' request_json = json.loads(httpretty.HTTPretty.latest_requests[0].body.decode('utf-8')) assert request_json.get('capabilities') is not None From 05648159ee91e9ee66ae528cb4b623e3e387e77a Mon Sep 17 00:00:00 2001 From: Kazuaki Matsuo Date: Fri, 18 Oct 2024 01:04:23 -0700 Subject: [PATCH 8/9] use external command --- appium/webdriver/extensions/context.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/appium/webdriver/extensions/context.py b/appium/webdriver/extensions/context.py index 356604fe4..152af32fb 100644 --- a/appium/webdriver/extensions/context.py +++ b/appium/webdriver/extensions/context.py @@ -58,8 +58,6 @@ def context(self) -> str: return self.current_context def _add_commands(self) -> None: - # noinspection PyProtectedMember,PyUnresolvedReferences - commands = self.command_executor._commands - commands[Command.CONTEXTS] = ('GET', '/session/$sessionId/contexts') - commands[Command.GET_CURRENT_CONTEXT] = ('GET', '/session/$sessionId/context') - commands[Command.SWITCH_TO_CONTEXT] = ('POST', '/session/$sessionId/context') + self.command_executor.extra_commands[Command.CONTEXTS] = ('GET', '/session/$sessionId/contexts') + self.command_executor.extra_commands[Command.GET_CURRENT_CONTEXT] = ('GET', '/session/$sessionId/context') + self.command_executor.extra_commands[Command.SWITCH_TO_CONTEXT] = ('POST', '/session/$sessionId/context') From 34dd9cc785a06018d7a41af5f9d33ce7b7f952cf Mon Sep 17 00:00:00 2001 From: Kazuaki Matsuo Date: Fri, 18 Oct 2024 01:06:11 -0700 Subject: [PATCH 9/9] use instance method --- appium/webdriver/extensions/context.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/appium/webdriver/extensions/context.py b/appium/webdriver/extensions/context.py index 152af32fb..628432e11 100644 --- a/appium/webdriver/extensions/context.py +++ b/appium/webdriver/extensions/context.py @@ -58,6 +58,6 @@ def context(self) -> str: return self.current_context def _add_commands(self) -> None: - self.command_executor.extra_commands[Command.CONTEXTS] = ('GET', '/session/$sessionId/contexts') - self.command_executor.extra_commands[Command.GET_CURRENT_CONTEXT] = ('GET', '/session/$sessionId/context') - self.command_executor.extra_commands[Command.SWITCH_TO_CONTEXT] = ('POST', '/session/$sessionId/context') + self.command_executor.add_command(Command.CONTEXTS, 'GET', '/session/$sessionId/contexts') + self.command_executor.add_command(Command.GET_CURRENT_CONTEXT, 'GET', '/session/$sessionId/context') + self.command_executor.add_command(Command.SWITCH_TO_CONTEXT, 'POST', '/session/$sessionId/context')