second commit
This commit is contained in:
16
venv/Lib/site-packages/selenium/webdriver/remote/__init__.py
Normal file
16
venv/Lib/site-packages/selenium/webdriver/remote/__init__.py
Normal file
@@ -0,0 +1,16 @@
|
||||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
@@ -0,0 +1,23 @@
|
||||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
class BidiConnection:
|
||||
|
||||
def __init__(self, session, cdp, devtools_import) -> None:
|
||||
self.session = session
|
||||
self.cdp = cdp
|
||||
self.devtools = devtools_import
|
||||
123
venv/Lib/site-packages/selenium/webdriver/remote/command.py
Normal file
123
venv/Lib/site-packages/selenium/webdriver/remote/command.py
Normal file
@@ -0,0 +1,123 @@
|
||||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
class Command(object):
|
||||
"""
|
||||
Defines constants for the standard WebDriver commands.
|
||||
|
||||
While these constants have no meaning in and of themselves, they are
|
||||
used to marshal commands through a service that implements WebDriver's
|
||||
remote wire protocol:
|
||||
|
||||
https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol
|
||||
|
||||
"""
|
||||
|
||||
# Keep in sync with org.openqa.selenium.remote.DriverCommand
|
||||
|
||||
NEW_SESSION = "newSession"
|
||||
DELETE_SESSION = "deleteSession"
|
||||
NEW_WINDOW = "newWindow"
|
||||
CLOSE = "close"
|
||||
QUIT = "quit"
|
||||
GET = "get"
|
||||
GO_BACK = "goBack"
|
||||
GO_FORWARD = "goForward"
|
||||
REFRESH = "refresh"
|
||||
ADD_COOKIE = "addCookie"
|
||||
GET_COOKIE = "getCookie"
|
||||
GET_ALL_COOKIES = "getCookies"
|
||||
DELETE_COOKIE = "deleteCookie"
|
||||
DELETE_ALL_COOKIES = "deleteAllCookies"
|
||||
FIND_ELEMENT = "findElement"
|
||||
FIND_ELEMENTS = "findElements"
|
||||
FIND_CHILD_ELEMENT = "findChildElement"
|
||||
FIND_CHILD_ELEMENTS = "findChildElements"
|
||||
CLEAR_ELEMENT = "clearElement"
|
||||
CLICK_ELEMENT = "clickElement"
|
||||
SEND_KEYS_TO_ELEMENT = "sendKeysToElement"
|
||||
UPLOAD_FILE = "uploadFile"
|
||||
W3C_GET_CURRENT_WINDOW_HANDLE = "w3cGetCurrentWindowHandle"
|
||||
W3C_GET_WINDOW_HANDLES = "w3cGetWindowHandles"
|
||||
SET_WINDOW_RECT = "setWindowRect"
|
||||
GET_WINDOW_RECT = "getWindowRect"
|
||||
SWITCH_TO_WINDOW = "switchToWindow"
|
||||
SWITCH_TO_FRAME = "switchToFrame"
|
||||
SWITCH_TO_PARENT_FRAME = "switchToParentFrame"
|
||||
W3C_GET_ACTIVE_ELEMENT = "w3cGetActiveElement"
|
||||
GET_CURRENT_URL = "getCurrentUrl"
|
||||
GET_PAGE_SOURCE = "getPageSource"
|
||||
GET_TITLE = "getTitle"
|
||||
W3C_EXECUTE_SCRIPT = "w3cExecuteScript"
|
||||
W3C_EXECUTE_SCRIPT_ASYNC = "w3cExecuteScriptAsync"
|
||||
GET_ELEMENT_TEXT = "getElementText"
|
||||
GET_ELEMENT_TAG_NAME = "getElementTagName"
|
||||
IS_ELEMENT_SELECTED = "isElementSelected"
|
||||
IS_ELEMENT_ENABLED = "isElementEnabled"
|
||||
GET_ELEMENT_RECT = "getElementRect"
|
||||
GET_ELEMENT_ATTRIBUTE = "getElementAttribute"
|
||||
GET_ELEMENT_PROPERTY = "getElementProperty"
|
||||
GET_ELEMENT_VALUE_OF_CSS_PROPERTY = "getElementValueOfCssProperty"
|
||||
GET_ELEMENT_ARIA_ROLE = "getElementAriaRole"
|
||||
GET_ELEMENT_ARIA_LABEL = "getElementAriaLabel"
|
||||
SCREENSHOT = "screenshot"
|
||||
ELEMENT_SCREENSHOT = "elementScreenshot"
|
||||
EXECUTE_ASYNC_SCRIPT = "executeAsyncScript"
|
||||
SET_TIMEOUTS = "setTimeouts"
|
||||
GET_TIMEOUTS = "getTimeouts"
|
||||
W3C_MAXIMIZE_WINDOW = "w3cMaximizeWindow"
|
||||
GET_LOG = "getLog"
|
||||
GET_AVAILABLE_LOG_TYPES = "getAvailableLogTypes"
|
||||
FULLSCREEN_WINDOW = "fullscreenWindow"
|
||||
MINIMIZE_WINDOW = "minimizeWindow"
|
||||
PRINT_PAGE = 'printPage'
|
||||
|
||||
# Alerts
|
||||
W3C_DISMISS_ALERT = "w3cDismissAlert"
|
||||
W3C_ACCEPT_ALERT = "w3cAcceptAlert"
|
||||
W3C_SET_ALERT_VALUE = "w3cSetAlertValue"
|
||||
W3C_GET_ALERT_TEXT = "w3cGetAlertText"
|
||||
|
||||
# Advanced user interactions
|
||||
W3C_ACTIONS = "actions"
|
||||
W3C_CLEAR_ACTIONS = "clearActionState"
|
||||
|
||||
# Screen Orientation
|
||||
SET_SCREEN_ORIENTATION = "setScreenOrientation"
|
||||
GET_SCREEN_ORIENTATION = "getScreenOrientation"
|
||||
|
||||
# Mobile
|
||||
GET_NETWORK_CONNECTION = "getNetworkConnection"
|
||||
SET_NETWORK_CONNECTION = "setNetworkConnection"
|
||||
CURRENT_CONTEXT_HANDLE = "getCurrentContextHandle"
|
||||
CONTEXT_HANDLES = "getContextHandles"
|
||||
SWITCH_TO_CONTEXT = "switchToContext"
|
||||
|
||||
# Web Components
|
||||
GET_SHADOW_ROOT = "getShadowRoot"
|
||||
FIND_ELEMENT_FROM_SHADOW_ROOT = "findElementFromShadowRoot"
|
||||
FIND_ELEMENTS_FROM_SHADOW_ROOT = "findElementsFromShadowRoot"
|
||||
|
||||
# Virtual Authenticator
|
||||
ADD_VIRTUAL_AUTHENTICATOR = "addVirtualAuthenticator"
|
||||
REMOVE_VIRTUAL_AUTHENTICATOR = "removeVirtualAuthenticator"
|
||||
ADD_CREDENTIAL = "addCredential"
|
||||
GET_CREDENTIALS = "getCredentials"
|
||||
REMOVE_CREDENTIAL = "removeCredential"
|
||||
REMOVE_ALL_CREDENTIALS = "removeAllCredentials"
|
||||
SET_USER_VERIFIED = "setUserVerified"
|
||||
250
venv/Lib/site-packages/selenium/webdriver/remote/errorhandler.py
Normal file
250
venv/Lib/site-packages/selenium/webdriver/remote/errorhandler.py
Normal file
@@ -0,0 +1,250 @@
|
||||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from typing import Any, Dict, Mapping, Type, TypeVar
|
||||
|
||||
from selenium.common.exceptions import (ElementClickInterceptedException,
|
||||
ElementNotInteractableException,
|
||||
ElementNotSelectableException,
|
||||
ElementNotVisibleException,
|
||||
InsecureCertificateException,
|
||||
InvalidCoordinatesException,
|
||||
InvalidElementStateException,
|
||||
InvalidSessionIdException,
|
||||
InvalidSelectorException,
|
||||
ImeNotAvailableException,
|
||||
ImeActivationFailedException,
|
||||
InvalidArgumentException,
|
||||
InvalidCookieDomainException,
|
||||
JavascriptException,
|
||||
MoveTargetOutOfBoundsException,
|
||||
NoSuchCookieException,
|
||||
NoSuchElementException,
|
||||
NoSuchFrameException,
|
||||
NoSuchShadowRootException,
|
||||
NoSuchWindowException,
|
||||
NoAlertPresentException,
|
||||
ScreenshotException,
|
||||
SessionNotCreatedException,
|
||||
StaleElementReferenceException,
|
||||
TimeoutException,
|
||||
UnableToSetCookieException,
|
||||
UnexpectedAlertPresentException,
|
||||
UnknownMethodException,
|
||||
WebDriverException)
|
||||
|
||||
|
||||
_KT = TypeVar("_KT")
|
||||
_VT = TypeVar("_VT")
|
||||
|
||||
|
||||
class ErrorCode(object):
|
||||
"""
|
||||
Error codes defined in the WebDriver wire protocol.
|
||||
"""
|
||||
# Keep in sync with org.openqa.selenium.remote.ErrorCodes and errorcodes.h
|
||||
SUCCESS = 0
|
||||
NO_SUCH_ELEMENT = [7, 'no such element']
|
||||
NO_SUCH_FRAME = [8, 'no such frame']
|
||||
NO_SUCH_SHADOW_ROOT = ["no such shadow root"]
|
||||
UNKNOWN_COMMAND = [9, 'unknown command']
|
||||
STALE_ELEMENT_REFERENCE = [10, 'stale element reference']
|
||||
ELEMENT_NOT_VISIBLE = [11, 'element not visible']
|
||||
INVALID_ELEMENT_STATE = [12, 'invalid element state']
|
||||
UNKNOWN_ERROR = [13, 'unknown error']
|
||||
ELEMENT_IS_NOT_SELECTABLE = [15, 'element not selectable']
|
||||
JAVASCRIPT_ERROR = [17, 'javascript error']
|
||||
XPATH_LOOKUP_ERROR = [19, 'invalid selector']
|
||||
TIMEOUT = [21, 'timeout']
|
||||
NO_SUCH_WINDOW = [23, 'no such window']
|
||||
INVALID_COOKIE_DOMAIN = [24, 'invalid cookie domain']
|
||||
UNABLE_TO_SET_COOKIE = [25, 'unable to set cookie']
|
||||
UNEXPECTED_ALERT_OPEN = [26, 'unexpected alert open']
|
||||
NO_ALERT_OPEN = [27, 'no such alert']
|
||||
SCRIPT_TIMEOUT = [28, 'script timeout']
|
||||
INVALID_ELEMENT_COORDINATES = [29, 'invalid element coordinates']
|
||||
IME_NOT_AVAILABLE = [30, 'ime not available']
|
||||
IME_ENGINE_ACTIVATION_FAILED = [31, 'ime engine activation failed']
|
||||
INVALID_SELECTOR = [32, 'invalid selector']
|
||||
SESSION_NOT_CREATED = [33, 'session not created']
|
||||
MOVE_TARGET_OUT_OF_BOUNDS = [34, 'move target out of bounds']
|
||||
INVALID_XPATH_SELECTOR = [51, 'invalid selector']
|
||||
INVALID_XPATH_SELECTOR_RETURN_TYPER = [52, 'invalid selector']
|
||||
|
||||
ELEMENT_NOT_INTERACTABLE = [60, 'element not interactable']
|
||||
INSECURE_CERTIFICATE = ['insecure certificate']
|
||||
INVALID_ARGUMENT = [61, 'invalid argument']
|
||||
INVALID_COORDINATES = ['invalid coordinates']
|
||||
INVALID_SESSION_ID = ['invalid session id']
|
||||
NO_SUCH_COOKIE = [62, 'no such cookie']
|
||||
UNABLE_TO_CAPTURE_SCREEN = [63, 'unable to capture screen']
|
||||
ELEMENT_CLICK_INTERCEPTED = [64, 'element click intercepted']
|
||||
UNKNOWN_METHOD = ['unknown method exception']
|
||||
|
||||
METHOD_NOT_ALLOWED = [405, 'unsupported operation']
|
||||
|
||||
|
||||
class ErrorHandler(object):
|
||||
"""
|
||||
Handles errors returned by the WebDriver server.
|
||||
"""
|
||||
|
||||
def check_response(self, response: Dict[str, Any]) -> None:
|
||||
"""
|
||||
Checks that a JSON response from the WebDriver does not have an error.
|
||||
|
||||
:Args:
|
||||
- response - The JSON response from the WebDriver server as a dictionary
|
||||
object.
|
||||
|
||||
:Raises: If the response contains an error message.
|
||||
"""
|
||||
status = response.get('status', None)
|
||||
if not status or status == ErrorCode.SUCCESS:
|
||||
return
|
||||
value = None
|
||||
message = response.get("message", "")
|
||||
screen: str = response.get("screen", "")
|
||||
stacktrace = None
|
||||
if isinstance(status, int):
|
||||
value_json = response.get('value', None)
|
||||
if value_json and isinstance(value_json, str):
|
||||
import json
|
||||
try:
|
||||
value = json.loads(value_json)
|
||||
if len(value.keys()) == 1:
|
||||
value = value['value']
|
||||
status = value.get('error', None)
|
||||
if not status:
|
||||
status = value.get("status", ErrorCode.UNKNOWN_ERROR)
|
||||
message = value.get("value") or value.get("message")
|
||||
if not isinstance(message, str):
|
||||
value = message
|
||||
message = message.get('message')
|
||||
else:
|
||||
message = value.get('message', None)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
exception_class: Type[WebDriverException]
|
||||
if status in ErrorCode.NO_SUCH_ELEMENT:
|
||||
exception_class = NoSuchElementException
|
||||
elif status in ErrorCode.NO_SUCH_FRAME:
|
||||
exception_class = NoSuchFrameException
|
||||
elif status in ErrorCode.NO_SUCH_SHADOW_ROOT:
|
||||
exception_class = NoSuchShadowRootException
|
||||
elif status in ErrorCode.NO_SUCH_WINDOW:
|
||||
exception_class = NoSuchWindowException
|
||||
elif status in ErrorCode.STALE_ELEMENT_REFERENCE:
|
||||
exception_class = StaleElementReferenceException
|
||||
elif status in ErrorCode.ELEMENT_NOT_VISIBLE:
|
||||
exception_class = ElementNotVisibleException
|
||||
elif status in ErrorCode.INVALID_ELEMENT_STATE:
|
||||
exception_class = InvalidElementStateException
|
||||
elif status in ErrorCode.INVALID_SELECTOR \
|
||||
or status in ErrorCode.INVALID_XPATH_SELECTOR \
|
||||
or status in ErrorCode.INVALID_XPATH_SELECTOR_RETURN_TYPER:
|
||||
exception_class = InvalidSelectorException
|
||||
elif status in ErrorCode.ELEMENT_IS_NOT_SELECTABLE:
|
||||
exception_class = ElementNotSelectableException
|
||||
elif status in ErrorCode.ELEMENT_NOT_INTERACTABLE:
|
||||
exception_class = ElementNotInteractableException
|
||||
elif status in ErrorCode.INVALID_COOKIE_DOMAIN:
|
||||
exception_class = InvalidCookieDomainException
|
||||
elif status in ErrorCode.UNABLE_TO_SET_COOKIE:
|
||||
exception_class = UnableToSetCookieException
|
||||
elif status in ErrorCode.TIMEOUT:
|
||||
exception_class = TimeoutException
|
||||
elif status in ErrorCode.SCRIPT_TIMEOUT:
|
||||
exception_class = TimeoutException
|
||||
elif status in ErrorCode.UNKNOWN_ERROR:
|
||||
exception_class = WebDriverException
|
||||
elif status in ErrorCode.UNEXPECTED_ALERT_OPEN:
|
||||
exception_class = UnexpectedAlertPresentException
|
||||
elif status in ErrorCode.NO_ALERT_OPEN:
|
||||
exception_class = NoAlertPresentException
|
||||
elif status in ErrorCode.IME_NOT_AVAILABLE:
|
||||
exception_class = ImeNotAvailableException
|
||||
elif status in ErrorCode.IME_ENGINE_ACTIVATION_FAILED:
|
||||
exception_class = ImeActivationFailedException
|
||||
elif status in ErrorCode.MOVE_TARGET_OUT_OF_BOUNDS:
|
||||
exception_class = MoveTargetOutOfBoundsException
|
||||
elif status in ErrorCode.JAVASCRIPT_ERROR:
|
||||
exception_class = JavascriptException
|
||||
elif status in ErrorCode.SESSION_NOT_CREATED:
|
||||
exception_class = SessionNotCreatedException
|
||||
elif status in ErrorCode.INVALID_ARGUMENT:
|
||||
exception_class = InvalidArgumentException
|
||||
elif status in ErrorCode.NO_SUCH_COOKIE:
|
||||
exception_class = NoSuchCookieException
|
||||
elif status in ErrorCode.UNABLE_TO_CAPTURE_SCREEN:
|
||||
exception_class = ScreenshotException
|
||||
elif status in ErrorCode.ELEMENT_CLICK_INTERCEPTED:
|
||||
exception_class = ElementClickInterceptedException
|
||||
elif status in ErrorCode.INSECURE_CERTIFICATE:
|
||||
exception_class = InsecureCertificateException
|
||||
elif status in ErrorCode.INVALID_COORDINATES:
|
||||
exception_class = InvalidCoordinatesException
|
||||
elif status in ErrorCode.INVALID_SESSION_ID:
|
||||
exception_class = InvalidSessionIdException
|
||||
elif status in ErrorCode.UNKNOWN_METHOD:
|
||||
exception_class = UnknownMethodException
|
||||
else:
|
||||
exception_class = WebDriverException
|
||||
if not value:
|
||||
value = response['value']
|
||||
if isinstance(value, str):
|
||||
raise exception_class(value)
|
||||
if message == "" and 'message' in value:
|
||||
message = value['message']
|
||||
|
||||
screen = None # type: ignore[assignment]
|
||||
if 'screen' in value:
|
||||
screen = value['screen']
|
||||
|
||||
stacktrace = None
|
||||
st_value = value.get('stackTrace') or value.get('stacktrace')
|
||||
if st_value:
|
||||
if isinstance(st_value, str):
|
||||
stacktrace = st_value.split('\n')
|
||||
else:
|
||||
stacktrace = []
|
||||
try:
|
||||
for frame in st_value:
|
||||
line = self._value_or_default(frame, 'lineNumber', '')
|
||||
file = self._value_or_default(frame, 'fileName', '<anonymous>')
|
||||
if line:
|
||||
file = "%s:%s" % (file, line)
|
||||
meth = self._value_or_default(frame, 'methodName', '<anonymous>')
|
||||
if 'className' in frame:
|
||||
meth = "%s.%s" % (frame['className'], meth)
|
||||
msg = " at %s (%s)"
|
||||
msg = msg % (meth, file)
|
||||
stacktrace.append(msg)
|
||||
except TypeError:
|
||||
pass
|
||||
if exception_class == UnexpectedAlertPresentException:
|
||||
alert_text = None
|
||||
if 'data' in value:
|
||||
alert_text = value['data'].get('text')
|
||||
elif 'alert' in value:
|
||||
alert_text = value['alert'].get('text')
|
||||
raise exception_class(message, screen, stacktrace, alert_text) # type: ignore[call-arg] # mypy is not smart enough here
|
||||
raise exception_class(message, screen, stacktrace)
|
||||
|
||||
def _value_or_default(self, obj: Mapping[_KT, _VT], key: _KT, default: _VT) -> _VT:
|
||||
return obj[key] if key in obj else default
|
||||
@@ -0,0 +1,61 @@
|
||||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from abc import ABCMeta, abstractmethod
|
||||
import os
|
||||
from typing import Optional
|
||||
from selenium.types import AnyKey
|
||||
from selenium.webdriver.common.utils import keys_to_typing
|
||||
|
||||
|
||||
class FileDetector(metaclass=ABCMeta):
|
||||
"""
|
||||
Used for identifying whether a sequence of chars represents the path to a
|
||||
file.
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def is_local_file(self, *keys: AnyKey) -> Optional[str]:
|
||||
return None
|
||||
|
||||
|
||||
class UselessFileDetector(FileDetector):
|
||||
"""
|
||||
A file detector that never finds anything.
|
||||
"""
|
||||
|
||||
def is_local_file(self, *keys: AnyKey) -> Optional[str]:
|
||||
return None
|
||||
|
||||
|
||||
class LocalFileDetector(FileDetector):
|
||||
"""
|
||||
Detects files on the local disk.
|
||||
"""
|
||||
|
||||
def is_local_file(self, *keys: AnyKey) -> Optional[str]:
|
||||
file_path = ''.join(keys_to_typing(keys))
|
||||
|
||||
if not file_path:
|
||||
return None
|
||||
|
||||
try:
|
||||
if os.path.isfile(file_path):
|
||||
return file_path
|
||||
except Exception:
|
||||
pass
|
||||
return None
|
||||
86
venv/Lib/site-packages/selenium/webdriver/remote/mobile.py
Normal file
86
venv/Lib/site-packages/selenium/webdriver/remote/mobile.py
Normal file
@@ -0,0 +1,86 @@
|
||||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from .command import Command
|
||||
|
||||
|
||||
class Mobile(object):
|
||||
|
||||
class ConnectionType(object):
|
||||
|
||||
def __init__(self, mask):
|
||||
self.mask = mask
|
||||
|
||||
@property
|
||||
def airplane_mode(self):
|
||||
return self.mask % 2 == 1
|
||||
|
||||
@property
|
||||
def wifi(self):
|
||||
return (self.mask / 2) % 2 == 1
|
||||
|
||||
@property
|
||||
def data(self):
|
||||
return (self.mask / 4) > 0
|
||||
|
||||
ALL_NETWORK = ConnectionType(6)
|
||||
WIFI_NETWORK = ConnectionType(2)
|
||||
DATA_NETWORK = ConnectionType(4)
|
||||
AIRPLANE_MODE = ConnectionType(1)
|
||||
|
||||
def __init__(self, driver):
|
||||
import weakref
|
||||
self._driver = weakref.proxy(driver)
|
||||
|
||||
@property
|
||||
def network_connection(self):
|
||||
return self.ConnectionType(self._driver.execute(Command.GET_NETWORK_CONNECTION)['value'])
|
||||
|
||||
def set_network_connection(self, network):
|
||||
"""
|
||||
Set the network connection for the remote device.
|
||||
|
||||
Example of setting airplane mode::
|
||||
|
||||
driver.mobile.set_network_connection(driver.mobile.AIRPLANE_MODE)
|
||||
"""
|
||||
mode = network.mask if isinstance(network, self.ConnectionType) else network
|
||||
return self.ConnectionType(self._driver.execute(
|
||||
Command.SET_NETWORK_CONNECTION, {
|
||||
'name': 'network_connection',
|
||||
'parameters': {'type': mode}})['value'])
|
||||
|
||||
@property
|
||||
def context(self):
|
||||
"""
|
||||
returns the current context (Native or WebView).
|
||||
"""
|
||||
return self._driver.execute(Command.CURRENT_CONTEXT_HANDLE)
|
||||
|
||||
@property
|
||||
def contexts(self):
|
||||
"""
|
||||
returns a list of available contexts
|
||||
"""
|
||||
return self._driver.execute(Command.CONTEXT_HANDLES)
|
||||
|
||||
@context.setter
|
||||
def context(self, new_context):
|
||||
"""
|
||||
sets the current context
|
||||
"""
|
||||
self._driver.execute(Command.SWITCH_TO_CONTEXT, {"name": new_context})
|
||||
@@ -0,0 +1,420 @@
|
||||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import logging
|
||||
import socket
|
||||
import string
|
||||
|
||||
import os
|
||||
import certifi
|
||||
import urllib3
|
||||
import platform
|
||||
|
||||
from base64 import b64encode
|
||||
|
||||
from urllib import parse
|
||||
from selenium import __version__
|
||||
from .command import Command
|
||||
from .errorhandler import ErrorCode
|
||||
from . import utils
|
||||
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class RemoteConnection(object):
|
||||
"""A connection with the Remote WebDriver server.
|
||||
|
||||
Communicates with the server using the WebDriver wire protocol:
|
||||
https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol"""
|
||||
|
||||
browser_name = None
|
||||
_timeout = socket._GLOBAL_DEFAULT_TIMEOUT
|
||||
_ca_certs = certifi.where()
|
||||
|
||||
@classmethod
|
||||
def get_timeout(cls):
|
||||
"""
|
||||
:Returns:
|
||||
Timeout value in seconds for all http requests made to the Remote Connection
|
||||
"""
|
||||
return None if cls._timeout == socket._GLOBAL_DEFAULT_TIMEOUT else cls._timeout
|
||||
|
||||
@classmethod
|
||||
def set_timeout(cls, timeout):
|
||||
"""
|
||||
Override the default timeout
|
||||
|
||||
:Args:
|
||||
- timeout - timeout value for http requests in seconds
|
||||
"""
|
||||
cls._timeout = timeout
|
||||
|
||||
@classmethod
|
||||
def reset_timeout(cls):
|
||||
"""
|
||||
Reset the http request timeout to socket._GLOBAL_DEFAULT_TIMEOUT
|
||||
"""
|
||||
cls._timeout = socket._GLOBAL_DEFAULT_TIMEOUT
|
||||
|
||||
@classmethod
|
||||
def get_certificate_bundle_path(cls):
|
||||
"""
|
||||
:Returns:
|
||||
Paths of the .pem encoded certificate to verify connection to command executor
|
||||
"""
|
||||
return cls._ca_certs
|
||||
|
||||
@classmethod
|
||||
def set_certificate_bundle_path(cls, path):
|
||||
"""
|
||||
Set the path to the certificate bundle to verify connection to command executor.
|
||||
Can also be set to None to disable certificate validation.
|
||||
|
||||
:Args:
|
||||
- path - path of a .pem encoded certificate chain.
|
||||
"""
|
||||
cls._ca_certs = path
|
||||
|
||||
@classmethod
|
||||
def get_remote_connection_headers(cls, parsed_url, keep_alive=False):
|
||||
"""
|
||||
Get headers for remote request.
|
||||
|
||||
:Args:
|
||||
- parsed_url - The parsed url
|
||||
- keep_alive (Boolean) - Is this a keep-alive connection (default: False)
|
||||
"""
|
||||
|
||||
system = platform.system().lower()
|
||||
if system == "darwin":
|
||||
system = "mac"
|
||||
|
||||
headers = {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json;charset=UTF-8',
|
||||
'User-Agent': 'selenium/{} (python {})'.format(__version__, system)
|
||||
}
|
||||
|
||||
if parsed_url.username:
|
||||
base64string = b64encode('{0.username}:{0.password}'.format(parsed_url).encode())
|
||||
headers.update({
|
||||
'Authorization': 'Basic {}'.format(base64string.decode())
|
||||
})
|
||||
|
||||
if keep_alive:
|
||||
headers.update({
|
||||
'Connection': 'keep-alive'
|
||||
})
|
||||
|
||||
return headers
|
||||
|
||||
def _get_proxy_url(self):
|
||||
if self._url.startswith('https://'):
|
||||
return os.environ.get('https_proxy', os.environ.get('HTTPS_PROXY'))
|
||||
elif self._url.startswith('http://'):
|
||||
return os.environ.get('http_proxy', os.environ.get('HTTP_PROXY'))
|
||||
|
||||
def _identify_http_proxy_auth(self):
|
||||
url = self._proxy_url
|
||||
url = url[url.find(":") + 3:]
|
||||
return True if "@" in url and len(url[:url.find('@')]) > 0 else False
|
||||
|
||||
def _seperate_http_proxy_auth(self):
|
||||
url = self._proxy_url
|
||||
protocol = url[:url.find(":") + 3]
|
||||
no_protocol = url[len(protocol):]
|
||||
auth = no_protocol[:no_protocol.find('@')]
|
||||
proxy_without_auth = protocol + no_protocol[len(auth) + 1:]
|
||||
return proxy_without_auth, auth
|
||||
|
||||
def _get_connection_manager(self):
|
||||
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
|
||||
|
||||
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)
|
||||
elif self._identify_http_proxy_auth():
|
||||
self._proxy_url, self._basic_proxy_auth = self._seperate_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)
|
||||
|
||||
def __init__(self, remote_server_addr, keep_alive=False, resolve_ip=None, ignore_proxy=False):
|
||||
if resolve_ip:
|
||||
import warnings
|
||||
warnings.warn(
|
||||
"'resolve_ip' option removed; ip addresses are now always resolved by urllib3.",
|
||||
DeprecationWarning)
|
||||
self.keep_alive = keep_alive
|
||||
self._url = remote_server_addr
|
||||
|
||||
# Env var NO_PROXY will override this part of the code
|
||||
_no_proxy = os.environ.get('no_proxy', os.environ.get('NO_PROXY'))
|
||||
if _no_proxy:
|
||||
for npu in _no_proxy.split(','):
|
||||
npu = npu.strip()
|
||||
if npu == "*":
|
||||
ignore_proxy = True
|
||||
break
|
||||
n_url = parse.urlparse(npu)
|
||||
remote_add = parse.urlparse(self._url)
|
||||
if n_url.netloc:
|
||||
if remote_add.netloc == n_url.netloc:
|
||||
ignore_proxy = True
|
||||
break
|
||||
else:
|
||||
if n_url.path in remote_add.netloc:
|
||||
ignore_proxy = True
|
||||
break
|
||||
|
||||
self._proxy_url = self._get_proxy_url() if not ignore_proxy else None
|
||||
if keep_alive:
|
||||
self._conn = self._get_connection_manager()
|
||||
|
||||
self._commands = {
|
||||
Command.NEW_SESSION: ('POST', '/session'),
|
||||
Command.QUIT: ('DELETE', '/session/$sessionId'),
|
||||
Command.W3C_GET_CURRENT_WINDOW_HANDLE:
|
||||
('GET', '/session/$sessionId/window'),
|
||||
Command.W3C_GET_WINDOW_HANDLES:
|
||||
('GET', '/session/$sessionId/window/handles'),
|
||||
Command.GET: ('POST', '/session/$sessionId/url'),
|
||||
Command.GO_FORWARD: ('POST', '/session/$sessionId/forward'),
|
||||
Command.GO_BACK: ('POST', '/session/$sessionId/back'),
|
||||
Command.REFRESH: ('POST', '/session/$sessionId/refresh'),
|
||||
Command.W3C_EXECUTE_SCRIPT:
|
||||
('POST', '/session/$sessionId/execute/sync'),
|
||||
Command.W3C_EXECUTE_SCRIPT_ASYNC:
|
||||
('POST', '/session/$sessionId/execute/async'),
|
||||
Command.GET_CURRENT_URL: ('GET', '/session/$sessionId/url'),
|
||||
Command.GET_TITLE: ('GET', '/session/$sessionId/title'),
|
||||
Command.GET_PAGE_SOURCE: ('GET', '/session/$sessionId/source'),
|
||||
Command.SCREENSHOT: ('GET', '/session/$sessionId/screenshot'),
|
||||
Command.ELEMENT_SCREENSHOT: ('GET', '/session/$sessionId/element/$id/screenshot'),
|
||||
Command.FIND_ELEMENT: ('POST', '/session/$sessionId/element'),
|
||||
Command.FIND_ELEMENTS: ('POST', '/session/$sessionId/elements'),
|
||||
Command.W3C_GET_ACTIVE_ELEMENT: ('GET', '/session/$sessionId/element/active'),
|
||||
Command.FIND_CHILD_ELEMENT:
|
||||
('POST', '/session/$sessionId/element/$id/element'),
|
||||
Command.FIND_CHILD_ELEMENTS:
|
||||
('POST', '/session/$sessionId/element/$id/elements'),
|
||||
Command.CLICK_ELEMENT: ('POST', '/session/$sessionId/element/$id/click'),
|
||||
Command.CLEAR_ELEMENT: ('POST', '/session/$sessionId/element/$id/clear'),
|
||||
Command.GET_ELEMENT_TEXT: ('GET', '/session/$sessionId/element/$id/text'),
|
||||
Command.SEND_KEYS_TO_ELEMENT:
|
||||
('POST', '/session/$sessionId/element/$id/value'),
|
||||
Command.UPLOAD_FILE: ('POST', "/session/$sessionId/se/file"),
|
||||
Command.GET_ELEMENT_TAG_NAME:
|
||||
('GET', '/session/$sessionId/element/$id/name'),
|
||||
Command.IS_ELEMENT_SELECTED:
|
||||
('GET', '/session/$sessionId/element/$id/selected'),
|
||||
Command.IS_ELEMENT_ENABLED:
|
||||
('GET', '/session/$sessionId/element/$id/enabled'),
|
||||
Command.GET_ELEMENT_RECT:
|
||||
('GET', '/session/$sessionId/element/$id/rect'),
|
||||
Command.GET_ELEMENT_ATTRIBUTE:
|
||||
('GET', '/session/$sessionId/element/$id/attribute/$name'),
|
||||
Command.GET_ELEMENT_PROPERTY:
|
||||
('GET', '/session/$sessionId/element/$id/property/$name'),
|
||||
Command.GET_ELEMENT_ARIA_ROLE:
|
||||
('GET', '/session/$sessionId/element/$id/computedrole'),
|
||||
Command.GET_ELEMENT_ARIA_LABEL:
|
||||
('GET', '/session/$sessionId/element/$id/computedlabel'),
|
||||
Command.GET_SHADOW_ROOT:
|
||||
('GET', '/session/$sessionId/element/$id/shadow'),
|
||||
Command.FIND_ELEMENT_FROM_SHADOW_ROOT:
|
||||
('POST', '/session/$sessionId/shadow/$shadowId/element'),
|
||||
Command.FIND_ELEMENTS_FROM_SHADOW_ROOT:
|
||||
('POST', '/session/$sessionId/shadow/$shadowId/elements'),
|
||||
Command.GET_ALL_COOKIES: ('GET', '/session/$sessionId/cookie'),
|
||||
Command.ADD_COOKIE: ('POST', '/session/$sessionId/cookie'),
|
||||
Command.GET_COOKIE: ('GET', '/session/$sessionId/cookie/$name'),
|
||||
Command.DELETE_ALL_COOKIES:
|
||||
('DELETE', '/session/$sessionId/cookie'),
|
||||
Command.DELETE_COOKIE:
|
||||
('DELETE', '/session/$sessionId/cookie/$name'),
|
||||
Command.SWITCH_TO_FRAME: ('POST', '/session/$sessionId/frame'),
|
||||
Command.SWITCH_TO_PARENT_FRAME: ('POST', '/session/$sessionId/frame/parent'),
|
||||
Command.SWITCH_TO_WINDOW: ('POST', '/session/$sessionId/window'),
|
||||
Command.NEW_WINDOW: ('POST', '/session/$sessionId/window/new'),
|
||||
Command.CLOSE: ('DELETE', '/session/$sessionId/window'),
|
||||
Command.GET_ELEMENT_VALUE_OF_CSS_PROPERTY:
|
||||
('GET', '/session/$sessionId/element/$id/css/$propertyName'),
|
||||
Command.EXECUTE_ASYNC_SCRIPT: ('POST', '/session/$sessionId/execute_async'),
|
||||
Command.SET_TIMEOUTS:
|
||||
('POST', '/session/$sessionId/timeouts'),
|
||||
Command.GET_TIMEOUTS:
|
||||
('GET', '/session/$sessionId/timeouts'),
|
||||
Command.W3C_DISMISS_ALERT:
|
||||
('POST', '/session/$sessionId/alert/dismiss'),
|
||||
Command.W3C_ACCEPT_ALERT:
|
||||
('POST', '/session/$sessionId/alert/accept'),
|
||||
Command.W3C_SET_ALERT_VALUE:
|
||||
('POST', '/session/$sessionId/alert/text'),
|
||||
Command.W3C_GET_ALERT_TEXT:
|
||||
('GET', '/session/$sessionId/alert/text'),
|
||||
Command.W3C_ACTIONS:
|
||||
('POST', '/session/$sessionId/actions'),
|
||||
Command.W3C_CLEAR_ACTIONS:
|
||||
('DELETE', '/session/$sessionId/actions'),
|
||||
Command.SET_WINDOW_RECT:
|
||||
('POST', '/session/$sessionId/window/rect'),
|
||||
Command.GET_WINDOW_RECT:
|
||||
('GET', '/session/$sessionId/window/rect'),
|
||||
Command.W3C_MAXIMIZE_WINDOW:
|
||||
('POST', '/session/$sessionId/window/maximize'),
|
||||
Command.SET_SCREEN_ORIENTATION:
|
||||
('POST', '/session/$sessionId/orientation'),
|
||||
Command.GET_SCREEN_ORIENTATION:
|
||||
('GET', '/session/$sessionId/orientation'),
|
||||
Command.GET_NETWORK_CONNECTION:
|
||||
('GET', '/session/$sessionId/network_connection'),
|
||||
Command.SET_NETWORK_CONNECTION:
|
||||
('POST', '/session/$sessionId/network_connection'),
|
||||
Command.GET_LOG:
|
||||
('POST', '/session/$sessionId/se/log'),
|
||||
Command.GET_AVAILABLE_LOG_TYPES:
|
||||
('GET', '/session/$sessionId/se/log/types'),
|
||||
Command.CURRENT_CONTEXT_HANDLE:
|
||||
('GET', '/session/$sessionId/context'),
|
||||
Command.CONTEXT_HANDLES:
|
||||
('GET', '/session/$sessionId/contexts'),
|
||||
Command.SWITCH_TO_CONTEXT:
|
||||
('POST', '/session/$sessionId/context'),
|
||||
Command.FULLSCREEN_WINDOW:
|
||||
('POST', '/session/$sessionId/window/fullscreen'),
|
||||
Command.MINIMIZE_WINDOW:
|
||||
('POST', '/session/$sessionId/window/minimize'),
|
||||
Command.PRINT_PAGE:
|
||||
('POST', '/session/$sessionId/print'),
|
||||
Command.ADD_VIRTUAL_AUTHENTICATOR:
|
||||
('POST', '/session/$sessionId/webauthn/authenticator'),
|
||||
Command.REMOVE_VIRTUAL_AUTHENTICATOR:
|
||||
('DELETE', '/session/$sessionId/webauthn/authenticator/$authenticatorId'),
|
||||
Command.ADD_CREDENTIAL:
|
||||
('POST', '/session/$sessionId/webauthn/authenticator/$authenticatorId/credential'),
|
||||
Command.GET_CREDENTIALS:
|
||||
('GET', '/session/$sessionId/webauthn/authenticator/$authenticatorId/credentials'),
|
||||
Command.REMOVE_CREDENTIAL:
|
||||
('DELETE', '/session/$sessionId/webauthn/authenticator/$authenticatorId/credentials/$credentialId'),
|
||||
Command.REMOVE_ALL_CREDENTIALS:
|
||||
('DELETE', '/session/$sessionId/webauthn/authenticator/$authenticatorId/credentials'),
|
||||
Command.SET_USER_VERIFIED:
|
||||
('POST', '/session/$sessionId/webauthn/authenticator/$authenticatorId/uv'),
|
||||
}
|
||||
|
||||
def execute(self, command, params):
|
||||
"""
|
||||
Send a command to the remote server.
|
||||
|
||||
Any path substitutions required for the URL mapped to the command should be
|
||||
included in the command parameters.
|
||||
|
||||
:Args:
|
||||
- command - A string specifying the command to execute.
|
||||
- params - A dictionary of named parameters to send with the command as
|
||||
its JSON payload.
|
||||
"""
|
||||
command_info = self._commands[command]
|
||||
assert command_info is not None, 'Unrecognised command %s' % command
|
||||
path = string.Template(command_info[1]).substitute(params)
|
||||
if isinstance(params, dict) and 'sessionId' in params:
|
||||
del params['sessionId']
|
||||
data = utils.dump_json(params)
|
||||
url = f"{self._url}{path}"
|
||||
return self._request(command_info[0], url, body=data)
|
||||
|
||||
def _request(self, method, url, body=None):
|
||||
"""
|
||||
Send an HTTP request to the remote server.
|
||||
|
||||
:Args:
|
||||
- method - A string for the HTTP method to send the request with.
|
||||
- url - A string for the URL to send the request to.
|
||||
- body - A string for request body. Ignored unless method is POST or PUT.
|
||||
|
||||
:Returns:
|
||||
A dictionary with the server's parsed JSON response.
|
||||
"""
|
||||
LOGGER.debug(f"{method} {url} {body}")
|
||||
parsed_url = parse.urlparse(url)
|
||||
headers = self.get_remote_connection_headers(parsed_url, self.keep_alive)
|
||||
response = None
|
||||
if body and method not in ("POST", "PUT"):
|
||||
body = None
|
||||
|
||||
if self.keep_alive:
|
||||
response = self._conn.request(method, url, body=body, headers=headers)
|
||||
statuscode = response.status
|
||||
else:
|
||||
conn = self._get_connection_manager()
|
||||
with conn as http:
|
||||
response = http.request(method, url, body=body, headers=headers)
|
||||
|
||||
statuscode = response.status
|
||||
if not hasattr(response, 'getheader'):
|
||||
if hasattr(response.headers, 'getheader'):
|
||||
response.getheader = lambda x: response.headers.getheader(x)
|
||||
elif hasattr(response.headers, 'get'):
|
||||
response.getheader = lambda x: response.headers.get(x)
|
||||
data = response.data.decode('UTF-8')
|
||||
LOGGER.debug(f"Remote response: status={response.status} | data={data} | headers={response.headers}")
|
||||
try:
|
||||
if 300 <= statuscode < 304:
|
||||
return self._request('GET', response.getheader('location'))
|
||||
if 399 < statuscode <= 500:
|
||||
return {'status': statuscode, 'value': data}
|
||||
content_type = []
|
||||
if response.getheader('Content-Type'):
|
||||
content_type = response.getheader('Content-Type').split(';')
|
||||
if not any([x.startswith('image/png') for x in content_type]):
|
||||
|
||||
try:
|
||||
data = utils.load_json(data.strip())
|
||||
except ValueError:
|
||||
if 199 < statuscode < 300:
|
||||
status = ErrorCode.SUCCESS
|
||||
else:
|
||||
status = ErrorCode.UNKNOWN_ERROR
|
||||
return {'status': status, 'value': data.strip()}
|
||||
|
||||
# Some drivers incorrectly return a response
|
||||
# with no 'value' field when they should return null.
|
||||
if 'value' not in data:
|
||||
data['value'] = None
|
||||
return data
|
||||
else:
|
||||
data = {'status': 0, 'value': data}
|
||||
return data
|
||||
finally:
|
||||
LOGGER.debug("Finished Request")
|
||||
response.close()
|
||||
|
||||
def close(self):
|
||||
"""
|
||||
Clean up resources when finished with the remote_connection
|
||||
"""
|
||||
if hasattr(self, '_conn'):
|
||||
self._conn.clear()
|
||||
@@ -0,0 +1,30 @@
|
||||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License
|
||||
import uuid
|
||||
|
||||
|
||||
class ScriptKey:
|
||||
|
||||
def __init__(self, id=None):
|
||||
self._id = id or uuid.uuid4()
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
return self._id
|
||||
|
||||
def __eq__(self, other):
|
||||
return self._id == other
|
||||
@@ -0,0 +1,90 @@
|
||||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from hashlib import md5 as md5_hash
|
||||
|
||||
from .command import Command
|
||||
from ..common.by import By
|
||||
|
||||
|
||||
class ShadowRoot:
|
||||
|
||||
# TODO: We should look and see how we can create a search context like Java/.NET
|
||||
|
||||
def __init__(self, session, id_):
|
||||
self.session = session
|
||||
self._id = id_
|
||||
|
||||
def __eq__(self, other_shadowroot):
|
||||
return self._id == other_shadowroot._id
|
||||
|
||||
def __hash__(self):
|
||||
return int(md5_hash(self._id.encode("utf-8")).hexdigest(), 16)
|
||||
|
||||
def __ne__(self, other_shadowroot):
|
||||
return not self.__eq__(other_shadowroot)
|
||||
|
||||
def __repr__(self):
|
||||
return '<{0.__module__}.{0.__name__} (session="{1}", element="{2}")>'.format(
|
||||
type(self), self.session.session_id, self._id
|
||||
)
|
||||
|
||||
def find_element(self, by: By = By.ID, value: str = None):
|
||||
if by == By.ID:
|
||||
by = By.CSS_SELECTOR
|
||||
value = '[id="%s"]' % value
|
||||
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(
|
||||
Command.FIND_ELEMENT_FROM_SHADOW_ROOT, {"using": by, "value": value}
|
||||
)["value"]
|
||||
|
||||
def find_elements(self, by: By = By.ID, value: str = None):
|
||||
if by == By.ID:
|
||||
by = By.CSS_SELECTOR
|
||||
value = '[id="%s"]' % value
|
||||
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(
|
||||
Command.FIND_ELEMENTS_FROM_SHADOW_ROOT, {"using": by, "value": value}
|
||||
)["value"]
|
||||
|
||||
# Private Methods
|
||||
def _execute(self, command, params=None):
|
||||
"""Executes a command against the underlying HTML element.
|
||||
|
||||
Args:
|
||||
command: The name of the command to _execute as a string.
|
||||
params: A dictionary of named parameters to send with the command.
|
||||
|
||||
Returns:
|
||||
The command's JSON response loaded into a dictionary object.
|
||||
"""
|
||||
if not params:
|
||||
params = {}
|
||||
params["shadowId"] = self._id
|
||||
return self.session.execute(command, params)
|
||||
154
venv/Lib/site-packages/selenium/webdriver/remote/switch_to.py
Normal file
154
venv/Lib/site-packages/selenium/webdriver/remote/switch_to.py
Normal file
@@ -0,0 +1,154 @@
|
||||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from .command import Command
|
||||
|
||||
from selenium.common.exceptions import (NoSuchElementException,
|
||||
NoSuchFrameException,
|
||||
NoSuchWindowException)
|
||||
from selenium.webdriver.common.alert import Alert
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.webdriver.remote.webelement import WebElement
|
||||
|
||||
|
||||
class SwitchTo:
|
||||
def __init__(self, driver):
|
||||
import weakref
|
||||
self._driver = weakref.proxy(driver)
|
||||
|
||||
@property
|
||||
def active_element(self) -> WebElement:
|
||||
"""
|
||||
Returns the element with focus, or BODY if nothing has focus.
|
||||
|
||||
:Usage:
|
||||
::
|
||||
|
||||
element = driver.switch_to.active_element
|
||||
"""
|
||||
return self._driver.execute(Command.W3C_GET_ACTIVE_ELEMENT)['value']
|
||||
|
||||
@property
|
||||
def alert(self) -> Alert:
|
||||
"""
|
||||
Switches focus to an alert on the page.
|
||||
|
||||
:Usage:
|
||||
::
|
||||
|
||||
alert = driver.switch_to.alert
|
||||
"""
|
||||
alert = Alert(self._driver)
|
||||
alert.text
|
||||
return alert
|
||||
|
||||
def default_content(self) -> None:
|
||||
"""
|
||||
Switch focus to the default frame.
|
||||
|
||||
:Usage:
|
||||
::
|
||||
|
||||
driver.switch_to.default_content()
|
||||
"""
|
||||
self._driver.execute(Command.SWITCH_TO_FRAME, {'id': None})
|
||||
|
||||
def frame(self, frame_reference) -> None:
|
||||
"""
|
||||
Switches focus to the specified frame, by index, name, or webelement.
|
||||
|
||||
:Args:
|
||||
- frame_reference: The name of the window to switch to, an integer representing the index,
|
||||
or a webelement that is an (i)frame to switch to.
|
||||
|
||||
:Usage:
|
||||
::
|
||||
|
||||
driver.switch_to.frame('frame_name')
|
||||
driver.switch_to.frame(1)
|
||||
driver.switch_to.frame(driver.find_elements(By.TAG_NAME, "iframe")[0])
|
||||
"""
|
||||
if isinstance(frame_reference, str):
|
||||
try:
|
||||
frame_reference = self._driver.find_element(By.ID, frame_reference)
|
||||
except NoSuchElementException:
|
||||
try:
|
||||
frame_reference = self._driver.find_element(By.NAME, frame_reference)
|
||||
except NoSuchElementException:
|
||||
raise NoSuchFrameException(frame_reference)
|
||||
|
||||
self._driver.execute(Command.SWITCH_TO_FRAME, {'id': frame_reference})
|
||||
|
||||
def new_window(self, type_hint=None) -> None:
|
||||
"""Switches to a new top-level browsing context.
|
||||
|
||||
The type hint can be one of "tab" or "window". If not specified the
|
||||
browser will automatically select it.
|
||||
|
||||
:Usage:
|
||||
::
|
||||
|
||||
driver.switch_to.new_window('tab')
|
||||
"""
|
||||
value = self._driver.execute(Command.NEW_WINDOW, {'type': type_hint})['value']
|
||||
self._w3c_window(value['handle'])
|
||||
|
||||
def parent_frame(self) -> None:
|
||||
"""
|
||||
Switches focus to the parent context. If the current context is the top
|
||||
level browsing context, the context remains unchanged.
|
||||
|
||||
:Usage:
|
||||
::
|
||||
|
||||
driver.switch_to.parent_frame()
|
||||
"""
|
||||
self._driver.execute(Command.SWITCH_TO_PARENT_FRAME)
|
||||
|
||||
def window(self, window_name) -> None:
|
||||
"""
|
||||
Switches focus to the specified window.
|
||||
|
||||
:Args:
|
||||
- window_name: The name or window handle of the window to switch to.
|
||||
|
||||
:Usage:
|
||||
::
|
||||
|
||||
driver.switch_to.window('main')
|
||||
"""
|
||||
self._w3c_window(window_name)
|
||||
return
|
||||
|
||||
def _w3c_window(self, window_name):
|
||||
def send_handle(h):
|
||||
self._driver.execute(Command.SWITCH_TO_WINDOW, {'handle': h})
|
||||
|
||||
try:
|
||||
# Try using it as a handle first.
|
||||
send_handle(window_name)
|
||||
except NoSuchWindowException as e:
|
||||
# Check every window to try to find the given window name.
|
||||
original_handle = self._driver.current_window_handle
|
||||
handles = self._driver.window_handles
|
||||
for handle in handles:
|
||||
send_handle(handle)
|
||||
current_name = self._driver.execute_script('return window.name')
|
||||
if window_name == current_name:
|
||||
return
|
||||
send_handle(original_handle)
|
||||
raise e
|
||||
27
venv/Lib/site-packages/selenium/webdriver/remote/utils.py
Normal file
27
venv/Lib/site-packages/selenium/webdriver/remote/utils.py
Normal file
@@ -0,0 +1,27 @@
|
||||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import json
|
||||
from typing import Any, Union
|
||||
|
||||
|
||||
def dump_json(json_struct: Any) -> str:
|
||||
return json.dumps(json_struct)
|
||||
|
||||
|
||||
def load_json(s: Union[str, bytes]) -> Any:
|
||||
return json.loads(s)
|
||||
1682
venv/Lib/site-packages/selenium/webdriver/remote/webdriver.py
Normal file
1682
venv/Lib/site-packages/selenium/webdriver/remote/webdriver.py
Normal file
File diff suppressed because it is too large
Load Diff
811
venv/Lib/site-packages/selenium/webdriver/remote/webelement.py
Normal file
811
venv/Lib/site-packages/selenium/webdriver/remote/webelement.py
Normal file
@@ -0,0 +1,811 @@
|
||||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import os
|
||||
from base64 import b64decode, encodebytes
|
||||
from hashlib import md5 as md5_hash
|
||||
import pkgutil
|
||||
import warnings
|
||||
import zipfile
|
||||
from abc import ABCMeta
|
||||
from io import BytesIO
|
||||
|
||||
from selenium.common.exceptions import WebDriverException
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.webdriver.common.utils import keys_to_typing
|
||||
from .command import Command
|
||||
from .shadowroot import ShadowRoot
|
||||
|
||||
|
||||
# TODO: When moving to supporting python 3.9 as the minimum version we can
|
||||
# use built in importlib_resources.files.
|
||||
_pkg = '.'.join(__name__.split('.')[:-1])
|
||||
getAttribute_js = pkgutil.get_data(_pkg, 'getAttribute.js').decode('utf8')
|
||||
isDisplayed_js = pkgutil.get_data(_pkg, 'isDisplayed.js').decode('utf8')
|
||||
|
||||
|
||||
class BaseWebElement(metaclass=ABCMeta):
|
||||
"""
|
||||
Abstract Base Class for WebElement.
|
||||
ABC's will allow custom types to be registered as a WebElement to pass type checks.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class WebElement(BaseWebElement):
|
||||
"""Represents a DOM element.
|
||||
|
||||
Generally, all interesting operations that interact with a document will be
|
||||
performed through this interface.
|
||||
|
||||
All method calls will do a freshness check to ensure that the element
|
||||
reference is still valid. This essentially determines whether or not the
|
||||
element is still attached to the DOM. If this test fails, then an
|
||||
``StaleElementReferenceException`` is thrown, and all future calls to this
|
||||
instance will fail."""
|
||||
|
||||
def __init__(self, parent, id_):
|
||||
self._parent = parent
|
||||
self._id = id_
|
||||
|
||||
def __repr__(self):
|
||||
return '<{0.__module__}.{0.__name__} (session="{1}", element="{2}")>'.format(
|
||||
type(self), self._parent.session_id, self._id)
|
||||
|
||||
@property
|
||||
def tag_name(self) -> str:
|
||||
"""This element's ``tagName`` property."""
|
||||
return self._execute(Command.GET_ELEMENT_TAG_NAME)['value']
|
||||
|
||||
@property
|
||||
def text(self) -> str:
|
||||
"""The text of the element."""
|
||||
return self._execute(Command.GET_ELEMENT_TEXT)['value']
|
||||
|
||||
def click(self) -> None:
|
||||
"""Clicks the element."""
|
||||
self._execute(Command.CLICK_ELEMENT)
|
||||
|
||||
def submit(self):
|
||||
"""Submits a form."""
|
||||
form = self.find_element(By.XPATH, "./ancestor-or-self::form")
|
||||
self._parent.execute_script(
|
||||
"var e = arguments[0].ownerDocument.createEvent('Event');"
|
||||
"e.initEvent('submit', true, true);"
|
||||
"if (arguments[0].dispatchEvent(e)) { arguments[0].submit() }", form)
|
||||
|
||||
def clear(self) -> None:
|
||||
"""Clears the text if it's a text entry element."""
|
||||
self._execute(Command.CLEAR_ELEMENT)
|
||||
|
||||
def get_property(self, name) -> str:
|
||||
"""
|
||||
Gets the given property of the element.
|
||||
|
||||
:Args:
|
||||
- name - Name of the property to retrieve.
|
||||
|
||||
:Usage:
|
||||
::
|
||||
|
||||
text_length = target_element.get_property("text_length")
|
||||
"""
|
||||
try:
|
||||
return self._execute(Command.GET_ELEMENT_PROPERTY, {"name": name})["value"]
|
||||
except WebDriverException:
|
||||
# if we hit an end point that doesn't understand getElementProperty lets fake it
|
||||
return self.parent.execute_script('return arguments[0][arguments[1]]', self, name)
|
||||
|
||||
def get_dom_attribute(self, name) -> str:
|
||||
"""
|
||||
Gets the given attribute of the element. Unlike :func:`~selenium.webdriver.remote.BaseWebElement.get_attribute`,
|
||||
this method only returns attributes declared in the element's HTML markup.
|
||||
|
||||
:Args:
|
||||
- name - Name of the attribute to retrieve.
|
||||
|
||||
:Usage:
|
||||
::
|
||||
|
||||
text_length = target_element.get_dom_attribute("class")
|
||||
"""
|
||||
return self._execute(Command.GET_ELEMENT_ATTRIBUTE, {"name": name})["value"]
|
||||
|
||||
def get_attribute(self, name) -> str:
|
||||
"""Gets the given attribute or property of the element.
|
||||
|
||||
This method will first try to return the value of a property with the
|
||||
given name. If a property with that name doesn't exist, it returns the
|
||||
value of the attribute with the same name. If there's no attribute with
|
||||
that name, ``None`` is returned.
|
||||
|
||||
Values which are considered truthy, that is equals "true" or "false",
|
||||
are returned as booleans. All other non-``None`` values are returned
|
||||
as strings. For attributes or properties which do not exist, ``None``
|
||||
is returned.
|
||||
|
||||
To obtain the exact value of the attribute or property,
|
||||
use :func:`~selenium.webdriver.remote.BaseWebElement.get_dom_attribute` or
|
||||
:func:`~selenium.webdriver.remote.BaseWebElement.get_property` methods respectively.
|
||||
|
||||
:Args:
|
||||
- name - Name of the attribute/property to retrieve.
|
||||
|
||||
Example::
|
||||
|
||||
# Check if the "active" CSS class is applied to an element.
|
||||
is_active = "active" in target_element.get_attribute("class")
|
||||
|
||||
"""
|
||||
|
||||
attribute_value = self.parent.execute_script(
|
||||
"return (%s).apply(null, arguments);" % getAttribute_js,
|
||||
self, name)
|
||||
return attribute_value
|
||||
|
||||
def is_selected(self) -> bool:
|
||||
"""Returns whether the element is selected.
|
||||
|
||||
Can be used to check if a checkbox or radio button is selected.
|
||||
"""
|
||||
return self._execute(Command.IS_ELEMENT_SELECTED)['value']
|
||||
|
||||
def is_enabled(self) -> bool:
|
||||
"""Returns whether the element is enabled."""
|
||||
return self._execute(Command.IS_ELEMENT_ENABLED)['value']
|
||||
|
||||
def find_element_by_id(self, id_):
|
||||
"""Finds element within this element's children by ID.
|
||||
|
||||
:Args:
|
||||
- id\\_ - ID of child element to locate.
|
||||
|
||||
:Returns:
|
||||
- WebElement - the element if it was found
|
||||
|
||||
:Raises:
|
||||
- NoSuchElementException - if the element wasn't found
|
||||
|
||||
:Usage:
|
||||
::
|
||||
|
||||
foo_element = element.find_element_by_id('foo')
|
||||
"""
|
||||
warnings.warn("find_element_by_id is deprecated. Please use find_element(by=By.ID, value=id_) instead",
|
||||
DeprecationWarning,
|
||||
stacklevel=2)
|
||||
return self.find_element(by=By.ID, value=id_)
|
||||
|
||||
def find_elements_by_id(self, id_):
|
||||
"""Finds a list of elements within this element's children by ID.
|
||||
Will return a list of webelements if found, or an empty list if not.
|
||||
|
||||
:Args:
|
||||
- id\\_ - Id of child element to find.
|
||||
|
||||
:Returns:
|
||||
- list of WebElement - a list with elements if any was found. An
|
||||
empty list if not
|
||||
|
||||
:Usage:
|
||||
::
|
||||
|
||||
elements = element.find_elements_by_id('foo')
|
||||
"""
|
||||
warnings.warn("find_elements_by_id is deprecated. Please use find_elements(by=By.ID, value=id_) instead",
|
||||
DeprecationWarning,
|
||||
stacklevel=2)
|
||||
return self.find_elements(by=By.ID, value=id_)
|
||||
|
||||
def find_element_by_name(self, name):
|
||||
"""Finds element within this element's children by name.
|
||||
|
||||
:Args:
|
||||
- name - name property of the element to find.
|
||||
|
||||
:Returns:
|
||||
- WebElement - the element if it was found
|
||||
|
||||
:Raises:
|
||||
- NoSuchElementException - if the element wasn't found
|
||||
|
||||
:Usage:
|
||||
::
|
||||
|
||||
element = element.find_element_by_name('foo')
|
||||
"""
|
||||
warnings.warn("find_element_by_name is deprecated. Please use find_element(by=By.NAME, value=name) instead",
|
||||
DeprecationWarning,
|
||||
stacklevel=2)
|
||||
return self.find_element(by=By.NAME, value=name)
|
||||
|
||||
def find_elements_by_name(self, name):
|
||||
"""Finds a list of elements within this element's children by name.
|
||||
|
||||
:Args:
|
||||
- name - name property to search for.
|
||||
|
||||
:Returns:
|
||||
- list of webelement - a list with elements if any was found. an
|
||||
empty list if not
|
||||
|
||||
:Usage:
|
||||
::
|
||||
|
||||
elements = element.find_elements_by_name('foo')
|
||||
"""
|
||||
warnings.warn("find_elements_by_name is deprecated. Please use find_elements(by=By.NAME, value=name) instead",
|
||||
DeprecationWarning,
|
||||
stacklevel=2)
|
||||
return self.find_elements(by=By.NAME, value=name)
|
||||
|
||||
def find_element_by_link_text(self, link_text):
|
||||
"""Finds element within this element's children by visible link text.
|
||||
|
||||
:Args:
|
||||
- link_text - Link text string to search for.
|
||||
|
||||
:Returns:
|
||||
- WebElement - the element if it was found
|
||||
|
||||
:Raises:
|
||||
- NoSuchElementException - if the element wasn't found
|
||||
|
||||
:Usage:
|
||||
::
|
||||
|
||||
element = element.find_element_by_link_text('Sign In')
|
||||
"""
|
||||
warnings.warn("find_element_by_link_text is deprecated. Please use find_element(by=By.LINK_TEXT, value=link_text) instead",
|
||||
DeprecationWarning,
|
||||
stacklevel=2)
|
||||
return self.find_element(by=By.LINK_TEXT, value=link_text)
|
||||
|
||||
def find_elements_by_link_text(self, link_text):
|
||||
"""Finds a list of elements within this element's children by visible link text.
|
||||
|
||||
:Args:
|
||||
- link_text - Link text string to search for.
|
||||
|
||||
:Returns:
|
||||
- list of webelement - a list with elements if any was found. an
|
||||
empty list if not
|
||||
|
||||
:Usage:
|
||||
::
|
||||
|
||||
elements = element.find_elements_by_link_text('Sign In')
|
||||
"""
|
||||
warnings.warn("find_elements_by_link_text is deprecated. Please use find_elements(by=By.LINK_TEXT, value=text) instead",
|
||||
DeprecationWarning,
|
||||
stacklevel=2)
|
||||
return self.find_elements(by=By.LINK_TEXT, value=link_text)
|
||||
|
||||
def find_element_by_partial_link_text(self, link_text):
|
||||
"""Finds element within this element's children by partially visible link text.
|
||||
|
||||
:Args:
|
||||
- link_text: The text of the element to partially match on.
|
||||
|
||||
:Returns:
|
||||
- WebElement - the element if it was found
|
||||
|
||||
:Raises:
|
||||
- NoSuchElementException - if the element wasn't found
|
||||
|
||||
:Usage:
|
||||
::
|
||||
|
||||
element = element.find_element_by_partial_link_text('Sign')
|
||||
"""
|
||||
warnings.warn("find_element_by_partial_link_text is deprecated. Please use find_element(by=By.PARTIAL_LINK_TEXT, value=link_text) instead",
|
||||
DeprecationWarning,
|
||||
stacklevel=2)
|
||||
return self.find_element(by=By.PARTIAL_LINK_TEXT, value=link_text)
|
||||
|
||||
def find_elements_by_partial_link_text(self, link_text):
|
||||
"""Finds a list of elements within this element's children by link text.
|
||||
|
||||
:Args:
|
||||
- link_text: The text of the element to partial match on.
|
||||
|
||||
:Returns:
|
||||
- list of webelement - a list with elements if any was found. an
|
||||
empty list if not
|
||||
|
||||
:Usage:
|
||||
::
|
||||
|
||||
elements = element.find_elements_by_partial_link_text('Sign')
|
||||
"""
|
||||
warnings.warn("find_elements_by_partial_link_text is deprecated. Please use find_elements(by=By.PARTIAL_LINK_TEXT, value=link_text) instead",
|
||||
DeprecationWarning,
|
||||
stacklevel=2)
|
||||
return self.find_elements(by=By.PARTIAL_LINK_TEXT, value=link_text)
|
||||
|
||||
def find_element_by_tag_name(self, name):
|
||||
"""Finds element within this element's children by tag name.
|
||||
|
||||
:Args:
|
||||
- name - name of html tag (eg: h1, a, span)
|
||||
|
||||
:Returns:
|
||||
- WebElement - the element if it was found
|
||||
|
||||
:Raises:
|
||||
- NoSuchElementException - if the element wasn't found
|
||||
|
||||
:Usage:
|
||||
::
|
||||
|
||||
element = element.find_element_by_tag_name('h1')
|
||||
"""
|
||||
warnings.warn("find_element_by_tag_name is deprecated. Please use find_element(by=By.TAG_NAME, value=name) instead",
|
||||
DeprecationWarning,
|
||||
stacklevel=2)
|
||||
return self.find_element(by=By.TAG_NAME, value=name)
|
||||
|
||||
def find_elements_by_tag_name(self, name):
|
||||
"""Finds a list of elements within this element's children by tag name.
|
||||
|
||||
:Args:
|
||||
- name - name of html tag (eg: h1, a, span)
|
||||
|
||||
:Returns:
|
||||
- list of WebElement - a list with elements if any was found. An
|
||||
empty list if not
|
||||
|
||||
:Usage:
|
||||
::
|
||||
|
||||
elements = element.find_elements_by_tag_name('h1')
|
||||
"""
|
||||
warnings.warn("find_elements_by_tag_name is deprecated. Please use find_elements(by=By.TAG_NAME, value=name) instead",
|
||||
DeprecationWarning,
|
||||
stacklevel=2)
|
||||
return self.find_elements(by=By.TAG_NAME, value=name)
|
||||
|
||||
def find_element_by_xpath(self, xpath):
|
||||
"""Finds element by xpath.
|
||||
|
||||
:Args:
|
||||
- xpath - xpath of element to locate. "//input[@class='myelement']"
|
||||
|
||||
Note: The base path will be relative to this element's location.
|
||||
|
||||
This will select the first link under this element.
|
||||
|
||||
::
|
||||
|
||||
myelement.find_element_by_xpath(".//a")
|
||||
|
||||
However, this will select the first link on the page.
|
||||
|
||||
::
|
||||
|
||||
myelement.find_element_by_xpath("//a")
|
||||
|
||||
:Returns:
|
||||
- WebElement - the element if it was found
|
||||
|
||||
:Raises:
|
||||
- NoSuchElementException - if the element wasn't found
|
||||
|
||||
:Usage:
|
||||
::
|
||||
|
||||
element = element.find_element_by_xpath('//div/td[1]')
|
||||
"""
|
||||
warnings.warn("find_element_by_xpath is deprecated. Please use find_element(by=By.XPATH, value=xpath) instead",
|
||||
DeprecationWarning,
|
||||
stacklevel=2)
|
||||
return self.find_element(by=By.XPATH, value=xpath)
|
||||
|
||||
def find_elements_by_xpath(self, xpath):
|
||||
"""Finds elements within the element by xpath.
|
||||
|
||||
:Args:
|
||||
- xpath - xpath locator string.
|
||||
|
||||
Note: The base path will be relative to this element's location.
|
||||
|
||||
This will select all links under this element.
|
||||
|
||||
::
|
||||
|
||||
myelement.find_elements_by_xpath(".//a")
|
||||
|
||||
However, this will select all links in the page itself.
|
||||
|
||||
::
|
||||
|
||||
myelement.find_elements_by_xpath("//a")
|
||||
|
||||
:Returns:
|
||||
- list of WebElement - a list with elements if any was found. An
|
||||
empty list if not
|
||||
|
||||
:Usage:
|
||||
::
|
||||
|
||||
elements = element.find_elements_by_xpath("//div[contains(@class, 'foo')]")
|
||||
|
||||
"""
|
||||
warnings.warn("find_elements_by_xpath is deprecated. Please use find_elements(by=By.XPATH, value=xpath) instead",
|
||||
DeprecationWarning,
|
||||
stacklevel=2)
|
||||
return self.find_elements(by=By.XPATH, value=xpath)
|
||||
|
||||
def find_element_by_class_name(self, name):
|
||||
"""Finds element within this element's children by class name.
|
||||
|
||||
:Args:
|
||||
- name: The class name of the element to find.
|
||||
|
||||
:Returns:
|
||||
- WebElement - the element if it was found
|
||||
|
||||
:Raises:
|
||||
- NoSuchElementException - if the element wasn't found
|
||||
|
||||
:Usage:
|
||||
::
|
||||
|
||||
element = element.find_element_by_class_name('foo')
|
||||
"""
|
||||
warnings.warn("find_element_by_class_name is deprecated. Please use find_element(by=By.CLASS_NAME, value=name) instead",
|
||||
DeprecationWarning,
|
||||
stacklevel=2)
|
||||
return self.find_element(by=By.CLASS_NAME, value=name)
|
||||
|
||||
def find_elements_by_class_name(self, name):
|
||||
"""Finds a list of elements within this element's children by class name.
|
||||
|
||||
:Args:
|
||||
- name: The class name of the elements to find.
|
||||
|
||||
:Returns:
|
||||
- list of WebElement - a list with elements if any was found. An
|
||||
empty list if not
|
||||
|
||||
:Usage:
|
||||
::
|
||||
|
||||
elements = element.find_elements_by_class_name('foo')
|
||||
"""
|
||||
warnings.warn("find_elements_by_class_name is deprecated. Please use find_elements(by=By.CLASS_NAME, value=name) instead", DeprecationWarning)
|
||||
return self.find_elements(by=By.CLASS_NAME, value=name)
|
||||
|
||||
def find_element_by_css_selector(self, css_selector):
|
||||
"""Finds element within this element's children by CSS selector.
|
||||
|
||||
:Args:
|
||||
- css_selector - CSS selector string, ex: 'a.nav#home'
|
||||
|
||||
:Returns:
|
||||
- WebElement - the element if it was found
|
||||
|
||||
:Raises:
|
||||
- NoSuchElementException - if the element wasn't found
|
||||
|
||||
:Usage:
|
||||
::
|
||||
|
||||
element = element.find_element_by_css_selector('#foo')
|
||||
"""
|
||||
warnings.warn("find_element_by_css_selector is deprecated. Please use find_element(by=By.CSS_SELECTOR, value=css_selector) instead",
|
||||
DeprecationWarning,
|
||||
stacklevel=2)
|
||||
return self.find_element(by=By.CSS_SELECTOR, value=css_selector)
|
||||
|
||||
def find_elements_by_css_selector(self, css_selector):
|
||||
"""Finds a list of elements within this element's children by CSS selector.
|
||||
|
||||
:Args:
|
||||
- css_selector - CSS selector string, ex: 'a.nav#home'
|
||||
|
||||
:Returns:
|
||||
- list of WebElement - a list with elements if any was found. An
|
||||
empty list if not
|
||||
|
||||
:Usage:
|
||||
::
|
||||
|
||||
elements = element.find_elements_by_css_selector('.foo')
|
||||
"""
|
||||
warnings.warn("find_elements_by_css_selector is deprecated. Please use find_elements(by=By.CSS_SELECTOR, value=css_selector) instead",
|
||||
DeprecationWarning,
|
||||
stacklevel=2)
|
||||
return self.find_elements(by=By.CSS_SELECTOR, value=css_selector)
|
||||
|
||||
def send_keys(self, *value) -> None:
|
||||
"""Simulates typing into the element.
|
||||
|
||||
:Args:
|
||||
- value - A string for typing, or setting form fields. For setting
|
||||
file inputs, this could be a local file path.
|
||||
|
||||
Use this to send simple key events or to fill out form fields::
|
||||
|
||||
form_textfield = driver.find_element(By.NAME, 'username')
|
||||
form_textfield.send_keys("admin")
|
||||
|
||||
This can also be used to set file inputs.
|
||||
|
||||
::
|
||||
|
||||
file_input = driver.find_element(By.NAME, 'profilePic')
|
||||
file_input.send_keys("path/to/profilepic.gif")
|
||||
# Generally it's better to wrap the file path in one of the methods
|
||||
# in os.path to return the actual path to support cross OS testing.
|
||||
# file_input.send_keys(os.path.abspath("path/to/profilepic.gif"))
|
||||
|
||||
"""
|
||||
# transfer file to another machine only if remote driver is used
|
||||
# the same behaviour as for java binding
|
||||
if self.parent._is_remote:
|
||||
local_files = list(map(lambda keys_to_send:
|
||||
self.parent.file_detector.is_local_file(str(keys_to_send)),
|
||||
''.join(map(str, value)).split('\n')))
|
||||
if None not in local_files:
|
||||
remote_files = []
|
||||
for file in local_files:
|
||||
remote_files.append(self._upload(file))
|
||||
value = '\n'.join(remote_files)
|
||||
|
||||
self._execute(Command.SEND_KEYS_TO_ELEMENT,
|
||||
{'text': "".join(keys_to_typing(value)),
|
||||
'value': keys_to_typing(value)})
|
||||
|
||||
@property
|
||||
def shadow_root(self) -> ShadowRoot:
|
||||
"""
|
||||
Returns a shadow root of the element if there is one or an error. Only works from
|
||||
Chromium 96 onwards. Previous versions of Chromium based browsers will throw an
|
||||
assertion exception.
|
||||
|
||||
:Returns:
|
||||
- ShadowRoot object or
|
||||
- NoSuchShadowRoot - if no shadow root was attached to element
|
||||
"""
|
||||
browser_main_version = int(self._parent.caps["browserVersion"].split(".")[0])
|
||||
assert self._parent.caps["browserName"].lower() not in ["firefox", "safari"], "This only currently works in Chromium based browsers"
|
||||
assert not browser_main_version <= 95, f"Please use Chromium based browsers with version 96 or later. Version used {self._parent.caps['browserVersion']}"
|
||||
return self._execute(Command.GET_SHADOW_ROOT)['value']
|
||||
|
||||
# RenderedWebElement Items
|
||||
def is_displayed(self) -> bool:
|
||||
"""Whether the element is visible to a user."""
|
||||
# Only go into this conditional for browsers that don't use the atom themselves
|
||||
return self.parent.execute_script(
|
||||
"return (%s).apply(null, arguments);" % isDisplayed_js,
|
||||
self)
|
||||
|
||||
@property
|
||||
def location_once_scrolled_into_view(self) -> dict:
|
||||
"""THIS PROPERTY MAY CHANGE WITHOUT WARNING. Use this to discover
|
||||
where on the screen an element is so that we can click it. This method
|
||||
should cause the element to be scrolled into view.
|
||||
|
||||
Returns the top lefthand corner location on the screen, or ``None`` if
|
||||
the element is not visible.
|
||||
|
||||
"""
|
||||
old_loc = self._execute(Command.W3C_EXECUTE_SCRIPT, {
|
||||
'script': "arguments[0].scrollIntoView(true); return arguments[0].getBoundingClientRect()",
|
||||
'args': [self]})['value']
|
||||
return {"x": round(old_loc['x']),
|
||||
"y": round(old_loc['y'])}
|
||||
|
||||
@property
|
||||
def size(self) -> dict:
|
||||
"""The size of the element."""
|
||||
size = self._execute(Command.GET_ELEMENT_RECT)['value']
|
||||
new_size = {"height": size["height"],
|
||||
"width": size["width"]}
|
||||
return new_size
|
||||
|
||||
def value_of_css_property(self, property_name) -> str:
|
||||
"""The value of a CSS property."""
|
||||
return self._execute(Command.GET_ELEMENT_VALUE_OF_CSS_PROPERTY, {
|
||||
'propertyName': property_name})['value']
|
||||
|
||||
@property
|
||||
def location(self) -> dict:
|
||||
"""The location of the element in the renderable canvas."""
|
||||
old_loc = self._execute(Command.GET_ELEMENT_RECT)['value']
|
||||
new_loc = {"x": round(old_loc['x']),
|
||||
"y": round(old_loc['y'])}
|
||||
return new_loc
|
||||
|
||||
@property
|
||||
def rect(self) -> dict:
|
||||
"""A dictionary with the size and location of the element."""
|
||||
return self._execute(Command.GET_ELEMENT_RECT)['value']
|
||||
|
||||
@property
|
||||
def aria_role(self) -> str:
|
||||
""" Returns the ARIA role of the current web element"""
|
||||
return self._execute(Command.GET_ELEMENT_ARIA_ROLE)['value']
|
||||
|
||||
@property
|
||||
def accessible_name(self) -> str:
|
||||
"""Returns the ARIA Level of the current webelement"""
|
||||
return self._execute(Command.GET_ELEMENT_ARIA_LABEL)['value']
|
||||
|
||||
@property
|
||||
def screenshot_as_base64(self) -> str:
|
||||
"""
|
||||
Gets the screenshot of the current element as a base64 encoded string.
|
||||
|
||||
:Usage:
|
||||
::
|
||||
|
||||
img_b64 = element.screenshot_as_base64
|
||||
"""
|
||||
return self._execute(Command.ELEMENT_SCREENSHOT)['value']
|
||||
|
||||
@property
|
||||
def screenshot_as_png(self) -> str:
|
||||
"""
|
||||
Gets the screenshot of the current element as a binary data.
|
||||
|
||||
:Usage:
|
||||
::
|
||||
|
||||
element_png = element.screenshot_as_png
|
||||
"""
|
||||
return b64decode(self.screenshot_as_base64.encode('ascii'))
|
||||
|
||||
def screenshot(self, filename) -> bool:
|
||||
"""
|
||||
Saves a screenshot of the current element to a PNG image file. Returns
|
||||
False if there is any IOError, else returns True. Use full paths in
|
||||
your filename.
|
||||
|
||||
:Args:
|
||||
- filename: The full path you wish to save your screenshot to. This
|
||||
should end with a `.png` extension.
|
||||
|
||||
:Usage:
|
||||
::
|
||||
|
||||
element.screenshot('/Screenshots/foo.png')
|
||||
"""
|
||||
if not filename.lower().endswith('.png'):
|
||||
warnings.warn("name used for saved screenshot does not match file "
|
||||
"type. It should end with a `.png` extension", UserWarning)
|
||||
png = self.screenshot_as_png
|
||||
try:
|
||||
with open(filename, 'wb') as f:
|
||||
f.write(png)
|
||||
except IOError:
|
||||
return False
|
||||
finally:
|
||||
del png
|
||||
return True
|
||||
|
||||
@property
|
||||
def parent(self):
|
||||
"""Internal reference to the WebDriver instance this element was found from."""
|
||||
return self._parent
|
||||
|
||||
@property
|
||||
def id(self) -> str:
|
||||
"""Internal ID used by selenium.
|
||||
|
||||
This is mainly for internal use. Simple use cases such as checking if 2
|
||||
webelements refer to the same element, can be done using ``==``::
|
||||
|
||||
if element1 == element2:
|
||||
print("These 2 are equal")
|
||||
|
||||
"""
|
||||
return self._id
|
||||
|
||||
def __eq__(self, element):
|
||||
return hasattr(element, 'id') and self._id == element.id
|
||||
|
||||
def __ne__(self, element):
|
||||
return not self.__eq__(element)
|
||||
|
||||
# Private Methods
|
||||
def _execute(self, command, params=None):
|
||||
"""Executes a command against the underlying HTML element.
|
||||
|
||||
Args:
|
||||
command: The name of the command to _execute as a string.
|
||||
params: A dictionary of named parameters to send with the command.
|
||||
|
||||
Returns:
|
||||
The command's JSON response loaded into a dictionary object.
|
||||
"""
|
||||
if not params:
|
||||
params = {}
|
||||
params['id'] = self._id
|
||||
return self._parent.execute(command, params)
|
||||
|
||||
def find_element(self, by=By.ID, value=None):
|
||||
"""
|
||||
Find an element given a By strategy and locator.
|
||||
|
||||
:Usage:
|
||||
::
|
||||
|
||||
element = element.find_element(By.ID, 'foo')
|
||||
|
||||
:rtype: WebElement
|
||||
"""
|
||||
if by == By.ID:
|
||||
by = By.CSS_SELECTOR
|
||||
value = '[id="%s"]' % value
|
||||
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(Command.FIND_CHILD_ELEMENT,
|
||||
{"using": by, "value": value})['value']
|
||||
|
||||
def find_elements(self, by=By.ID, value=None):
|
||||
"""
|
||||
Find elements given a By strategy and locator.
|
||||
|
||||
:Usage:
|
||||
::
|
||||
|
||||
element = element.find_elements(By.CLASS_NAME, 'foo')
|
||||
|
||||
:rtype: list of WebElement
|
||||
"""
|
||||
if by == By.ID:
|
||||
by = By.CSS_SELECTOR
|
||||
value = '[id="%s"]' % value
|
||||
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(Command.FIND_CHILD_ELEMENTS,
|
||||
{"using": by, "value": value})['value']
|
||||
|
||||
def __hash__(self):
|
||||
return int(md5_hash(self._id.encode('utf-8')).hexdigest(), 16)
|
||||
|
||||
def _upload(self, filename):
|
||||
fp = BytesIO()
|
||||
zipped = zipfile.ZipFile(fp, 'w', zipfile.ZIP_DEFLATED)
|
||||
zipped.write(filename, os.path.split(filename)[1])
|
||||
zipped.close()
|
||||
content = encodebytes(fp.getvalue())
|
||||
if not isinstance(content, str):
|
||||
content = content.decode('utf-8')
|
||||
try:
|
||||
return self._execute(Command.UPLOAD_FILE, {'file': content})['value']
|
||||
except WebDriverException as e:
|
||||
if "Unrecognized command: POST" in e.__str__():
|
||||
return filename
|
||||
elif "Command not found: POST " in e.__str__():
|
||||
return filename
|
||||
elif '{"status":405,"value":["GET","HEAD","DELETE"]}' in e.__str__():
|
||||
return filename
|
||||
else:
|
||||
raise e
|
||||
Reference in New Issue
Block a user