赞
踩
简单示例:
- from selenium import webdriver
- from selenium.webdriver.common.by import By
- from selenium.webdriver.support.ui import WebDriverWait
- from selenium.webdriver.support import expected_conditions as EC
-
- driver = webdriver.Chrome()
-
- try:
- driver.get("https://www.hao123.com/")
- # print('*****driver_type',type(driver)) # driver_type <class 'selenium.webdriver.chrome.webdriver.WebDriver'>
- input_elements = driver.find_elements(By.TAG_NAME, "input")
- for input_element in input_elements:
- print(input_element.get_attribute("outerHTML"))
- search_input = driver.find_element(By.CSS_SELECTOR, 'input[data-hook="searchInput"]')
- # print('*****search_input_type',type(search_input)) # search_input_type <class 'selenium.webdriver.remote.webelement.WebElement'>
- search_input.send_keys("Selenium库")
- search_button = driver.find_element(By.CSS_SELECTOR, 'input[data-hook="searchSubmit"]')
- search_button.click()
- handles = driver.window_handles
- print(type(handles))
- if len(handles) > 1:
- driver.switch_to.window(handles[1])
- # Wait for the search results to load completely
- WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.CSS_SELECTOR, 'div[id="content_left"]')))
- finally:
- driver.quit()
-
- # In this updated version, we added the following imports:
- # from selenium.webdriver.support.ui import WebDriverWait
- # from selenium.webdriver.support import expected_conditions as EC
- # We then added a WebDriverWait statement to wait for the presence of a specific element in the search results page:
- # WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.CSS_SELECTOR, 'div[class="search-list-item"]')))
- # This statement waits for up to 10 seconds for an element with the CSS selector 'div[class="search-list-item"]' to appear on the page.
- # Once the element appears, the script continues to execute.
-
- # Note that you may need to adjust the CSS selector or the wait time to suit your specific needs.
- # (By.CSS_SELECTOR, 'div[id="content_left"]'),
------------
Selenium 的 WebDriver 实现的源代码提供了以下基本操作和功能:
初始化浏览器会话:通过实例化 WebDriver 对象,可以初始化一个特定的浏览器会话,如Chrome、Firefox等。
执行 JavaScript:可以通过 WebDriver.execute_script() 方法执行 JavaScript 代码,实现对页面的动态操作或数据获取。
窗口管理:WebDriver 可以管理浏览器窗口,包括最大化窗口、最小化窗口、设置窗口大小等功能。
元素定位:使用 WebDriver 提供的方法,如定位元素的 find_element
和 find_elements
等,可以定位页面上的元素,进行操作或获取元素信息。
截图:WebDriver 可以对当前页面进行截图,并保存为图片文件,方便后续分析或记录。
下载文件:通过 WebDriver 控制浏览器下载文件,并保存到指定的目录中。
以上是 Selenium WebDriver 实现的源代码提供的一些基本操作和功能,这些功能可以帮助用户进行自动化测试、网页操作或数据采集等任务。selenium.webdriver.chrome.webdriver.WebDriver
类是 Selenium WebDriver 中用于操作 Chrome 浏览器的主要类之一,它继承自 selenium.webdriver.remote.webdriver.WebDriver
类。这个类提供了丰富的方法和属性,用于实现对 Chrome 浏览器的控制和页面操作。
常用方法:
常用属性:
current_url: 当前页面的 URL。
title: 当前页面的标题。
page_source: 当前页面的源代码。
window_handles: 当前浏览器窗口的句柄列表。
current_window_handle: 当前窗口的句柄。
capabilities: 浏览器的配置信息和能力。
name: 浏览器实例的名称。
session_id: 浏览器会话的 ID。
desired_capabilities: 期望的浏览器配置信息。
通过这些常用方法和属性,您可以更好地利用 selenium.webdriver.chrome.webdriver.WebDriver
类来控制 Chrome 浏览器进行自动化测试、网页操作等各种任务。
-
- """The WebDriver implementation."""
- import base64
- import contextlib
- import copy
- import os
- import pkgutil
- import types
- import typing
- import warnings
- import zipfile
- from abc import ABCMeta
- from base64 import b64decode
- from base64 import urlsafe_b64encode
- from contextlib import asynccontextmanager
- from contextlib import contextmanager
- from importlib import import_module
- from typing import Dict
- from typing import List
- from typing import Optional
- from typing import Union
-
- from selenium.common.exceptions import InvalidArgumentException
- from selenium.common.exceptions import JavascriptException
- from selenium.common.exceptions import NoSuchCookieException
- from selenium.common.exceptions import NoSuchElementException
- from selenium.common.exceptions import WebDriverException
- from selenium.webdriver.common.by import By
- from selenium.webdriver.common.options import BaseOptions
- from selenium.webdriver.common.print_page_options import PrintOptions
- from selenium.webdriver.common.timeouts import Timeouts
- from selenium.webdriver.common.virtual_authenticator import Credential
- from selenium.webdriver.common.virtual_authenticator import VirtualAuthenticatorOptions
- from selenium.webdriver.common.virtual_authenticator import (
- required_virtual_authenticator,
- )
- from selenium.webdriver.support.relative_locator import RelativeBy
-
- from .bidi_connection import BidiConnection
- from .command import Command
- from .errorhandler import ErrorHandler
- from .file_detector import FileDetector
- from .file_detector import LocalFileDetector
- from .mobile import Mobile
- from .remote_connection import RemoteConnection
- from .script_key import ScriptKey
- from .shadowroot import ShadowRoot
- from .switch_to import SwitchTo
- from .webelement import WebElement
-
- cdp = None
-
-
- def import_cdp():
- global cdp
- if not cdp:
- cdp = import_module("selenium.webdriver.common.bidi.cdp")
-
-
- def _create_caps(caps):
- """Makes a W3C alwaysMatch capabilities object.
- Filters out capability names that are not in the W3C spec. Spec-compliant
- drivers will reject requests containing unknown capability names.
- Moves the Firefox profile, if present, from the old location to the new Firefox
- options object.
- :Args:
- - caps - A dictionary of capabilities requested by the caller.
- """
- caps = copy.deepcopy(caps)
- always_match = {}
- for k, v in caps.items():
- always_match[k] = v
- return {"capabilities": {"firstMatch": [{}], "alwaysMatch": always_match}}
-
-
- def get_remote_connection(capabilities, command_executor, keep_alive, ignore_local_proxy=False):
- from selenium.webdriver.chrome.remote_connection import ChromeRemoteConnection
- from selenium.webdriver.edge.remote_connection import EdgeRemoteConnection
- from selenium.webdriver.firefox.remote_connection import FirefoxRemoteConnection
- from selenium.webdriver.safari.remote_connection import SafariRemoteConnection
-
- candidates = [ChromeRemoteConnection, EdgeRemoteConnection, SafariRemoteConnection, FirefoxRemoteConnection]
- handler = next((c for c in candidates if c.browser_name == capabilities.get("browserName")), RemoteConnection)
-
- return handler(command_executor, keep_alive=keep_alive, ignore_proxy=ignore_local_proxy)
-
-
- def create_matches(options: List[BaseOptions]) -> Dict:
- capabilities = {"capabilities": {}}
- opts = []
- for opt in options:
- opts.append(opt.to_capabilities())
- opts_size = len(opts)
- samesies = {}
-
- # Can not use bitwise operations on the dicts or lists due to
- # https://bugs.python.org/issue38210
- for i in range(opts_size):
- min_index = i
- if i + 1 < opts_size:
- first_keys = opts[min_index].keys()
-
- for kys in first_keys:
- if kys in opts[i + 1].keys():
- if opts[min_index][kys] == opts[i + 1][kys]:
- samesies.update({kys: opts[min_index][kys]})
-
- always = {}
- for k, v in samesies.items():
- always[k] = v
-
- for i in opts:
- for k in always:
- del i[k]
-
- capabilities["capabilities"]["alwaysMatch"] = always
- capabilities["capabilities"]["firstMatch"] = opts
-
- return capabilities
-
-
- class BaseWebDriver(metaclass=ABCMeta):
- """Abstract Base Class for all Webdriver subtypes.
- ABC's allow custom implementations of Webdriver to be registered so
- that isinstance type checks will succeed.
- """
-
-
- class WebDriver(BaseWebDriver):
- """Controls a browser by sending commands to a remote server. This server
- is expected to be running the WebDriver wire protocol as defined at
- https://www.selenium.dev/documentation/legacy/json_wire_protocol/.
- :Attributes:
- - session_id - String ID of the browser session started and controlled by this WebDriver.
- - capabilities - Dictionary of effective capabilities of this browser session as returned
- by the remote server. See https://www.selenium.dev/documentation/legacy/desired_capabilities/
- - command_executor - remote_connection.RemoteConnection object used to execute commands.
- - error_handler - errorhandler.ErrorHandler object used to handle errors.
- """
-
- _web_element_cls = WebElement
- _shadowroot_cls = ShadowRoot
-
- def __init__(
- self,
- command_executor="http://127.0.0.1:4444",
- keep_alive=True,
- file_detector=None,
- options: Union[BaseOptions, List[BaseOptions]] = None,
- ) -> None:
- """Create a new driver that will issue commands using the wire
- protocol.
- :Args:
- - command_executor - Either a string representing URL of the remote server or a custom
- remote_connection.RemoteConnection object. Defaults to 'http://127.0.0.1:4444/wd/hub'.
- - keep_alive - Whether to configure remote_connection.RemoteConnection to use
- HTTP keep-alive. Defaults to True.
- - file_detector - Pass custom file detector object during instantiation. If None,
- then default LocalFileDetector() will be used.
- - options - instance of a driver options.Options class
- """
-
- if isinstance(options, list):
- capabilities = create_matches(options)
- _ignore_local_proxy = False
- else:
- capabilities = options.to_capabilities()
- _ignore_local_proxy = options._ignore_local_proxy
- self.command_executor = command_executor
- if isinstance(self.command_executor, (str, bytes)):
- self.command_executor = get_remote_connection(
- capabilities,
- command_executor=command_executor,
- keep_alive=keep_alive,
- ignore_local_proxy=_ignore_local_proxy,
- )
- self._is_remote = True
- self.session_id = None
- self.caps = {}
- self.pinned_scripts = {}
- self.error_handler = ErrorHandler()
- self._switch_to = SwitchTo(self)
- self._mobile = Mobile(self)
- self.file_detector = file_detector or LocalFileDetector()
- self._authenticator_id = None
- self.start_client()
- self.start_session(capabilities)
-
- def __repr__(self):
- return f'<{type(self).__module__}.{type(self).__name__} (session="{self.session_id}")>'
-
- def __enter__(self):
- return self
-
- def __exit__(
- self,
- exc_type: typing.Optional[typing.Type[BaseException]],
- exc: typing.Optional[BaseException],
- traceback: typing.Optional[types.TracebackType],
- ):
- self.quit()
-
- @contextmanager
- def file_detector_context(self, file_detector_class, *args, **kwargs):
- """Overrides the current file detector (if necessary) in limited
- context. Ensures the original file detector is set afterwards.
- Example::
- with webdriver.file_detector_context(UselessFileDetector):
- someinput.send_keys('/etc/hosts')
- :Args:
- - file_detector_class - Class of the desired file detector. If the class is different
- from the current file_detector, then the class is instantiated with args and kwargs
- and used as a file detector during the duration of the context manager.
- - args - Optional arguments that get passed to the file detector class during
- instantiation.
- - kwargs - Keyword arguments, passed the same way as args.
- """
- last_detector = None
- if not isinstance(self.file_detector, file_detector_class):
- last_detector = self.file_detector
- self.file_detector = file_detector_class(*args, **kwargs)
- try:
- yield
- finally:
- if last_detector:
- self.file_detector = last_detector
-
- @property
- def mobile(self) -> Mobile:
- return self._mobile
-
- @property
- def name(self) -> str:
- """Returns the name of the underlying browser for this instance.
- :Usage:
- ::
- name = driver.name
- """
- if "browserName" in self.caps:
- return self.caps["browserName"]
- raise KeyError("browserName not specified in session capabilities")
-
- def start_client(self):
- """Called before starting a new session.
- This method may be overridden to define custom startup behavior.
- """
- pass
-
- def stop_client(self):
- """Called after executing a quit command.
- This method may be overridden to define custom shutdown
- behavior.
- """
- pass
-
- def start_session(self, capabilities: dict) -> None:
- """Creates a new session with the desired capabilities.
- :Args:
- - capabilities - a capabilities dict to start the session with.
- """
-
- caps = _create_caps(capabilities)
- response = self.execute(Command.NEW_SESSION, caps)["value"]
- self.session_id = response.get("sessionId")
- self.caps = response.get("capabilities")
-
- def _wrap_value(self, value):
- if isinstance(value, dict):
- converted = {}
- for key, val in value.items():
- converted[key] = self._wrap_value(val)
- return converted
- if isinstance(value, self._web_element_cls):
- return {"element-6066-11e4-a52e-4f735466cecf": value.id}
- if isinstance(value, self._shadowroot_cls):
- return {"shadow-6066-11e4-a52e-4f735466cecf": value.id}
- if isinstance(value, list):
- return list(self._wrap_value(item) for item in value)
- return value
-
- def create_web_element(self, element_id: str) -> WebElement:
- """Creates a web element with the specified `element_id`."""
- return self._web_element_cls(self, element_id)
-
- def _unwrap_value(self, value):
- if isinstance(value, dict):
- if "element-6066-11e4-a52e-4f735466cecf" in value:
- return self.create_web_element(value["element-6066-11e4-a52e-4f735466cecf"])
- if "shadow-6066-11e4-a52e-4f735466cecf" in value:
- return self._shadowroot_cls(self, value["shadow-6066-11e4-a52e-4f735466cecf"])
- for key, val in value.items():
- value[key] = self._unwrap_value(val)
- return value
- if isinstance(value, list):
- return list(self._unwrap_value(item) for item in value)
- return value
-
- def execute(self, driver_command: str, params: dict = None) -> dict:
- """Sends a command to be executed by a command.CommandExecutor.
- :Args:
- - driver_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.
- """
- params = self._wrap_value(params)
-
- if self.session_id:
- if not params:
- params = {"sessionId": self.session_id}
- elif "sessionId" not in params:
- params["sessionId"] = self.session_id
-
- response = self.command_executor.execute(driver_command, params)
- if response:
- self.error_handler.check_response(response)
- response["value"] = self._unwrap_value(response.get("value", None))
- return response
- # If the server doesn't send a response, assume the command was
- # a success
- return {"success": 0, "value": None, "sessionId": self.session_id}
-
- def get(self, url: str) -> None:
- """Loads a web page in the current browser session."""
- self.execute(Command.GET, {"url": url})
-
- @property
- def title(self) -> str:
- """Returns the title of the current page.
- :Usage:
- ::
- title = driver.title
- """
- return self.execute(Command.GET_TITLE).get("value", "")
-
- def pin_script(self, script: str, script_key=None) -> ScriptKey:
- """Store common javascript scripts to be executed later by a unique
- hashable ID."""
- script_key_instance = ScriptKey(script_key)
- self.pinned_scripts[script_key_instance.id] = script
- return script_key_instance
-
- def unpin(self, script_key: ScriptKey) -> None:
- """Remove a pinned script from storage."""
- try:
- self.pinned_scripts.pop(script_key.id)
- except KeyError:
- raise KeyError(f"No script with key: {script_key} existed in {self.pinned_scripts}") from None
-
- def get_pinned_scripts(self) -> List[str]:
- return list(self.pinned_scripts)
-
- def execute_script(self, script, *args):
- """Synchronously Executes JavaScript in the current window/frame.
- :Args:
- - script: The JavaScript to execute.
- - \\*args: Any applicable arguments for your JavaScript.
- :Usage:
- ::
- driver.execute_script('return document.title;')
- """
- if isinstance(script, ScriptKey):
- try:
- script = self.pinned_scripts[script.id]
- except KeyError:
- raise JavascriptException("Pinned script could not be found")
-
- converted_args = list(args)
- command = Command.W3C_EXECUTE_SCRIPT
-
- return self.execute(command, {"script": script, "args": converted_args})["value"]
-
- def execute_async_script(self, script: str, *args):
- """Asynchronously Executes JavaScript in the current window/frame.
- :Args:
- - script: The JavaScript to execute.
- - \\*args: Any applicable arguments for your JavaScript.
- :Usage:
- ::
- script = "var callback = arguments[arguments.length - 1]; " \\
- "window.setTimeout(function(){ callback('timeout') }, 3000);"
- driver.execute_async_script(script)
- """
- converted_args = list(args)
- command = Command.W3C_EXECUTE_SCRIPT_ASYNC
-
- return self.execute(command, {"script": script, "args": converted_args})["value"]
-
- @property
- def current_url(self) -> str:
- """Gets the URL of the current page.
- :Usage:
- ::
- driver.current_url
- """
- return self.execute(Command.GET_CURRENT_URL)["value"]
-
- @property
- def page_source(self) -> str:
- """Gets the source of the current page.
- :Usage:
- ::
- driver.page_source
- """
- return self.execute(Command.GET_PAGE_SOURCE)["value"]
-
- def close(self) -> None:
- """Closes the current window.
- :Usage:
- ::
- driver.close()
- """
- self.execute(Command.CLOSE)
-
- def quit(self) -> None:
- """Quits the driver and closes every associated window.
- :Usage:
- ::
- driver.quit()
- """
- try:
- self.execute(Command.QUIT)
- finally:
- self.stop_client()
- self.command_executor.close()
-
- @property
- def current_window_handle(self) -> str:
- """Returns the handle of the current window.
- :Usage:
- ::
- driver.current_window_handle
- """
- return self.execute(Command.W3C_GET_CURRENT_WINDOW_HANDLE)["value"]
-
- @property
- def window_handles(self) -> List[str]:
- """Returns the handles of all windows within the current session.
- :Usage:
- ::
- driver.window_handles
- """
- return self.execute(Command.W3C_GET_WINDOW_HANDLES)["value"]
-
- def maximize_window(self) -> None:
- """Maximizes the current window that webdriver is using."""
- command = Command.W3C_MAXIMIZE_WINDOW
- self.execute(command, None)
-
- def fullscreen_window(self) -> None:
- """Invokes the window manager-specific 'full screen' operation."""
- self.execute(Command.FULLSCREEN_WINDOW)
-
- def minimize_window(self) -> None:
- """Invokes the window manager-specific 'minimize' operation."""
- self.execute(Command.MINIMIZE_WINDOW)
-
- def print_page(self, print_options: Optional[PrintOptions] = None) -> str:
- """Takes PDF of the current page.
- The driver makes a best effort to return a PDF based on the
- provided parameters.
- """
- options = {}
- if print_options:
- options = print_options.to_dict()
-
- return self.execute(Command.PRINT_PAGE, options)["value"]
-
- @property
- def switch_to(self) -> SwitchTo:
- """
- :Returns:
- - SwitchTo: an object containing all options to switch focus into
- :Usage:
- ::
- element = driver.switch_to.active_element
- alert = driver.switch_to.alert
- driver.switch_to.default_content()
- driver.switch_to.frame('frame_name')
- driver.switch_to.frame(1)
- driver.switch_to.frame(driver.find_elements(By.TAG_NAME, "iframe")[0])
- driver.switch_to.parent_frame()
- driver.switch_to.window('main')
- """
- return self._switch_to
-
- # Navigation
- def back(self) -> None:
- """Goes one step backward in the browser history.
- :Usage:
- ::
- driver.back()
- """
- self.execute(Command.GO_BACK)
-
- def forward(self) -> None:
- """Goes one step forward in the browser history.
- :Usage:
- ::
- driver.forward()
- """
- self.execute(Command.GO_FORWARD)
-
- def refresh(self) -> None:
- """Refreshes the current page.
- :Usage:
- ::
- driver.refresh()
- """
- self.execute(Command.REFRESH)
-
- # Options
- def get_cookies(self) -> List[dict]:
- """Returns a set of dictionaries, corresponding to cookies visible in
- the current session.
- :Usage:
- ::
- driver.get_cookies()
- """
- return self.execute(Command.GET_ALL_COOKIES)["value"]
-
- def get_cookie(self, name) -> typing.Optional[typing.Dict]:
- """Get a single cookie by name. Returns the cookie if found, None if
- not.
- :Usage:
- ::
- driver.get_cookie('my_cookie')
- """
- with contextlib.suppress(NoSuchCookieException):
- return self.execute(Command.GET_COOKIE, {"name": name})["value"]
- return None
-
- def delete_cookie(self, name) -> None:
- """Deletes a single cookie with the given name.
- :Usage:
- ::
- driver.delete_cookie('my_cookie')
- """
- self.execute(Command.DELETE_COOKIE, {"name": name})
-
- def delete_all_cookies(self) -> None:
- """Delete all cookies in the scope of the session.
- :Usage:
- ::
- driver.delete_all_cookies()
- """
- self.execute(Command.DELETE_ALL_COOKIES)
-
- def add_cookie(self, cookie_dict) -> None:
- """Adds a cookie to your current session.
- :Args:
- - cookie_dict: A dictionary object, with required keys - "name" and "value";
- optional keys - "path", "domain", "secure", "httpOnly", "expiry", "sameSite"
- :Usage:
- ::
- driver.add_cookie({'name' : 'foo', 'value' : 'bar'})
- driver.add_cookie({'name' : 'foo', 'value' : 'bar', 'path' : '/'})
- driver.add_cookie({'name' : 'foo', 'value' : 'bar', 'path' : '/', 'secure' : True})
- driver.add_cookie({'name' : 'foo', 'value' : 'bar', 'sameSite' : 'Strict'})
- """
- if "sameSite" in cookie_dict:
- assert cookie_dict["sameSite"] in ["Strict", "Lax", "None"]
- self.execute(Command.ADD_COOKIE, {"cookie": cookie_dict})
- else:
- self.execute(Command.ADD_COOKIE, {"cookie": cookie_dict})
-
- # Timeouts
- def implicitly_wait(self, time_to_wait: float) -> None:
- """Sets a sticky timeout to implicitly wait for an element to be found,
- or a command to complete. This method only needs to be called one time
- per session. To set the timeout for calls to execute_async_script, see
- set_script_timeout.
- :Args:
- - time_to_wait: Amount of time to wait (in seconds)
- :Usage:
- ::
- driver.implicitly_wait(30)
- """
- self.execute(Command.SET_TIMEOUTS, {"implicit": int(float(time_to_wait) * 1000)})
-
- def set_script_timeout(self, time_to_wait: float) -> None:
- """Set the amount of time that the script should wait during an
- execute_async_script call before throwing an error.
- :Args:
- - time_to_wait: The amount of time to wait (in seconds)
- :Usage:
- ::
- driver.set_script_timeout(30)
- """
- self.execute(Command.SET_TIMEOUTS, {"script": int(float(time_to_wait) * 1000)})
-
- def set_page_load_timeout(self, time_to_wait: float) -> None:
- """Set the amount of time to wait for a page load to complete before
- throwing an error.
- :Args:
- - time_to_wait: The amount of time to wait
- :Usage:
- ::
- driver.set_page_load_timeout(30)
- """
- try:
- self.execute(Command.SET_TIMEOUTS, {"pageLoad": int(float(time_to_wait) * 1000)})
- except WebDriverException:
- self.execute(Command.SET_TIMEOUTS, {"ms": float(time_to_wait) * 1000, "type": "page load"})
-
- @property
- def timeouts(self) -> Timeouts:
- """Get all the timeouts that have been set on the current session.
- :Usage:
- ::
- driver.timeouts
- :rtype: Timeout
- """
- timeouts = self.execute(Command.GET_TIMEOUTS)["value"]
- timeouts["implicit_wait"] = timeouts.pop("implicit") / 1000
- timeouts["page_load"] = timeouts.pop("pageLoad") / 1000
- timeouts["script"] = timeouts.pop("script") / 1000
- return Timeouts(**timeouts)
-
- @timeouts.setter
- def timeouts(self, timeouts) -> None:
- """Set all timeouts for the session. This will override any previously
- set timeouts.
- :Usage:
- ::
- my_timeouts = Timeouts()
- my_timeouts.implicit_wait = 10
- driver.timeouts = my_timeouts
- """
- _ = self.execute(Command.SET_TIMEOUTS, timeouts._to_json())["value"]
-
- def find_element(self, by=By.ID, value: Optional[str] = None) -> WebElement:
- """Find an element given a By strategy and locator.
- :Usage:
- ::
- element = driver.find_element(By.ID, 'foo')
- :rtype: WebElement
- """
- if isinstance(by, RelativeBy):
- elements = self.find_elements(by=by, value=value)
- if not elements:
- raise NoSuchElementException(f"Cannot locate relative element with: {by.root}")
- return elements[0]
-
- if by == By.ID:
- by = By.CSS_SELECTOR
- value = f'[id="{value}"]'
- elif by == By.CLASS_NAME:
- by = By.CSS_SELECTOR
- value = f".{value}"
- elif by == By.NAME:
- by = By.CSS_SELECTOR
- value = f'[name="{value}"]'
-
- return self.execute(Command.FIND_ELEMENT, {"using": by, "value": value})["value"]
-
- def find_elements(self, by=By.ID, value: Optional[str] = None) -> List[WebElement]:
- """Find elements given a By strategy and locator.
- :Usage:
- ::
- elements = driver.find_elements(By.CLASS_NAME, 'foo')
- :rtype: list of WebElement
- """
- if isinstance(by, RelativeBy):
- _pkg = ".".join(__name__.split(".")[:-1])
- raw_function = pkgutil.get_data(_pkg, "findElements.js").decode("utf8")
- find_element_js = f"/* findElements */return ({raw_function}).apply(null, arguments);"
- return self.execute_script(find_element_js, by.to_dict())
-
- if by == By.ID:
- by = By.CSS_SELECTOR
- value = f'[id="{value}"]'
- elif by == By.CLASS_NAME:
- by = By.CSS_SELECTOR
- value = f".{value}"
- elif by == By.NAME:
- by = By.CSS_SELECTOR
- value = f'[name="{value}"]'
-
- # Return empty list if driver returns null
- # See https://github.com/SeleniumHQ/selenium/issues/4555
- return self.execute(Command.FIND_ELEMENTS, {"using": by, "value": value})["value"] or []
-
- @property
- def capabilities(self) -> dict:
- """Returns the drivers current capabilities being used."""
- return self.caps
-
- def get_screenshot_as_file(self, filename) -> bool:
- """Saves a screenshot of the current window 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:
- ::
- driver.get_screenshot_as_file('/Screenshots/foo.png')
- """
- if not str(filename).lower().endswith(".png"):
- warnings.warn(
- "name used for saved screenshot does not match file type. It should end with a `.png` extension",
- UserWarning,
- stacklevel=2,
- )
- png = self.get_screenshot_as_png()
- try:
- with open(filename, "wb") as f:
- f.write(png)
- except OSError:
- return False
- finally:
- del png
- return True
-
- def save_screenshot(self, filename) -> bool:
- """Saves a screenshot of the current window 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:
- ::
- driver.save_screenshot('/Screenshots/foo.png')
- """
- return self.get_screenshot_as_file(filename)
-
- def get_screenshot_as_png(self) -> bytes:
- """Gets the screenshot of the current window as a binary data.
- :Usage:
- ::
- driver.get_screenshot_as_png()
- """
- return b64decode(self.get_screenshot_as_base64().encode("ascii"))
-
- def get_screenshot_as_base64(self) -> str:
- """Gets the screenshot of the current window as a base64 encoded string
- which is useful in embedded images in HTML.
- :Usage:
- ::
- driver.get_screenshot_as_base64()
- """
- return self.execute(Command.SCREENSHOT)["value"]
-
- def set_window_size(self, width, height, windowHandle: str = "current") -> None:
- """Sets the width and height of the current window. (window.resizeTo)
- :Args:
- - width: the width in pixels to set the window to
- - height: the height in pixels to set the window to
- :Usage:
- ::
- driver.set_window_size(800,600)
- """
- self._check_if_window_handle_is_current(windowHandle)
- self.set_window_rect(width=int(width), height=int(height))
-
- def get_window_size(self, windowHandle: str = "current") -> dict:
- """Gets the width and height of the current window.
- :Usage:
- ::
- driver.get_window_size()
- """
-
- self._check_if_window_handle_is_current(windowHandle)
- size = self.get_window_rect()
-
- if size.get("value", None):
- size = size["value"]
-
- return {k: size[k] for k in ("width", "height")}
-
- def set_window_position(self, x, y, windowHandle: str = "current") -> dict:
- """Sets the x,y position of the current window. (window.moveTo)
- :Args:
- - x: the x-coordinate in pixels to set the window position
- - y: the y-coordinate in pixels to set the window position
- :Usage:
- ::
- driver.set_window_position(0,0)
- """
- self._check_if_window_handle_is_current(windowHandle)
- return self.set_window_rect(x=int(x), y=int(y))
-
- def get_window_position(self, windowHandle="current") -> dict:
- """Gets the x,y position of the current window.
- :Usage:
- ::
- driver.get_window_position()
- """
-
- self._check_if_window_handle_is_current(windowHandle)
- position = self.get_window_rect()
-
- return {k: position[k] for k in ("x", "y")}
-
- def _check_if_window_handle_is_current(self, windowHandle: str) -> None:
- """Warns if the window handle is not equal to `current`."""
- if windowHandle != "current":
- warnings.warn("Only 'current' window is supported for W3C compatible browsers.", stacklevel=2)
-
- def get_window_rect(self) -> dict:
- """Gets the x, y coordinates of the window as well as height and width
- of the current window.
- :Usage:
- ::
- driver.get_window_rect()
- """
- return self.execute(Command.GET_WINDOW_RECT)["value"]
-
- def set_window_rect(self, x=None, y=None, width=None, height=None) -> dict:
- """Sets the x, y coordinates of the window as well as height and width
- of the current window. This method is only supported for W3C compatible
- browsers; other browsers should use `set_window_position` and
- `set_window_size`.
- :Usage:
- ::
- driver.set_window_rect(x=10, y=10)
- driver.set_window_rect(width=100, height=200)
- driver.set_window_rect(x=10, y=10, width=100, height=200)
- """
-
- if (x is None and y is None) and (not height and not width):
- raise InvalidArgumentException("x and y or height and width need values")
-
- return self.execute(Command.SET_WINDOW_RECT, {"x": x, "y": y, "width": width, "height": height})["value"]
-
- @property
- def file_detector(self) -> FileDetector:
- return self._file_detector
-
- @file_detector.setter
- def file_detector(self, detector) -> None:
- """Set the file detector to be used when sending keyboard input. By
- default, this is set to a file detector that does nothing.
- see FileDetector
- see LocalFileDetector
- see UselessFileDetector
- :Args:
- - detector: The detector to use. Must not be None.
- """
- if not detector:
- raise WebDriverException("You may not set a file detector that is null")
- if not isinstance(detector, FileDetector):
- raise WebDriverException("Detector has to be instance of FileDetector")
- self._file_detector = detector
-
- @property
- def orientation(self):
- """Gets the current orientation of the device.
- :Usage:
- ::
- orientation = driver.orientation
- """
- return self.execute(Command.GET_SCREEN_ORIENTATION)["value"]
-
- @orientation.setter
- def orientation(self, value) -> None:
- """Sets the current orientation of the device.
- :Args:
- - value: orientation to set it to.
- :Usage:
- ::
- driver.orientation = 'landscape'
- """
- allowed_values = ["LANDSCAPE", "PORTRAIT"]
- if value.upper() in allowed_values:
- self.execute(Command.SET_SCREEN_ORIENTATION, {"orientation": value})
- else:
- raise WebDriverException("You can only set the orientation to 'LANDSCAPE' and 'PORTRAIT'")
-
- @property
- def log_types(self):
- """Gets a list of the available log types. This only works with w3c
- compliant browsers.
- :Usage:
- ::
- driver.log_types
- """
- return self.execute(Command.GET_AVAILABLE_LOG_TYPES)["value"]
-
- def get_log(self, log_type):
- """Gets the log for a given log type.
- :Args:
- - log_type: type of log that which will be returned
- :Usage:
- ::
- driver.get_log('browser')
- driver.get_log('driver')
- driver.get_log('client')
- driver.get_log('server')
- """
- return self.execute(Command.GET_LOG, {"type": log_type})["value"]
-
- @asynccontextmanager
- async def bidi_connection(self):
- global cdp
- import_cdp()
- if self.caps.get("se:cdp"):
- ws_url = self.caps.get("se:cdp")
- version = self.caps.get("se:cdpVersion").split(".")[0]
- else:
- version, ws_url = self._get_cdp_details()
-
- if not ws_url:
- raise WebDriverException("Unable to find url to connect to from capabilities")
-
- devtools = cdp.import_devtools(version)
- async with cdp.open_cdp(ws_url) as conn:
- targets = await conn.execute(devtools.target.get_targets())
- target_id = targets[0].target_id
- async with conn.open_session(target_id) as session:
- yield BidiConnection(session, cdp, devtools)
-
- def _get_cdp_details(self):
- import json
-
- import urllib3
-
- http = urllib3.PoolManager()
- _firefox = False
- if self.caps.get("browserName") == "chrome":
- debugger_address = self.caps.get("goog:chromeOptions").get("debuggerAddress")
- elif self.caps.get("browserName") == "msedge":
- debugger_address = self.caps.get("ms:edgeOptions").get("debuggerAddress")
- else:
- _firefox = True
- debugger_address = self.caps.get("moz:debuggerAddress")
- res = http.request("GET", f"http://{debugger_address}/json/version")
- data = json.loads(res.data)
-
- browser_version = data.get("Browser")
- websocket_url = data.get("webSocketDebuggerUrl")
-
- import re
-
- if _firefox:
- # Mozilla Automation Team asked to only support 85
- # until WebDriver Bidi is available.
- version = 85
- else:
- version = re.search(r".*/(\d+)\.", browser_version).group(1)
-
- return version, websocket_url
-
- # Virtual Authenticator Methods
- def add_virtual_authenticator(self, options: VirtualAuthenticatorOptions) -> None:
- """Adds a virtual authenticator with the given options."""
- self._authenticator_id = self.execute(Command.ADD_VIRTUAL_AUTHENTICATOR, options.to_dict())["value"]
-
- @property
- def virtual_authenticator_id(self) -> str:
- """Returns the id of the virtual authenticator."""
- return self._authenticator_id
-
- @required_virtual_authenticator
- def remove_virtual_authenticator(self) -> None:
- """Removes a previously added virtual authenticator.
- The authenticator is no longer valid after removal, so no
- methods may be called.
- """
- self.execute(Command.REMOVE_VIRTUAL_AUTHENTICATOR, {"authenticatorId": self._authenticator_id})
- self._authenticator_id = None
-
- @required_virtual_authenticator
- def add_credential(self, credential: Credential) -> None:
- """Injects a credential into the authenticator."""
- self.execute(Command.ADD_CREDENTIAL, {**credential.to_dict(), "authenticatorId": self._authenticator_id})
-
- @required_virtual_authenticator
- def get_credentials(self) -> List[Credential]:
- """Returns the list of credentials owned by the authenticator."""
- credential_data = self.execute(Command.GET_CREDENTIALS, {"authenticatorId": self._authenticator_id})
- return [Credential.from_dict(credential) for credential in credential_data["value"]]
-
- @required_virtual_authenticator
- def remove_credential(self, credential_id: Union[str, bytearray]) -> None:
- """Removes a credential from the authenticator."""
- # Check if the credential is bytearray converted to b64 string
- if isinstance(credential_id, bytearray):
- credential_id = urlsafe_b64encode(credential_id).decode()
-
- self.execute(
- Command.REMOVE_CREDENTIAL, {"credentialId": credential_id, "authenticatorId": self._authenticator_id}
- )
-
- @required_virtual_authenticator
- def remove_all_credentials(self) -> None:
- """Removes all credentials from the authenticator."""
- self.execute(Command.REMOVE_ALL_CREDENTIALS, {"authenticatorId": self._authenticator_id})
-
- @required_virtual_authenticator
- def set_user_verified(self, verified: bool) -> None:
- """Sets whether the authenticator will simulate success or fail on user
- verification.
- verified: True if the authenticator will pass user verification, False otherwise.
- """
- self.execute(Command.SET_USER_VERIFIED, {"authenticatorId": self._authenticator_id, "isUserVerified": verified})
-
- def get_downloadable_files(self) -> dict:
- """Retrieves the downloadable files as a map of file names and their
- corresponding URLs."""
- if "se:downloadsEnabled" not in self.capabilities:
- raise WebDriverException("You must enable downloads in order to work with downloadable files.")
-
- return self.execute(Command.GET_DOWNLOADABLE_FILES)["value"]["names"]
-
- def download_file(self, file_name: str, target_directory: str) -> None:
- """Downloads a file with the specified file name to the target
- directory.
- file_name: The name of the file to download.
- target_directory: The path to the directory to save the downloaded file.
- """
- if "se:downloadsEnabled" not in self.capabilities:
- raise WebDriverException("You must enable downloads in order to work with downloadable files.")
-
- if not os.path.exists(target_directory):
- os.makedirs(target_directory)
-
- contents = self.execute(Command.DOWNLOAD_FILE, {"name": file_name})["value"]["contents"]
-
- target_file = os.path.join(target_directory, file_name)
- with open(target_file, "wb") as file:
- file.write(base64.b64decode(contents))
-
- with zipfile.ZipFile(target_file, "r") as zip_ref:
- zip_ref.extractall(target_directory)
-
- def delete_downloadable_files(self) -> None:
- """Deletes all downloadable files."""
- if "se:downloadsEnabled" not in self.capabilities:
- raise WebDriverException("You must enable downloads in order to work with downloadable files.")
-
- self.execute(Command.DELETE_DOWNLOADABLE_FILES)
-------------------
这段代码看起来是关于对 Web 元素进行操作的,使用了 Selenium WebDriver 库。它定义了一个 WebElement 类,用于表示网页中的 DOM 元素,并提供了一系列方法来操作元素,比如点击、输入文本、获取属性、截图等。除此之外,还包括了一些私有方法和元素定位的方法。这段代码主要是基于 Python 的 Selenium 库,用于模拟浏览器操作网页元素。selenium.webdriver.remote.webelement.WebElement
类是 Selenium WebDriver 中用于表示网页上的元素(如按钮、输入框、链接等)的主要类之一。通过该类,可以对网页上的元素进行查找、操作和获取属性等操作。
常用方法:
click(self) -> None: 点击元素。
submit(self): 提交表单。
clear(self) -> None: 清除文本(如果是文本输入元素)。
send_keys(self, *value) -> None: 模拟向元素输入文本。
find_element(self, by=By.ID, value=None) -> WebElement: 根据查找策略和定位器查找元素。
find_elements(self, by=By.ID, value=None) -> List[WebElement]: 根据查找策略和定位器查找多个元素。
get_attribute(name): 获取元素的指定属性值。
is_displayed(): 判断元素是否可见。
is_enabled(): 判断元素是否可操作。
is_selected(): 判断元素是否被选中(针对复选框、单选框等元素)。
常用属性:
text - 获取元素的文本内容
tag_name - 获取元素的标签名
size - 获取元素的尺寸
location - 获取元素在页面中的位置
rect - 获取元素的位置和大小信息的字典表示
通过这些方法和属性,我们可以对网页元素执行点击、输入文本、获取属性、截图等操作,并获取元素的相关信息。这些方法和属性可以帮助我们实现自动化测试、网页数据采集等功能。
-
- from __future__ import annotations
-
- import os
- import pkgutil
- import warnings
- import zipfile
- from abc import ABCMeta
- from base64 import b64decode
- from base64 import encodebytes
- from hashlib import md5 as md5_hash
- from io import BytesIO
- from typing import List
-
- from selenium.common.exceptions import JavascriptException
- 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.
- getAttribute_js = None
- isDisplayed_js = None
-
-
- def _load_js():
- global getAttribute_js
- global isDisplayed_js
- _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 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_) -> None:
- self._parent = parent
- self._id = id_
-
- def __repr__(self):
- return f'<{type(self).__module__}.{type(self).__name__} (session="{self._parent.session_id}", element="{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."""
- script = (
- "/* submitForm */var form = arguments[0];\n"
- 'while (form.nodeName != "FORM" && form.parentNode) {\n'
- " form = form.parentNode;\n"
- "}\n"
- "if (!form) { throw Error('Unable to find containing form element'); }\n"
- "if (!form.ownerDocument) { throw Error('Unable to find owning document'); }\n"
- "var e = form.ownerDocument.createEvent('Event');\n"
- "e.initEvent('submit', true, true);\n"
- "if (form.dispatchEvent(e)) { HTMLFormElement.prototype.submit.call(form) }\n"
- )
-
- try:
- self._parent.execute_script(script, self)
- except JavascriptException as exc:
- raise WebDriverException("To submit an element, it must be nested inside a form element") from exc
-
- 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 | bool | WebElement | dict:
- """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 | None:
- """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")
- """
- if getAttribute_js is None:
- _load_js()
- attribute_value = self.parent.execute_script(
- f"/* getAttribute */return ({getAttribute_js}).apply(null, arguments);", 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 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, Firefox 96, and Safari 16.4 onwards.
- :Returns:
- - ShadowRoot object or
- - NoSuchShadowRoot - if no shadow root was attached to element
- """
- 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
- if isDisplayed_js is None:
- _load_js()
- return self.parent.execute_script(f"/* isDisplayed */return ({isDisplayed_js}).apply(null, arguments);", 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 zero
- coordinates 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) -> bytes:
- """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 OSError:
- 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) -> WebElement:
- """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 = f'[id="{value}"]'
- elif by == By.CLASS_NAME:
- by = By.CSS_SELECTOR
- value = f".{value}"
- elif by == By.NAME:
- by = By.CSS_SELECTOR
- value = f'[name="{value}"]'
-
- return self._execute(Command.FIND_CHILD_ELEMENT, {"using": by, "value": value})["value"]
-
- def find_elements(self, by=By.ID, value=None) -> List[WebElement]:
- """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 = f'[id="{value}"]'
- elif by == By.CLASS_NAME:
- by = By.CSS_SELECTOR
- value = f".{value}"
- elif by == By.NAME:
- by = By.CSS_SELECTOR
- value = f'[name="{value}"]'
-
- return self._execute(Command.FIND_CHILD_ELEMENTS, {"using": by, "value": value})["value"]
-
- def __hash__(self) -> int:
- 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 str(e):
- return filename
- if "Command not found: POST " in str(e):
- return filename
- if '{"status":405,"value":["GET","HEAD","DELETE"]}' in str(e):
- return filename
- raise
-----------------------
这段代码主要是使用 Python 中的 Selenium WebDriver 库来实现网页交互操作,通过封装常见的用户操作,如点击、鼠标移动、键盘输入等。其中 ActionChains 类可以实现更复杂的用户操作,如移动到元素、点击元素、双击、拖放等。用户可以通过调用不同的方法来模拟在页面上进行各种操作,并通过 perform() 方法执行这些操作。这段代码为进行页面交互操作提供了一个基础框架,可根据具体需求进行扩展和定制。
ActionChains 类涉及到的主要方法和属性如下:
move_to_element(element):将鼠标移动到指定元素上。
click():模拟鼠标左键点击操作。
context_click():模拟鼠标右键点击操作。
double_click():模拟鼠标双击操作。
drag_and_drop(source, target):模拟拖放操作,从源元素拖动到目标元素。
key_down(key):按下指定的键。
key_up(key):释放指定的键。
send_keys(keys):向页面元素发送按键输入。
perform():执行所有排队的操作。
属性:
actions:用于存储需要执行的操作序列。
这些方法和属性可以帮助用户实现各种复杂的页面交互操作,包括鼠标操作、键盘输入等。通过组合调用这些方法,可以模拟用户在页面上的各种操作行为。
-
- """The ActionChains implementation."""
- from __future__ import annotations
-
- from typing import TYPE_CHECKING
- from typing import Union
-
- from selenium.webdriver.remote.webelement import WebElement
-
- from .actions.action_builder import ActionBuilder
- from .actions.key_input import KeyInput
- from .actions.pointer_input import PointerInput
- from .actions.wheel_input import ScrollOrigin
- from .actions.wheel_input import WheelInput
- from .utils import keys_to_typing
-
- if TYPE_CHECKING:
- from selenium.webdriver.remote.webdriver import WebDriver
-
- AnyDevice = Union[PointerInput, KeyInput, WheelInput]
-
-
- class ActionChains:
- """ActionChains are a way to automate low level interactions such as mouse
- movements, mouse button actions, key press, and context menu interactions.
- This is useful for doing more complex actions like hover over and drag and
- drop.
- Generate user actions.
- When you call methods for actions on the ActionChains object,
- the actions are stored in a queue in the ActionChains object.
- When you call perform(), the events are fired in the order they
- are queued up.
- ActionChains can be used in a chain pattern::
- menu = driver.find_element(By.CSS_SELECTOR, ".nav")
- hidden_submenu = driver.find_element(By.CSS_SELECTOR, ".nav #submenu1")
- ActionChains(driver).move_to_element(menu).click(hidden_submenu).perform()
- Or actions can be queued up one by one, then performed.::
- menu = driver.find_element(By.CSS_SELECTOR, ".nav")
- hidden_submenu = driver.find_element(By.CSS_SELECTOR, ".nav #submenu1")
- actions = ActionChains(driver)
- actions.move_to_element(menu)
- actions.click(hidden_submenu)
- actions.perform()
- Either way, the actions are performed in the order they are called, one after
- another.
- """
-
- def __init__(self, driver: WebDriver, duration: int = 250, devices: list[AnyDevice] | None = None) -> None:
- """Creates a new ActionChains.
- :Args:
- - driver: The WebDriver instance which performs user actions.
- - duration: override the default 250 msecs of DEFAULT_MOVE_DURATION in PointerInput
- """
- self._driver = driver
- mouse = None
- keyboard = None
- wheel = None
- if devices is not None and isinstance(devices, list):
- for device in devices:
- if isinstance(device, PointerInput):
- mouse = device
- if isinstance(device, KeyInput):
- keyboard = device
- if isinstance(device, WheelInput):
- wheel = device
- self.w3c_actions = ActionBuilder(driver, mouse=mouse, keyboard=keyboard, wheel=wheel, duration=duration)
-
- def perform(self) -> None:
- """Performs all stored actions."""
- self.w3c_actions.perform()
-
- def reset_actions(self) -> None:
- """Clears actions that are already stored locally and on the remote
- end."""
- self.w3c_actions.clear_actions()
- for device in self.w3c_actions.devices:
- device.clear_actions()
-
- def click(self, on_element: WebElement | None = None) -> ActionChains:
- """Clicks an element.
- :Args:
- - on_element: The element to click.
- If None, clicks on current mouse position.
- """
- if on_element:
- self.move_to_element(on_element)
-
- self.w3c_actions.pointer_action.click()
- self.w3c_actions.key_action.pause()
- self.w3c_actions.key_action.pause()
-
- return self
-
- def click_and_hold(self, on_element: WebElement | None = None) -> ActionChains:
- """Holds down the left mouse button on an element.
- :Args:
- - on_element: The element to mouse down.
- If None, clicks on current mouse position.
- """
- if on_element:
- self.move_to_element(on_element)
-
- self.w3c_actions.pointer_action.click_and_hold()
- self.w3c_actions.key_action.pause()
-
- return self
-
- def context_click(self, on_element: WebElement | None = None) -> ActionChains:
- """Performs a context-click (right click) on an element.
- :Args:
- - on_element: The element to context-click.
- If None, clicks on current mouse position.
- """
- if on_element:
- self.move_to_element(on_element)
-
- self.w3c_actions.pointer_action.context_click()
- self.w3c_actions.key_action.pause()
- self.w3c_actions.key_action.pause()
-
- return self
-
- def double_click(self, on_element: WebElement | None = None) -> ActionChains:
- """Double-clicks an element.
- :Args:
- - on_element: The element to double-click.
- If None, clicks on current mouse position.
- """
- if on_element:
- self.move_to_element(on_element)
-
- self.w3c_actions.pointer_action.double_click()
- for _ in range(4):
- self.w3c_actions.key_action.pause()
-
- return self
-
- def drag_and_drop(self, source: WebElement, target: WebElement) -> ActionChains:
- """Holds down the left mouse button on the source element, then moves
- to the target element and releases the mouse button.
- :Args:
- - source: The element to mouse down.
- - target: The element to mouse up.
- """
- self.click_and_hold(source)
- self.release(target)
- return self
-
- def drag_and_drop_by_offset(self, source: WebElement, xoffset: int, yoffset: int) -> ActionChains:
- """Holds down the left mouse button on the source element, then moves
- to the target offset and releases the mouse button.
- :Args:
- - source: The element to mouse down.
- - xoffset: X offset to move to.
- - yoffset: Y offset to move to.
- """
- self.click_and_hold(source)
- self.move_by_offset(xoffset, yoffset)
- self.release()
- return self
-
- def key_down(self, value: str, element: WebElement | None = None) -> ActionChains:
- """Sends a key press only, without releasing it. Should only be used
- with modifier keys (Control, Alt and Shift).
- :Args:
- - value: The modifier key to send. Values are defined in `Keys` class.
- - element: The element to send keys.
- If None, sends a key to current focused element.
- Example, pressing ctrl+c::
- ActionChains(driver).key_down(Keys.CONTROL).send_keys('c').key_up(Keys.CONTROL).perform()
- """
- if element:
- self.click(element)
-
- self.w3c_actions.key_action.key_down(value)
- self.w3c_actions.pointer_action.pause()
-
- return self
-
- def key_up(self, value: str, element: WebElement | None = None) -> ActionChains:
- """Releases a modifier key.
- :Args:
- - value: The modifier key to send. Values are defined in Keys class.
- - element: The element to send keys.
- If None, sends a key to current focused element.
- Example, pressing ctrl+c::
- ActionChains(driver).key_down(Keys.CONTROL).send_keys('c').key_up(Keys.CONTROL).perform()
- """
- if element:
- self.click(element)
-
- self.w3c_actions.key_action.key_up(value)
- self.w3c_actions.pointer_action.pause()
-
- return self
-
- def move_by_offset(self, xoffset: int, yoffset: int) -> ActionChains:
- """Moving the mouse to an offset from current mouse position.
- :Args:
- - xoffset: X offset to move to, as a positive or negative integer.
- - yoffset: Y offset to move to, as a positive or negative integer.
- """
-
- self.w3c_actions.pointer_action.move_by(xoffset, yoffset)
- self.w3c_actions.key_action.pause()
-
- return self
-
- def move_to_element(self, to_element: WebElement) -> ActionChains:
- """Moving the mouse to the middle of an element.
- :Args:
- - to_element: The WebElement to move to.
- """
-
- self.w3c_actions.pointer_action.move_to(to_element)
- self.w3c_actions.key_action.pause()
-
- return self
-
- def move_to_element_with_offset(self, to_element: WebElement, xoffset: int, yoffset: int) -> ActionChains:
- """Move the mouse by an offset of the specified element. Offsets are
- relative to the in-view center point of the element.
- :Args:
- - to_element: The WebElement to move to.
- - xoffset: X offset to move to, as a positive or negative integer.
- - yoffset: Y offset to move to, as a positive or negative integer.
- """
-
- self.w3c_actions.pointer_action.move_to(to_element, int(xoffset), int(yoffset))
- self.w3c_actions.key_action.pause()
-
- return self
-
- def pause(self, seconds: float | int) -> ActionChains:
- """Pause all inputs for the specified duration in seconds."""
-
- self.w3c_actions.pointer_action.pause(seconds)
- self.w3c_actions.key_action.pause(seconds)
-
- return self
-
- def release(self, on_element: WebElement | None = None) -> ActionChains:
- """Releasing a held mouse button on an element.
- :Args:
- - on_element: The element to mouse up.
- If None, releases on current mouse position.
- """
- if on_element:
- self.move_to_element(on_element)
-
- self.w3c_actions.pointer_action.release()
- self.w3c_actions.key_action.pause()
-
- return self
-
- def send_keys(self, *keys_to_send: str) -> ActionChains:
- """Sends keys to current focused element.
- :Args:
- - keys_to_send: The keys to send. Modifier keys constants can be found in the
- 'Keys' class.
- """
- typing = keys_to_typing(keys_to_send)
-
- for key in typing:
- self.key_down(key)
- self.key_up(key)
-
- return self
-
- def send_keys_to_element(self, element: WebElement, *keys_to_send: str) -> ActionChains:
- """Sends keys to an element.
- :Args:
- - element: The element to send keys.
- - keys_to_send: The keys to send. Modifier keys constants can be found in the
- 'Keys' class.
- """
- self.click(element)
- self.send_keys(*keys_to_send)
- return self
-
- def scroll_to_element(self, element: WebElement) -> ActionChains:
- """If the element is outside the viewport, scrolls the bottom of the
- element to the bottom of the viewport.
- :Args:
- - element: Which element to scroll into the viewport.
- """
-
- self.w3c_actions.wheel_action.scroll(origin=element)
- return self
-
- def scroll_by_amount(self, delta_x: int, delta_y: int) -> ActionChains:
- """Scrolls by provided amounts with the origin in the top left corner
- of the viewport.
- :Args:
- - delta_x: Distance along X axis to scroll using the wheel. A negative value scrolls left.
- - delta_y: Distance along Y axis to scroll using the wheel. A negative value scrolls up.
- """
-
- self.w3c_actions.wheel_action.scroll(delta_x=delta_x, delta_y=delta_y)
- return self
-
- def scroll_from_origin(self, scroll_origin: ScrollOrigin, delta_x: int, delta_y: int) -> ActionChains:
- """Scrolls by provided amount based on a provided origin. The scroll
- origin is either the center of an element or the upper left of the
- viewport plus any offsets. If the origin is an element, and the element
- is not in the viewport, the bottom of the element will first be
- scrolled to the bottom of the viewport.
- :Args:
- - origin: Where scroll originates (viewport or element center) plus provided offsets.
- - delta_x: Distance along X axis to scroll using the wheel. A negative value scrolls left.
- - delta_y: Distance along Y axis to scroll using the wheel. A negative value scrolls up.
- :Raises: If the origin with offset is outside the viewport.
- - MoveTargetOutOfBoundsException - If the origin with offset is outside the viewport.
- """
-
- if not isinstance(scroll_origin, ScrollOrigin):
- raise TypeError(f"Expected object of type ScrollOrigin, got: {type(scroll_origin)}")
-
- self.w3c_actions.wheel_action.scroll(
- origin=scroll_origin.origin,
- x=scroll_origin.x_offset,
- y=scroll_origin.y_offset,
- delta_x=delta_x,
- delta_y=delta_y,
- )
- return self
-
- # Context manager so ActionChains can be used in a 'with .. as' statements.
-
- def __enter__(self) -> ActionChains:
- return self # Return created instance of self.
-
- def __exit__(self, _type, _value, _traceback) -> None:
- pass # Do nothing, does not require additional cleanup.
-----------------------
这是一段 Python 代码,它定义了一些用于 WebDriver 测试的“预期条件”(Expected Conditions)。预期条件是一种在测试中使用的断言,用于检查页面或元素的状态是否符合预期。
这段代码中定义了许多常用的预期条件函数,如检查页面标题、检查元素是否可见、检查元素是否存在、检查元素是否包含文本等。你可以根据需要选择并使用这些预期条件函数来辅助你的测试。
这段代码还使用了 Selenium 提供的异常类,如 NoAlertPresentException、NoSuchElementException 等,用于处理在测试过程中可能出现的异常情况。
总之,这段代码提供了一些方便的方法来进行基于 WebDriver 的测试,并对页面和元素的状态进行验证。你可以根据自己的需求和场景使用这些预期条件函数。
这段代码涉及了一些 Selenium WebDriver 测试中常用的预期条件方法和属性,主要包括以下内容:
Expected Conditions 方法:
title_is(title):检查页面标题是否等于给定的标题。
title_contains(title):检查页面标题是否包含给定的字符串。
presence_of_element_located(locator):检查是否存在满足定位器条件的元素。
visibility_of_element_located(locator):检查满足定位器条件的元素是否可见。
visibility_of(element):检查元素是否可见。
text_to_be_present_in_element(element, text_):检查元素是否包含指定文本。
element_to_be_clickable(locator):检查元素是否可点击。
Selenium 异常类:
NoSuchElementException:当找不到元素时抛出此异常。
TimeoutException:超时异常,用于处理等待超时的情况。
NoAlertPresentException:当尝试操作不存在的警报时抛出。
这些方法和属性能够帮助测试人员编写更健壮和可靠的测试脚本,用于验证页面和元素的状态是否符合预期。通过结合预期条件和异常处理,可以提高测试的稳定性和准确性。
-
-
- import re
- from collections.abc import Iterable
- from typing import Any
- from typing import Callable
- from typing import List
- from typing import Literal
- from typing import Tuple
- from typing import TypeVar
- from typing import Union
-
- from selenium.common.exceptions import NoAlertPresentException
- from selenium.common.exceptions import NoSuchElementException
- from selenium.common.exceptions import NoSuchFrameException
- from selenium.common.exceptions import StaleElementReferenceException
- from selenium.common.exceptions import WebDriverException
- from selenium.webdriver.common.alert import Alert
- from selenium.webdriver.remote.webdriver import WebDriver
- from selenium.webdriver.remote.webdriver import WebElement
-
- """
- * Canned "Expected Conditions" which are generally useful within webdriver
- * tests.
- """
-
- D = TypeVar("D")
- T = TypeVar("T")
-
- WebDriverOrWebElement = Union[WebDriver, WebElement]
-
-
- def title_is(title: str) -> Callable[[WebDriver], bool]:
- """An expectation for checking the title of a page.
- title is the expected title, which must be an exact match returns
- True if the title matches, false otherwise.
- """
-
- def _predicate(driver: WebDriver):
- return driver.title == title
-
- return _predicate
-
-
- def title_contains(title: str) -> Callable[[WebDriver], bool]:
- """An expectation for checking that the title contains a case-sensitive
- substring.
- title is the fragment of title expected returns True when the title
- matches, False otherwise
- """
-
- def _predicate(driver: WebDriver):
- return title in driver.title
-
- return _predicate
-
-
- def presence_of_element_located(locator: Tuple[str, str]) -> Callable[[WebDriverOrWebElement], WebElement]:
- """An expectation for checking that an element is present on the DOM of a
- page. This does not necessarily mean that the element is visible.
- locator - used to find the element
- returns the WebElement once it is located
- """
-
- def _predicate(driver: WebDriverOrWebElement):
- return driver.find_element(*locator)
-
- return _predicate
-
-
- def url_contains(url: str) -> Callable[[WebDriver], bool]:
- """An expectation for checking that the current url contains a case-
- sensitive substring.
- url is the fragment of url expected, returns True when the url
- matches, False otherwise
- """
-
- def _predicate(driver: WebDriver):
- return url in driver.current_url
-
- return _predicate
-
-
- def url_matches(pattern: str) -> Callable[[WebDriver], bool]:
- """An expectation for checking the current url.
- pattern is the expected pattern. This finds the first occurrence of
- pattern in the current url and as such does not require an exact
- full match.
- """
-
- def _predicate(driver: WebDriver):
- return re.search(pattern, driver.current_url) is not None
-
- return _predicate
-
-
- def url_to_be(url: str) -> Callable[[WebDriver], bool]:
- """An expectation for checking the current url.
- url is the expected url, which must be an exact match returns True
- if the url matches, false otherwise.
- """
-
- def _predicate(driver: WebDriver):
- return url == driver.current_url
-
- return _predicate
-
-
- def url_changes(url: str) -> Callable[[WebDriver], bool]:
- """An expectation for checking the current url.
- url is the expected url, which must not be an exact match returns
- True if the url is different, false otherwise.
- """
-
- def _predicate(driver: WebDriver):
- return url != driver.current_url
-
- return _predicate
-
-
- def visibility_of_element_located(
- locator: Tuple[str, str]
- ) -> Callable[[WebDriverOrWebElement], Union[Literal[False], WebElement]]:
- """An expectation for checking that an element is present on the DOM of a
- page and visible. Visibility means that the element is not only displayed
- but also has a height and width that is greater than 0.
- locator - used to find the element
- returns the WebElement once it is located and visible
- """
-
- def _predicate(driver: WebDriverOrWebElement):
- try:
- return _element_if_visible(driver.find_element(*locator))
- except StaleElementReferenceException:
- return False
-
- return _predicate
-
-
- def visibility_of(element: WebElement) -> Callable[[Any], Union[Literal[False], WebElement]]:
- """An expectation for checking that an element, known to be present on the
- DOM of a page, is visible.
- Visibility means that the element is not only displayed but also has
- a height and width that is greater than 0. element is the WebElement
- returns the (same) WebElement once it is visible
- """
-
- def _predicate(_):
- return _element_if_visible(element)
-
- return _predicate
-
-
- def _element_if_visible(element: WebElement, visibility: bool = True) -> Union[Literal[False], WebElement]:
- return element if element.is_displayed() == visibility else False
-
-
- def presence_of_all_elements_located(locator: Tuple[str, str]) -> Callable[[WebDriverOrWebElement], List[WebElement]]:
- """An expectation for checking that there is at least one element present
- on a web page.
- locator is used to find the element returns the list of WebElements
- once they are located
- """
-
- def _predicate(driver: WebDriverOrWebElement):
- return driver.find_elements(*locator)
-
- return _predicate
-
-
- def visibility_of_any_elements_located(locator: Tuple[str, str]) -> Callable[[WebDriverOrWebElement], List[WebElement]]:
- """An expectation for checking that there is at least one element visible
- on a web page.
- locator is used to find the element returns the list of WebElements
- once they are located
- """
-
- def _predicate(driver: WebDriverOrWebElement):
- return [element for element in driver.find_elements(*locator) if _element_if_visible(element)]
-
- return _predicate
-
-
- def visibility_of_all_elements_located(
- locator: Tuple[str, str]
- ) -> Callable[[WebDriverOrWebElement], Union[List[WebElement], Literal[False]]]:
- """An expectation for checking that all elements are present on the DOM of
- a page and visible. Visibility means that the elements are not only
- displayed but also has a height and width that is greater than 0.
- locator - used to find the elements
- returns the list of WebElements once they are located and visible
- """
-
- def _predicate(driver: WebDriverOrWebElement):
- try:
- elements = driver.find_elements(*locator)
- for element in elements:
- if _element_if_visible(element, visibility=False):
- return False
- return elements
- except StaleElementReferenceException:
- return False
-
- return _predicate
-
-
- def text_to_be_present_in_element(locator: Tuple[str, str], text_: str) -> Callable[[WebDriverOrWebElement], bool]:
- """An expectation for checking if the given text is present in the
- specified element.
- locator, text
- """
-
- def _predicate(driver: WebDriverOrWebElement):
- try:
- element_text = driver.find_element(*locator).text
- return text_ in element_text
- except StaleElementReferenceException:
- return False
-
- return _predicate
-
-
- def text_to_be_present_in_element_value(
- locator: Tuple[str, str], text_: str
- ) -> Callable[[WebDriverOrWebElement], bool]:
- """An expectation for checking if the given text is present in the
- element's value.
- locator, text
- """
-
- def _predicate(driver: WebDriverOrWebElement):
- try:
- element_text = driver.find_element(*locator).get_attribute("value")
- return text_ in element_text
- except StaleElementReferenceException:
- return False
-
- return _predicate
-
-
- def text_to_be_present_in_element_attribute(
- locator: Tuple[str, str], attribute_: str, text_: str
- ) -> Callable[[WebDriverOrWebElement], bool]:
- """An expectation for checking if the given text is present in the
- element's attribute.
- locator, attribute, text
- """
-
- def _predicate(driver: WebDriverOrWebElement):
- try:
- element_text = driver.find_element(*locator).get_attribute(attribute_)
- if element_text is None:
- return False
- return text_ in element_text
- except StaleElementReferenceException:
- return False
-
- return _predicate
-
-
- def frame_to_be_available_and_switch_to_it(locator: Union[Tuple[str, str], str]) -> Callable[[WebDriver], bool]:
- """An expectation for checking whether the given frame is available to
- switch to.
- If the frame is available it switches the given driver to the
- specified frame.
- """
-
- def _predicate(driver: WebDriver):
- try:
- if isinstance(locator, Iterable) and not isinstance(locator, str):
- driver.switch_to.frame(driver.find_element(*locator))
- else:
- driver.switch_to.frame(locator)
- return True
- except NoSuchFrameException:
- return False
-
- return _predicate
-
-
- def invisibility_of_element_located(
- locator: Union[WebElement, Tuple[str, str]]
- ) -> Callable[[WebDriverOrWebElement], Union[WebElement, bool]]:
- """An Expectation for checking that an element is either invisible or not
- present on the DOM.
- locator used to find the element
- """
-
- def _predicate(driver: WebDriverOrWebElement):
- try:
- target = locator
- if not isinstance(target, WebElement):
- target = driver.find_element(*target)
- return _element_if_visible(target, visibility=False)
- except (NoSuchElementException, StaleElementReferenceException):
- # In the case of NoSuchElement, returns true because the element is
- # not present in DOM. The try block checks if the element is present
- # but is invisible.
- # In the case of StaleElementReference, returns true because stale
- # element reference implies that element is no longer visible.
- return True
-
- return _predicate
-
-
- def invisibility_of_element(
- element: Union[WebElement, Tuple[str, str]]
- ) -> Callable[[WebDriverOrWebElement], Union[WebElement, bool]]:
- """An Expectation for checking that an element is either invisible or not
- present on the DOM.
- element is either a locator (text) or an WebElement
- """
- return invisibility_of_element_located(element)
-
-
- def element_to_be_clickable(
- mark: Union[WebElement, Tuple[str, str]]
- ) -> Callable[[WebDriverOrWebElement], Union[Literal[False], WebElement]]:
- """An Expectation for checking an element is visible and enabled such that
- you can click it.
- element is either a locator (text) or an WebElement
- """
-
- # renamed argument to 'mark', to indicate that both locator
- # and WebElement args are valid
- def _predicate(driver: WebDriverOrWebElement):
- target = mark
- if not isinstance(target, WebElement): # if given locator instead of WebElement
- target = driver.find_element(*target) # grab element at locator
- element = visibility_of(target)(driver)
- if element and element.is_enabled():
- return element
- return False
-
- return _predicate
-
-
- def staleness_of(element: WebElement) -> Callable[[Any], bool]:
- """Wait until an element is no longer attached to the DOM.
- element is the element to wait for. returns False if the element is
- still attached to the DOM, true otherwise.
- """
-
- def _predicate(_):
- try:
- # Calling any method forces a staleness check
- element.is_enabled()
- return False
- except StaleElementReferenceException:
- return True
-
- return _predicate
-
-
- def element_to_be_selected(element: WebElement) -> Callable[[Any], bool]:
- """An expectation for checking the selection is selected.
- element is WebElement object
- """
-
- def _predicate(_):
- return element.is_selected()
-
- return _predicate
-
-
- def element_located_to_be_selected(locator: Tuple[str, str]) -> Callable[[WebDriverOrWebElement], bool]:
- """An expectation for the element to be located is selected.
- locator is a tuple of (by, path)
- """
-
- def _predicate(driver: WebDriverOrWebElement):
- return driver.find_element(*locator).is_selected()
-
- return _predicate
-
-
- def element_selection_state_to_be(element: WebElement, is_selected: bool) -> Callable[[Any], bool]:
- """An expectation for checking if the given element is selected.
- element is WebElement object is_selected is a Boolean.
- """
-
- def _predicate(_):
- return element.is_selected() == is_selected
-
- return _predicate
-
-
- def element_located_selection_state_to_be(
- locator: Tuple[str, str], is_selected: bool
- ) -> Callable[[WebDriverOrWebElement], bool]:
- """An expectation to locate an element and check if the selection state
- specified is in that state.
- locator is a tuple of (by, path) is_selected is a boolean
- """
-
- def _predicate(driver: WebDriverOrWebElement):
- try:
- element = driver.find_element(*locator)
- return element.is_selected() == is_selected
- except StaleElementReferenceException:
- return False
-
- return _predicate
-
-
- def number_of_windows_to_be(num_windows: int) -> Callable[[WebDriver], bool]:
- """An expectation for the number of windows to be a certain value."""
-
- def _predicate(driver: WebDriver):
- return len(driver.window_handles) == num_windows
-
- return _predicate
-
-
- def new_window_is_opened(current_handles: List[str]) -> Callable[[WebDriver], bool]:
- """An expectation that a new window will be opened and have the number of
- windows handles increase."""
-
- def _predicate(driver: WebDriver):
- return len(driver.window_handles) > len(current_handles)
-
- return _predicate
-
-
- def alert_is_present() -> Callable[[WebDriver], Union[Alert, Literal[False]]]:
- """An expectation for checking if an alert is currently present and
- switching to it."""
-
- def _predicate(driver: WebDriver):
- try:
- return driver.switch_to.alert
- except NoAlertPresentException:
- return False
-
- return _predicate
-
-
- def element_attribute_to_include(locator: Tuple[str, str], attribute_: str) -> Callable[[WebDriverOrWebElement], bool]:
- """An expectation for checking if the given attribute is included in the
- specified element.
- locator, attribute
- """
-
- def _predicate(driver: WebDriverOrWebElement):
- try:
- element_attribute = driver.find_element(*locator).get_attribute(attribute_)
- return element_attribute is not None
- except StaleElementReferenceException:
- return False
-
- return _predicate
-
-
- def any_of(*expected_conditions: Callable[[D], T]) -> Callable[[D], Union[Literal[False], T]]:
- """An expectation that any of multiple expected conditions is true.
- Equivalent to a logical 'OR'. Returns results of the first matching
- condition, or False if none do.
- """
-
- def any_of_condition(driver: D):
- for expected_condition in expected_conditions:
- try:
- result = expected_condition(driver)
- if result:
- return result
- except WebDriverException:
- pass
- return False
-
- return any_of_condition
-
-
- def all_of(
- *expected_conditions: Callable[[D], Union[T, Literal[False]]]
- ) -> Callable[[D], Union[List[T], Literal[False]]]:
- """An expectation that all of multiple expected conditions is true.
- Equivalent to a logical 'AND'.
- Returns: When any ExpectedCondition is not met: False.
- When all ExpectedConditions are met: A List with each ExpectedCondition's return value.
- """
-
- def all_of_condition(driver: D):
- results: List[T] = []
- for expected_condition in expected_conditions:
- try:
- result = expected_condition(driver)
- if not result:
- return False
- results.append(result)
- except WebDriverException:
- return False
- return results
-
- return all_of_condition
-
-
- def none_of(*expected_conditions: Callable[[D], Any]) -> Callable[[D], bool]:
- """An expectation that none of 1 or multiple expected conditions is true.
- Equivalent to a logical 'NOT-OR'. Returns a Boolean
- """
-
- def none_of_condition(driver: D):
- for expected_condition in expected_conditions:
- try:
- result = expected_condition(driver)
- if result:
- return False
- except WebDriverException:
- pass
- return True
-
- return none_of_condition
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。