Ausgabe der neuen DB Einträge
This commit is contained in:
parent
bad48e1627
commit
cfbbb9ee3d
2399 changed files with 843193 additions and 43 deletions
|
|
@ -0,0 +1,16 @@
|
|||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
class AbstractEventListener(object):
|
||||
"""
|
||||
Event listener must subclass and implement this fully or partially
|
||||
"""
|
||||
|
||||
def before_navigate_to(self, url, driver):
|
||||
pass
|
||||
|
||||
def after_navigate_to(self, url, driver):
|
||||
pass
|
||||
|
||||
def before_navigate_back(self, driver):
|
||||
pass
|
||||
|
||||
def after_navigate_back(self, driver):
|
||||
pass
|
||||
|
||||
def before_navigate_forward(self, driver):
|
||||
pass
|
||||
|
||||
def after_navigate_forward(self, driver):
|
||||
pass
|
||||
|
||||
def before_find(self, by, value, driver):
|
||||
pass
|
||||
|
||||
def after_find(self, by, value, driver):
|
||||
pass
|
||||
|
||||
def before_click(self, element, driver):
|
||||
pass
|
||||
|
||||
def after_click(self, element, driver):
|
||||
pass
|
||||
|
||||
def before_change_value_of(self, element, driver):
|
||||
pass
|
||||
|
||||
def after_change_value_of(self, element, driver):
|
||||
pass
|
||||
|
||||
def before_execute_script(self, script, driver):
|
||||
pass
|
||||
|
||||
def after_execute_script(self, script, driver):
|
||||
pass
|
||||
|
||||
def before_close(self, driver):
|
||||
pass
|
||||
|
||||
def after_close(self, driver):
|
||||
pass
|
||||
|
||||
def before_quit(self, driver):
|
||||
pass
|
||||
|
||||
def after_quit(self, driver):
|
||||
pass
|
||||
|
||||
def on_exception(self, exception, driver):
|
||||
pass
|
||||
|
|
@ -0,0 +1,310 @@
|
|||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
RGB_PATTERN = r"^\s*rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)\s*$"
|
||||
RGB_PCT_PATTERN = r"^\s*rgb\(\s*(\d{1,3}|\d{1,2}\.\d+)%\s*,\s*(\d{1,3}|\d{1,2}\.\d+)%\s*,\s*(\d{1,3}|\d{1,2}\.\d+)%\s*\)\s*$"
|
||||
RGBA_PATTERN = r"^\s*rgba\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(0|1|0\.\d+)\s*\)\s*$"
|
||||
RGBA_PCT_PATTERN = r"^\s*rgba\(\s*(\d{1,3}|\d{1,2}\.\d+)%\s*,\s*(\d{1,3}|\d{1,2}\.\d+)%\s*,\s*(\d{1,3}|\d{1,2}\.\d+)%\s*,\s*(0|1|0\.\d+)\s*\)\s*$"
|
||||
HEX_PATTERN = r"#([A-Fa-f0-9]{2})([A-Fa-f0-9]{2})([A-Fa-f0-9]{2})"
|
||||
HEX3_PATTERN = r"#([A-Fa-f0-9])([A-Fa-f0-9])([A-Fa-f0-9])"
|
||||
HSL_PATTERN = r"^\s*hsl\(\s*(\d{1,3})\s*,\s*(\d{1,3})%\s*,\s*(\d{1,3})%\s*\)\s*$"
|
||||
HSLA_PATTERN = r"^\s*hsla\(\s*(\d{1,3})\s*,\s*(\d{1,3})%\s*,\s*(\d{1,3})%\s*,\s*(0|1|0\.\d+)\s*\)\s*$"
|
||||
|
||||
|
||||
class Color(object):
|
||||
"""
|
||||
Color conversion support class
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from selenium.webdriver.support.color import Color
|
||||
|
||||
print(Color.from_string('#00ff33').rgba)
|
||||
print(Color.from_string('rgb(1, 255, 3)').hex)
|
||||
print(Color.from_string('blue').rgba)
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def from_string(str_):
|
||||
import re
|
||||
|
||||
class Matcher(object):
|
||||
def __init__(self):
|
||||
self.match_obj = None
|
||||
|
||||
def match(self, pattern, str_):
|
||||
self.match_obj = re.match(pattern, str_)
|
||||
return self.match_obj
|
||||
|
||||
@property
|
||||
def groups(self):
|
||||
return () if self.match_obj is None else self.match_obj.groups()
|
||||
|
||||
m = Matcher()
|
||||
|
||||
if m.match(RGB_PATTERN, str_):
|
||||
return Color(*m.groups)
|
||||
elif m.match(RGB_PCT_PATTERN, str_):
|
||||
rgb = tuple([float(each) / 100 * 255 for each in m.groups])
|
||||
return Color(*rgb)
|
||||
elif m.match(RGBA_PATTERN, str_):
|
||||
return Color(*m.groups)
|
||||
elif m.match(RGBA_PCT_PATTERN, str_):
|
||||
rgba = tuple([float(each) / 100 * 255 for each in m.groups[:3]] + [m.groups[3]])
|
||||
return Color(*rgba)
|
||||
elif m.match(HEX_PATTERN, str_):
|
||||
rgb = tuple([int(each, 16) for each in m.groups])
|
||||
return Color(*rgb)
|
||||
elif m.match(HEX3_PATTERN, str_):
|
||||
rgb = tuple([int(each * 2, 16) for each in m.groups])
|
||||
return Color(*rgb)
|
||||
elif m.match(HSL_PATTERN, str_) or m.match(HSLA_PATTERN, str_):
|
||||
return Color._from_hsl(*m.groups)
|
||||
elif str_.upper() in Colors.keys():
|
||||
return Colors[str_.upper()]
|
||||
else:
|
||||
raise ValueError("Could not convert %s into color" % str_)
|
||||
|
||||
@staticmethod
|
||||
def _from_hsl(h, s, l, a=1):
|
||||
h = float(h) / 360
|
||||
s = float(s) / 100
|
||||
l = float(l) / 100
|
||||
|
||||
if s == 0:
|
||||
r = l
|
||||
g = r
|
||||
b = r
|
||||
else:
|
||||
luminocity2 = l * (1 + s) if l < 0.5 else l + s - l * s
|
||||
luminocity1 = 2 * l - luminocity2
|
||||
|
||||
def hue_to_rgb(lum1, lum2, hue):
|
||||
if hue < 0.0:
|
||||
hue += 1
|
||||
if hue > 1.0:
|
||||
hue -= 1
|
||||
|
||||
if hue < 1.0 / 6.0:
|
||||
return (lum1 + (lum2 - lum1) * 6.0 * hue)
|
||||
elif hue < 1.0 / 2.0:
|
||||
return lum2
|
||||
elif hue < 2.0 / 3.0:
|
||||
return lum1 + (lum2 - lum1) * ((2.0 / 3.0) - hue) * 6.0
|
||||
else:
|
||||
return lum1
|
||||
|
||||
r = hue_to_rgb(luminocity1, luminocity2, h + 1.0 / 3.0)
|
||||
g = hue_to_rgb(luminocity1, luminocity2, h)
|
||||
b = hue_to_rgb(luminocity1, luminocity2, h - 1.0 / 3.0)
|
||||
|
||||
return Color(round(r * 255), round(g * 255), round(b * 255), a)
|
||||
|
||||
def __init__(self, red, green, blue, alpha=1):
|
||||
self.red = int(red)
|
||||
self.green = int(green)
|
||||
self.blue = int(blue)
|
||||
self.alpha = "1" if float(alpha) == 1 else str(float(alpha) or 0)
|
||||
|
||||
@property
|
||||
def rgb(self):
|
||||
return "rgb(%d, %d, %d)" % (self.red, self.green, self.blue)
|
||||
|
||||
@property
|
||||
def rgba(self):
|
||||
return "rgba(%d, %d, %d, %s)" % (self.red, self.green, self.blue, self.alpha)
|
||||
|
||||
@property
|
||||
def hex(self):
|
||||
return "#%02x%02x%02x" % (self.red, self.green, self.blue)
|
||||
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, Color):
|
||||
return self.rgba == other.rgba
|
||||
return NotImplemented
|
||||
|
||||
def __ne__(self, other):
|
||||
result = self.__eq__(other)
|
||||
if result is NotImplemented:
|
||||
return result
|
||||
return not result
|
||||
|
||||
def __hash__(self):
|
||||
return hash((self.red, self.green, self.blue, self.alpha))
|
||||
|
||||
def __repr__(self):
|
||||
return "Color(red=%d, green=%d, blue=%d, alpha=%s)" % (self.red, self.green, self.blue, self.alpha)
|
||||
|
||||
def __str__(self):
|
||||
return "Color: %s" % self.rgba
|
||||
|
||||
|
||||
# Basic, extended and transparent colour keywords as defined by the W3C HTML4 spec
|
||||
# See http://www.w3.org/TR/css3-color/#html4
|
||||
Colors = {
|
||||
"TRANSPARENT": Color(0, 0, 0, 0),
|
||||
"ALICEBLUE": Color(240, 248, 255),
|
||||
"ANTIQUEWHITE": Color(250, 235, 215),
|
||||
"AQUA": Color(0, 255, 255),
|
||||
"AQUAMARINE": Color(127, 255, 212),
|
||||
"AZURE": Color(240, 255, 255),
|
||||
"BEIGE": Color(245, 245, 220),
|
||||
"BISQUE": Color(255, 228, 196),
|
||||
"BLACK": Color(0, 0, 0),
|
||||
"BLANCHEDALMOND": Color(255, 235, 205),
|
||||
"BLUE": Color(0, 0, 255),
|
||||
"BLUEVIOLET": Color(138, 43, 226),
|
||||
"BROWN": Color(165, 42, 42),
|
||||
"BURLYWOOD": Color(222, 184, 135),
|
||||
"CADETBLUE": Color(95, 158, 160),
|
||||
"CHARTREUSE": Color(127, 255, 0),
|
||||
"CHOCOLATE": Color(210, 105, 30),
|
||||
"CORAL": Color(255, 127, 80),
|
||||
"CORNFLOWERBLUE": Color(100, 149, 237),
|
||||
"CORNSILK": Color(255, 248, 220),
|
||||
"CRIMSON": Color(220, 20, 60),
|
||||
"CYAN": Color(0, 255, 255),
|
||||
"DARKBLUE": Color(0, 0, 139),
|
||||
"DARKCYAN": Color(0, 139, 139),
|
||||
"DARKGOLDENROD": Color(184, 134, 11),
|
||||
"DARKGRAY": Color(169, 169, 169),
|
||||
"DARKGREEN": Color(0, 100, 0),
|
||||
"DARKGREY": Color(169, 169, 169),
|
||||
"DARKKHAKI": Color(189, 183, 107),
|
||||
"DARKMAGENTA": Color(139, 0, 139),
|
||||
"DARKOLIVEGREEN": Color(85, 107, 47),
|
||||
"DARKORANGE": Color(255, 140, 0),
|
||||
"DARKORCHID": Color(153, 50, 204),
|
||||
"DARKRED": Color(139, 0, 0),
|
||||
"DARKSALMON": Color(233, 150, 122),
|
||||
"DARKSEAGREEN": Color(143, 188, 143),
|
||||
"DARKSLATEBLUE": Color(72, 61, 139),
|
||||
"DARKSLATEGRAY": Color(47, 79, 79),
|
||||
"DARKSLATEGREY": Color(47, 79, 79),
|
||||
"DARKTURQUOISE": Color(0, 206, 209),
|
||||
"DARKVIOLET": Color(148, 0, 211),
|
||||
"DEEPPINK": Color(255, 20, 147),
|
||||
"DEEPSKYBLUE": Color(0, 191, 255),
|
||||
"DIMGRAY": Color(105, 105, 105),
|
||||
"DIMGREY": Color(105, 105, 105),
|
||||
"DODGERBLUE": Color(30, 144, 255),
|
||||
"FIREBRICK": Color(178, 34, 34),
|
||||
"FLORALWHITE": Color(255, 250, 240),
|
||||
"FORESTGREEN": Color(34, 139, 34),
|
||||
"FUCHSIA": Color(255, 0, 255),
|
||||
"GAINSBORO": Color(220, 220, 220),
|
||||
"GHOSTWHITE": Color(248, 248, 255),
|
||||
"GOLD": Color(255, 215, 0),
|
||||
"GOLDENROD": Color(218, 165, 32),
|
||||
"GRAY": Color(128, 128, 128),
|
||||
"GREY": Color(128, 128, 128),
|
||||
"GREEN": Color(0, 128, 0),
|
||||
"GREENYELLOW": Color(173, 255, 47),
|
||||
"HONEYDEW": Color(240, 255, 240),
|
||||
"HOTPINK": Color(255, 105, 180),
|
||||
"INDIANRED": Color(205, 92, 92),
|
||||
"INDIGO": Color(75, 0, 130),
|
||||
"IVORY": Color(255, 255, 240),
|
||||
"KHAKI": Color(240, 230, 140),
|
||||
"LAVENDER": Color(230, 230, 250),
|
||||
"LAVENDERBLUSH": Color(255, 240, 245),
|
||||
"LAWNGREEN": Color(124, 252, 0),
|
||||
"LEMONCHIFFON": Color(255, 250, 205),
|
||||
"LIGHTBLUE": Color(173, 216, 230),
|
||||
"LIGHTCORAL": Color(240, 128, 128),
|
||||
"LIGHTCYAN": Color(224, 255, 255),
|
||||
"LIGHTGOLDENRODYELLOW": Color(250, 250, 210),
|
||||
"LIGHTGRAY": Color(211, 211, 211),
|
||||
"LIGHTGREEN": Color(144, 238, 144),
|
||||
"LIGHTGREY": Color(211, 211, 211),
|
||||
"LIGHTPINK": Color(255, 182, 193),
|
||||
"LIGHTSALMON": Color(255, 160, 122),
|
||||
"LIGHTSEAGREEN": Color(32, 178, 170),
|
||||
"LIGHTSKYBLUE": Color(135, 206, 250),
|
||||
"LIGHTSLATEGRAY": Color(119, 136, 153),
|
||||
"LIGHTSLATEGREY": Color(119, 136, 153),
|
||||
"LIGHTSTEELBLUE": Color(176, 196, 222),
|
||||
"LIGHTYELLOW": Color(255, 255, 224),
|
||||
"LIME": Color(0, 255, 0),
|
||||
"LIMEGREEN": Color(50, 205, 50),
|
||||
"LINEN": Color(250, 240, 230),
|
||||
"MAGENTA": Color(255, 0, 255),
|
||||
"MAROON": Color(128, 0, 0),
|
||||
"MEDIUMAQUAMARINE": Color(102, 205, 170),
|
||||
"MEDIUMBLUE": Color(0, 0, 205),
|
||||
"MEDIUMORCHID": Color(186, 85, 211),
|
||||
"MEDIUMPURPLE": Color(147, 112, 219),
|
||||
"MEDIUMSEAGREEN": Color(60, 179, 113),
|
||||
"MEDIUMSLATEBLUE": Color(123, 104, 238),
|
||||
"MEDIUMSPRINGGREEN": Color(0, 250, 154),
|
||||
"MEDIUMTURQUOISE": Color(72, 209, 204),
|
||||
"MEDIUMVIOLETRED": Color(199, 21, 133),
|
||||
"MIDNIGHTBLUE": Color(25, 25, 112),
|
||||
"MINTCREAM": Color(245, 255, 250),
|
||||
"MISTYROSE": Color(255, 228, 225),
|
||||
"MOCCASIN": Color(255, 228, 181),
|
||||
"NAVAJOWHITE": Color(255, 222, 173),
|
||||
"NAVY": Color(0, 0, 128),
|
||||
"OLDLACE": Color(253, 245, 230),
|
||||
"OLIVE": Color(128, 128, 0),
|
||||
"OLIVEDRAB": Color(107, 142, 35),
|
||||
"ORANGE": Color(255, 165, 0),
|
||||
"ORANGERED": Color(255, 69, 0),
|
||||
"ORCHID": Color(218, 112, 214),
|
||||
"PALEGOLDENROD": Color(238, 232, 170),
|
||||
"PALEGREEN": Color(152, 251, 152),
|
||||
"PALETURQUOISE": Color(175, 238, 238),
|
||||
"PALEVIOLETRED": Color(219, 112, 147),
|
||||
"PAPAYAWHIP": Color(255, 239, 213),
|
||||
"PEACHPUFF": Color(255, 218, 185),
|
||||
"PERU": Color(205, 133, 63),
|
||||
"PINK": Color(255, 192, 203),
|
||||
"PLUM": Color(221, 160, 221),
|
||||
"POWDERBLUE": Color(176, 224, 230),
|
||||
"PURPLE": Color(128, 0, 128),
|
||||
"REBECCAPURPLE": Color(128, 51, 153),
|
||||
"RED": Color(255, 0, 0),
|
||||
"ROSYBROWN": Color(188, 143, 143),
|
||||
"ROYALBLUE": Color(65, 105, 225),
|
||||
"SADDLEBROWN": Color(139, 69, 19),
|
||||
"SALMON": Color(250, 128, 114),
|
||||
"SANDYBROWN": Color(244, 164, 96),
|
||||
"SEAGREEN": Color(46, 139, 87),
|
||||
"SEASHELL": Color(255, 245, 238),
|
||||
"SIENNA": Color(160, 82, 45),
|
||||
"SILVER": Color(192, 192, 192),
|
||||
"SKYBLUE": Color(135, 206, 235),
|
||||
"SLATEBLUE": Color(106, 90, 205),
|
||||
"SLATEGRAY": Color(112, 128, 144),
|
||||
"SLATEGREY": Color(112, 128, 144),
|
||||
"SNOW": Color(255, 250, 250),
|
||||
"SPRINGGREEN": Color(0, 255, 127),
|
||||
"STEELBLUE": Color(70, 130, 180),
|
||||
"TAN": Color(210, 180, 140),
|
||||
"TEAL": Color(0, 128, 128),
|
||||
"THISTLE": Color(216, 191, 216),
|
||||
"TOMATO": Color(255, 99, 71),
|
||||
"TURQUOISE": Color(64, 224, 208),
|
||||
"VIOLET": Color(238, 130, 238),
|
||||
"WHEAT": Color(245, 222, 179),
|
||||
"WHITE": Color(255, 255, 255),
|
||||
"WHITESMOKE": Color(245, 245, 245),
|
||||
"YELLOW": Color(255, 255, 0),
|
||||
"YELLOWGREEN": Color(154, 205, 50)
|
||||
}
|
||||
|
|
@ -0,0 +1,322 @@
|
|||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from selenium.common.exceptions import WebDriverException
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.webdriver.remote.webdriver import WebDriver
|
||||
from selenium.webdriver.remote.webelement import WebElement
|
||||
from .abstract_event_listener import AbstractEventListener
|
||||
|
||||
|
||||
def _wrap_elements(result, ef_driver):
|
||||
if isinstance(result, WebElement):
|
||||
return EventFiringWebElement(result, ef_driver)
|
||||
elif isinstance(result, list):
|
||||
return [_wrap_elements(item, ef_driver) for item in result]
|
||||
else:
|
||||
return result
|
||||
|
||||
|
||||
class EventFiringWebDriver(object):
|
||||
"""
|
||||
A wrapper around an arbitrary WebDriver instance which supports firing events
|
||||
"""
|
||||
|
||||
def __init__(self, driver, event_listener):
|
||||
"""
|
||||
Creates a new instance of the EventFiringWebDriver
|
||||
|
||||
:Args:
|
||||
- driver : A WebDriver instance
|
||||
- event_listener : Instance of a class that subclasses AbstractEventListener and implements it fully or partially
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from selenium.webdriver import Firefox
|
||||
from selenium.webdriver.support.events import EventFiringWebDriver, AbstractEventListener
|
||||
|
||||
class MyListener(AbstractEventListener):
|
||||
def before_navigate_to(self, url, driver):
|
||||
print("Before navigate to %s" % url)
|
||||
def after_navigate_to(self, url, driver):
|
||||
print("After navigate to %s" % url)
|
||||
|
||||
driver = Firefox()
|
||||
ef_driver = EventFiringWebDriver(driver, MyListener())
|
||||
ef_driver.get("http://www.google.co.in/")
|
||||
"""
|
||||
if not isinstance(driver, WebDriver):
|
||||
raise WebDriverException("A WebDriver instance must be supplied")
|
||||
if not isinstance(event_listener, AbstractEventListener):
|
||||
raise WebDriverException("Event listener must be a subclass of AbstractEventListener")
|
||||
self._driver = driver
|
||||
self._driver._wrap_value = self._wrap_value
|
||||
self._listener = event_listener
|
||||
|
||||
@property
|
||||
def wrapped_driver(self):
|
||||
"""Returns the WebDriver instance wrapped by this EventsFiringWebDriver"""
|
||||
return self._driver
|
||||
|
||||
def get(self, url):
|
||||
self._dispatch("navigate_to", (url, self._driver), "get", (url, ))
|
||||
|
||||
def back(self):
|
||||
self._dispatch("navigate_back", (self._driver,), "back", ())
|
||||
|
||||
def forward(self):
|
||||
self._dispatch("navigate_forward", (self._driver,), "forward", ())
|
||||
|
||||
def execute_script(self, script, *args):
|
||||
unwrapped_args = (script,) + self._unwrap_element_args(args)
|
||||
return self._dispatch("execute_script", (script, self._driver), "execute_script", unwrapped_args)
|
||||
|
||||
def execute_async_script(self, script, *args):
|
||||
unwrapped_args = (script,) + self._unwrap_element_args(args)
|
||||
return self._dispatch("execute_script", (script, self._driver), "execute_async_script", unwrapped_args)
|
||||
|
||||
def close(self):
|
||||
self._dispatch("close", (self._driver,), "close", ())
|
||||
|
||||
def quit(self):
|
||||
self._dispatch("quit", (self._driver,), "quit", ())
|
||||
|
||||
def find_element(self, by=By.ID, value=None):
|
||||
return self._dispatch("find", (by, value, self._driver), "find_element", (by, value))
|
||||
|
||||
def find_elements(self, by=By.ID, value=None):
|
||||
return self._dispatch("find", (by, value, self._driver), "find_elements", (by, value))
|
||||
|
||||
def find_element_by_id(self, id_):
|
||||
return self.find_element(by=By.ID, value=id_)
|
||||
|
||||
def find_elements_by_id(self, id_):
|
||||
return self.find_elements(by=By.ID, value=id_)
|
||||
|
||||
def find_element_by_xpath(self, xpath):
|
||||
return self.find_element(by=By.XPATH, value=xpath)
|
||||
|
||||
def find_elements_by_xpath(self, xpath):
|
||||
return self.find_elements(by=By.XPATH, value=xpath)
|
||||
|
||||
def find_element_by_link_text(self, link_text):
|
||||
return self.find_element(by=By.LINK_TEXT, value=link_text)
|
||||
|
||||
def find_elements_by_link_text(self, text):
|
||||
return self.find_elements(by=By.LINK_TEXT, value=text)
|
||||
|
||||
def find_element_by_partial_link_text(self, link_text):
|
||||
return self.find_element(by=By.PARTIAL_LINK_TEXT, value=link_text)
|
||||
|
||||
def find_elements_by_partial_link_text(self, link_text):
|
||||
return self.find_elements(by=By.PARTIAL_LINK_TEXT, value=link_text)
|
||||
|
||||
def find_element_by_name(self, name):
|
||||
return self.find_element(by=By.NAME, value=name)
|
||||
|
||||
def find_elements_by_name(self, name):
|
||||
return self.find_elements(by=By.NAME, value=name)
|
||||
|
||||
def find_element_by_tag_name(self, name):
|
||||
return self.find_element(by=By.TAG_NAME, value=name)
|
||||
|
||||
def find_elements_by_tag_name(self, name):
|
||||
return self.find_elements(by=By.TAG_NAME, value=name)
|
||||
|
||||
def find_element_by_class_name(self, name):
|
||||
return self.find_element(by=By.CLASS_NAME, value=name)
|
||||
|
||||
def find_elements_by_class_name(self, name):
|
||||
return self.find_elements(by=By.CLASS_NAME, value=name)
|
||||
|
||||
def find_element_by_css_selector(self, css_selector):
|
||||
return self.find_element(by=By.CSS_SELECTOR, value=css_selector)
|
||||
|
||||
def find_elements_by_css_selector(self, css_selector):
|
||||
return self.find_elements(by=By.CSS_SELECTOR, value=css_selector)
|
||||
|
||||
def _dispatch(self, l_call, l_args, d_call, d_args):
|
||||
getattr(self._listener, "before_%s" % l_call)(*l_args)
|
||||
try:
|
||||
result = getattr(self._driver, d_call)(*d_args)
|
||||
except Exception as e:
|
||||
self._listener.on_exception(e, self._driver)
|
||||
raise e
|
||||
getattr(self._listener, "after_%s" % l_call)(*l_args)
|
||||
return _wrap_elements(result, self)
|
||||
|
||||
def _unwrap_element_args(self, args):
|
||||
if isinstance(args, EventFiringWebElement):
|
||||
return args.wrapped_element
|
||||
elif isinstance(args, tuple):
|
||||
return tuple([self._unwrap_element_args(item) for item in args])
|
||||
elif isinstance(args, list):
|
||||
return [self._unwrap_element_args(item) for item in args]
|
||||
else:
|
||||
return args
|
||||
|
||||
def _wrap_value(self, value):
|
||||
if isinstance(value, EventFiringWebElement):
|
||||
return WebDriver._wrap_value(self._driver, value.wrapped_element)
|
||||
return WebDriver._wrap_value(self._driver, value)
|
||||
|
||||
def __setattr__(self, item, value):
|
||||
if item.startswith("_") or not hasattr(self._driver, item):
|
||||
object.__setattr__(self, item, value)
|
||||
else:
|
||||
try:
|
||||
object.__setattr__(self._driver, item, value)
|
||||
except Exception as e:
|
||||
self._listener.on_exception(e, self._driver)
|
||||
raise e
|
||||
|
||||
def __getattr__(self, name):
|
||||
def _wrap(*args, **kwargs):
|
||||
try:
|
||||
result = attrib(*args, **kwargs)
|
||||
return _wrap_elements(result, self)
|
||||
except Exception as e:
|
||||
self._listener.on_exception(e, self._driver)
|
||||
raise
|
||||
|
||||
try:
|
||||
attrib = getattr(self._driver, name)
|
||||
return _wrap if callable(attrib) else attrib
|
||||
except Exception as e:
|
||||
self._listener.on_exception(e, self._driver)
|
||||
raise
|
||||
|
||||
|
||||
class EventFiringWebElement(object):
|
||||
""""
|
||||
A wrapper around WebElement instance which supports firing events
|
||||
"""
|
||||
|
||||
def __init__(self, webelement, ef_driver):
|
||||
"""
|
||||
Creates a new instance of the EventFiringWebElement
|
||||
"""
|
||||
self._webelement = webelement
|
||||
self._ef_driver = ef_driver
|
||||
self._driver = ef_driver.wrapped_driver
|
||||
self._listener = ef_driver._listener
|
||||
|
||||
@property
|
||||
def wrapped_element(self):
|
||||
"""Returns the WebElement wrapped by this EventFiringWebElement instance"""
|
||||
return self._webelement
|
||||
|
||||
def click(self):
|
||||
self._dispatch("click", (self._webelement, self._driver), "click", ())
|
||||
|
||||
def clear(self):
|
||||
self._dispatch("change_value_of", (self._webelement, self._driver), "clear", ())
|
||||
|
||||
def send_keys(self, *value):
|
||||
self._dispatch("change_value_of", (self._webelement, self._driver), "send_keys", value)
|
||||
|
||||
def find_element(self, by=By.ID, value=None):
|
||||
return self._dispatch("find", (by, value, self._driver), "find_element", (by, value))
|
||||
|
||||
def find_elements(self, by=By.ID, value=None):
|
||||
return self._dispatch("find", (by, value, self._driver), "find_elements", (by, value))
|
||||
|
||||
def find_element_by_id(self, id_):
|
||||
return self.find_element(by=By.ID, value=id_)
|
||||
|
||||
def find_elements_by_id(self, id_):
|
||||
return self.find_elements(by=By.ID, value=id_)
|
||||
|
||||
def find_element_by_name(self, name):
|
||||
return self.find_element(by=By.NAME, value=name)
|
||||
|
||||
def find_elements_by_name(self, name):
|
||||
return self.find_elements(by=By.NAME, value=name)
|
||||
|
||||
def find_element_by_link_text(self, link_text):
|
||||
return self.find_element(by=By.LINK_TEXT, value=link_text)
|
||||
|
||||
def find_elements_by_link_text(self, link_text):
|
||||
return self.find_elements(by=By.LINK_TEXT, value=link_text)
|
||||
|
||||
def find_element_by_partial_link_text(self, link_text):
|
||||
return self.find_element(by=By.PARTIAL_LINK_TEXT, value=link_text)
|
||||
|
||||
def find_elements_by_partial_link_text(self, link_text):
|
||||
return self.find_elements(by=By.PARTIAL_LINK_TEXT, value=link_text)
|
||||
|
||||
def find_element_by_tag_name(self, name):
|
||||
return self.find_element(by=By.TAG_NAME, value=name)
|
||||
|
||||
def find_elements_by_tag_name(self, name):
|
||||
return self.find_elements(by=By.TAG_NAME, value=name)
|
||||
|
||||
def find_element_by_xpath(self, xpath):
|
||||
return self.find_element(by=By.XPATH, value=xpath)
|
||||
|
||||
def find_elements_by_xpath(self, xpath):
|
||||
return self.find_elements(by=By.XPATH, value=xpath)
|
||||
|
||||
def find_element_by_class_name(self, name):
|
||||
return self.find_element(by=By.CLASS_NAME, value=name)
|
||||
|
||||
def find_elements_by_class_name(self, name):
|
||||
return self.find_elements(by=By.CLASS_NAME, value=name)
|
||||
|
||||
def find_element_by_css_selector(self, css_selector):
|
||||
return self.find_element(by=By.CSS_SELECTOR, value=css_selector)
|
||||
|
||||
def find_elements_by_css_selector(self, css_selector):
|
||||
return self.find_elements(by=By.CSS_SELECTOR, value=css_selector)
|
||||
|
||||
def _dispatch(self, l_call, l_args, d_call, d_args):
|
||||
getattr(self._listener, "before_%s" % l_call)(*l_args)
|
||||
try:
|
||||
result = getattr(self._webelement, d_call)(*d_args)
|
||||
except Exception as e:
|
||||
self._listener.on_exception(e, self._driver)
|
||||
raise e
|
||||
getattr(self._listener, "after_%s" % l_call)(*l_args)
|
||||
return _wrap_elements(result, self._ef_driver)
|
||||
|
||||
def __setattr__(self, item, value):
|
||||
if item.startswith("_") or not hasattr(self._webelement, item):
|
||||
object.__setattr__(self, item, value)
|
||||
else:
|
||||
try:
|
||||
object.__setattr__(self._webelement, item, value)
|
||||
except Exception as e:
|
||||
self._listener.on_exception(e, self._driver)
|
||||
raise e
|
||||
|
||||
def __getattr__(self, name):
|
||||
def _wrap(*args, **kwargs):
|
||||
try:
|
||||
result = attrib(*args, **kwargs)
|
||||
return _wrap_elements(result, self._ef_driver)
|
||||
except Exception as e:
|
||||
self._listener.on_exception(e, self._driver)
|
||||
raise
|
||||
|
||||
try:
|
||||
attrib = getattr(self._webelement, name)
|
||||
return _wrap if callable(attrib) else attrib
|
||||
except Exception as e:
|
||||
self._listener.on_exception(e, self._driver)
|
||||
raise
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from .abstract_event_listener import AbstractEventListener # noqa
|
||||
from .event_firing_webdriver import EventFiringWebDriver # noqa
|
||||
|
|
@ -0,0 +1,422 @@
|
|||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from 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.common.exceptions import NoAlertPresentException
|
||||
from selenium.webdriver.remote.webdriver import WebElement
|
||||
|
||||
"""
|
||||
* Canned "Expected Conditions" which are generally useful within webdriver
|
||||
* tests.
|
||||
"""
|
||||
|
||||
|
||||
class title_is(object):
|
||||
"""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 __init__(self, title):
|
||||
self.title = title
|
||||
|
||||
def __call__(self, driver):
|
||||
return self.title == driver.title
|
||||
|
||||
|
||||
class title_contains(object):
|
||||
""" 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 __init__(self, title):
|
||||
self.title = title
|
||||
|
||||
def __call__(self, driver):
|
||||
return self.title in driver.title
|
||||
|
||||
|
||||
class presence_of_element_located(object):
|
||||
""" 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 __init__(self, locator):
|
||||
self.locator = locator
|
||||
|
||||
def __call__(self, driver):
|
||||
return _find_element(driver, self.locator)
|
||||
|
||||
|
||||
class url_contains(object):
|
||||
""" 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 __init__(self, url):
|
||||
self.url = url
|
||||
|
||||
def __call__(self, driver):
|
||||
return self.url in driver.current_url
|
||||
|
||||
|
||||
class url_matches(object):
|
||||
"""An expectation for checking the current url.
|
||||
pattern is the expected pattern, which must be an exact match
|
||||
returns True if the url matches, false otherwise."""
|
||||
def __init__(self, pattern):
|
||||
self.pattern = pattern
|
||||
|
||||
def __call__(self, driver):
|
||||
import re
|
||||
match = re.search(self.pattern, driver.current_url)
|
||||
|
||||
return match is not None
|
||||
|
||||
|
||||
class url_to_be(object):
|
||||
"""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 __init__(self, url):
|
||||
self.url = url
|
||||
|
||||
def __call__(self, driver):
|
||||
return self.url == driver.current_url
|
||||
|
||||
|
||||
class url_changes(object):
|
||||
"""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 __init__(self, url):
|
||||
self.url = url
|
||||
|
||||
def __call__(self, driver):
|
||||
return self.url != driver.current_url
|
||||
|
||||
|
||||
class visibility_of_element_located(object):
|
||||
""" 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 __init__(self, locator):
|
||||
self.locator = locator
|
||||
|
||||
def __call__(self, driver):
|
||||
try:
|
||||
return _element_if_visible(_find_element(driver, self.locator))
|
||||
except StaleElementReferenceException:
|
||||
return False
|
||||
|
||||
|
||||
class visibility_of(object):
|
||||
""" 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 __init__(self, element):
|
||||
self.element = element
|
||||
|
||||
def __call__(self, ignored):
|
||||
return _element_if_visible(self.element)
|
||||
|
||||
|
||||
def _element_if_visible(element, visibility=True):
|
||||
return element if element.is_displayed() == visibility else False
|
||||
|
||||
|
||||
class presence_of_all_elements_located(object):
|
||||
""" 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 __init__(self, locator):
|
||||
self.locator = locator
|
||||
|
||||
def __call__(self, driver):
|
||||
return _find_elements(driver, self.locator)
|
||||
|
||||
|
||||
class visibility_of_any_elements_located(object):
|
||||
""" 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 __init__(self, locator):
|
||||
self.locator = locator
|
||||
|
||||
def __call__(self, driver):
|
||||
return [element for element in _find_elements(driver, self.locator) if _element_if_visible(element)]
|
||||
|
||||
|
||||
class visibility_of_all_elements_located(object):
|
||||
""" 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 __init__(self, locator):
|
||||
self.locator = locator
|
||||
|
||||
def __call__(self, driver):
|
||||
try:
|
||||
elements = _find_elements(driver, self.locator)
|
||||
for element in elements:
|
||||
if _element_if_visible(element, visibility=False):
|
||||
return False
|
||||
return elements
|
||||
except StaleElementReferenceException:
|
||||
return False
|
||||
|
||||
|
||||
class text_to_be_present_in_element(object):
|
||||
""" An expectation for checking if the given text is present in the
|
||||
specified element.
|
||||
locator, text
|
||||
"""
|
||||
def __init__(self, locator, text_):
|
||||
self.locator = locator
|
||||
self.text = text_
|
||||
|
||||
def __call__(self, driver):
|
||||
try:
|
||||
element_text = _find_element(driver, self.locator).text
|
||||
return self.text in element_text
|
||||
except StaleElementReferenceException:
|
||||
return False
|
||||
|
||||
|
||||
class text_to_be_present_in_element_value(object):
|
||||
"""
|
||||
An expectation for checking if the given text is present in the element's
|
||||
locator, text
|
||||
"""
|
||||
def __init__(self, locator, text_):
|
||||
self.locator = locator
|
||||
self.text = text_
|
||||
|
||||
def __call__(self, driver):
|
||||
try:
|
||||
element_text = _find_element(driver,
|
||||
self.locator).get_attribute("value")
|
||||
if element_text:
|
||||
return self.text in element_text
|
||||
else:
|
||||
return False
|
||||
except StaleElementReferenceException:
|
||||
return False
|
||||
|
||||
|
||||
class frame_to_be_available_and_switch_to_it(object):
|
||||
""" 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 __init__(self, locator):
|
||||
self.frame_locator = locator
|
||||
|
||||
def __call__(self, driver):
|
||||
try:
|
||||
if isinstance(self.frame_locator, tuple):
|
||||
driver.switch_to.frame(_find_element(driver,
|
||||
self.frame_locator))
|
||||
else:
|
||||
driver.switch_to.frame(self.frame_locator)
|
||||
return True
|
||||
except NoSuchFrameException:
|
||||
return False
|
||||
|
||||
|
||||
class invisibility_of_element_located(object):
|
||||
""" An Expectation for checking that an element is either invisible or not
|
||||
present on the DOM.
|
||||
|
||||
locator used to find the element
|
||||
"""
|
||||
def __init__(self, locator):
|
||||
self.target = locator
|
||||
|
||||
def __call__(self, driver):
|
||||
try:
|
||||
target = self.target
|
||||
if not isinstance(target, WebElement):
|
||||
target = _find_element(driver, target)
|
||||
return _element_if_visible(target, 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
|
||||
|
||||
|
||||
class invisibility_of_element(invisibility_of_element_located):
|
||||
""" 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
|
||||
"""
|
||||
def __init(self, element):
|
||||
self.target = element
|
||||
|
||||
|
||||
class element_to_be_clickable(object):
|
||||
""" An Expectation for checking an element is visible and enabled such that
|
||||
you can click it."""
|
||||
def __init__(self, locator):
|
||||
self.locator = locator
|
||||
|
||||
def __call__(self, driver):
|
||||
element = visibility_of_element_located(self.locator)(driver)
|
||||
if element and element.is_enabled():
|
||||
return element
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
class staleness_of(object):
|
||||
""" 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 __init__(self, element):
|
||||
self.element = element
|
||||
|
||||
def __call__(self, ignored):
|
||||
try:
|
||||
# Calling any method forces a staleness check
|
||||
self.element.is_enabled()
|
||||
return False
|
||||
except StaleElementReferenceException:
|
||||
return True
|
||||
|
||||
|
||||
class element_to_be_selected(object):
|
||||
""" An expectation for checking the selection is selected.
|
||||
element is WebElement object
|
||||
"""
|
||||
def __init__(self, element):
|
||||
self.element = element
|
||||
|
||||
def __call__(self, ignored):
|
||||
return self.element.is_selected()
|
||||
|
||||
|
||||
class element_located_to_be_selected(object):
|
||||
"""An expectation for the element to be located is selected.
|
||||
locator is a tuple of (by, path)"""
|
||||
def __init__(self, locator):
|
||||
self.locator = locator
|
||||
|
||||
def __call__(self, driver):
|
||||
return _find_element(driver, self.locator).is_selected()
|
||||
|
||||
|
||||
class element_selection_state_to_be(object):
|
||||
""" An expectation for checking if the given element is selected.
|
||||
element is WebElement object
|
||||
is_selected is a Boolean."
|
||||
"""
|
||||
def __init__(self, element, is_selected):
|
||||
self.element = element
|
||||
self.is_selected = is_selected
|
||||
|
||||
def __call__(self, ignored):
|
||||
return self.element.is_selected() == self.is_selected
|
||||
|
||||
|
||||
class element_located_selection_state_to_be(object):
|
||||
""" 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 __init__(self, locator, is_selected):
|
||||
self.locator = locator
|
||||
self.is_selected = is_selected
|
||||
|
||||
def __call__(self, driver):
|
||||
try:
|
||||
element = _find_element(driver, self.locator)
|
||||
return element.is_selected() == self.is_selected
|
||||
except StaleElementReferenceException:
|
||||
return False
|
||||
|
||||
|
||||
class number_of_windows_to_be(object):
|
||||
""" An expectation for the number of windows to be a certain value."""
|
||||
|
||||
def __init__(self, num_windows):
|
||||
self.num_windows = num_windows
|
||||
|
||||
def __call__(self, driver):
|
||||
return len(driver.window_handles) == self.num_windows
|
||||
|
||||
|
||||
class new_window_is_opened(object):
|
||||
""" An expectation that a new window will be opened and have the number of
|
||||
windows handles increase"""
|
||||
|
||||
def __init__(self, current_handles):
|
||||
self.current_handles = current_handles
|
||||
|
||||
def __call__(self, driver):
|
||||
return len(driver.window_handles) > len(self.current_handles)
|
||||
|
||||
|
||||
class alert_is_present(object):
|
||||
""" Expect an alert to be present."""
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def __call__(self, driver):
|
||||
try:
|
||||
alert = driver.switch_to.alert
|
||||
return alert
|
||||
except NoAlertPresentException:
|
||||
return False
|
||||
|
||||
|
||||
def _find_element(driver, by):
|
||||
"""Looks up an element. Logs and re-raises ``WebDriverException``
|
||||
if thrown."""
|
||||
try:
|
||||
return driver.find_element(*by)
|
||||
except NoSuchElementException as e:
|
||||
raise e
|
||||
except WebDriverException as e:
|
||||
raise e
|
||||
|
||||
|
||||
def _find_elements(driver, by):
|
||||
try:
|
||||
return driver.find_elements(*by)
|
||||
except WebDriverException as e:
|
||||
raise e
|
||||
|
|
@ -0,0 +1,241 @@
|
|||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.common.exceptions import NoSuchElementException, UnexpectedTagNameException
|
||||
|
||||
|
||||
class Select(object):
|
||||
|
||||
def __init__(self, webelement):
|
||||
"""
|
||||
Constructor. A check is made that the given element is, indeed, a SELECT tag. If it is not,
|
||||
then an UnexpectedTagNameException is thrown.
|
||||
|
||||
:Args:
|
||||
- webelement - element SELECT element to wrap
|
||||
|
||||
Example:
|
||||
from selenium.webdriver.support.ui import Select \n
|
||||
Select(driver.find_element_by_tag_name("select")).select_by_index(2)
|
||||
"""
|
||||
if webelement.tag_name.lower() != "select":
|
||||
raise UnexpectedTagNameException(
|
||||
"Select only works on <select> elements, not on <%s>" %
|
||||
webelement.tag_name)
|
||||
self._el = webelement
|
||||
multi = self._el.get_attribute("multiple")
|
||||
self.is_multiple = multi and multi != "false"
|
||||
|
||||
@property
|
||||
def options(self):
|
||||
"""Returns a list of all options belonging to this select tag"""
|
||||
return self._el.find_elements(By.TAG_NAME, 'option')
|
||||
|
||||
@property
|
||||
def all_selected_options(self):
|
||||
"""Returns a list of all selected options belonging to this select tag"""
|
||||
ret = []
|
||||
for opt in self.options:
|
||||
if opt.is_selected():
|
||||
ret.append(opt)
|
||||
return ret
|
||||
|
||||
@property
|
||||
def first_selected_option(self):
|
||||
"""The first selected option in this select tag (or the currently selected option in a
|
||||
normal select)"""
|
||||
for opt in self.options:
|
||||
if opt.is_selected():
|
||||
return opt
|
||||
raise NoSuchElementException("No options are selected")
|
||||
|
||||
def select_by_value(self, value):
|
||||
"""Select all options that have a value matching the argument. That is, when given "foo" this
|
||||
would select an option like:
|
||||
|
||||
<option value="foo">Bar</option>
|
||||
|
||||
:Args:
|
||||
- value - The value to match against
|
||||
|
||||
throws NoSuchElementException If there is no option with specisied value in SELECT
|
||||
"""
|
||||
css = "option[value =%s]" % self._escapeString(value)
|
||||
opts = self._el.find_elements(By.CSS_SELECTOR, css)
|
||||
matched = False
|
||||
for opt in opts:
|
||||
self._setSelected(opt)
|
||||
if not self.is_multiple:
|
||||
return
|
||||
matched = True
|
||||
if not matched:
|
||||
raise NoSuchElementException("Cannot locate option with value: %s" % value)
|
||||
|
||||
def select_by_index(self, index):
|
||||
"""Select the option at the given index. This is done by examing the "index" attribute of an
|
||||
element, and not merely by counting.
|
||||
|
||||
:Args:
|
||||
- index - The option at this index will be selected
|
||||
|
||||
throws NoSuchElementException If there is no option with specisied index in SELECT
|
||||
"""
|
||||
match = str(index)
|
||||
for opt in self.options:
|
||||
if opt.get_attribute("index") == match:
|
||||
self._setSelected(opt)
|
||||
return
|
||||
raise NoSuchElementException("Could not locate element with index %d" % index)
|
||||
|
||||
def select_by_visible_text(self, text):
|
||||
"""Select all options that display text matching the argument. That is, when given "Bar" this
|
||||
would select an option like:
|
||||
|
||||
<option value="foo">Bar</option>
|
||||
|
||||
:Args:
|
||||
- text - The visible text to match against
|
||||
|
||||
throws NoSuchElementException If there is no option with specisied text in SELECT
|
||||
"""
|
||||
xpath = ".//option[normalize-space(.) = %s]" % self._escapeString(text)
|
||||
opts = self._el.find_elements(By.XPATH, xpath)
|
||||
matched = False
|
||||
for opt in opts:
|
||||
self._setSelected(opt)
|
||||
if not self.is_multiple:
|
||||
return
|
||||
matched = True
|
||||
|
||||
if len(opts) == 0 and " " in text:
|
||||
subStringWithoutSpace = self._get_longest_token(text)
|
||||
if subStringWithoutSpace == "":
|
||||
candidates = self.options
|
||||
else:
|
||||
xpath = ".//option[contains(.,%s)]" % self._escapeString(subStringWithoutSpace)
|
||||
candidates = self._el.find_elements(By.XPATH, xpath)
|
||||
for candidate in candidates:
|
||||
if text == candidate.text:
|
||||
self._setSelected(candidate)
|
||||
if not self.is_multiple:
|
||||
return
|
||||
matched = True
|
||||
|
||||
if not matched:
|
||||
raise NoSuchElementException("Could not locate element with visible text: %s" % text)
|
||||
|
||||
def deselect_all(self):
|
||||
"""Clear all selected entries. This is only valid when the SELECT supports multiple selections.
|
||||
throws NotImplementedError If the SELECT does not support multiple selections
|
||||
"""
|
||||
if not self.is_multiple:
|
||||
raise NotImplementedError("You may only deselect all options of a multi-select")
|
||||
for opt in self.options:
|
||||
self._unsetSelected(opt)
|
||||
|
||||
def deselect_by_value(self, value):
|
||||
"""Deselect all options that have a value matching the argument. That is, when given "foo" this
|
||||
would deselect an option like:
|
||||
|
||||
<option value="foo">Bar</option>
|
||||
|
||||
:Args:
|
||||
- value - The value to match against
|
||||
|
||||
throws NoSuchElementException If there is no option with specisied value in SELECT
|
||||
"""
|
||||
if not self.is_multiple:
|
||||
raise NotImplementedError("You may only deselect options of a multi-select")
|
||||
matched = False
|
||||
css = "option[value = %s]" % self._escapeString(value)
|
||||
opts = self._el.find_elements(By.CSS_SELECTOR, css)
|
||||
for opt in opts:
|
||||
self._unsetSelected(opt)
|
||||
matched = True
|
||||
if not matched:
|
||||
raise NoSuchElementException("Could not locate element with value: %s" % value)
|
||||
|
||||
def deselect_by_index(self, index):
|
||||
"""Deselect the option at the given index. This is done by examing the "index" attribute of an
|
||||
element, and not merely by counting.
|
||||
|
||||
:Args:
|
||||
- index - The option at this index will be deselected
|
||||
|
||||
throws NoSuchElementException If there is no option with specisied index in SELECT
|
||||
"""
|
||||
if not self.is_multiple:
|
||||
raise NotImplementedError("You may only deselect options of a multi-select")
|
||||
for opt in self.options:
|
||||
if opt.get_attribute("index") == str(index):
|
||||
self._unsetSelected(opt)
|
||||
return
|
||||
raise NoSuchElementException("Could not locate element with index %d" % index)
|
||||
|
||||
def deselect_by_visible_text(self, text):
|
||||
"""Deselect all options that display text matching the argument. That is, when given "Bar" this
|
||||
would deselect an option like:
|
||||
|
||||
<option value="foo">Bar</option>
|
||||
|
||||
:Args:
|
||||
- text - The visible text to match against
|
||||
"""
|
||||
if not self.is_multiple:
|
||||
raise NotImplementedError("You may only deselect options of a multi-select")
|
||||
matched = False
|
||||
xpath = ".//option[normalize-space(.) = %s]" % self._escapeString(text)
|
||||
opts = self._el.find_elements(By.XPATH, xpath)
|
||||
for opt in opts:
|
||||
self._unsetSelected(opt)
|
||||
matched = True
|
||||
if not matched:
|
||||
raise NoSuchElementException("Could not locate element with visible text: %s" % text)
|
||||
|
||||
def _setSelected(self, option):
|
||||
if not option.is_selected():
|
||||
option.click()
|
||||
|
||||
def _unsetSelected(self, option):
|
||||
if option.is_selected():
|
||||
option.click()
|
||||
|
||||
def _escapeString(self, value):
|
||||
if '"' in value and "'" in value:
|
||||
substrings = value.split("\"")
|
||||
result = ["concat("]
|
||||
for substring in substrings:
|
||||
result.append("\"%s\"" % substring)
|
||||
result.append(", '\"', ")
|
||||
result = result[0:-1]
|
||||
if value.endswith('"'):
|
||||
result.append(", '\"'")
|
||||
return "".join(result) + ")"
|
||||
|
||||
if '"' in value:
|
||||
return "'%s'" % value
|
||||
|
||||
return "\"%s\"" % value
|
||||
|
||||
def _get_longest_token(self, value):
|
||||
items = value.split(" ")
|
||||
longest = ""
|
||||
for item in items:
|
||||
if len(item) > len(longest):
|
||||
longest = item
|
||||
return longest
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from .select import Select # noqa
|
||||
from .wait import WebDriverWait # noqa
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import time
|
||||
from selenium.common.exceptions import NoSuchElementException
|
||||
from selenium.common.exceptions import TimeoutException
|
||||
|
||||
POLL_FREQUENCY = 0.5 # How long to sleep inbetween calls to the method
|
||||
IGNORED_EXCEPTIONS = (NoSuchElementException,) # exceptions ignored during calls to the method
|
||||
|
||||
|
||||
class WebDriverWait(object):
|
||||
def __init__(self, driver, timeout, poll_frequency=POLL_FREQUENCY, ignored_exceptions=None):
|
||||
"""Constructor, takes a WebDriver instance and timeout in seconds.
|
||||
|
||||
:Args:
|
||||
- driver - Instance of WebDriver (Ie, Firefox, Chrome or Remote)
|
||||
- timeout - Number of seconds before timing out
|
||||
- poll_frequency - sleep interval between calls
|
||||
By default, it is 0.5 second.
|
||||
- ignored_exceptions - iterable structure of exception classes ignored during calls.
|
||||
By default, it contains NoSuchElementException only.
|
||||
|
||||
Example:
|
||||
from selenium.webdriver.support.ui import WebDriverWait \n
|
||||
element = WebDriverWait(driver, 10).until(lambda x: x.find_element_by_id("someId")) \n
|
||||
is_disappeared = WebDriverWait(driver, 30, 1, (ElementNotVisibleException)).\ \n
|
||||
until_not(lambda x: x.find_element_by_id("someId").is_displayed())
|
||||
"""
|
||||
self._driver = driver
|
||||
self._timeout = timeout
|
||||
self._poll = poll_frequency
|
||||
# avoid the divide by zero
|
||||
if self._poll == 0:
|
||||
self._poll = POLL_FREQUENCY
|
||||
exceptions = list(IGNORED_EXCEPTIONS)
|
||||
if ignored_exceptions is not None:
|
||||
try:
|
||||
exceptions.extend(iter(ignored_exceptions))
|
||||
except TypeError: # ignored_exceptions is not iterable
|
||||
exceptions.append(ignored_exceptions)
|
||||
self._ignored_exceptions = tuple(exceptions)
|
||||
|
||||
def __repr__(self):
|
||||
return '<{0.__module__}.{0.__name__} (session="{1}")>'.format(
|
||||
type(self), self._driver.session_id)
|
||||
|
||||
def until(self, method, message=''):
|
||||
"""Calls the method provided with the driver as an argument until the \
|
||||
return value is not False."""
|
||||
screen = None
|
||||
stacktrace = None
|
||||
|
||||
end_time = time.time() + self._timeout
|
||||
while True:
|
||||
try:
|
||||
value = method(self._driver)
|
||||
if value:
|
||||
return value
|
||||
except self._ignored_exceptions as exc:
|
||||
screen = getattr(exc, 'screen', None)
|
||||
stacktrace = getattr(exc, 'stacktrace', None)
|
||||
time.sleep(self._poll)
|
||||
if time.time() > end_time:
|
||||
break
|
||||
raise TimeoutException(message, screen, stacktrace)
|
||||
|
||||
def until_not(self, method, message=''):
|
||||
"""Calls the method provided with the driver as an argument until the \
|
||||
return value is False."""
|
||||
end_time = time.time() + self._timeout
|
||||
while True:
|
||||
try:
|
||||
value = method(self._driver)
|
||||
if not value:
|
||||
return value
|
||||
except self._ignored_exceptions:
|
||||
return True
|
||||
time.sleep(self._poll)
|
||||
if time.time() > end_time:
|
||||
break
|
||||
raise TimeoutException(message)
|
||||
Loading…
Add table
Add a link
Reference in a new issue