Commit 44c0ecdd authored by Akshesh's avatar Akshesh Committed by Simon Charette
Browse files

Fixed #25364 -- Added generic way to test on all browsers supported by selenium.

Browser names should be passed as a comma separated list to the --selenium flag.

Thanks Tim Graham, Simon Charette and Moritz Sichert for review and discussion.
parent 93a135d1
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ answer newbie questions, and generally made Django that much better:
    ajs <adi@sieker.info>
    Akis Kesoglou <akiskesoglou@gmail.com>
    Aksel Ethem <aksel.ethem@gmail.com>
    Akshesh Doshi <aksheshdoshi+django@gmail.com>
    alang@bright-green.com
    Alasdair Nicol <http://al.sdair.co.uk/>
    Albert Wang <aywang31@gmail.com>
+3 −27
Original line number Diff line number Diff line
import os
from unittest import SkipTest

from django.contrib.staticfiles.testing import StaticLiveServerTestCase
from django.test import modify_settings, tag
from django.utils.module_loading import import_string
from django.test import modify_settings
from django.test.selenium import SeleniumTestCase
from django.utils.translation import ugettext as _


@@ -14,11 +11,10 @@ class CSPMiddleware(object):
        return response


@tag('selenium')
@modify_settings(
    MIDDLEWARE_CLASSES={'append': 'django.contrib.admin.tests.CSPMiddleware'},
)
class AdminSeleniumWebDriverTestCase(StaticLiveServerTestCase):
class AdminSeleniumTestCase(SeleniumTestCase, StaticLiveServerTestCase):

    available_apps = [
        'django.contrib.admin',
@@ -27,26 +23,6 @@ class AdminSeleniumWebDriverTestCase(StaticLiveServerTestCase):
        'django.contrib.sessions',
        'django.contrib.sites',
    ]
    webdriver_class = 'selenium.webdriver.firefox.webdriver.WebDriver'

    @classmethod
    def setUpClass(cls):
        if not os.environ.get('DJANGO_SELENIUM_TESTS', False):
            raise SkipTest('Selenium tests not requested')
        try:
            cls.selenium = import_string(cls.webdriver_class)()
        except Exception as e:
            raise SkipTest('Selenium webdriver "%s" not installed or not '
                           'operational: %s' % (cls.webdriver_class, str(e)))
        cls.selenium.implicitly_wait(10)
        # This has to be last to ensure that resources are cleaned up properly!
        super(AdminSeleniumWebDriverTestCase, cls).setUpClass()

    @classmethod
    def _tearDownClassInternal(cls):
        if hasattr(cls, 'selenium'):
            cls.selenium.quit()
        super(AdminSeleniumWebDriverTestCase, cls)._tearDownClassInternal()

    def wait_until(self, callback, timeout=10):
        """
+73 −0
Original line number Diff line number Diff line
from __future__ import unicode_literals

import sys
import unittest

from django.test import LiveServerTestCase, tag
from django.utils.module_loading import import_string
from django.utils.six import with_metaclass
from django.utils.text import capfirst


class SeleniumTestCaseBase(type(LiveServerTestCase)):
    # List of browsers to dynamically create test classes for.
    browsers = []
    # Sentinel value to differentiate browser-specific instances.
    browser = None

    def __new__(cls, name, bases, attrs):
        """
        Dynamically create new classes and add them to the test module when
        multiple browsers specs are provided (e.g. --selenium=firefox,chrome).
        """
        test_class = super(SeleniumTestCaseBase, cls).__new__(cls, name, bases, attrs)
        # If the test class is either browser-specific or a test base, return it.
        if test_class.browser or not any(name.startswith('test') and callable(value) for name, value in attrs.items()):
            return test_class
        elif test_class.browsers:
            # Reuse the created test class to make it browser-specific.
            # We can't rename it to include the browser name or create a
            # subclass like we do with the remaining browsers as it would
            # either duplicate tests or prevent pickling of its instances.
            first_browser = test_class.browsers[0]
            test_class.browser = first_browser
            # Create subclasses for each of the remaining browsers and expose
            # them through the test's module namespace.
            module = sys.modules[test_class.__module__]
            for browser in test_class.browsers[1:]:
                browser_test_class = cls.__new__(
                    cls,
                    str("%s%s" % (capfirst(browser), name)),
                    (test_class,),
                    {'browser': browser, '__module__': test_class.__module__}
                )
                setattr(module, browser_test_class.__name__, browser_test_class)
            return test_class
        # If no browsers were specified, skip this class (it'll still be discovered).
        return unittest.skip('No browsers specified.')(test_class)

    @classmethod
    def import_webdriver(cls, browser):
        return import_string("selenium.webdriver.%s.webdriver.WebDriver" % browser)

    def create_webdriver(self):
        return self.import_webdriver(self.browser)()


@tag('selenium')
class SeleniumTestCase(with_metaclass(SeleniumTestCaseBase, LiveServerTestCase)):

    @classmethod
    def setUpClass(cls):
        cls.selenium = cls.create_webdriver()
        cls.selenium.implicitly_wait(10)
        super(SeleniumTestCase, cls).setUpClass()

    @classmethod
    def _tearDownClassInternal(cls):
        # quit() the WebDriver before attempting to terminate and join the
        # single-threaded LiveServerThread to avoid a dead lock if the browser
        # kept a connection alive.
        if hasattr(cls, 'selenium'):
            cls.selenium.quit()
        super(SeleniumTestCase, cls)._tearDownClassInternal()
+9 −5
Original line number Diff line number Diff line
@@ -119,16 +119,20 @@ Going beyond that, you can specify an individual test method like this::
Running the Selenium tests
--------------------------

Some tests require Selenium and a Web browser (Firefox, Google Chrome, or
Internet Explorer). To allow those tests to be run rather than skipped, you must
install the selenium_ package into your Python path and run the tests with the
``--selenium`` option::
Some tests require Selenium and a Web browser. To run these tests, you must
install the selenium_ package and run the tests with the
``--selenium=<BROWSERS>`` option. For example, if you have Firefox and Google
Chrome installed::

   $ ./runtests.py --settings=test_sqlite --selenium admin_inlines
   $ ./runtests.py --selenium=firefox,chrome

See the `selenium.webdriver`_ package for the list of available browsers.

Specifying ``--selenium`` automatically sets ``--tags=selenium`` to run only
the tests that require selenium.

.. _selenium.webdriver: https://github.com/SeleniumHQ/selenium/tree/master/py/selenium/webdriver

.. _running-unit-tests-dependencies:

Running all the tests
+3 −12
Original line number Diff line number Diff line
@@ -6,7 +6,7 @@ from django.contrib import admin
from django.contrib.admin.models import LogEntry
from django.contrib.admin.options import IncorrectLookupParameters
from django.contrib.admin.templatetags.admin_list import pagination
from django.contrib.admin.tests import AdminSeleniumWebDriverTestCase
from django.contrib.admin.tests import AdminSeleniumTestCase
from django.contrib.admin.views.main import ALL_VAR, SEARCH_VAR, ChangeList
from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType
@@ -882,10 +882,9 @@ class AdminLogNodeTestCase(TestCase):


@override_settings(ROOT_URLCONF='admin_changelist.urls')
class SeleniumFirefoxTests(AdminSeleniumWebDriverTestCase):
class SeleniumTests(AdminSeleniumTestCase):

    available_apps = ['admin_changelist'] + AdminSeleniumWebDriverTestCase.available_apps
    webdriver_class = 'selenium.webdriver.firefox.webdriver.WebDriver'
    available_apps = ['admin_changelist'] + AdminSeleniumTestCase.available_apps

    def setUp(self):
        User.objects.create_superuser(username='super', password='secret', email=None)
@@ -915,11 +914,3 @@ class SeleniumFirefoxTests(AdminSeleniumWebDriverTestCase):
            '%s #result_list tbody tr:first-child .action-select' % form_id)
        row_selector.click()
        self.assertEqual(selection_indicator.text, "1 of 1 selected")


class SeleniumChromeTests(SeleniumFirefoxTests):
    webdriver_class = 'selenium.webdriver.chrome.webdriver.WebDriver'


class SeleniumIETests(SeleniumFirefoxTests):
    webdriver_class = 'selenium.webdriver.ie.webdriver.WebDriver'
Loading