5 Commits
Author SHA1 Message Date
keitaoouchi dc23020b90 version 0.3.5 => 0.4.0 2012-09-13 16:44:38 +09:00
keitaoouchi 86ff1e7046 ignore PyCharm 2012-09-13 16:43:50 +09:00
keitaoouchi c2e6ea051c Add syntax sugar to javascript's scroll methods 2012-09-13 16:43:05 +09:00
Keita Oouchi 04ee72be9c Add attr shortcut 2012-07-27 22:35:11 +09:00
Johannes c464ab2e59 Fixed some typos in exception texts 2012-07-26 15:44:34 +02:00
5 changed files with 157 additions and 62 deletions
+1
View File
@@ -6,3 +6,4 @@ chromedriver.log
build/
dist/
*.egg-info
.idea
+45 -14
View File
@@ -122,6 +122,11 @@ SeleniumWrapper
>>> br.timeout
10
* attr(name)
Shortcut to get_attribute::
>>> br.attr('href')
* click(timeout=3, presleep=0, postsleep=0)
Continue to polling until timeout or element is displayed and clickable::
@@ -142,6 +147,26 @@ SeleniumWrapper
>>> br.script('alert("hoge")')
* scroll_to(x, y)
equivalent to javascript's scrollTo::
>>> br.scrollTo(0, 100)
* scroll_by(x, y)
equivalent to javascript's scrollBy::
>>> br.scrollBy(10, 10)
* scroll_into_view(jq_identifier, align_with_top=True)
find elements by jq_identifier and retrieve its first element and invoke scrollIntoView to it::
>>> var element = $('#hoge');
>>> element && element.scrollIntoView(true)
second argument is passed as javascript's boolean to scrollIntoView::
>>> br.scrollIntoView('#hoge', False)
* waitfor(type, target, eager=False, timeout=3)
See source::
@@ -238,28 +263,34 @@ SeleniumContainerWrapper
Recent Change
-------------
* 0.4.0
* Added **scroll_to**, **scroll_by**, **scroll_into_view** methods.
* Added **jquery** method to support jquery selecter.
* Added **load_js**, **script** methods.
* 0.3.5
* Added **attr** method.
* Fixed some typos.
* 0.3.4
* Add javascript support(**load_js**, **jquery**, **script** methods). -- *UNSTABLE* --
* Add size property to SeleniumContainerWrapper
* Added size property to SeleniumContainerWrapper
* Fixed to be able to change default timeout.
* 0.3.3
* Fixed bugs of string formatting.
* 0.3.2
* Change **alert** to wait until Alert's text is accesible.
* Changed **alert** to wait until Alert's text is accesible.
* Override **current_url** to wait for page body loaded.
* 0.3.1
* Add **connect** functon.
* Added **connect** functon.
* 0.3.0
* Change **tag** method to **by_tag**.
* Add **checkbox**, **radio**.
* Change **select** property to method.
* Add **sample**, **choice** methods to SeleniumContainerWrapper.
* Fix **click** bug.
* Changed **tag** method to **by_tag**.
* Added **checkbox**, **radio**.
* Changed **select** property to method.
* Added **sample**, **choice** methods to SeleniumContainerWrapper.
* Fixed **click** bug.
* 0.2.4
* Fix bug.
* Fixed bug.
* 0.2.3
* Add ext argument to **img** (alt and ext are both optional.)
* Added ext argument to **img** (alt and ext are both optional.)
* 0.2.2
* Add new property **alert**
* Change **img**'s argument from ext to alt( find_element_by_xpath("//img[@alt='{}'.format(alt)) )
* Modify SeleniumContainerWrapper's __contains__ behavior to unwrap given object if it is a SeleniumWrapper.
* Added new property **alert**
* Changed **img**'s argument from ext to alt( find_element_by_xpath("//img[@alt='{}'.format(alt)) )
* Modified SeleniumContainerWrapper's __contains__ behavior to unwrap given object if it is a SeleniumWrapper.
+2 -2
View File
@@ -22,14 +22,14 @@ classifiers = [
'Topic :: Software Development :: Libraries :: Python Modules'
]
requires = ['selenium>=2.22.1']
requires = ['selenium>=2.25.0']
setup(
author='Keita Oouchi',
author_email='keita.oouchi@gmail.com',
url = 'https://github.com/keitaoouchi/seleniumwrapper',
name = 'seleniumwrapper',
version = '0.3.4',
version = '0.4.0',
package_dir={"":"src"},
packages = ['seleniumwrapper'],
test_suite = "test_seleniumwrapper.suite",
+78 -43
View File
@@ -34,6 +34,7 @@ def create(drivername, *args, **kwargs):
msg = "drivername should be one of [IE, Opera, Chrome, Firefox](case-insentive). given {0}".format(drivername)
raise ValueError(msg)
def connect(drivername, executor, custom_capabilities=None, **kwargs):
if not isinstance(drivername, str):
msg = "drivername should be an instance of string. given {0}".format(type(drivername))
@@ -51,7 +52,7 @@ def connect(drivername, executor, custom_capabilities=None, **kwargs):
'android': DesiredCapabilities.ANDROID}
dname = drivername.lower()
if dname in capabilities:
capability = capabilities[drivername]
capability = capabilities[dname]
custom_capabilities = custom_capabilities or {}
for key in custom_capabilities:
capability[key] = custom_capabilities[key]
@@ -64,12 +65,14 @@ def connect(drivername, executor, custom_capabilities=None, **kwargs):
msg = "drivername should be one of [IE, Opera, Chrome, Firefox](case-insentive). given {0}".format(drivername)
raise ValueError(msg)
def _is_wrappable(obj):
if isinstance(obj, WebDriver) or isinstance(obj, WebElement):
return True
else:
return False
def _chainreact(__getattr__):
def containment(*methodname):
def wrap_or_else(obj):
@@ -77,6 +80,7 @@ def _chainreact(__getattr__):
return SeleniumWrapper(obj)
else:
return obj
self, methodobj = __getattr__(*methodname)
if inspect.isroutine(methodobj):
def reaction(*realargs):
@@ -84,16 +88,18 @@ def _chainreact(__getattr__):
# for side-effective method(append, ...)
result = result if result is not None else self
return wrap_or_else(result)
return reaction
else:
return wrap_or_else(methodobj)
return containment
class SeleniumWrapper(object):
class SeleniumWrapper(object):
def __init__(self, driver):
if _is_wrappable(driver):
self._driver = driver
self._wrapped = driver
self._timeout = 5
else:
msg = "2nd argument should be an instance of WebDriver or WebElement. given {0}.".format(type(driver))
@@ -101,11 +107,11 @@ class SeleniumWrapper(object):
@property
def unwrap(self):
return self._driver
return self._wrapped
@property
def parent(self):
if isinstance(self._driver, WebElement):
if isinstance(self._wrapped, WebElement):
return self.xpath("./parent::node()", timeout=self._timeout)
else:
raise AttributeError("'WebDriver' object has no attribute 'parent'")
@@ -121,7 +127,7 @@ class SeleniumWrapper(object):
timeout = time.time() + self._timeout
while time.time() < timeout:
try:
alert = self._driver.switch_to_alert()
alert = self._wrapped.switch_to_alert()
alert.text
return alert
except NoAlertPresentException:
@@ -145,20 +151,20 @@ class SeleniumWrapper(object):
@_chainreact
def __getattr__(self, name):
return self._driver, getattr(self._driver, name)
return self._wrapped, getattr(self._wrapped, name)
@property
def current_url(self):
self.by_tag("body", self._timeout)
return self._driver.current_url
return self._wrapped.current_url
def _is_selectable(self):
return self.unwrap.tag_name == 'select'
def _is_stopping(self, interval):
before = (self._driver.location['x'], self._driver.location['y'])
before = (self._wrapped.location['x'], self._wrapped.location['y'])
time.sleep(interval)
after = (self._driver.location['x'], self._driver.location['y'])
after = (self._wrapped.location['x'], self._wrapped.location['y'])
return before[0] == after[0] and before[1] == after[1]
def _wait_until_stopping(self, timeout, interval):
@@ -169,36 +175,36 @@ class SeleniumWrapper(object):
else:
time.sleep(interval)
if not self._is_stopping(interval):
raise WebDriverException("Element is not stably displayed for {sec} seconds.".format(sec=timeout))
raise WebDriverException("Element was not stably displayed for {sec} seconds.".format(sec=timeout))
def _wait_until_clickable(self, timeout, interval):
err_messages = []
endtime = time.time() + timeout
while True:
try:
self._driver.click()
self._wrapped.click()
break
except WebDriverException as e:
err_messages.append(e.msg.split(":")[-1].strip())
time.sleep(interval)
if (time.time() > endtime):
if err_messages:
template = ("Wait for elemtent to be clickable for {sec} seconds, ",
template = ("Waited for element to be clickable for {sec} seconds, ",
"but clicked other elements. {err}")
msg = "".join(template).format(sec=timeout, err=err_messages)
raise WebDriverException(msg)
def _wait_until_displayed(self, timeout, interval):
try:
WebDriverWait(self._driver, timeout, interval).until(lambda d: d.is_displayed())
WebDriverWait(self._wrapped, timeout, interval).until(lambda d: d.is_displayed())
except TimeoutException:
template = ("Wait for elemtent to be displayed for {sec} seconds, ",
template = ("Waited for element to be displayed for {sec} seconds, ",
"but <{target} ...> was not displayed:: <{dumped}>")
msg = "".join(template).format(sec=timeout, target=self._driver.tag_name, dumped=self._dump())
msg = "".join(template).format(sec=timeout, target=self._wrapped.tag_name, dumped=self._dump())
raise ElementNotVisibleException(msg)
def _dump(self):
element = self._driver
element = self._wrapped
info = {"visibility": element.value_of_css_property("visibility"),
"display": element.value_of_css_property("display"),
"height": element.value_of_css_property("height"),
@@ -208,9 +214,15 @@ class SeleniumWrapper(object):
dumped = " ".join(["{k}:{v}".format(k=k, v=info[k]) for k in info])
return dumped
def attr(self, name):
if isinstance(self._wrapped, WebElement):
return self._wrapped.get_attribute(name)
else:
raise AttributeError("This is WebDriver wrapped object.")
def click(self, timeout=None, presleep=0, postsleep=0):
timeout = timeout or self._timeout
if isinstance(self._driver, WebElement):
if isinstance(self._wrapped, WebElement):
try:
if presleep:
time.sleep(presleep)
@@ -227,17 +239,17 @@ class SeleniumWrapper(object):
if isinstance(path_or_file, str) and os.path.isfile(path_or_file):
with open(path_or_file, 'r') as f:
library = f.read()
self._driver.execute_script(library)
self._wrapped.execute_script(library)
elif hasattr(path_or_file, 'read'):
library = path_or_file.read()
self._driver.execute_script(library)
self._wrapped.execute_script(library)
else:
raise AttributeError('Given argument is not both file or /path/to/file:: {0}'.format(str(path_or_file)))
def jquery(self, target):
"""Returns SeleniumContainerWrapper if any elements is found."""
script = 'return $("{0}")'.format(target)
result = self._driver.execute_script(script)
script = "try{{return $('{0}');}}catch(e){{}}".format(target)
result = self._wrapped.execute_script(script)
if result:
if isinstance(result, collections.Sequence):
return SeleniumContainerWrapper(result)
@@ -248,36 +260,59 @@ class SeleniumWrapper(object):
def script(self, javascript, *args):
"""Synchronously execute given javascript."""
result = self._driver.execute_script(javascript, *args)
result = self._wrapped.execute_script(javascript, *args)
if result:
if isinstance(result, collections.Sequence):
return SeleniumContainerWrapper(result)
else:
return SeleniumWrapper(result)
def scroll_to(self, x, y):
if isinstance(self._wrapped, WebDriver):
return self._wrapped.execute_script("window.scrollTo({:d}, {:d})".format(x, y))
else:
raise AttributeError("This is WebElement wrapped object.")
def scroll_by(self, x, y):
if isinstance(self._wrapped, WebDriver):
return self._wrapped.execute_script("window.scrollBy({:d}, {:d})".format(x, y))
else:
raise AttributeError("This is WebElement wrapped object.")
def scroll_into_view(self, jq_identifier, align_with_top=True):
if isinstance(self._wrapped, WebDriver):
if self._wrapped.execute_script("try{return $;}catch(e){}"):
script_template = "try{{$('{0}') && $('{0}')[0].scrollIntoView({1})}}catch(e){{}}"
script = script_template.format(jq_identifier, 'true' if align_with_top else 'false')
self._wrapped.execute_script(script)
else:
raise AttributeError("You must load jquery library.")
else:
raise AttributeError("This is WebElement wrapped object.")
def waitfor(self, type, target, eager=False, timeout=None):
timeout = timeout or self._timeout
if eager:
types = {"id":lambda d: d.find_elements_by_id(target),
"name":lambda d: d.find_elements_by_name(target),
"xpath":lambda d: d.find_elements_by_xpath(target),
"link_text":lambda d: d.find_elements_by_link_text(target),
"partial_link_text":lambda d: d.find_elements_by_partial_link_text(target),
"tag":lambda d: d.find_elements_by_tag_name(target),
"class":lambda d: d.find_elements_by_class_name(target),
"css":lambda d: d.find_elements_by_css_selector(target), }
types = {"id": lambda d: d.find_elements_by_id(target),
"name": lambda d: d.find_elements_by_name(target),
"xpath": lambda d: d.find_elements_by_xpath(target),
"link_text": lambda d: d.find_elements_by_link_text(target),
"partial_link_text": lambda d: d.find_elements_by_partial_link_text(target),
"tag": lambda d: d.find_elements_by_tag_name(target),
"class": lambda d: d.find_elements_by_class_name(target),
"css": lambda d: d.find_elements_by_css_selector(target), }
else:
types = {"id":lambda d: d.find_element_by_id(target),
"name":lambda d: d.find_element_by_name(target),
"xpath":lambda d: d.find_element_by_xpath(target),
"link_text":lambda d: d.find_element_by_link_text(target),
"partial_link_text":lambda d: d.find_element_by_partial_link_text(target),
"tag":lambda d: d.find_element_by_tag_name(target),
"class":lambda d: d.find_element_by_class_name(target),
"css":lambda d: d.find_element_by_css_selector(target), }
types = {"id": lambda d: d.find_element_by_id(target),
"name": lambda d: d.find_element_by_name(target),
"xpath": lambda d: d.find_element_by_xpath(target),
"link_text": lambda d: d.find_element_by_link_text(target),
"partial_link_text": lambda d: d.find_element_by_partial_link_text(target),
"tag": lambda d: d.find_element_by_tag_name(target),
"class": lambda d: d.find_element_by_class_name(target),
"css": lambda d: d.find_element_by_css_selector(target), }
finder = types[type]
try:
result = WebDriverWait(self._driver, timeout).until(finder)
result = WebDriverWait(self._wrapped, timeout).until(finder)
if eager and len(result):
return SeleniumContainerWrapper(result)
elif _is_wrappable(result):
@@ -285,7 +320,7 @@ class SeleniumWrapper(object):
else:
return result
except TimeoutException:
template = ("Wait for elemtent to appear for {sec} seconds, ",
template = ("Waited for element to appear for {sec} seconds, ",
"but {type}:{target} didn't appear.")
msg = "".join(template).format(sec=timeout, type=type, target=target)
raise NoSuchElementException(msg)
@@ -357,13 +392,13 @@ class SeleniumWrapper(object):
selected._iterable = [Select(element) for element in iterable if element.tag_name == 'select']
return selected
else:
template = ("Wait for elemtent to appear for {sec} seconds, ",
template = ("Waited for element to appear for {sec} seconds, ",
"but select:{attr} didn't appear.")
msg = "".join(template).format(sec=timeout, attr=attributes)
raise NoSuchElementException(msg)
class SeleniumContainerWrapper(object):
class SeleniumContainerWrapper(object):
def __init__(self, iterable):
if not isinstance(iterable, collections.Sequence):
msg = "2nd argument should be an instance of collections.Sequence. given {0}".format(type(iterable))
+31 -3
View File
@@ -12,7 +12,7 @@ from selenium.webdriver.remote.webdriver import WebDriver
from selenium.webdriver.remote.webelement import WebElement
from seleniumwrapper.wrapper import SeleniumWrapper
from seleniumwrapper.wrapper import SeleniumContainerWrapper
from selenium.common.exceptions import TimeoutException, NoSuchElementException, WebDriverException, ElementNotVisibleException
from selenium.common.exceptions import NoSuchElementException, WebDriverException, ElementNotVisibleException
class TestSeleniumWrapperFactory(unittest.TestCase):
@@ -163,6 +163,16 @@ class TestSeleniumWrapperAliases(unittest.TestCase):
wrapper = SeleniumWrapper(self.mock)
self.assertIsInstance(wrapper.waitfor("xpath", "dummy", eager=True), SeleniumContainerWrapper)
def test_attr_raise_if_invoked_from_webdriver_wrapped_object(self):
wrapper = SeleniumWrapper(self.mock)
self.assertRaises(AttributeError, wrapper.attr, 'hoge')
def test_attr_invoke_get_attribute_if_invoked_from_webelement_wrapped(self):
mock_elem = mock.Mock(WebElement)
mock_elem.get_attribute.return_value = True
wrapped = SeleniumWrapper(mock_elem)
self.assertTrue(wrapped.attr('hoge'))
def test_aliases_work_correctly(self):
mock_elem = mock.Mock(WebElement)
self.mock.find_element_by_xpath.return_value = mock_elem
@@ -224,10 +234,28 @@ class TestSeleniumWrapperJavascriptSupport(unittest.TestCase):
wrapper = SeleniumWrapper(self.mock)
self.assertRaises(NoSuchElementException, wrapper.jquery, 'hoge')
def test_scroll_methods_raise_if_wrapped_is_not_webdriver(self):
wrapper = SeleniumWrapper(mock.Mock(WebElement))
self.assertRaises(AttributeError, wrapper.scroll_to, *[10, 10])
self.assertRaises(AttributeError, wrapper.scroll_by, *[10, 10])
self.assertRaises(AttributeError, wrapper.scroll_into_view, '#hoge')
def test_scroll_into_view_raise_if_no_jquery_found(self):
wrapper = SeleniumWrapper(self.mock)
self.mock.execute_script.return_value = None
self.assertRaises(AttributeError, wrapper.scroll_into_view, '#hoge')
def test_scroll_into_view_execute_script_if_jquery_was_found(self):
wrapper = SeleniumWrapper(self.mock)
wrapper.scroll_into_view('#hoge')
calls = [mock.call.execute_script("try{return $;}catch(e){}"),
mock.call.execute_script("try{$('#hoge') && $('#hoge')[0].scrollIntoView(true)}catch(e){}")]
self.mock.assert_has_calls(calls)
def suite():
suite = unittest.TestSuite()
#suite.addTests(unittest.makeSuite(TestSeleniumWrapperAliases))
#suite.addTests(unittest.makeSuite(TestSeleniumWrapper))
suite.addTests(unittest.makeSuite(TestSeleniumWrapperAliases))
suite.addTests(unittest.makeSuite(TestSeleniumWrapper))
suite.addTests(unittest.makeSuite(TestSeleniumWrapperJavascriptSupport))
return suite