From fc6bb5bf02f0435dae392bffa1370106a6ac35e1 Mon Sep 17 00:00:00 2001
From: Kazuaki Matsuo <fly.49.89.over@gmail.com>
Date: Mon, 14 Dec 2020 10:45:23 -0800
Subject: [PATCH 1/9] bump selenium

---
 Pipfile  | 2 +-
 setup.py | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/Pipfile b/Pipfile
index 17f15897..2d01ff46 100644
--- a/Pipfile
+++ b/Pipfile
@@ -7,7 +7,7 @@ verify_ssl = true
 pre-commit = "~=2.6"
 
 [packages]
-selenium = "~=3.141"
+selenium = "==4.0.0.a7"
 
 autopep8 = "~=1.5"
 
diff --git a/setup.py b/setup.py
index d3b6f507..f18eea32 100644
--- a/setup.py
+++ b/setup.py
@@ -53,5 +53,5 @@
         'Topic :: Software Development :: Quality Assurance',
         'Topic :: Software Development :: Testing'
     ],
-    install_requires=['selenium >= 3.14.1, < 4']
+    install_requires=['selenium == 4.0.0.a7']
 )

From 83b11a35adf8929bb49dad9dbf625d314b8ab8ce Mon Sep 17 00:00:00 2001
From: Kazuaki Matsuo <fly.49.89.over@gmail.com>
Date: Mon, 14 Dec 2020 17:43:08 -0800
Subject: [PATCH 2/9] tweak format

---
 Pipfile | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Pipfile b/Pipfile
index 2d01ff46..70cf895d 100644
--- a/Pipfile
+++ b/Pipfile
@@ -7,7 +7,7 @@ verify_ssl = true
 pre-commit = "~=2.6"
 
 [packages]
-selenium = "==4.0.0.a7"
+selenium = "4.0.0.a7"
 
 autopep8 = "~=1.5"
 

From e66dd5498bfa556438a919445467129ebd438c60 Mon Sep 17 00:00:00 2001
From: Kazuaki Matsuo <fly.49.89.over@gmail.com>
Date: Mon, 14 Dec 2020 17:53:06 -0800
Subject: [PATCH 3/9] set python 3.8

---
 ci-jobs/functional/run_appium.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/ci-jobs/functional/run_appium.yml b/ci-jobs/functional/run_appium.yml
index c9b21b0c..11d4fc0c 100644
--- a/ci-jobs/functional/run_appium.yml
+++ b/ci-jobs/functional/run_appium.yml
@@ -10,7 +10,7 @@ steps:
   displayName: Install opencv4nodejs
 - task: UsePythonVersion@0
   inputs:
-    versionSpec: '3.x'
+    versionSpec: '3.8'
 - script: brew install ffmpeg
   displayName: Resolve dependencies (Appium server)
 - script: python setup.py install

From 548a750d9637fcf98af5cc62a5c2ae7cd461002c Mon Sep 17 00:00:00 2001
From: Kazuaki Matsuo <fly.49.89.over@gmail.com>
Date: Mon, 14 Dec 2020 18:32:32 -0800
Subject: [PATCH 4/9] install 3.14 first since trio-websocket failed

---
 Pipfile | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Pipfile b/Pipfile
index 70cf895d..5af79be7 100644
--- a/Pipfile
+++ b/Pipfile
@@ -7,6 +7,7 @@ verify_ssl = true
 pre-commit = "~=2.6"
 
 [packages]
+selenium = "~=3.141"
 selenium = "4.0.0.a7"
 
 autopep8 = "~=1.5"

From 9bbe7b8a534b398edf2b93b1c0a50dd0e0c47d66 Mon Sep 17 00:00:00 2001
From: Kazuaki Matsuo <fly.49.89.over@gmail.com>
Date: Tue, 15 Dec 2020 15:05:57 -0800
Subject: [PATCH 5/9] refer to selenium 4.0.0a7

---
 appium/webdriver/extensions/action_helpers.py |  2 +-
 appium/webdriver/webdriver.py                 | 29 +++++++------------
 2 files changed, 11 insertions(+), 20 deletions(-)

diff --git a/appium/webdriver/extensions/action_helpers.py b/appium/webdriver/extensions/action_helpers.py
index 37628440..08fed51e 100644
--- a/appium/webdriver/extensions/action_helpers.py
+++ b/appium/webdriver/extensions/action_helpers.py
@@ -46,7 +46,7 @@ def scroll(self: T, origin_el: WebElement, destination_el: WebElement, duration:
         """
 
         # XCUITest x W3C spec has no duration by default in server side
-        if self.w3c and duration is None:
+        if duration is None:
             duration = 600
 
         action = TouchAction(self)
diff --git a/appium/webdriver/webdriver.py b/appium/webdriver/webdriver.py
index 72d0dafe..52b087a0 100644
--- a/appium/webdriver/webdriver.py
+++ b/appium/webdriver/webdriver.py
@@ -182,17 +182,17 @@ def _update_command_executor(self, keep_alive: bool) -> None:
         direct_port = 'directConnectPort'
         direct_path = 'directConnectPath'
 
-        if (not {direct_protocol, direct_host, direct_port, direct_path}.issubset(set(self.capabilities))):
+        if (not {direct_protocol, direct_host, direct_port, direct_path}.issubset(set(self.caps))):
             message = 'Direct connect capabilities from server were:\n'
             for key in [direct_protocol, direct_host, direct_port, direct_path]:
-                message += '{}: \'{}\'\n'.format(key, self.capabilities.get(key, ''))
+                message += '{}: \'{}\'\n'.format(key, self.caps.get(key, ''))
             logger.warning(message)
             return
 
-        protocol = self.capabilities[direct_protocol]
-        hostname = self.capabilities[direct_host]
-        port = self.capabilities[direct_port]
-        path = self.capabilities[direct_path]
+        protocol = self.caps[direct_protocol]
+        hostname = self.caps[direct_host]
+        port = self.caps[direct_port]
+        path = self.caps[direct_path]
         executor = f'{protocol}://{hostname}:{port}{path}'
 
         logger.info('Updated request endpoint to %s', executor)
@@ -226,12 +226,12 @@ def start_session(self, capabilities: Dict, browser_profile: Optional[str] = Non
         if 'sessionId' not in response:
             response = response['value']
         self.session_id = response['sessionId']
-        self.capabilities = response.get('value')
+        self.caps = response.get('value')
 
         # if capabilities is none we are probably speaking to
         # a W3C endpoint
-        if self.capabilities is None:
-            self.capabilities = response.get('capabilities')
+        if self.caps is None:
+            self.caps = response.get('capabilities')
 
         # Double check to see if we have a W3C Compliant browser
         self.w3c = response.get('status') is None
@@ -240,13 +240,6 @@ def start_session(self, capabilities: Dict, browser_profile: Optional[str] = Non
     def _merge_capabilities(self, capabilities: Dict) -> Dict[str, Any]:
         """Manage capabilities whether W3C format or MJSONWP format
         """
-        if _FORCE_MJSONWP in capabilities:
-            force_mjsonwp = capabilities[_FORCE_MJSONWP]
-            del capabilities[_FORCE_MJSONWP]
-
-            if force_mjsonwp != False:
-                return {'desiredCapabilities': capabilities}
-
         w3c_caps = _make_w3c_caps(capabilities)
         return {'capabilities': w3c_caps, 'desiredCapabilities': capabilities}
 
@@ -263,7 +256,6 @@ def find_element(self, by: str = By.ID, value: Union[str, Dict] = None) -> Mobil
 
         """
         # TODO: If we need, we should enable below converter for Web context
-        # if self.w3c:
         #     if by == By.ID:
         #         by = By.CSS_SELECTOR
         #         value = '[id="%s"]' % value
@@ -293,7 +285,6 @@ def find_elements(self, by: str = By.ID, value: Union[str, Dict]
             :obj:`list` of :obj:`appium.webdriver.webelement.WebElement`: The found elements
         """
         # TODO: If we need, we should enable below converter for Web context
-        # if self.w3c:
         #     if by == By.ID:
         #         by = By.CSS_SELECTOR
         #         value = '[id="%s"]' % value
@@ -313,7 +304,7 @@ def find_elements(self, by: str = By.ID, value: Union[str, Dict]
             'using': by,
             'value': value})['value'] or []
 
-    def create_web_element(self, element_id: Union[int, str], w3c: bool = False) -> MobileWebElement:
+    def create_web_element(self, element_id: Union[int, str], w3c: bool = True) -> MobileWebElement:
         """Creates a web element with the specified element_id.
 
         Overrides method in Selenium WebDriver in order to always give them

From 64ab4a76d364e53aa30414c160edf3101b897355 Mon Sep 17 00:00:00 2001
From: Kazuaki Matsuo <fly.49.89.over@gmail.com>
Date: Tue, 15 Dec 2020 17:18:01 -0800
Subject: [PATCH 6/9] tweak

---
 appium/webdriver/webdriver.py       |  1 +
 test/functional/ios/safari_tests.py | 11 ++++++++++-
 2 files changed, 11 insertions(+), 1 deletion(-)

diff --git a/appium/webdriver/webdriver.py b/appium/webdriver/webdriver.py
index 52b087a0..531c93da 100644
--- a/appium/webdriver/webdriver.py
+++ b/appium/webdriver/webdriver.py
@@ -200,6 +200,7 @@ def _update_command_executor(self, keep_alive: bool) -> None:
         self.command_executor = RemoteConnection(executor, keep_alive=keep_alive)
         self._addCommands()
 
+    # https://github.com/SeleniumHQ/selenium/blob/06fdf2966df6bca47c0ae45e8201cd30db9b9a49/py/selenium/webdriver/remote/webdriver.py#L277
     def start_session(self, capabilities: Dict, browser_profile: Optional[str] = None) -> None:
         """Creates a new session with the desired capabilities.
 
diff --git a/test/functional/ios/safari_tests.py b/test/functional/ios/safari_tests.py
index 23d907fc..8dadb20b 100644
--- a/test/functional/ios/safari_tests.py
+++ b/test/functional/ios/safari_tests.py
@@ -12,6 +12,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import time
+
 from appium import webdriver
 
 from .helper.desired_capabilities import get_desired_capabilities
@@ -38,4 +40,11 @@ def test_context(self) -> None:
 
     def test_get(self) -> None:
         self.driver.get("http://google.com")
-        assert 'Google' == self.driver.title
+        for _ in range(5):
+            time.sleep(0.5)
+            try:
+                assert 'Google' == self.driver.title
+                return
+            except Exception as e:
+
+        assert False, 'The title was wrong'

From 5f50867256472354244563545fca9851a5cd6626 Mon Sep 17 00:00:00 2001
From: Kazuaki Matsuo <fly.49.89.over@gmail.com>
Date: Tue, 15 Dec 2020 19:24:25 -0800
Subject: [PATCH 7/9] allow self cert

---
 appium/webdriver/appium_connection.py | 17 +++++++++++++++++
 appium/webdriver/webdriver.py         |  4 ++++
 2 files changed, 21 insertions(+)

diff --git a/appium/webdriver/appium_connection.py b/appium/webdriver/appium_connection.py
index f643a168..90d8abab 100644
--- a/appium/webdriver/appium_connection.py
+++ b/appium/webdriver/appium_connection.py
@@ -13,6 +13,7 @@
 # limitations under the License.
 
 import uuid
+import urllib3
 from typing import TYPE_CHECKING, Any, Dict
 
 from selenium.webdriver.remote.remote_connection import RemoteConnection
@@ -25,6 +26,22 @@
 
 class AppiumConnection(RemoteConnection):
 
+    def _get_connection_manager(self):
+        # https://github.com/SeleniumHQ/selenium/blob/0e0194b0e52a34e7df4b841f1ed74506beea5c3e/py/selenium/webdriver/remote/remote_connection.py#L134
+        pool_manager_init_args = {
+            'timeout': self._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['ca_certs'] = None
+
+        return urllib3.PoolManager(**pool_manager_init_args) if self._proxy_url is None else \
+            urllib3.ProxyManager(self._proxy_url, **pool_manager_init_args)
+
     @classmethod
     def get_remote_connection_headers(cls, parsed_url: 'ParseResult', keep_alive: bool = True) -> Dict[str, Any]:
         """Override get_remote_connection_headers in RemoteConnection"""
diff --git a/appium/webdriver/webdriver.py b/appium/webdriver/webdriver.py
index 531c93da..ca15d68a 100644
--- a/appium/webdriver/webdriver.py
+++ b/appium/webdriver/webdriver.py
@@ -155,6 +155,10 @@ def __init__(self, command_executor: str = 'http://127.0.0.1:4444/wd/hub',
             proxy
         )
 
+        # TODO: make
+        import urllib3
+        urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
+
         if hasattr(self, 'command_executor'):
             self._addCommands()
 

From 7f7898a7e7532af2c98cfaaab79458d9e15d7c3d Mon Sep 17 00:00:00 2001
From: Kazuaki Matsuo <fly.49.89.over@gmail.com>
Date: Tue, 15 Dec 2020 21:13:58 -0800
Subject: [PATCH 8/9] make ssl stuff as a flag

---
 appium/webdriver/appium_connection.py            |  1 -
 appium/webdriver/webdriver.py                    | 16 +++++++++++-----
 .../ios/helper/desired_capabilities.py           |  2 +-
 3 files changed, 12 insertions(+), 7 deletions(-)

diff --git a/appium/webdriver/appium_connection.py b/appium/webdriver/appium_connection.py
index 90d8abab..bfe6197b 100644
--- a/appium/webdriver/appium_connection.py
+++ b/appium/webdriver/appium_connection.py
@@ -37,7 +37,6 @@ def _get_connection_manager(self):
         else:
             # This line is necessary to disable certificate verification
             pool_manager_init_args['cert_reqs'] = 'CERT_NONE'
-            pool_manager_init_args['ca_certs'] = None
 
         return urllib3.PoolManager(**pool_manager_init_args) if self._proxy_url is None else \
             urllib3.ProxyManager(self._proxy_url, **pool_manager_init_args)
diff --git a/appium/webdriver/webdriver.py b/appium/webdriver/webdriver.py
index ca15d68a..c37a4766 100644
--- a/appium/webdriver/webdriver.py
+++ b/appium/webdriver/webdriver.py
@@ -146,7 +146,17 @@ class WebDriver(
 ):
 
     def __init__(self, command_executor: str = 'http://127.0.0.1:4444/wd/hub',
-                 desired_capabilities: Optional[Dict] = None, browser_profile: str = None, proxy: str = None, keep_alive: bool = True, direct_connection: bool = False):
+                 desired_capabilities: Optional[Dict] = None,
+                 browser_profile: str = None,
+                 proxy: str = None,
+                 keep_alive: bool = True,
+                 direct_connection: bool = True,
+                 strict_ssl: bool = True):
+
+        if strict_ssl is False:
+            import urllib3
+            AppiumConnection.set_certificate_bundle_path(None)
+            urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
 
         super().__init__(
             AppiumConnection(command_executor, keep_alive=keep_alive),
@@ -155,10 +165,6 @@ def __init__(self, command_executor: str = 'http://127.0.0.1:4444/wd/hub',
             proxy
         )
 
-        # TODO: make
-        import urllib3
-        urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
-
         if hasattr(self, 'command_executor'):
             self._addCommands()
 
diff --git a/test/functional/ios/helper/desired_capabilities.py b/test/functional/ios/helper/desired_capabilities.py
index 032df84a..ed301cd3 100644
--- a/test/functional/ios/helper/desired_capabilities.py
+++ b/test/functional/ios/helper/desired_capabilities.py
@@ -29,7 +29,7 @@ def get_desired_capabilities(app: Optional[str] = None) -> Dict[str, Any]:
     desired_caps: Dict[str, Any] = {
         'deviceName': iphone_device_name(),
         'platformName': 'iOS',
-        'platformVersion': '13.6',
+        'platformVersion': '12.4',
         'automationName': 'XCUITest',
         'allowTouchIdEnroll': True,
         'wdaLocalPort': wda_port(),

From 54bb895f14337124343fa0ef12390634f5b8b1eb Mon Sep 17 00:00:00 2001
From: Kazuaki Matsuo <fly.49.89.over@gmail.com>
Date: Tue, 15 Dec 2020 22:01:41 -0800
Subject: [PATCH 9/9] update readme

---
 README.md                                     | 19 +++++++++++++++++++
 appium/webdriver/extensions/action_helpers.py |  2 +-
 ci-jobs/functional/run_appium.yml             |  2 +-
 .../ios/helper/desired_capabilities.py        |  2 +-
 4 files changed, 22 insertions(+), 3 deletions(-)

diff --git a/README.md b/README.md
index 5a196a9c..ad19f26c 100644
--- a/README.md
+++ b/README.md
@@ -127,6 +127,25 @@ desired_caps = dict(
 self.driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps, direct_connection=True)
 ```
 
+## Relax SSL validation
+
+`strict_ssl` option allows you to send commands to an invalid certificate host like self-certificated SSL.
+
+```python
+import unittest
+from appium import webdriver
+
+desired_caps = dict(
+    platformName='iOS',
+    platformVersion='13.4',
+    automationName='xcuitest',
+    deviceName='iPhone Simulator',
+    app=PATH('../../apps/UICatalog.app.zip')
+)
+
+self.driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps, strict_ssl=False)
+```
+
 ## Documentation
 
 https://appium.github.io/python-client-sphinx/ is detailed documentation
diff --git a/appium/webdriver/extensions/action_helpers.py b/appium/webdriver/extensions/action_helpers.py
index 08fed51e..37628440 100644
--- a/appium/webdriver/extensions/action_helpers.py
+++ b/appium/webdriver/extensions/action_helpers.py
@@ -46,7 +46,7 @@ def scroll(self: T, origin_el: WebElement, destination_el: WebElement, duration:
         """
 
         # XCUITest x W3C spec has no duration by default in server side
-        if duration is None:
+        if self.w3c and duration is None:
             duration = 600
 
         action = TouchAction(self)
diff --git a/ci-jobs/functional/run_appium.yml b/ci-jobs/functional/run_appium.yml
index 11d4fc0c..c9b21b0c 100644
--- a/ci-jobs/functional/run_appium.yml
+++ b/ci-jobs/functional/run_appium.yml
@@ -10,7 +10,7 @@ steps:
   displayName: Install opencv4nodejs
 - task: UsePythonVersion@0
   inputs:
-    versionSpec: '3.8'
+    versionSpec: '3.x'
 - script: brew install ffmpeg
   displayName: Resolve dependencies (Appium server)
 - script: python setup.py install
diff --git a/test/functional/ios/helper/desired_capabilities.py b/test/functional/ios/helper/desired_capabilities.py
index ed301cd3..032df84a 100644
--- a/test/functional/ios/helper/desired_capabilities.py
+++ b/test/functional/ios/helper/desired_capabilities.py
@@ -29,7 +29,7 @@ def get_desired_capabilities(app: Optional[str] = None) -> Dict[str, Any]:
     desired_caps: Dict[str, Any] = {
         'deviceName': iphone_device_name(),
         'platformName': 'iOS',
-        'platformVersion': '12.4',
+        'platformVersion': '13.6',
         'automationName': 'XCUITest',
         'allowTouchIdEnroll': True,
         'wdaLocalPort': wda_port(),
