splinter webdriver API 的基本实现

# -*- coding: utf-8 -*-

# Copyright 2012 splinter authors. All rights reserved.
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.

from __future__ import with_statement
import logging
import subprocess
import time
import re
from contextlib import contextmanager

from selenium.common.exceptions import NoSuchElementException
from selenium.webdriver.common.action_chains import ActionChains

from splinter.driver import DriverAPI, ElementAPI
from splinter.element_list import ElementList
from splinter.utils import warn_deprecated
from tempfile import TemporaryFile


class BaseWebDriver(DriverAPI):
    old_popen = subprocess.Popen

    def __init__(self, wait_time=2):
        self.wait_time = wait_time

    def _patch_subprocess(self):
        loggers_to_silence = [
            'selenium.webdriver.firefox.extension_connection',
            'selenium.webdriver.remote.remote_connection',
            'selenium.webdriver.remote.utils',
        ]

        class MutedHandler(logging.Handler):
            pass

        for name in loggers_to_silence:
            logger = logging.getLogger(name)
            logger.addHandler(MutedHandler())
            logger.setLevel(99999)

        # selenium is such a verbose guy let's make it open the
        # browser without showing all the meaningless output
        def MyPopen(*args, **kw):
            kw['stdout'] = TemporaryFile()
            kw['stderr'] = TemporaryFile()
            kw['close_fds'] = True
            return self.old_popen(*args, **kw)

        subprocess.Popen = MyPopen

    def _unpatch_subprocess(self):
        # cleaning up the house
        subprocess.Popen = self.old_popen

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        self.quit()

    @property
    def title(self):
        return self.driver.title

    @property
    def html(self):
        return self.driver.page_source

    @property
    def url(self):
        return self.driver.current_url

    def visit(self, url):
        self.connect(url)
        self.ensure_success_response()
        self.driver.get(url)

    def back(self):
        self.driver.back()

    def forward(self):
        self.driver.forward()

    def reload(self):
        self.driver.refresh()

    def execute_script(self, script):
        self.driver.execute_script(script)

    def evaluate_script(self, script):
        return self.driver.execute_script("return %s" % script)

    def is_element_present(self, finder, selector, wait_time=None):
        wait_time = wait_time or self.wait_time
        end_time = time.time() + wait_time

        while time.time() < end_time:
            if finder(selector):
                return True
        return False

    def is_element_not_present(self, finder, selector, wait_time=None):
        wait_time = wait_time or self.wait_time
        end_time = time.time() + wait_time

        while time.time() < end_time:
            if not finder(selector):
                return True
        return False

    def is_element_present_by_css(self, css_selector, wait_time=None):
        return self.is_element_present(self.find_by_css, css_selector, wait_time)

    def is_element_not_present_by_css(self, css_selector, wait_time=None):
        return self.is_element_not_present(self.find_by_css, css_selector, wait_time)

    def is_element_present_by_xpath(self, xpath, wait_time=None):
        return self.is_element_present(self.find_by_xpath, xpath, wait_time)

    def is_element_not_present_by_xpath(self, xpath, wait_time=None):
        return self.is_element_not_present(self.find_by_xpath, xpath, wait_time)

    def is_element_present_by_tag(self, tag, wait_time=None):
        return self.is_element_present(self.find_by_tag, tag, wait_time)

    def is_element_not_present_by_tag(self, tag, wait_time=None):
        return self.is_element_not_present(self.find_by_tag, tag, wait_time)

    def is_element_present_by_name(self, name, wait_time=None):
        return self.is_element_present(self.find_by_name, name, wait_time)

    def is_element_not_present_by_name(self, name, wait_time=None):
        return self.is_element_not_present(self.find_by_name, name, wait_time)

    def is_element_present_by_value(self, value, wait_time=None):
        return self.is_element_present(self.find_by_value, value, wait_time)

    def is_element_not_present_by_value(self, value, wait_time=None):
        return self.is_element_not_present(self.find_by_value, value, wait_time)

    def is_element_present_by_id(self, id, wait_time=None):
        return self.is_element_present(self.find_by_id, id, wait_time)

    def is_element_not_present_by_id(self, id, wait_time=None):
        return self.is_element_not_present(self.find_by_id, id, wait_time)

    def get_alert(self):
        return AlertElement(self.driver.switch_to_alert())

    def is_text_present(self, text, wait_time=None):
        wait_time = wait_time or self.wait_time
        end_time = time.time() + wait_time

        while time.time() < end_time:
            try:
                self.driver.find_element_by_tag_name('body').text.index(text)
                return True
            except ValueError:
                pass
        return False

    def is_text_not_present(self, text, wait_time=None):
        wait_time = wait_time or self.wait_time
        end_time = time.time() + wait_time

        while time.time() < end_time:
            try:
                self.driver.find_element_by_tag_name('body').text.index(text)
            except ValueError:
                return True
        return False

    @contextmanager
    def get_iframe(self, id):
        self.driver.switch_to_frame(id)
        try:
            yield self
        finally:
            self.driver.switch_to_frame(None)

    def find_option_by_value(self, value):
        return self.find_by_xpath('//option[@value="%s"]' % value, original_find="option by value", original_query=value)

    def find_option_by_text(self, text):
        return self.find_by_xpath('//option[normalize-space(text())="%s"]' % text, original_find="option by text", original_query=text)

    def find_link_by_href(self, href):
        return self.find_by_xpath('//a[@href="%s"]' % href, original_find="link by href", original_query=href)

    def find_link_by_partial_href(self, partial_href):
        return self.find_by_xpath('//a[contains(@href, "%s")]' % partial_href, original_find="link by partial href", original_query=partial_href)

    def find_link_by_partial_text(self, partial_text):
        return self.find_by_xpath('//a[contains(text(), "%s")]' % partial_text, original_find="link by partial text", original_query=partial_text)

    def find_link_by_text(self, text):
        return self.find_by_xpath('//a[text()="%s"]' % text, original_find="link by text", original_query=text)

    def find_by(self, finder, selector, original_find=None, original_query=None):
        elements = None
        end_time = time.time() + self.wait_time

        func_name = finder.im_func.func_name
        find_by = original_find or func_name[func_name.rfind('_by_') + 4:]
        query = original_query or selector

        while time.time() < end_time:
            try:
                elements = finder(selector)
                if not isinstance(elements, list):
                    elements = [elements]
            except NoSuchElementException:
                pass

            if elements:
                return ElementList([self.element_class(element, self) for element in elements], find_by=find_by, query=query)
        return ElementList([], find_by=find_by, query=query)

    def find_by_css(self, css_selector):
        return self.find_by(self.driver.find_elements_by_css_selector, css_selector, original_find='css', original_query=css_selector)

    def find_by_xpath(self, xpath, original_find=None, original_query=None):
        original_find = original_find or "xpath"
        original_query = original_query or xpath
        return self.find_by(self.driver.find_elements_by_xpath, xpath, original_find=original_find, original_query=original_query)

    def find_by_name(self, name):
        return self.find_by(self.driver.find_elements_by_name, name)

    def find_by_tag(self, tag):
        return self.find_by(self.driver.find_elements_by_tag_name, tag)

    def find_by_value(self, value):
        return self.find_by_xpath('//*[@value="%s"]' % value, original_find='value', original_query=value)

    def find_by_id(self, id):
        return self.find_by(self.driver.find_element_by_id, id)

    def fill(self, name, value):
        field = self.find_by_name(name).first
        field.value = value

    attach_file = fill

    def fill_form(self, field_values):
        for name, value in field_values.items():
            elements = self.find_by_name(name)
            element = elements.first
            if element['type'] == 'text' or element.tag_name == 'textarea':
                element.value = value
            elif element['type'] == 'checkbox':
                if value:
                    element.check()
                else:
                    element.uncheck()
            elif element['type'] == 'radio':
                for field in elements:
                    if field.value == value:
                        field.click()
            elif element._element.tag_name == 'select':
                element.find_by_value(value).first._element.click()

    def type(self, name, value, slowly=False):
        element = self.driver.find_element_by_css_selector('input[name="%s"]' % name)
        if slowly:
            return TypeIterator(element, value)
        element.send_keys(value)
        return value

    def choose(self, name, value):
        fields = self.find_by_name(name)
        for field in fields:
            if field.value == value:
                field.click()

    def check(self, name):
        self.find_by_name(name).first.check()

    def uncheck(self, name):
        self.find_by_name(name).first.uncheck()

    def select(self, name, value):
        self.find_by_xpath('//select[@name="%s"]/option[@value="%s"]' % (name, value)).first._element.click()

    def quit(self):
        self.driver.quit()

    @property
    def cookies(self):
        return self._cookie_manager


class TypeIterator(object):


    def __init__(self, element, keys):
        self._element = element
        self._keys = keys

    def __iter__(self):
        for key in self._keys:
            self._element.send_keys(key)
            yield key


class WebDriverElement(ElementAPI):

    def __init__(self, element, parent):
        self._element = element
        self.parent = parent
        self.action_chains = ActionChains(parent.driver)

    def _get_value(self):
        return self["value"] or self._element.text

    def _set_value(self, value):
        if self._element.get_attribute('type') != 'file':
            self._element.clear()
        self._element.send_keys(value)

    value = property(_get_value, _set_value)

    @property
    def text(self):
        return self._element.text

    @property
    def tag_name(self):
        return self._element.tag_name

    def fill(self, value):
        self.value = value

    def type(self, value, slowly=False):
        if slowly:
            return TypeIterator(self._element, value)

        self._element.send_keys(value)
        return value

    def click(self):
        self._element.click()

    def check(self):
        if not self.checked:
            self._element.click()

    def uncheck(self):
        if self.checked:
            self._element.click()

    @property
    def checked(self):
        return self._element.is_selected()

    selected = checked

    @property
    def visible(self):
        return self._element.is_displayed()

    def find_by_css(self, selector, original_find=None, original_query=None):
        find_by = original_find or 'css'
        query = original_query or selector

        elements = self._element.find_elements_by_css_selector(selector)
        return ElementList([self.__class__(element, self.parent) for element in elements], find_by=find_by, query=query)

    def find_by_xpath(self, selector):
        elements = ElementList(self._element.find_elements_by_xpath(selector))
        return ElementList([self.__class__(element, self.parent) for element in elements], find_by='xpath', query=selector)

    def find_by_name(self, name):
        elements = ElementList(self._element.find_elements_by_name(name))
        return ElementList([self.__class__(element, self.parent) for element in elements], find_by='name', query=name)

    def find_by_tag(self, tag):
        elements = ElementList(self._element.find_elements_by_tag_name(tag))
        return ElementList([self.__class__(element, self.parent) for element in elements], find_by='tag', query=tag)

    def find_by_value(self, value):
        selector = '[value="%s"]' % value
        return self.find_by_css(selector, original_find='value', original_query=value)

    def find_by_id(self, id):
        elements = ElementList(self._element.find_elements_by_id(id))
        return ElementList([self.__class__(element, self.parent) for element in elements], find_by='id', query=id)

    def has_class(self, class_name):
        element_class_name = self._element.get_attribute('class')
        return bool(re.search(r'(?:^|\s)' + re.escape(class_name) + r'(?:$|\s)', element_class_name))

    def mouse_over(self):
        """
        Performs a mouse over the element.

        Currently works only on Chrome driver.
        """
        self.action_chains.move_to_element(self._element)
        self.action_chains.perform()

    def mouse_out(self):
        """
        Performs a mouse out the element.

        Currently works only on Chrome driver.
        """
        self.action_chains.move_by_offset(5000, 5000)
        self.action_chains.perform()

    mouseover = warn_deprecated(mouse_over, 'mouseover')
    mouseout = warn_deprecated(mouse_out, 'mouseout')

    def double_click(self):
        """
        Performs a double click in the element.

        Currently works only on Chrome driver.
        """
        self.action_chains.double_click(self._element)
        self.action_chains.perform()

    def right_click(self):
        """
        Performs a right click in the element.

        Currently works only on Chrome driver.
        """
        self.action_chains.context_click(self._element)
        self.action_chains.perform()

    def drag_and_drop(self, droppable):
        """
        Performs drag a element to another elmenet.

        Currently works only on Chrome driver.
        """
        self.action_chains.drag_and_drop(self._element, droppable._element)
        self.action_chains.perform()

    def __getitem__(self, attr):
        return self._element.get_attribute(attr)


class AlertElement(object):

    def __init__(self, alert):
        self._alert = alert
        self.text = alert.text

    def accept(self):
        self._alert.accept()

    def dismiss(self):
        self._alert.dismiss()

    def fill_with(self, text):
        self._alert.send_keys(text)

    def __enter__(self):
        return self

    def __exit__(self, type, value, traceback):
        pass
原文地址:https://www.cnblogs.com/hzhida/p/2634553.html