1# Licensed to the Software Freedom Conservancy (SFC) under one 2# or more contributor license agreements. See the NOTICE file 3# distributed with this work for additional information 4# regarding copyright ownership. The SFC licenses this file 5# to you under the Apache License, Version 2.0 (the 6# "License"); you may not use this file except in compliance 7# with the License. You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, 12# software distributed under the License is distributed on an 13# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14# KIND, either express or implied. See the License for the 15# specific language governing permissions and limitations 16# under the License. 17 18"""The WebDriver implementation.""" 19 20import base64 21import copy 22import warnings 23from contextlib import contextmanager 24 25from .command import Command 26from .webelement import WebElement 27from .remote_connection import RemoteConnection 28from .errorhandler import ErrorHandler 29from .switch_to import SwitchTo 30from .mobile import Mobile 31from .file_detector import FileDetector, LocalFileDetector 32from selenium.common.exceptions import (InvalidArgumentException, 33 WebDriverException, 34 NoSuchCookieException) 35from selenium.webdriver.common.by import By 36from selenium.webdriver.common.html5.application_cache import ApplicationCache 37 38try: 39 str = basestring 40except NameError: 41 pass 42 43 44_W3C_CAPABILITY_NAMES = frozenset([ 45 'acceptInsecureCerts', 46 'browserName', 47 'browserVersion', 48 'platformName', 49 'pageLoadStrategy', 50 'proxy', 51 'setWindowRect', 52 'timeouts', 53 'unhandledPromptBehavior', 54]) 55 56_OSS_W3C_CONVERSION = { 57 'acceptSslCerts': 'acceptInsecureCerts', 58 'version': 'browserVersion', 59 'platform': 'platformName' 60} 61 62 63def _make_w3c_caps(caps): 64 """Makes a W3C alwaysMatch capabilities object. 65 66 Filters out capability names that are not in the W3C spec. Spec-compliant 67 drivers will reject requests containing unknown capability names. 68 69 Moves the Firefox profile, if present, from the old location to the new Firefox 70 options object. 71 72 :Args: 73 - caps - A dictionary of capabilities requested by the caller. 74 """ 75 caps = copy.deepcopy(caps) 76 profile = caps.get('firefox_profile') 77 always_match = {} 78 if caps.get('proxy') and caps['proxy'].get('proxyType'): 79 caps['proxy']['proxyType'] = caps['proxy']['proxyType'].lower() 80 for k, v in caps.items(): 81 if v and k in _OSS_W3C_CONVERSION: 82 always_match[_OSS_W3C_CONVERSION[k]] = v.lower() if k == 'platform' else v 83 if k in _W3C_CAPABILITY_NAMES or ':' in k: 84 always_match[k] = v 85 if profile: 86 moz_opts = always_match.get('moz:firefoxOptions', {}) 87 # If it's already present, assume the caller did that intentionally. 88 if 'profile' not in moz_opts: 89 # Don't mutate the original capabilities. 90 new_opts = copy.deepcopy(moz_opts) 91 new_opts['profile'] = profile 92 always_match['moz:firefoxOptions'] = new_opts 93 return {"firstMatch": [{}], "alwaysMatch": always_match} 94 95 96class WebDriver(object): 97 """ 98 Controls a browser by sending commands to a remote server. 99 This server is expected to be running the WebDriver wire protocol 100 as defined at 101 https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol 102 103 :Attributes: 104 - session_id - String ID of the browser session started and controlled by this WebDriver. 105 - capabilities - Dictionaty of effective capabilities of this browser session as returned 106 by the remote server. See https://github.com/SeleniumHQ/selenium/wiki/DesiredCapabilities 107 - command_executor - remote_connection.RemoteConnection object used to execute commands. 108 - error_handler - errorhandler.ErrorHandler object used to handle errors. 109 """ 110 111 _web_element_cls = WebElement 112 113 def __init__(self, command_executor='http://127.0.0.1:4444/wd/hub', 114 desired_capabilities=None, browser_profile=None, proxy=None, 115 keep_alive=False, file_detector=None, options=None): 116 """ 117 Create a new driver that will issue commands using the wire protocol. 118 119 :Args: 120 - command_executor - Either a string representing URL of the remote server or a custom 121 remote_connection.RemoteConnection object. Defaults to 'http://127.0.0.1:4444/wd/hub'. 122 - desired_capabilities - A dictionary of capabilities to request when 123 starting the browser session. Required parameter. 124 - browser_profile - A selenium.webdriver.firefox.firefox_profile.FirefoxProfile object. 125 Only used if Firefox is requested. Optional. 126 - proxy - A selenium.webdriver.common.proxy.Proxy object. The browser session will 127 be started with given proxy settings, if possible. Optional. 128 - keep_alive - Whether to configure remote_connection.RemoteConnection to use 129 HTTP keep-alive. Defaults to False. 130 - file_detector - Pass custom file detector object during instantiation. If None, 131 then default LocalFileDetector() will be used. 132 - options - instance of a driver options.Options class 133 """ 134 capabilities = {} 135 if options is not None: 136 capabilities = options.to_capabilities() 137 if desired_capabilities is not None: 138 if not isinstance(desired_capabilities, dict): 139 raise WebDriverException("Desired Capabilities must be a dictionary") 140 else: 141 capabilities.update(desired_capabilities) 142 if proxy is not None: 143 warnings.warn("Please use FirefoxOptions to set proxy", 144 DeprecationWarning, stacklevel=2) 145 proxy.add_to_capabilities(capabilities) 146 self.command_executor = command_executor 147 if type(self.command_executor) is bytes or isinstance(self.command_executor, str): 148 self.command_executor = RemoteConnection(command_executor, keep_alive=keep_alive) 149 self._is_remote = True 150 self.session_id = None 151 self.capabilities = {} 152 self.error_handler = ErrorHandler() 153 self.start_client() 154 if browser_profile is not None: 155 warnings.warn("Please use FirefoxOptions to set browser profile", 156 DeprecationWarning, stacklevel=2) 157 self.start_session(capabilities, browser_profile) 158 self._switch_to = SwitchTo(self) 159 self._mobile = Mobile(self) 160 self.file_detector = file_detector or LocalFileDetector() 161 162 def __repr__(self): 163 return '<{0.__module__}.{0.__name__} (session="{1}")>'.format( 164 type(self), self.session_id) 165 166 def __enter__(self): 167 return self 168 169 def __exit__(self, *args): 170 self.quit() 171 172 @contextmanager 173 def file_detector_context(self, file_detector_class, *args, **kwargs): 174 """ 175 Overrides the current file detector (if necessary) in limited context. 176 Ensures the original file detector is set afterwards. 177 178 Example: 179 180 with webdriver.file_detector_context(UselessFileDetector): 181 someinput.send_keys('/etc/hosts') 182 183 :Args: 184 - file_detector_class - Class of the desired file detector. If the class is different 185 from the current file_detector, then the class is instantiated with args and kwargs 186 and used as a file detector during the duration of the context manager. 187 - args - Optional arguments that get passed to the file detector class during 188 instantiation. 189 - kwargs - Keyword arguments, passed the same way as args. 190 """ 191 last_detector = None 192 if not isinstance(self.file_detector, file_detector_class): 193 last_detector = self.file_detector 194 self.file_detector = file_detector_class(*args, **kwargs) 195 try: 196 yield 197 finally: 198 if last_detector is not None: 199 self.file_detector = last_detector 200 201 @property 202 def mobile(self): 203 return self._mobile 204 205 @property 206 def name(self): 207 """Returns the name of the underlying browser for this instance. 208 209 :Usage: 210 name = driver.name 211 """ 212 if 'browserName' in self.capabilities: 213 return self.capabilities['browserName'] 214 else: 215 raise KeyError('browserName not specified in session capabilities') 216 217 def start_client(self): 218 """ 219 Called before starting a new session. This method may be overridden 220 to define custom startup behavior. 221 """ 222 pass 223 224 def stop_client(self): 225 """ 226 Called after executing a quit command. This method may be overridden 227 to define custom shutdown behavior. 228 """ 229 pass 230 231 def start_session(self, capabilities, browser_profile=None): 232 """ 233 Creates a new session with the desired capabilities. 234 235 :Args: 236 - browser_name - The name of the browser to request. 237 - version - Which browser version to request. 238 - platform - Which platform to request the browser on. 239 - javascript_enabled - Whether the new session should support JavaScript. 240 - browser_profile - A selenium.webdriver.firefox.firefox_profile.FirefoxProfile object. Only used if Firefox is requested. 241 """ 242 if not isinstance(capabilities, dict): 243 raise InvalidArgumentException("Capabilities must be a dictionary") 244 if browser_profile: 245 if "moz:firefoxOptions" in capabilities: 246 capabilities["moz:firefoxOptions"]["profile"] = browser_profile.encoded 247 else: 248 capabilities.update({'firefox_profile': browser_profile.encoded}) 249 w3c_caps = _make_w3c_caps(capabilities) 250 parameters = {"capabilities": w3c_caps, 251 "desiredCapabilities": capabilities} 252 response = self.execute(Command.NEW_SESSION, parameters) 253 if 'sessionId' not in response: 254 response = response['value'] 255 self.session_id = response['sessionId'] 256 self.capabilities = response.get('value') 257 258 # if capabilities is none we are probably speaking to 259 # a W3C endpoint 260 if self.capabilities is None: 261 self.capabilities = response.get('capabilities') 262 263 # Double check to see if we have a W3C Compliant browser 264 self.w3c = response.get('status') is None 265 self.command_executor.w3c = self.w3c 266 267 def _wrap_value(self, value): 268 if isinstance(value, dict): 269 converted = {} 270 for key, val in value.items(): 271 converted[key] = self._wrap_value(val) 272 return converted 273 elif isinstance(value, self._web_element_cls): 274 return {'ELEMENT': value.id, 'element-6066-11e4-a52e-4f735466cecf': value.id} 275 elif isinstance(value, list): 276 return list(self._wrap_value(item) for item in value) 277 else: 278 return value 279 280 def create_web_element(self, element_id): 281 """Creates a web element with the specified `element_id`.""" 282 return self._web_element_cls(self, element_id, w3c=self.w3c) 283 284 def _unwrap_value(self, value): 285 if isinstance(value, dict): 286 if 'ELEMENT' in value or 'element-6066-11e4-a52e-4f735466cecf' in value: 287 wrapped_id = value.get('ELEMENT', None) 288 if wrapped_id: 289 return self.create_web_element(value['ELEMENT']) 290 else: 291 return self.create_web_element(value['element-6066-11e4-a52e-4f735466cecf']) 292 else: 293 for key, val in value.items(): 294 value[key] = self._unwrap_value(val) 295 return value 296 elif isinstance(value, list): 297 return list(self._unwrap_value(item) for item in value) 298 else: 299 return value 300 301 def execute(self, driver_command, params=None): 302 """ 303 Sends a command to be executed by a command.CommandExecutor. 304 305 :Args: 306 - driver_command: The name of the command to execute as a string. 307 - params: A dictionary of named parameters to send with the command. 308 309 :Returns: 310 The command's JSON response loaded into a dictionary object. 311 """ 312 if self.session_id is not None: 313 if not params: 314 params = {'sessionId': self.session_id} 315 elif 'sessionId' not in params: 316 params['sessionId'] = self.session_id 317 318 params = self._wrap_value(params) 319 response = self.command_executor.execute(driver_command, params) 320 if response: 321 self.error_handler.check_response(response) 322 response['value'] = self._unwrap_value( 323 response.get('value', None)) 324 return response 325 # If the server doesn't send a response, assume the command was 326 # a success 327 return {'success': 0, 'value': None, 'sessionId': self.session_id} 328 329 def get(self, url): 330 """ 331 Loads a web page in the current browser session. 332 """ 333 self.execute(Command.GET, {'url': url}) 334 335 @property 336 def title(self): 337 """Returns the title of the current page. 338 339 :Usage: 340 title = driver.title 341 """ 342 resp = self.execute(Command.GET_TITLE) 343 return resp['value'] if resp['value'] is not None else "" 344 345 def find_element_by_id(self, id_): 346 """Finds an element by id. 347 348 :Args: 349 - id\_ - The id of the element to be found. 350 351 :Returns: 352 - WebElement - the element if it was found 353 354 :Raises: 355 - NoSuchElementException - if the element wasn't found 356 357 :Usage: 358 element = driver.find_element_by_id('foo') 359 """ 360 return self.find_element(by=By.ID, value=id_) 361 362 def find_elements_by_id(self, id_): 363 """ 364 Finds multiple elements by id. 365 366 :Args: 367 - id\_ - The id of the elements to be found. 368 369 :Returns: 370 - list of WebElement - a list with elements if any was found. An 371 empty list if not 372 373 :Usage: 374 elements = driver.find_elements_by_id('foo') 375 """ 376 return self.find_elements(by=By.ID, value=id_) 377 378 def find_element_by_xpath(self, xpath): 379 """ 380 Finds an element by xpath. 381 382 :Args: 383 - xpath - The xpath locator of the element to find. 384 385 :Returns: 386 - WebElement - the element if it was found 387 388 :Raises: 389 - NoSuchElementException - if the element wasn't found 390 391 :Usage: 392 element = driver.find_element_by_xpath('//div/td[1]') 393 """ 394 return self.find_element(by=By.XPATH, value=xpath) 395 396 def find_elements_by_xpath(self, xpath): 397 """ 398 Finds multiple elements by xpath. 399 400 :Args: 401 - xpath - The xpath locator of the elements to be found. 402 403 :Returns: 404 - list of WebElement - a list with elements if any was found. An 405 empty list if not 406 407 :Usage: 408 elements = driver.find_elements_by_xpath("//div[contains(@class, 'foo')]") 409 """ 410 return self.find_elements(by=By.XPATH, value=xpath) 411 412 def find_element_by_link_text(self, link_text): 413 """ 414 Finds an element by link text. 415 416 :Args: 417 - link_text: The text of the element to be found. 418 419 :Returns: 420 - WebElement - the element if it was found 421 422 :Raises: 423 - NoSuchElementException - if the element wasn't found 424 425 :Usage: 426 element = driver.find_element_by_link_text('Sign In') 427 """ 428 return self.find_element(by=By.LINK_TEXT, value=link_text) 429 430 def find_elements_by_link_text(self, text): 431 """ 432 Finds elements by link text. 433 434 :Args: 435 - link_text: The text of the elements to be found. 436 437 :Returns: 438 - list of webelement - a list with elements if any was found. an 439 empty list if not 440 441 :Usage: 442 elements = driver.find_elements_by_link_text('Sign In') 443 """ 444 return self.find_elements(by=By.LINK_TEXT, value=text) 445 446 def find_element_by_partial_link_text(self, link_text): 447 """ 448 Finds an element by a partial match of its link text. 449 450 :Args: 451 - link_text: The text of the element to partially match on. 452 453 :Returns: 454 - WebElement - the element if it was found 455 456 :Raises: 457 - NoSuchElementException - if the element wasn't found 458 459 :Usage: 460 element = driver.find_element_by_partial_link_text('Sign') 461 """ 462 return self.find_element(by=By.PARTIAL_LINK_TEXT, value=link_text) 463 464 def find_elements_by_partial_link_text(self, link_text): 465 """ 466 Finds elements by a partial match of their link text. 467 468 :Args: 469 - link_text: The text of the element to partial match on. 470 471 :Returns: 472 - list of webelement - a list with elements if any was found. an 473 empty list if not 474 475 :Usage: 476 elements = driver.find_elements_by_partial_link_text('Sign') 477 """ 478 return self.find_elements(by=By.PARTIAL_LINK_TEXT, value=link_text) 479 480 def find_element_by_name(self, name): 481 """ 482 Finds an element by name. 483 484 :Args: 485 - name: The name of the element to find. 486 487 :Returns: 488 - WebElement - the element if it was found 489 490 :Raises: 491 - NoSuchElementException - if the element wasn't found 492 493 :Usage: 494 element = driver.find_element_by_name('foo') 495 """ 496 return self.find_element(by=By.NAME, value=name) 497 498 def find_elements_by_name(self, name): 499 """ 500 Finds elements by name. 501 502 :Args: 503 - name: The name of the elements to find. 504 505 :Returns: 506 - list of webelement - a list with elements if any was found. an 507 empty list if not 508 509 :Usage: 510 elements = driver.find_elements_by_name('foo') 511 """ 512 return self.find_elements(by=By.NAME, value=name) 513 514 def find_element_by_tag_name(self, name): 515 """ 516 Finds an element by tag name. 517 518 :Args: 519 - name - name of html tag (eg: h1, a, span) 520 521 :Returns: 522 - WebElement - the element if it was found 523 524 :Raises: 525 - NoSuchElementException - if the element wasn't found 526 527 :Usage: 528 element = driver.find_element_by_tag_name('h1') 529 """ 530 return self.find_element(by=By.TAG_NAME, value=name) 531 532 def find_elements_by_tag_name(self, name): 533 """ 534 Finds elements by tag name. 535 536 :Args: 537 - name - name of html tag (eg: h1, a, span) 538 539 :Returns: 540 - list of WebElement - a list with elements if any was found. An 541 empty list if not 542 543 :Usage: 544 elements = driver.find_elements_by_tag_name('h1') 545 """ 546 return self.find_elements(by=By.TAG_NAME, value=name) 547 548 def find_element_by_class_name(self, name): 549 """ 550 Finds an element by class name. 551 552 :Args: 553 - name: The class name of the element to find. 554 555 :Returns: 556 - WebElement - the element if it was found 557 558 :Raises: 559 - NoSuchElementException - if the element wasn't found 560 561 :Usage: 562 element = driver.find_element_by_class_name('foo') 563 """ 564 return self.find_element(by=By.CLASS_NAME, value=name) 565 566 def find_elements_by_class_name(self, name): 567 """ 568 Finds elements by class name. 569 570 :Args: 571 - name: The class name of the elements to find. 572 573 :Returns: 574 - list of WebElement - a list with elements if any was found. An 575 empty list if not 576 577 :Usage: 578 elements = driver.find_elements_by_class_name('foo') 579 """ 580 return self.find_elements(by=By.CLASS_NAME, value=name) 581 582 def find_element_by_css_selector(self, css_selector): 583 """ 584 Finds an element by css selector. 585 586 :Args: 587 - css_selector - CSS selector string, ex: 'a.nav#home' 588 589 :Returns: 590 - WebElement - the element if it was found 591 592 :Raises: 593 - NoSuchElementException - if the element wasn't found 594 595 :Usage: 596 element = driver.find_element_by_css_selector('#foo') 597 """ 598 return self.find_element(by=By.CSS_SELECTOR, value=css_selector) 599 600 def find_elements_by_css_selector(self, css_selector): 601 """ 602 Finds elements by css selector. 603 604 :Args: 605 - css_selector - CSS selector string, ex: 'a.nav#home' 606 607 :Returns: 608 - list of WebElement - a list with elements if any was found. An 609 empty list if not 610 611 :Usage: 612 elements = driver.find_elements_by_css_selector('.foo') 613 """ 614 return self.find_elements(by=By.CSS_SELECTOR, value=css_selector) 615 616 def execute_script(self, script, *args): 617 """ 618 Synchronously Executes JavaScript in the current window/frame. 619 620 :Args: 621 - script: The JavaScript to execute. 622 - \*args: Any applicable arguments for your JavaScript. 623 624 :Usage: 625 driver.execute_script('return document.title;') 626 """ 627 converted_args = list(args) 628 command = None 629 if self.w3c: 630 command = Command.W3C_EXECUTE_SCRIPT 631 else: 632 command = Command.EXECUTE_SCRIPT 633 634 return self.execute(command, { 635 'script': script, 636 'args': converted_args})['value'] 637 638 def execute_async_script(self, script, *args): 639 """ 640 Asynchronously Executes JavaScript in the current window/frame. 641 642 :Args: 643 - script: The JavaScript to execute. 644 - \*args: Any applicable arguments for your JavaScript. 645 646 :Usage: 647 script = "var callback = arguments[arguments.length - 1]; " \ 648 "window.setTimeout(function(){ callback('timeout') }, 3000);" 649 driver.execute_async_script(script) 650 """ 651 converted_args = list(args) 652 if self.w3c: 653 command = Command.W3C_EXECUTE_SCRIPT_ASYNC 654 else: 655 command = Command.EXECUTE_ASYNC_SCRIPT 656 657 return self.execute(command, { 658 'script': script, 659 'args': converted_args})['value'] 660 661 @property 662 def current_url(self): 663 """ 664 Gets the URL of the current page. 665 666 :Usage: 667 driver.current_url 668 """ 669 return self.execute(Command.GET_CURRENT_URL)['value'] 670 671 @property 672 def page_source(self): 673 """ 674 Gets the source of the current page. 675 676 :Usage: 677 driver.page_source 678 """ 679 return self.execute(Command.GET_PAGE_SOURCE)['value'] 680 681 def close(self): 682 """ 683 Closes the current window. 684 685 :Usage: 686 driver.close() 687 """ 688 self.execute(Command.CLOSE) 689 690 def quit(self): 691 """ 692 Quits the driver and closes every associated window. 693 694 :Usage: 695 driver.quit() 696 """ 697 try: 698 self.execute(Command.QUIT) 699 finally: 700 self.stop_client() 701 702 @property 703 def current_window_handle(self): 704 """ 705 Returns the handle of the current window. 706 707 :Usage: 708 driver.current_window_handle 709 """ 710 if self.w3c: 711 return self.execute(Command.W3C_GET_CURRENT_WINDOW_HANDLE)['value'] 712 else: 713 return self.execute(Command.GET_CURRENT_WINDOW_HANDLE)['value'] 714 715 @property 716 def window_handles(self): 717 """ 718 Returns the handles of all windows within the current session. 719 720 :Usage: 721 driver.window_handles 722 """ 723 if self.w3c: 724 return self.execute(Command.W3C_GET_WINDOW_HANDLES)['value'] 725 else: 726 return self.execute(Command.GET_WINDOW_HANDLES)['value'] 727 728 def maximize_window(self): 729 """ 730 Maximizes the current window that webdriver is using 731 """ 732 params = None 733 command = Command.W3C_MAXIMIZE_WINDOW 734 if not self.w3c: 735 command = Command.MAXIMIZE_WINDOW 736 params = {'windowHandle': 'current'} 737 self.execute(command, params) 738 739 def fullscreen_window(self): 740 """ 741 Invokes the window manager-specific 'full screen' operation 742 """ 743 self.execute(Command.FULLSCREEN_WINDOW) 744 745 def minimize_window(self): 746 """ 747 Invokes the window manager-specific 'minimize' operation 748 """ 749 self.execute(Command.MINIMIZE_WINDOW) 750 751 @property 752 def switch_to(self): 753 """ 754 :Returns: 755 - SwitchTo: an object containing all options to switch focus into 756 757 :Usage: 758 element = driver.switch_to.active_element 759 alert = driver.switch_to.alert 760 driver.switch_to.default_content() 761 driver.switch_to.frame('frame_name') 762 driver.switch_to.frame(1) 763 driver.switch_to.frame(driver.find_elements_by_tag_name("iframe")[0]) 764 driver.switch_to.parent_frame() 765 driver.switch_to.window('main') 766 """ 767 return self._switch_to 768 769 # Target Locators 770 def switch_to_active_element(self): 771 """ Deprecated use driver.switch_to.active_element 772 """ 773 warnings.warn("use driver.switch_to.active_element instead", 774 DeprecationWarning, stacklevel=2) 775 return self._switch_to.active_element 776 777 def switch_to_window(self, window_name): 778 """ Deprecated use driver.switch_to.window 779 """ 780 warnings.warn("use driver.switch_to.window instead", 781 DeprecationWarning, stacklevel=2) 782 self._switch_to.window(window_name) 783 784 def switch_to_frame(self, frame_reference): 785 """ Deprecated use driver.switch_to.frame 786 """ 787 warnings.warn("use driver.switch_to.frame instead", 788 DeprecationWarning, stacklevel=2) 789 self._switch_to.frame(frame_reference) 790 791 def switch_to_default_content(self): 792 """ Deprecated use driver.switch_to.default_content 793 """ 794 warnings.warn("use driver.switch_to.default_content instead", 795 DeprecationWarning, stacklevel=2) 796 self._switch_to.default_content() 797 798 def switch_to_alert(self): 799 """ Deprecated use driver.switch_to.alert 800 """ 801 warnings.warn("use driver.switch_to.alert instead", 802 DeprecationWarning, stacklevel=2) 803 return self._switch_to.alert 804 805 # Navigation 806 def back(self): 807 """ 808 Goes one step backward in the browser history. 809 810 :Usage: 811 driver.back() 812 """ 813 self.execute(Command.GO_BACK) 814 815 def forward(self): 816 """ 817 Goes one step forward in the browser history. 818 819 :Usage: 820 driver.forward() 821 """ 822 self.execute(Command.GO_FORWARD) 823 824 def refresh(self): 825 """ 826 Refreshes the current page. 827 828 :Usage: 829 driver.refresh() 830 """ 831 self.execute(Command.REFRESH) 832 833 # Options 834 def get_cookies(self): 835 """ 836 Returns a set of dictionaries, corresponding to cookies visible in the current session. 837 838 :Usage: 839 driver.get_cookies() 840 """ 841 return self.execute(Command.GET_ALL_COOKIES)['value'] 842 843 def get_cookie(self, name): 844 """ 845 Get a single cookie by name. Returns the cookie if found, None if not. 846 847 :Usage: 848 driver.get_cookie('my_cookie') 849 """ 850 if self.w3c: 851 try: 852 return self.execute(Command.GET_COOKIE, {'name': name})['value'] 853 except NoSuchCookieException: 854 return None 855 else: 856 cookies = self.get_cookies() 857 for cookie in cookies: 858 if cookie['name'] == name: 859 return cookie 860 return None 861 862 def delete_cookie(self, name): 863 """ 864 Deletes a single cookie with the given name. 865 866 :Usage: 867 driver.delete_cookie('my_cookie') 868 """ 869 self.execute(Command.DELETE_COOKIE, {'name': name}) 870 871 def delete_all_cookies(self): 872 """ 873 Delete all cookies in the scope of the session. 874 875 :Usage: 876 driver.delete_all_cookies() 877 """ 878 self.execute(Command.DELETE_ALL_COOKIES) 879 880 def add_cookie(self, cookie_dict): 881 """ 882 Adds a cookie to your current session. 883 884 :Args: 885 - cookie_dict: A dictionary object, with required keys - "name" and "value"; 886 optional keys - "path", "domain", "secure", "expiry" 887 888 Usage: 889 driver.add_cookie({'name' : 'foo', 'value' : 'bar'}) 890 driver.add_cookie({'name' : 'foo', 'value' : 'bar', 'path' : '/'}) 891 driver.add_cookie({'name' : 'foo', 'value' : 'bar', 'path' : '/', 'secure':True}) 892 893 """ 894 self.execute(Command.ADD_COOKIE, {'cookie': cookie_dict}) 895 896 # Timeouts 897 def implicitly_wait(self, time_to_wait): 898 """ 899 Sets a sticky timeout to implicitly wait for an element to be found, 900 or a command to complete. This method only needs to be called one 901 time per session. To set the timeout for calls to 902 execute_async_script, see set_script_timeout. 903 904 :Args: 905 - time_to_wait: Amount of time to wait (in seconds) 906 907 :Usage: 908 driver.implicitly_wait(30) 909 """ 910 if self.w3c: 911 self.execute(Command.SET_TIMEOUTS, { 912 'implicit': int(float(time_to_wait) * 1000)}) 913 else: 914 self.execute(Command.IMPLICIT_WAIT, { 915 'ms': float(time_to_wait) * 1000}) 916 917 def set_script_timeout(self, time_to_wait): 918 """ 919 Set the amount of time that the script should wait during an 920 execute_async_script call before throwing an error. 921 922 :Args: 923 - time_to_wait: The amount of time to wait (in seconds) 924 925 :Usage: 926 driver.set_script_timeout(30) 927 """ 928 if self.w3c: 929 self.execute(Command.SET_TIMEOUTS, { 930 'script': int(float(time_to_wait) * 1000)}) 931 else: 932 self.execute(Command.SET_SCRIPT_TIMEOUT, { 933 'ms': float(time_to_wait) * 1000}) 934 935 def set_page_load_timeout(self, time_to_wait): 936 """ 937 Set the amount of time to wait for a page load to complete 938 before throwing an error. 939 940 :Args: 941 - time_to_wait: The amount of time to wait 942 943 :Usage: 944 driver.set_page_load_timeout(30) 945 """ 946 try: 947 self.execute(Command.SET_TIMEOUTS, { 948 'pageLoad': int(float(time_to_wait) * 1000)}) 949 except WebDriverException: 950 self.execute(Command.SET_TIMEOUTS, { 951 'ms': float(time_to_wait) * 1000, 952 'type': 'page load'}) 953 954 def find_element(self, by=By.ID, value=None): 955 """ 956 Find an element given a By strategy and locator. Prefer the find_element_by_* methods when 957 possible. 958 959 :Usage: 960 element = driver.find_element(By.ID, 'foo') 961 962 :rtype: WebElement 963 """ 964 if self.w3c: 965 if by == By.ID: 966 by = By.CSS_SELECTOR 967 value = '[id="%s"]' % value 968 elif by == By.TAG_NAME: 969 by = By.CSS_SELECTOR 970 elif by == By.CLASS_NAME: 971 by = By.CSS_SELECTOR 972 value = ".%s" % value 973 elif by == By.NAME: 974 by = By.CSS_SELECTOR 975 value = '[name="%s"]' % value 976 return self.execute(Command.FIND_ELEMENT, { 977 'using': by, 978 'value': value})['value'] 979 980 def find_elements(self, by=By.ID, value=None): 981 """ 982 Find elements given a By strategy and locator. Prefer the find_elements_by_* methods when 983 possible. 984 985 :Usage: 986 elements = driver.find_elements(By.CLASS_NAME, 'foo') 987 988 :rtype: list of WebElement 989 """ 990 if self.w3c: 991 if by == By.ID: 992 by = By.CSS_SELECTOR 993 value = '[id="%s"]' % value 994 elif by == By.TAG_NAME: 995 by = By.CSS_SELECTOR 996 elif by == By.CLASS_NAME: 997 by = By.CSS_SELECTOR 998 value = ".%s" % value 999 elif by == By.NAME: 1000 by = By.CSS_SELECTOR 1001 value = '[name="%s"]' % value 1002 1003 # Return empty list if driver returns null 1004 # See https://github.com/SeleniumHQ/selenium/issues/4555 1005 return self.execute(Command.FIND_ELEMENTS, { 1006 'using': by, 1007 'value': value})['value'] or [] 1008 1009 @property 1010 def desired_capabilities(self): 1011 """ 1012 returns the drivers current desired capabilities being used 1013 """ 1014 return self.capabilities 1015 1016 def get_screenshot_as_file(self, filename): 1017 """ 1018 Saves a screenshot of the current window to a PNG image file. Returns 1019 False if there is any IOError, else returns True. Use full paths in 1020 your filename. 1021 1022 :Args: 1023 - filename: The full path you wish to save your screenshot to. This 1024 should end with a `.png` extension. 1025 1026 :Usage: 1027 driver.get_screenshot_as_file('/Screenshots/foo.png') 1028 """ 1029 if not filename.lower().endswith('.png'): 1030 warnings.warn("name used for saved screenshot does not match file " 1031 "type. It should end with a `.png` extension", UserWarning) 1032 png = self.get_screenshot_as_png() 1033 try: 1034 with open(filename, 'wb') as f: 1035 f.write(png) 1036 except IOError: 1037 return False 1038 finally: 1039 del png 1040 return True 1041 1042 def save_screenshot(self, filename): 1043 """ 1044 Saves a screenshot of the current window to a PNG image file. Returns 1045 False if there is any IOError, else returns True. Use full paths in 1046 your filename. 1047 1048 :Args: 1049 - filename: The full path you wish to save your screenshot to. This 1050 should end with a `.png` extension. 1051 1052 :Usage: 1053 driver.save_screenshot('/Screenshots/foo.png') 1054 """ 1055 return self.get_screenshot_as_file(filename) 1056 1057 def get_screenshot_as_png(self): 1058 """ 1059 Gets the screenshot of the current window as a binary data. 1060 1061 :Usage: 1062 driver.get_screenshot_as_png() 1063 """ 1064 return base64.b64decode(self.get_screenshot_as_base64().encode('ascii')) 1065 1066 def get_screenshot_as_base64(self): 1067 """ 1068 Gets the screenshot of the current window as a base64 encoded string 1069 which is useful in embedded images in HTML. 1070 1071 :Usage: 1072 driver.get_screenshot_as_base64() 1073 """ 1074 return self.execute(Command.SCREENSHOT)['value'] 1075 1076 def set_window_size(self, width, height, windowHandle='current'): 1077 """ 1078 Sets the width and height of the current window. (window.resizeTo) 1079 1080 :Args: 1081 - width: the width in pixels to set the window to 1082 - height: the height in pixels to set the window to 1083 1084 :Usage: 1085 driver.set_window_size(800,600) 1086 """ 1087 if self.w3c: 1088 if windowHandle != 'current': 1089 warnings.warn("Only 'current' window is supported for W3C compatibile browsers.") 1090 self.set_window_rect(width=int(width), height=int(height)) 1091 else: 1092 self.execute(Command.SET_WINDOW_SIZE, { 1093 'width': int(width), 1094 'height': int(height), 1095 'windowHandle': windowHandle}) 1096 1097 def get_window_size(self, windowHandle='current'): 1098 """ 1099 Gets the width and height of the current window. 1100 1101 :Usage: 1102 driver.get_window_size() 1103 """ 1104 command = Command.GET_WINDOW_SIZE 1105 if self.w3c: 1106 if windowHandle != 'current': 1107 warnings.warn("Only 'current' window is supported for W3C compatibile browsers.") 1108 size = self.get_window_rect() 1109 else: 1110 size = self.execute(command, {'windowHandle': windowHandle}) 1111 1112 if size.get('value', None) is not None: 1113 size = size['value'] 1114 1115 return {k: size[k] for k in ('width', 'height')} 1116 1117 def set_window_position(self, x, y, windowHandle='current'): 1118 """ 1119 Sets the x,y position of the current window. (window.moveTo) 1120 1121 :Args: 1122 - x: the x-coordinate in pixels to set the window position 1123 - y: the y-coordinate in pixels to set the window position 1124 1125 :Usage: 1126 driver.set_window_position(0,0) 1127 """ 1128 if self.w3c: 1129 if windowHandle != 'current': 1130 warnings.warn("Only 'current' window is supported for W3C compatibile browsers.") 1131 return self.set_window_rect(x=int(x), y=int(y)) 1132 else: 1133 self.execute(Command.SET_WINDOW_POSITION, 1134 { 1135 'x': int(x), 1136 'y': int(y), 1137 'windowHandle': windowHandle 1138 }) 1139 1140 def get_window_position(self, windowHandle='current'): 1141 """ 1142 Gets the x,y position of the current window. 1143 1144 :Usage: 1145 driver.get_window_position() 1146 """ 1147 if self.w3c: 1148 if windowHandle != 'current': 1149 warnings.warn("Only 'current' window is supported for W3C compatibile browsers.") 1150 position = self.get_window_rect() 1151 else: 1152 position = self.execute(Command.GET_WINDOW_POSITION, 1153 {'windowHandle': windowHandle})['value'] 1154 1155 return {k: position[k] for k in ('x', 'y')} 1156 1157 def get_window_rect(self): 1158 """ 1159 Gets the x, y coordinates of the window as well as height and width of 1160 the current window. 1161 1162 :Usage: 1163 driver.get_window_rect() 1164 """ 1165 return self.execute(Command.GET_WINDOW_RECT)['value'] 1166 1167 def set_window_rect(self, x=None, y=None, width=None, height=None): 1168 """ 1169 Sets the x, y coordinates of the window as well as height and width of 1170 the current window. 1171 1172 :Usage: 1173 driver.set_window_rect(x=10, y=10) 1174 driver.set_window_rect(width=100, height=200) 1175 driver.set_window_rect(x=10, y=10, width=100, height=200) 1176 """ 1177 if (x is None and y is None) and (height is None and width is None): 1178 raise InvalidArgumentException("x and y or height and width need values") 1179 1180 return self.execute(Command.SET_WINDOW_RECT, {"x": x, "y": y, 1181 "width": width, 1182 "height": height})['value'] 1183 1184 @property 1185 def file_detector(self): 1186 return self._file_detector 1187 1188 @file_detector.setter 1189 def file_detector(self, detector): 1190 """ 1191 Set the file detector to be used when sending keyboard input. 1192 By default, this is set to a file detector that does nothing. 1193 1194 see FileDetector 1195 see LocalFileDetector 1196 see UselessFileDetector 1197 1198 :Args: 1199 - detector: The detector to use. Must not be None. 1200 """ 1201 if detector is None: 1202 raise WebDriverException("You may not set a file detector that is null") 1203 if not isinstance(detector, FileDetector): 1204 raise WebDriverException("Detector has to be instance of FileDetector") 1205 self._file_detector = detector 1206 1207 @property 1208 def orientation(self): 1209 """ 1210 Gets the current orientation of the device 1211 1212 :Usage: 1213 orientation = driver.orientation 1214 """ 1215 return self.execute(Command.GET_SCREEN_ORIENTATION)['value'] 1216 1217 @orientation.setter 1218 def orientation(self, value): 1219 """ 1220 Sets the current orientation of the device 1221 1222 :Args: 1223 - value: orientation to set it to. 1224 1225 :Usage: 1226 driver.orientation = 'landscape' 1227 """ 1228 allowed_values = ['LANDSCAPE', 'PORTRAIT'] 1229 if value.upper() in allowed_values: 1230 self.execute(Command.SET_SCREEN_ORIENTATION, {'orientation': value}) 1231 else: 1232 raise WebDriverException("You can only set the orientation to 'LANDSCAPE' and 'PORTRAIT'") 1233 1234 @property 1235 def application_cache(self): 1236 """ Returns a ApplicationCache Object to interact with the browser app cache""" 1237 return ApplicationCache(self) 1238 1239 @property 1240 def log_types(self): 1241 """ 1242 Gets a list of the available log types 1243 1244 :Usage: 1245 driver.log_types 1246 """ 1247 return self.execute(Command.GET_AVAILABLE_LOG_TYPES)['value'] 1248 1249 def get_log(self, log_type): 1250 """ 1251 Gets the log for a given log type 1252 1253 :Args: 1254 - log_type: type of log that which will be returned 1255 1256 :Usage: 1257 driver.get_log('browser') 1258 driver.get_log('driver') 1259 driver.get_log('client') 1260 driver.get_log('server') 1261 """ 1262 return self.execute(Command.GET_LOG, {'type': log_type})['value'] 1263