2013-02-24 00:10:48 +08:00
|
|
|
import os
|
2013-07-01 20:22:27 +08:00
|
|
|
from unittest import SkipTest
|
2013-02-24 00:10:48 +08:00
|
|
|
|
2014-08-28 14:44:26 +08:00
|
|
|
from django.contrib.staticfiles.testing import StaticLiveServerTestCase
|
2014-01-21 04:15:14 +08:00
|
|
|
from django.utils.module_loading import import_string
|
Fixed #2879 -- Added support for the integration with Selenium and other in-browser testing frameworks. Also added the first Selenium tests for `contrib.admin`. Many thanks to everyone for their contributions and feedback: Mikeal Rogers, Dirk Datzert, mir, Simon G., Almad, Russell Keith-Magee, Denis Golomazov, devin, robertrv, andrewbadr, Idan Gazit, voidspace, Tom Christie, hjwp2, Adam Nelson, Jannis Leidel, Anssi Kääriäinen, Preston Holmes, Bruno Renié and Jacob Kaplan-Moss.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@17241 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2011-12-22 16:33:58 +08:00
|
|
|
from django.utils.translation import ugettext as _
|
|
|
|
|
2013-06-04 14:09:29 +08:00
|
|
|
|
2014-08-28 14:44:26 +08:00
|
|
|
class AdminSeleniumWebDriverTestCase(StaticLiveServerTestCase):
|
2013-06-04 14:09:29 +08:00
|
|
|
|
|
|
|
available_apps = [
|
|
|
|
'django.contrib.admin',
|
|
|
|
'django.contrib.auth',
|
|
|
|
'django.contrib.contenttypes',
|
|
|
|
'django.contrib.sessions',
|
|
|
|
'django.contrib.sites',
|
|
|
|
]
|
Fixed #2879 -- Added support for the integration with Selenium and other in-browser testing frameworks. Also added the first Selenium tests for `contrib.admin`. Many thanks to everyone for their contributions and feedback: Mikeal Rogers, Dirk Datzert, mir, Simon G., Almad, Russell Keith-Magee, Denis Golomazov, devin, robertrv, andrewbadr, Idan Gazit, voidspace, Tom Christie, hjwp2, Adam Nelson, Jannis Leidel, Anssi Kääriäinen, Preston Holmes, Bruno Renié and Jacob Kaplan-Moss.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@17241 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2011-12-22 16:33:58 +08:00
|
|
|
webdriver_class = 'selenium.webdriver.firefox.webdriver.WebDriver'
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def setUpClass(cls):
|
2013-02-26 01:14:42 +08:00
|
|
|
if not os.environ.get('DJANGO_SELENIUM_TESTS', False):
|
|
|
|
raise SkipTest('Selenium tests not requested')
|
Fixed #2879 -- Added support for the integration with Selenium and other in-browser testing frameworks. Also added the first Selenium tests for `contrib.admin`. Many thanks to everyone for their contributions and feedback: Mikeal Rogers, Dirk Datzert, mir, Simon G., Almad, Russell Keith-Magee, Denis Golomazov, devin, robertrv, andrewbadr, Idan Gazit, voidspace, Tom Christie, hjwp2, Adam Nelson, Jannis Leidel, Anssi Kääriäinen, Preston Holmes, Bruno Renié and Jacob Kaplan-Moss.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@17241 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2011-12-22 16:33:58 +08:00
|
|
|
try:
|
2014-01-21 04:15:14 +08:00
|
|
|
cls.selenium = import_string(cls.webdriver_class)()
|
2013-09-15 05:23:45 +08:00
|
|
|
except Exception as e:
|
Final attempt to solve sporadic test failures.
tearDownClass is not called if setUpClass throws an exception, in our case
this means that LiveServerTestCase leaks LiveServerThread sockets if the
test happens to be skipped later on, and AdminSeleniumWebDriverTestCase
doesn't close it's already open browser window. To prevent this leakage
we catch errors where needed and manually call _tearDownClassInternal.
_tearDownClassInternal should be written as defensively as possible since
it is not allowed to make any assumptions on how far setUpClass got.
This patch should fix the sporadic "Address already in use"-errors on jenkins
and also the "This code isn't under transaction management"-error for sqlite
(also just on jenkins).
After discussion with koniiiik, jezdez, kmtracey, tos9, lifeless, nedbat and
voidspace it was decided that this is the safest approach (thanks to everyone
for their comments and help). Manually calling tearDownClass was shut down
cause we don't know how our users override our classes.
This is a private and very specialized API on purpose and should not be used
without a strong reason!
This patch partially reverts the earlier attempts to fix those issues,
namely:
2fa0dd73b18f55d0fdd1c1d54b1d18031bfcf1ed and
3c5775d36f7e431d9691829a78580873111cb714
Final note: If this patch breaks in a later version of Django, please be
very careful on how you fix it, you might not see test failures locally.
That said, this patch hopefully doesn't produce even more failures.
2013-09-17 22:28:20 +08:00
|
|
|
raise SkipTest('Selenium webdriver "%s" not installed or not '
|
|
|
|
'operational: %s' % (cls.webdriver_class, str(e)))
|
|
|
|
# This has to be last to ensure that resources are cleaned up properly!
|
|
|
|
super(AdminSeleniumWebDriverTestCase, cls).setUpClass()
|
Fixed #2879 -- Added support for the integration with Selenium and other in-browser testing frameworks. Also added the first Selenium tests for `contrib.admin`. Many thanks to everyone for their contributions and feedback: Mikeal Rogers, Dirk Datzert, mir, Simon G., Almad, Russell Keith-Magee, Denis Golomazov, devin, robertrv, andrewbadr, Idan Gazit, voidspace, Tom Christie, hjwp2, Adam Nelson, Jannis Leidel, Anssi Kääriäinen, Preston Holmes, Bruno Renié and Jacob Kaplan-Moss.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@17241 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2011-12-22 16:33:58 +08:00
|
|
|
|
|
|
|
@classmethod
|
Final attempt to solve sporadic test failures.
tearDownClass is not called if setUpClass throws an exception, in our case
this means that LiveServerTestCase leaks LiveServerThread sockets if the
test happens to be skipped later on, and AdminSeleniumWebDriverTestCase
doesn't close it's already open browser window. To prevent this leakage
we catch errors where needed and manually call _tearDownClassInternal.
_tearDownClassInternal should be written as defensively as possible since
it is not allowed to make any assumptions on how far setUpClass got.
This patch should fix the sporadic "Address already in use"-errors on jenkins
and also the "This code isn't under transaction management"-error for sqlite
(also just on jenkins).
After discussion with koniiiik, jezdez, kmtracey, tos9, lifeless, nedbat and
voidspace it was decided that this is the safest approach (thanks to everyone
for their comments and help). Manually calling tearDownClass was shut down
cause we don't know how our users override our classes.
This is a private and very specialized API on purpose and should not be used
without a strong reason!
This patch partially reverts the earlier attempts to fix those issues,
namely:
2fa0dd73b18f55d0fdd1c1d54b1d18031bfcf1ed and
3c5775d36f7e431d9691829a78580873111cb714
Final note: If this patch breaks in a later version of Django, please be
very careful on how you fix it, you might not see test failures locally.
That said, this patch hopefully doesn't produce even more failures.
2013-09-17 22:28:20 +08:00
|
|
|
def _tearDownClassInternal(cls):
|
Fixed #2879 -- Added support for the integration with Selenium and other in-browser testing frameworks. Also added the first Selenium tests for `contrib.admin`. Many thanks to everyone for their contributions and feedback: Mikeal Rogers, Dirk Datzert, mir, Simon G., Almad, Russell Keith-Magee, Denis Golomazov, devin, robertrv, andrewbadr, Idan Gazit, voidspace, Tom Christie, hjwp2, Adam Nelson, Jannis Leidel, Anssi Kääriäinen, Preston Holmes, Bruno Renié and Jacob Kaplan-Moss.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@17241 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2011-12-22 16:33:58 +08:00
|
|
|
if hasattr(cls, 'selenium'):
|
|
|
|
cls.selenium.quit()
|
2013-09-18 00:38:12 +08:00
|
|
|
super(AdminSeleniumWebDriverTestCase, cls)._tearDownClassInternal()
|
Fixed #2879 -- Added support for the integration with Selenium and other in-browser testing frameworks. Also added the first Selenium tests for `contrib.admin`. Many thanks to everyone for their contributions and feedback: Mikeal Rogers, Dirk Datzert, mir, Simon G., Almad, Russell Keith-Magee, Denis Golomazov, devin, robertrv, andrewbadr, Idan Gazit, voidspace, Tom Christie, hjwp2, Adam Nelson, Jannis Leidel, Anssi Kääriäinen, Preston Holmes, Bruno Renié and Jacob Kaplan-Moss.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@17241 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2011-12-22 16:33:58 +08:00
|
|
|
|
2011-12-29 06:20:30 +08:00
|
|
|
def wait_until(self, callback, timeout=10):
|
|
|
|
"""
|
|
|
|
Helper function that blocks the execution of the tests until the
|
|
|
|
specified callback returns a value that is not falsy. This function can
|
|
|
|
be called, for example, after clicking a link or submitting a form.
|
|
|
|
See the other public methods that call this function for more details.
|
|
|
|
"""
|
|
|
|
from selenium.webdriver.support.wait import WebDriverWait
|
|
|
|
WebDriverWait(self.selenium, timeout).until(callback)
|
|
|
|
|
|
|
|
def wait_loaded_tag(self, tag_name, timeout=10):
|
|
|
|
"""
|
|
|
|
Helper function that blocks until the element with the given tag name
|
|
|
|
is found on the page.
|
|
|
|
"""
|
2014-01-03 07:31:53 +08:00
|
|
|
self.wait_for(tag_name, timeout)
|
|
|
|
|
|
|
|
def wait_for(self, css_selector, timeout=10):
|
|
|
|
"""
|
2014-03-02 22:25:53 +08:00
|
|
|
Helper function that blocks until a CSS selector is found on the page.
|
2014-01-03 07:31:53 +08:00
|
|
|
"""
|
|
|
|
from selenium.webdriver.common.by import By
|
|
|
|
from selenium.webdriver.support import expected_conditions as ec
|
|
|
|
self.wait_until(
|
|
|
|
ec.presence_of_element_located((By.CSS_SELECTOR, css_selector)),
|
|
|
|
timeout
|
|
|
|
)
|
|
|
|
|
|
|
|
def wait_for_text(self, css_selector, text, timeout=10):
|
|
|
|
"""
|
2014-03-02 22:25:53 +08:00
|
|
|
Helper function that blocks until the text is found in the CSS selector.
|
2014-01-03 07:31:53 +08:00
|
|
|
"""
|
|
|
|
from selenium.webdriver.common.by import By
|
2014-01-03 18:25:24 +08:00
|
|
|
from selenium.webdriver.support import expected_conditions as ec
|
|
|
|
self.wait_until(
|
|
|
|
ec.text_to_be_present_in_element(
|
|
|
|
(By.CSS_SELECTOR, css_selector), text),
|
|
|
|
timeout
|
|
|
|
)
|
|
|
|
|
|
|
|
def wait_for_value(self, css_selector, text, timeout=10):
|
|
|
|
"""
|
2014-03-02 22:25:53 +08:00
|
|
|
Helper function that blocks until the value is found in the CSS selector.
|
2014-01-03 18:25:24 +08:00
|
|
|
"""
|
|
|
|
from selenium.webdriver.common.by import By
|
2014-01-03 07:31:53 +08:00
|
|
|
from selenium.webdriver.support import expected_conditions as ec
|
2011-12-29 06:20:30 +08:00
|
|
|
self.wait_until(
|
2014-01-03 07:31:53 +08:00
|
|
|
ec.text_to_be_present_in_element_value(
|
|
|
|
(By.CSS_SELECTOR, css_selector), text),
|
2011-12-29 06:20:30 +08:00
|
|
|
timeout
|
|
|
|
)
|
|
|
|
|
2013-02-20 09:08:55 +08:00
|
|
|
def wait_page_loaded(self):
|
|
|
|
"""
|
|
|
|
Block until page has started to load.
|
|
|
|
"""
|
|
|
|
from selenium.common.exceptions import TimeoutException
|
|
|
|
try:
|
|
|
|
# Wait for the next page to be loaded
|
|
|
|
self.wait_loaded_tag('body')
|
|
|
|
except TimeoutException:
|
2014-03-02 22:25:53 +08:00
|
|
|
# IE7 occasionally returns an error "Internet Explorer cannot
|
2013-02-20 09:08:55 +08:00
|
|
|
# display the webpage" and doesn't load the next page. We just
|
|
|
|
# ignore it.
|
|
|
|
pass
|
|
|
|
|
Fixed #2879 -- Added support for the integration with Selenium and other in-browser testing frameworks. Also added the first Selenium tests for `contrib.admin`. Many thanks to everyone for their contributions and feedback: Mikeal Rogers, Dirk Datzert, mir, Simon G., Almad, Russell Keith-Magee, Denis Golomazov, devin, robertrv, andrewbadr, Idan Gazit, voidspace, Tom Christie, hjwp2, Adam Nelson, Jannis Leidel, Anssi Kääriäinen, Preston Holmes, Bruno Renié and Jacob Kaplan-Moss.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@17241 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2011-12-22 16:33:58 +08:00
|
|
|
def admin_login(self, username, password, login_url='/admin/'):
|
|
|
|
"""
|
|
|
|
Helper function to log into the admin.
|
|
|
|
"""
|
|
|
|
self.selenium.get('%s%s' % (self.live_server_url, login_url))
|
|
|
|
username_input = self.selenium.find_element_by_name('username')
|
|
|
|
username_input.send_keys(username)
|
|
|
|
password_input = self.selenium.find_element_by_name('password')
|
|
|
|
password_input.send_keys(password)
|
|
|
|
login_text = _('Log in')
|
|
|
|
self.selenium.find_element_by_xpath(
|
|
|
|
'//input[@value="%s"]' % login_text).click()
|
2013-02-20 09:08:55 +08:00
|
|
|
self.wait_page_loaded()
|
Fixed #2879 -- Added support for the integration with Selenium and other in-browser testing frameworks. Also added the first Selenium tests for `contrib.admin`. Many thanks to everyone for their contributions and feedback: Mikeal Rogers, Dirk Datzert, mir, Simon G., Almad, Russell Keith-Magee, Denis Golomazov, devin, robertrv, andrewbadr, Idan Gazit, voidspace, Tom Christie, hjwp2, Adam Nelson, Jannis Leidel, Anssi Kääriäinen, Preston Holmes, Bruno Renié and Jacob Kaplan-Moss.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@17241 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2011-12-22 16:33:58 +08:00
|
|
|
|
|
|
|
def get_css_value(self, selector, attribute):
|
|
|
|
"""
|
|
|
|
Helper function that returns the value for the CSS attribute of an
|
|
|
|
DOM element specified by the given selector. Uses the jQuery that ships
|
|
|
|
with Django.
|
|
|
|
"""
|
|
|
|
return self.selenium.execute_script(
|
2012-02-20 00:42:12 +08:00
|
|
|
'return django.jQuery("%s").css("%s")' % (selector, attribute))
|
|
|
|
|
2012-02-23 16:07:07 +08:00
|
|
|
def get_select_option(self, selector, value):
|
2012-02-20 00:42:12 +08:00
|
|
|
"""
|
2012-02-23 16:07:07 +08:00
|
|
|
Returns the <OPTION> with the value `value` inside the <SELECT> widget
|
|
|
|
identified by the CSS selector `selector`.
|
2012-02-20 00:42:12 +08:00
|
|
|
"""
|
2012-02-20 00:53:55 +08:00
|
|
|
from selenium.common.exceptions import NoSuchElementException
|
2012-03-05 16:13:09 +08:00
|
|
|
options = self.selenium.find_elements_by_css_selector('%s > option' % selector)
|
2012-02-20 00:42:12 +08:00
|
|
|
for option in options:
|
|
|
|
if option.get_attribute('value') == value:
|
2012-02-23 16:07:07 +08:00
|
|
|
return option
|
|
|
|
raise NoSuchElementException('Option "%s" not found in "%s"' % (value, selector))
|
|
|
|
|
|
|
|
def assertSelectOptions(self, selector, values):
|
|
|
|
"""
|
|
|
|
Asserts that the <SELECT> widget identified by `selector` has the
|
|
|
|
options with the given `values`.
|
|
|
|
"""
|
2012-03-05 16:13:09 +08:00
|
|
|
options = self.selenium.find_elements_by_css_selector('%s > option' % selector)
|
2012-02-23 16:07:07 +08:00
|
|
|
actual_values = []
|
|
|
|
for option in options:
|
|
|
|
actual_values.append(option.get_attribute('value'))
|
2012-03-04 09:20:03 +08:00
|
|
|
self.assertEqual(values, actual_values)
|
|
|
|
|
|
|
|
def has_css_class(self, selector, klass):
|
|
|
|
"""
|
|
|
|
Returns True if the element identified by `selector` has the CSS class
|
|
|
|
`klass`.
|
|
|
|
"""
|
|
|
|
return (self.selenium.find_element_by_css_selector(selector)
|
2012-10-04 18:45:22 +08:00
|
|
|
.get_attribute('class').find(klass) != -1)
|