pip install webdriverwrapper
Support for Python 2.6 and higher (including Python 3).
- Adds messges into webdriver's exceptions (by default there is no information about which element is missing etc.).
- Adds some usefull methods to WebDriver and WebElement. Such as
find_element_by_text
,contains_text
,wait_for_element
,- or
go_to
(explained below). - Makes filling out forms easier.
- If some WebElement is also form element, you can call method
fill_out
with dictionary as parameter. More info below. - If some WebElement is also Select, you can call methods from webdriver's Select class.
- Provide TestCase.
Just install Chrome/ium and put chromedriver into /bin
.
This is documentation of wrapper. Documentation of Selenium is on googlecode.com.
from webdriverwrapper import Firefox
driver = Firefox()
elements = driver.find_elements_by_text('hello')
another_elements = elements[0].find_element_by_text('world')
If you want to ignore some elements (for example some debug texts in development), add to these elements attribute data-selenium-not-search
.
if driver.contains_text(text):
print 'good'
get_elm(id_|class_name|name|tag_name|xpath[, parent_id|parent_class_name|parent_name|parent_tag_name])
Returns first element from list of found elements.
elm = driver.find_element_by_id('someid')
elm.find_elements_by_class_name('someclasss')
# or with webdriverwrapper
elm = driver.get_elm(class_name='someclass', parent_id='someid')
get_elms(id_|class_name|name|tag_name|xpath[, parent_id|parent_class_name|parent_name|parent_tag_name])
Same as get_elm
but it returns all found elements.
click([id_|class_name|name|tag_name|xpath[, parent_id|parent_class_name|parent_name|parent_tag_name]])
Clicks on first found element if you pass some arguments. Otherwise it calls webdriver's click method.
elm = driver.find_element_by_id('someid')
elm = elm.find_elements_by_class_name('someclasss')[0]
elm.click()
# or with webdriverwrapper
elm = driver.get_elm(class_name='someclass', parent_id='someid')
elm.click()
# or
driver.get_elm('someid').click(class_name='someclass')
# or
driver.click(class_name='someclass', parent_id='someid')
wait_for_element(timeout=10, message='', id_|class_name|name|tag_name|xpath[, parent_id|parent_class_name|parent_name|parent_tag_name])
Alias for WebDriverWait(driver, timeout).until(lambda driver: driver.get_elm(...), message)
.
driver.wait_for_element(id_='someid')
Alias for WebDriverWait(driver, timeout)
.
driver.wait().until_not(lambda driver: len(driver.get_elms('id')) > 10)
Go to page. It uses driver.get
method.
When domain is not supplied, it is parsed from current_url
, so you can define domain only once. Query can be string or dictionary. path
can be whole URL.
driver.go_to(domain='google.com')
driver.go_to('search') # See, I do not need to write again google.com.
driver.go_to(query={'q': 'hello from cool webdriverwrapper'})
Switch to window with window_name
(Selenium's classic way) or title
or url
.
driver.click('some-link-which-opens-popup')
driver.switch_to_window(title='New popup window')
# make some work in popup
Param url
can be dictionary and this dictionary can have three keys: path
, query
and domain
. It's the same as for method go_to
. If you don't specify domain
, it will be recognized from current url.
driver.switch_to_window(url='http://www.example.com/path')
# or
driver.switch_to_window(url={'path': 'path'})
Close window and stay in current window. Params are same as in switch_to_window
.
Close all windows except the current window.
Returns instance of selenium.webdriver.common.alert.Alert
. It's just alias, because it's hard to remember where that class is.
driver.get_alert().accept()
driver.get_alert().dismiss()
Downloads page from url
or current_url
if not supplied. Works the same way as download_file()
.
f = driver.download_url('http://myurl.com')
f.status_code
f.headers # Dictionary of all response headers.
f.data
It is not a good idea to download file by clicking on a link using Selenium. With Chrome it's working thanks to no save dialog by default, but still you can't check status code, data, headers, etc. For that purpose there is special method download_file
. You can call it will try download file using Python's urllib2
. Method returns special object (not response or file data) which hold some useful information.
f = driver.get_elm('some-link').download_file()
f.method # You can check if method was GET or POST (returns in lowercase).
f.status_code
f.headers # Dictionary of all response headers.
f.data
This method supports downloading from link (use attribute href) or from button in a form (use action or current URL). It looks for attribute method of a form, so is used correctly GET or POST. Method collects all data from form and pass it to URL (in case of method GET) or to data request (in case of method POST).
If some element is form, it provides some more methods.
form = driver.get_elm('formid')
form.fill_out({
'name': 'WebdriverWrapper', # Normal input
'type': 'testing-tool', # Selectbox
'is_awesome': True, # Checkbox
'whatever': [1, 2, 3], # Multicheckbox
})
Method will send key TAB if element is input of type text or textarea. Purpose of this is because of onchange event - JS function registred on that event will be called after losing of focus.
Same as fill_out
, but after that calls submit
.
Some forms have more buttons than one. Simple example is: one for submit and one for reset. This method firstly look for element with id "form id
_submit" and click on it. Otherwise it calls WebElement's submit.
Looks for element with id "form id
_reset" and clicks on it.
If some element is selected, it returns Select instance inherited from selenium's Select and WebDriver wrapper. So you can use all method from WebDriver and Select.
select = driver.get_elm(tag_name='select')
select.get_attribute('name')
select.select_by_value('value')
select.find_elements_by_text('Option text')
You can import all Selenium's exceptions from webdriverwrapper:
import webdriver.exceptions
WebdriverTestCase
provides method aliases on driver and some other cool stuff. If you need driver instance, it's hide in self.driver
.
from webdriverwrapper.testcase import WebdriverTestCase
class TestCase(WebdriverTestCase):
def test(self):
self.go_to('http://www.google.com')
self.click('gbqfsb') # I'm feeling luck.
self.contains_text('Doodles')
Tip: if you want to write something into __init__
, write it into method init
and you do not have to call parent's __init__
.
By default WebdriverTestCase
creates instance of Firefox. You can overwrite this method and create instance of driver you want.
WebdriverTestCase
check your web application on errors. When your page contains some elements with class error
, this method finds them and print that there is some problem.
WebdriverTestCase
looks for JavaScript errors in your web application. For that you need put into your site this code:
<script type="text/javascript">
window.jsErrors = [];
window.onerror = function(errorMessage) {
window.jsErrors[window.jsErrors.length] = errorMessage;
}
</script>
When your test needs to check errors somewhere in the middle of the test, just call this method. It call same check as is called after each test with one difference that it not depends on decorator. It means that no error is allowed.
You can take screenshot of what is currently seen in browser window by this method. If you will not provide screenshot_name
, name of test will be used.
If you want to take screenshot of failed test, just provide class variable screenshot_path
.
class TestCase(WebdriverTestCase):
screenshot_path = '/tmp'
def test_fail(self):
self.go_to('http://google.com')
self.click('some-non-exist-element') # Fail, so screenshot will be taken automatically.
def test_ok(self):
self.go_to('http://google.com')
self.make_screenshot('some-screenshot.png') # Will not fail, but I can take screenshot manually.
Show message in console. (Uses module logging
.)
Waits for user input. Good for debuging.
from webdriverwrapper.decorators import *
class TestCase(WebdriverTestCase):
@GoToPage('http://www.google.com')
@ShouldBeOnPage('doodles/finder/2013/All%20doodles')
def test(self):
self.click('gbqfsb')
self.contains_text('Doodles')
By default WebdriverTestCase
looks for error messages in elements with class error
. If you want to test that some page has some error message, use this decorator.
class TestCase(WebdriverTestCase):
@ShouldBeError('some-error')
def test(self):
# ...
Same as ShouldBeError
, but if test ends without error, it's ok as well as with error.
Similar to ShouldByError
, but it looks for error page, not error message. By error page I mean page with code 403, 404, 500 and so on. By default error page is decoded from .error-page h1
.
You can override method get_error_page
which returns title of error page (for example 404 Not found).
class TestCase(WebdriverTestCase):
@ShouldBeErrorPage(403)
def test(self):
# ...
You can override method get_error_messages
which returns list of error messages on page. Default implementation returns all texts from elements with class error
. But text is bad for testing (it can be changed one day), so you can specify attribute error
:
<div class="error" error="username_is_mandatory">Username is mandatory</div>
Same as ShoulBeError
, just looks for info messages.
class TestCase(WebdriverTestCase):
@ShouldBeInfo('some-info')
def test(self):
# ...
You can override method get_info_messages
which returns list of info messages on page. Default implementation returns all texts from elements with class info
. But text is bad for testing (it can be changed one day), so you can specify attribute info
:
<div class="info" info="successfully_saved">Your profile was successfully saved</div>
By default you have to specify domain in first go_to
call. It's not good because you should not know which test is called first. So you can specify domain by this class variable.
class TestCase(WebdriverTestCase):
domain = 'www.example.com'
By default WebdriverTestCase
create one driver for all tests. If you want one driver for every TestCase
or for every test, change this variable.
Note: It's good to define it in some base TestCase for all TestCases.
from webdriverwrapper.testcase import ONE_INSTANCE_FOR_ALL_TESTS
class TestCase(WebdriverTestCase):
instances_of_driver = ONE_INSTANCE_FOR_ALL_TESTS
Options are:
ONE_INSTANCE_FOR_ALL_TESTS
ONE_INSTANCE_PER_TESTCASE
ONE_INSTANCE_PER_TEST
Warning: If you use option ONE_INSTANCE_FOR_ALL_TESTS
(which is default), you need to call by yourself class method quit_driver
for example like this:
from webdriverwrapper.testcase import WebdriverTestCase
import nose
ok = nose.run(...)
WebdriverTestCase.quit_driver()
When you have to do some debug page (for example with Firebug or with Chrome Developer tools), you can set wait_after_test
and after each test it waits for input to continue.
class TestCase(WebdriverTestCase):
wait_after_test = True
If you in your test switch to another window, you don't have to remeber that you have to switch back into main window. WebdriverTestCase
ensure that every tests starts in main window.
Trust me, it's good to test web app by randomly clicking on links and buttons. It can find a lot of bugs, mostly JS (when you call some JS and some element doesn't exist and so on). For this webdriverwrapper provide FuzzyTestCase
. It randomly click on clickable elements and looks for page error (code 500) and JS errors.
Whole example is in examples/fuzzy.py.
You can override several methods:
By default calls get_error_page
and returns True
if curren page is error page. You shoul override it when you override method get_error_page
.
When error occurs by randomly clicking, it's good to restart to some default state. By default is called self.go_to('/')
. If you need something more, there is place where you should put that code.
Returns all clickable elememnts on current page. By default it returns all a
, submit
and input[@type="submit"]
elements which doesn't have class selenium_donotclick
.
TIP: When you want to make some elements more important, just add more references for them. For example if you want click mostly on some elements in page and don't click very ofter to menu, you can write something like this:
class FuzzyTest(FuzzyTestCase):
def get_clickable_elements(self):
elms_in_menu = self.get_elms(...)
other_elms = self.get_elms(...)
return elms_in_menu + other_elms * 5
TIP 2: Do not forget remove elements with class selenium_donotclick
. Then you can add this class to elements which you want to test by fuzzy. Because it can for example delete something from database and destroy rest of test.
You have to use this metaclass for your test. It create by default 50 tests (50 random clicks) which you can override by class variable count_of_clicks
.
If you overrided method get_error_messages
, you should pass your class CanBeError
to this class by class variable can_be_error_decorator
. In fuzzy testing some error messages are ignored. If you don't want ignore error messages, pass None
.