summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore4
-rw-r--r--docs/requirements-dev.txt1
-rw-r--r--patchwork/tests/browser.py165
-rw-r--r--patchwork/tests/test_user_browser.py38
-rw-r--r--tox.ini1
5 files changed, 209 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
index b718f0a..94481ad 100644
--- a/.gitignore
+++ b/.gitignore
@@ -28,3 +28,7 @@ cscope.*
*.orig
*.rej
+
+# test artifacts
+/selenium.log
+/selenium_screenshots
diff --git a/docs/requirements-dev.txt b/docs/requirements-dev.txt
index eee6463..12b8bef 100644
--- a/docs/requirements-dev.txt
+++ b/docs/requirements-dev.txt
@@ -1 +1,2 @@
-r requirements-base.txt
+selenium
diff --git a/patchwork/tests/browser.py b/patchwork/tests/browser.py
new file mode 100644
index 0000000..1845000
--- /dev/null
+++ b/patchwork/tests/browser.py
@@ -0,0 +1,165 @@
+# Patchwork - automated patch tracking system
+# Copyright (C) 2015 Intel Corporation
+#
+# This file is part of the Patchwork package.
+#
+# Patchwork is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# Patchwork is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Patchwork; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+import errno
+import os
+import time
+
+try: # django 1.7+
+ from django.contrib.staticfiles.testing import StaticLiveServerTestCase
+except:
+ from django.test import LiveServerTestCase as StaticLiveServerTestCase
+from selenium.common.exceptions import (
+ NoSuchElementException, StaleElementReferenceException,
+ TimeoutException)
+from selenium import webdriver
+from selenium.webdriver.support.ui import WebDriverWait
+
+
+class Wait(WebDriverWait):
+ """Subclass of WebDriverWait.
+
+ Includes a predetermined timeout and poll frequency. Also deals with a
+ wider variety of exceptions.
+ """
+ _TIMEOUT = 10
+ _POLL_FREQUENCY = 0.5
+
+ def __init__(self, driver):
+ super(Wait, self).__init__(driver, self._TIMEOUT, self._POLL_FREQUENCY)
+
+ def until(self, method, message=''):
+ """Call method with driver until it returns True."""
+ end_time = time.time() + self._timeout
+
+ while True:
+ try:
+ value = method(self._driver)
+ if value:
+ return value
+ except NoSuchElementException:
+ pass
+ except StaleElementReferenceException:
+ pass
+
+ time.sleep(self._poll)
+ if(time.time() > end_time):
+ break
+
+ raise TimeoutException(message)
+
+ def until_not(self, method, message=''):
+ """Call method with driver until it returns True."""
+ end_time = time.time() + self._timeout
+ while(True):
+ try:
+ value = method(self._driver)
+ if not value:
+ return value
+ except NoSuchElementException:
+ return True
+ except StaleElementReferenceException:
+ pass
+
+ time.sleep(self._poll)
+ if(time.time() > end_time):
+ break
+
+ raise TimeoutException(message)
+
+
+def mkdir(path):
+ try:
+ os.makedirs(path)
+ except OSError as error:
+ if error.errno == errno.EEXIST and os.path.isdir(path):
+ pass
+ else:
+ raise
+
+
+class SeleniumTestCase(StaticLiveServerTestCase):
+ # TODO(stephenfin): This should handle non-UNIX paths
+ _SCREENSHOT_DIR = os.path.dirname(__file__) + '/../../selenium_screenshots'
+
+ def setUp(self):
+ super(SeleniumTestCase, self).setUp()
+
+ self.browser = os.getenv('SELENIUM_BROWSER', 'chrome')
+ if self.browser == 'firefox':
+ self.selenium = webdriver.Firefox()
+ if self.browser == 'chrome':
+ self.selenium = webdriver.Chrome(
+ service_args=['--verbose', '--log-path=selenium.log']
+ )
+
+ mkdir(self._SCREENSHOT_DIR)
+ self._screenshot_number = 1
+
+ def tearDown(self):
+ self.selenium.quit()
+ super(SeleniumTestCase, self).tearDown()
+
+ def screenshot(self):
+ name = '%s_%d.png' % (self._testMethodName, self._screenshot_number)
+ path = os.path.join(self._SCREENSHOT_DIR, name)
+ self.selenium.get_screenshot_as_file(path)
+ self._screenshot_number += 1
+
+ def get(self, relative_url):
+ self.selenium.get('%s%s' % (self.live_server_url, relative_url))
+ self.screenshot()
+
+ def find(self, selector):
+ return self.selenium.find_element_by_css_selector(selector)
+
+ def focused_element(self):
+ return self.selenium.switch_to.active_element
+
+ def wait_until_present(self, name):
+ is_present = lambda driver: driver.find_element_by_name(name)
+ msg = "An element named '%s' should be on the page" % name
+ element = Wait(self.selenium).until(is_present, msg)
+ self.screenshot()
+ return element
+
+ def wait_until_visible(self, selector):
+ is_visible = lambda driver: self.find(selector).is_displayed()
+ msg = "The element matching '%s' should be visible" % selector
+ Wait(self.selenium).until(is_visible, msg)
+ self.screenshot()
+ return self.find(selector)
+
+ def wait_until_focused(self, selector):
+ is_focused = lambda driver: (
+ self.find(selector) == self.focused_element())
+ msg = "The element matching '%s' should be focused" % selector
+ Wait(self.selenium).until(is_focused, msg)
+ self.screenshot()
+ return self.find(selector)
+
+ def enter_text(self, name, value):
+ field = self.wait_until_present(name)
+ field.send_keys(value)
+ return field
+
+ def click(self, selector):
+ element = self.wait_until_visible(selector)
+ element.click()
+ return element
diff --git a/patchwork/tests/test_user_browser.py b/patchwork/tests/test_user_browser.py
new file mode 100644
index 0000000..2b9ed2e
--- /dev/null
+++ b/patchwork/tests/test_user_browser.py
@@ -0,0 +1,38 @@
+# Patchwork - automated patch tracking system
+# Copyright (C) 2015 Intel Corporation
+#
+# This file is part of the Patchwork package.
+#
+# Patchwork is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# Patchwork is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Patchwork; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+from patchwork.tests.browser import SeleniumTestCase
+from patchwork.tests.test_user import TestUser
+
+class LoginTestCase(SeleniumTestCase):
+ def setUp(self):
+ super(LoginTestCase, self).setUp()
+ self.user = TestUser()
+
+ def test_default_focus(self):
+ self.get('/user/login/')
+ self.wait_until_focused('#id_username')
+
+ def test_login(self):
+ self.get('/user/login/')
+ self.enter_text('username', self.user.username)
+ self.enter_text('password', self.user.password)
+ self.click('input[value="Login"]')
+ dropdown = self.wait_until_visible('a.dropdown-toggle strong')
+ self.assertEquals(dropdown.text, 'testuser')
diff --git a/tox.ini b/tox.ini
index e44c884..8647cf3 100644
--- a/tox.ini
+++ b/tox.ini
@@ -19,6 +19,7 @@ commands =
passenv =
http_proxy HTTP_PROXY https_proxy HTTPS_PROXY no_proxy NO_PROXY
PW_TEST_DB_USER PW_TEST_DB_PASS
+ DISPLAY SELENIUM_BROWSER
[testenv:pep8]
basepython = python2.7