Compare commits
5
Commits
devel
..
javascript
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dc23020b90 | ||
|
|
86ff1e7046 | ||
|
|
c2e6ea051c | ||
|
|
04ee72be9c | ||
|
|
c464ab2e59 |
@@ -6,3 +6,4 @@ chromedriver.log
|
||||
build/
|
||||
dist/
|
||||
*.egg-info
|
||||
.idea
|
||||
|
||||
+45
-14
@@ -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.
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user