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.
This commit is contained in:
Akshesh 2016-02-07 07:54:36 +05:30 committed by Simon Charette
parent 93a135d111
commit 44c0ecdd92
11 changed files with 138 additions and 188 deletions

View File

@ -19,6 +19,7 @@ answer newbie questions, and generally made Django that much better:
ajs <adi@sieker.info> ajs <adi@sieker.info>
Akis Kesoglou <akiskesoglou@gmail.com> Akis Kesoglou <akiskesoglou@gmail.com>
Aksel Ethem <aksel.ethem@gmail.com> Aksel Ethem <aksel.ethem@gmail.com>
Akshesh Doshi <aksheshdoshi+django@gmail.com>
alang@bright-green.com alang@bright-green.com
Alasdair Nicol <http://al.sdair.co.uk/> Alasdair Nicol <http://al.sdair.co.uk/>
Albert Wang <aywang31@gmail.com> Albert Wang <aywang31@gmail.com>

View File

@ -1,9 +1,6 @@
import os
from unittest import SkipTest
from django.contrib.staticfiles.testing import StaticLiveServerTestCase from django.contrib.staticfiles.testing import StaticLiveServerTestCase
from django.test import modify_settings, tag from django.test import modify_settings
from django.utils.module_loading import import_string from django.test.selenium import SeleniumTestCase
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
@ -14,11 +11,10 @@ class CSPMiddleware(object):
return response return response
@tag('selenium')
@modify_settings( @modify_settings(
MIDDLEWARE_CLASSES={'append': 'django.contrib.admin.tests.CSPMiddleware'}, MIDDLEWARE_CLASSES={'append': 'django.contrib.admin.tests.CSPMiddleware'},
) )
class AdminSeleniumWebDriverTestCase(StaticLiveServerTestCase): class AdminSeleniumTestCase(SeleniumTestCase, StaticLiveServerTestCase):
available_apps = [ available_apps = [
'django.contrib.admin', 'django.contrib.admin',
@ -27,26 +23,6 @@ class AdminSeleniumWebDriverTestCase(StaticLiveServerTestCase):
'django.contrib.sessions', 'django.contrib.sessions',
'django.contrib.sites', '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): def wait_until(self, callback, timeout=10):
""" """

73
django/test/selenium.py Normal file
View File

@ -0,0 +1,73 @@
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()

View File

@ -119,16 +119,20 @@ Going beyond that, you can specify an individual test method like this::
Running the Selenium tests Running the Selenium tests
-------------------------- --------------------------
Some tests require Selenium and a Web browser (Firefox, Google Chrome, or Some tests require Selenium and a Web browser. To run these tests, you must
Internet Explorer). To allow those tests to be run rather than skipped, you must install the selenium_ package and run the tests with the
install the selenium_ package into your Python path and run the tests with the ``--selenium=<BROWSERS>`` option. For example, if you have Firefox and Google
``--selenium`` option:: 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 Specifying ``--selenium`` automatically sets ``--tags=selenium`` to run only
the tests that require selenium. the tests that require selenium.
.. _selenium.webdriver: https://github.com/SeleniumHQ/selenium/tree/master/py/selenium/webdriver
.. _running-unit-tests-dependencies: .. _running-unit-tests-dependencies:
Running all the tests Running all the tests

View File

@ -6,7 +6,7 @@ from django.contrib import admin
from django.contrib.admin.models import LogEntry from django.contrib.admin.models import LogEntry
from django.contrib.admin.options import IncorrectLookupParameters from django.contrib.admin.options import IncorrectLookupParameters
from django.contrib.admin.templatetags.admin_list import pagination 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.admin.views.main import ALL_VAR, SEARCH_VAR, ChangeList
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
@ -882,10 +882,9 @@ class AdminLogNodeTestCase(TestCase):
@override_settings(ROOT_URLCONF='admin_changelist.urls') @override_settings(ROOT_URLCONF='admin_changelist.urls')
class SeleniumFirefoxTests(AdminSeleniumWebDriverTestCase): class SeleniumTests(AdminSeleniumTestCase):
available_apps = ['admin_changelist'] + AdminSeleniumWebDriverTestCase.available_apps available_apps = ['admin_changelist'] + AdminSeleniumTestCase.available_apps
webdriver_class = 'selenium.webdriver.firefox.webdriver.WebDriver'
def setUp(self): def setUp(self):
User.objects.create_superuser(username='super', password='secret', email=None) 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) '%s #result_list tbody tr:first-child .action-select' % form_id)
row_selector.click() row_selector.click()
self.assertEqual(selection_indicator.text, "1 of 1 selected") 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'

View File

@ -2,7 +2,7 @@ from __future__ import unicode_literals
from django.contrib.admin import ModelAdmin, TabularInline from django.contrib.admin import ModelAdmin, TabularInline
from django.contrib.admin.helpers import InlineAdminForm from django.contrib.admin.helpers import InlineAdminForm
from django.contrib.admin.tests import AdminSeleniumWebDriverTestCase from django.contrib.admin.tests import AdminSeleniumTestCase
from django.contrib.auth.models import Permission, User from django.contrib.auth.models import Permission, User
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.test import RequestFactory, TestCase, override_settings from django.test import RequestFactory, TestCase, override_settings
@ -688,10 +688,9 @@ class TestInlinePermissions(TestCase):
@override_settings(ROOT_URLCONF='admin_inlines.urls') @override_settings(ROOT_URLCONF='admin_inlines.urls')
class SeleniumFirefoxTests(AdminSeleniumWebDriverTestCase): class SeleniumTests(AdminSeleniumTestCase):
available_apps = ['admin_inlines'] + AdminSeleniumWebDriverTestCase.available_apps available_apps = ['admin_inlines'] + AdminSeleniumTestCase.available_apps
webdriver_class = 'selenium.webdriver.firefox.webdriver.WebDriver'
def setUp(self): def setUp(self):
User.objects.create_superuser(username='super', password='secret', email='super@example.com') User.objects.create_superuser(username='super', password='secret', email='super@example.com')
@ -871,11 +870,3 @@ class SeleniumFirefoxTests(AdminSeleniumWebDriverTestCase):
self.wait_until_visible(field_name) self.wait_until_visible(field_name)
hide_links[hide_index].click() hide_links[hide_index].click()
self.wait_until_invisible(field_name) self.wait_until_invisible(field_name)
class SeleniumChromeTests(SeleniumFirefoxTests):
webdriver_class = 'selenium.webdriver.chrome.webdriver.WebDriver'
class SeleniumIETests(SeleniumFirefoxTests):
webdriver_class = 'selenium.webdriver.ie.webdriver.WebDriver'

View File

@ -13,7 +13,7 @@ from django.contrib.admin.models import ADDITION, DELETION, LogEntry
from django.contrib.admin.options import TO_FIELD_VAR from django.contrib.admin.options import TO_FIELD_VAR
from django.contrib.admin.templatetags.admin_static import static from django.contrib.admin.templatetags.admin_static import static
from django.contrib.admin.templatetags.admin_urls import add_preserved_filters from django.contrib.admin.templatetags.admin_urls import add_preserved_filters
from django.contrib.admin.tests import AdminSeleniumWebDriverTestCase from django.contrib.admin.tests import AdminSeleniumTestCase
from django.contrib.admin.utils import quote from django.contrib.admin.utils import quote
from django.contrib.admin.views.main import IS_POPUP_VAR from django.contrib.admin.views.main import IS_POPUP_VAR
from django.contrib.auth import REDIRECT_FIELD_NAME, get_permission_codename from django.contrib.auth import REDIRECT_FIELD_NAME, get_permission_codename
@ -4117,10 +4117,9 @@ class PrePopulatedTest(TestCase):
@override_settings(ROOT_URLCONF='admin_views.urls') @override_settings(ROOT_URLCONF='admin_views.urls')
class SeleniumAdminViewsFirefoxTests(AdminSeleniumWebDriverTestCase): class SeleniumTests(AdminSeleniumTestCase):
available_apps = ['admin_views'] + AdminSeleniumWebDriverTestCase.available_apps available_apps = ['admin_views'] + AdminSeleniumTestCase.available_apps
webdriver_class = 'selenium.webdriver.firefox.webdriver.WebDriver'
def setUp(self): def setUp(self):
self.superuser = User.objects.create_superuser(username='super', password='secret', email='super@example.com') self.superuser = User.objects.create_superuser(username='super', password='secret', email='super@example.com')
@ -4443,14 +4442,6 @@ class SeleniumAdminViewsFirefoxTests(AdminSeleniumWebDriverTestCase):
self.assertEqual(select.first_selected_option.get_attribute('value'), '') self.assertEqual(select.first_selected_option.get_attribute('value'), '')
class SeleniumAdminViewsChromeTests(SeleniumAdminViewsFirefoxTests):
webdriver_class = 'selenium.webdriver.chrome.webdriver.WebDriver'
class SeleniumAdminViewsIETests(SeleniumAdminViewsFirefoxTests):
webdriver_class = 'selenium.webdriver.ie.webdriver.WebDriver'
@override_settings(ROOT_URLCONF='admin_views.urls') @override_settings(ROOT_URLCONF='admin_views.urls')
class ReadonlyTest(AdminFieldExtractionMixin, TestCase): class ReadonlyTest(AdminFieldExtractionMixin, TestCase):

View File

@ -11,7 +11,7 @@ from django import forms
from django.conf import settings from django.conf import settings
from django.contrib import admin from django.contrib import admin
from django.contrib.admin import widgets from django.contrib.admin import widgets
from django.contrib.admin.tests import AdminSeleniumWebDriverTestCase from django.contrib.admin.tests import AdminSeleniumTestCase
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.core.files.storage import default_storage from django.core.files.storage import default_storage
from django.core.files.uploadedfile import SimpleUploadedFile from django.core.files.uploadedfile import SimpleUploadedFile
@ -39,11 +39,6 @@ class TestDataMixin(object):
models.Car.objects.create(owner=cls.u2, make='BMW', model='M3') models.Car.objects.create(owner=cls.u2, make='BMW', model='M3')
class SeleniumDataMixin(object):
def setUp(self):
self.u1 = User.objects.create_superuser(username='super', password='secret', email='super@example.com')
class AdminFormfieldForDBFieldTests(SimpleTestCase): class AdminFormfieldForDBFieldTests(SimpleTestCase):
""" """
Tests for correct behavior of ModelAdmin.formfield_for_dbfield Tests for correct behavior of ModelAdmin.formfield_for_dbfield
@ -619,10 +614,15 @@ class RelatedFieldWidgetWrapperTests(SimpleTestCase):
@override_settings(ROOT_URLCONF='admin_widgets.urls') @override_settings(ROOT_URLCONF='admin_widgets.urls')
class DateTimePickerSeleniumFirefoxTests(SeleniumDataMixin, AdminSeleniumWebDriverTestCase): class AdminWidgetSeleniumTestCase(AdminSeleniumTestCase):
available_apps = ['admin_widgets'] + AdminSeleniumWebDriverTestCase.available_apps available_apps = ['admin_widgets'] + AdminSeleniumTestCase.available_apps
webdriver_class = 'selenium.webdriver.firefox.webdriver.WebDriver'
def setUp(self):
self.u1 = User.objects.create_superuser(username='super', password='secret', email='super@example.com')
class DateTimePickerSeleniumTests(AdminWidgetSeleniumTestCase):
def test_show_hide_date_time_picker_widgets(self): def test_show_hide_date_time_picker_widgets(self):
""" """
@ -786,19 +786,9 @@ class DateTimePickerSeleniumFirefoxTests(SeleniumDataMixin, AdminSeleniumWebDriv
self.wait_for_text('#calendarin0 caption', expected_caption) self.wait_for_text('#calendarin0 caption', expected_caption)
class DateTimePickerSeleniumChromeTests(DateTimePickerSeleniumFirefoxTests):
webdriver_class = 'selenium.webdriver.chrome.webdriver.WebDriver'
class DateTimePickerSeleniumIETests(DateTimePickerSeleniumFirefoxTests):
webdriver_class = 'selenium.webdriver.ie.webdriver.WebDriver'
@skipIf(pytz is None, "this test requires pytz") @skipIf(pytz is None, "this test requires pytz")
@override_settings(ROOT_URLCONF='admin_widgets.urls', TIME_ZONE='Asia/Singapore') @override_settings(TIME_ZONE='Asia/Singapore')
class DateTimePickerShortcutsSeleniumFirefoxTests(SeleniumDataMixin, AdminSeleniumWebDriverTestCase): class DateTimePickerShortcutsSeleniumTests(AdminWidgetSeleniumTestCase):
available_apps = ['admin_widgets'] + AdminSeleniumWebDriverTestCase.available_apps
webdriver_class = 'selenium.webdriver.firefox.webdriver.WebDriver'
def test_date_time_picker_shortcuts(self): def test_date_time_picker_shortcuts(self):
""" """
@ -853,37 +843,17 @@ class DateTimePickerShortcutsSeleniumFirefoxTests(SeleniumDataMixin, AdminSeleni
self.assertLess(member.birthdate, now + error_margin) self.assertLess(member.birthdate, now + error_margin)
class DateTimePickerShortcutsSeleniumChromeTests(DateTimePickerShortcutsSeleniumFirefoxTests):
webdriver_class = 'selenium.webdriver.chrome.webdriver.WebDriver'
class DateTimePickerShortcutsSeleniumIETests(DateTimePickerShortcutsSeleniumFirefoxTests):
webdriver_class = 'selenium.webdriver.ie.webdriver.WebDriver'
# The above tests run with Asia/Singapore which are on the positive side of # The above tests run with Asia/Singapore which are on the positive side of
# UTC. Here we test with a timezone on the negative side. # UTC. Here we test with a timezone on the negative side.
@override_settings(TIME_ZONE='US/Eastern') @override_settings(TIME_ZONE='US/Eastern')
class DateTimePickerAltTimezoneSeleniumFirefoxTests(DateTimePickerShortcutsSeleniumFirefoxTests): class DateTimePickerAltTimezoneSeleniumTests(DateTimePickerShortcutsSeleniumTests):
pass pass
class DateTimePickerAltTimezoneSeleniumChromeTests(DateTimePickerAltTimezoneSeleniumFirefoxTests): class HorizontalVerticalFilterSeleniumTests(AdminWidgetSeleniumTestCase):
webdriver_class = 'selenium.webdriver.chrome.webdriver.WebDriver'
class DateTimePickerAltTimezoneSeleniumIETests(DateTimePickerAltTimezoneSeleniumFirefoxTests):
webdriver_class = 'selenium.webdriver.ie.webdriver.WebDriver'
@override_settings(ROOT_URLCONF='admin_widgets.urls')
class HorizontalVerticalFilterSeleniumFirefoxTests(SeleniumDataMixin, AdminSeleniumWebDriverTestCase):
available_apps = ['admin_widgets'] + AdminSeleniumWebDriverTestCase.available_apps
webdriver_class = 'selenium.webdriver.firefox.webdriver.WebDriver'
def setUp(self): def setUp(self):
super(HorizontalVerticalFilterSeleniumFirefoxTests, self).setUp() super(HorizontalVerticalFilterSeleniumTests, self).setUp()
self.lisa = models.Student.objects.create(name='Lisa') self.lisa = models.Student.objects.create(name='Lisa')
self.john = models.Student.objects.create(name='John') self.john = models.Student.objects.create(name='John')
self.bob = models.Student.objects.create(name='Bob') self.bob = models.Student.objects.create(name='Bob')
@ -1168,21 +1138,10 @@ class HorizontalVerticalFilterSeleniumFirefoxTests(SeleniumDataMixin, AdminSelen
self.assertEqual(options_len, 2) self.assertEqual(options_len, 2)
class HorizontalVerticalFilterSeleniumChromeTests(HorizontalVerticalFilterSeleniumFirefoxTests): class AdminRawIdWidgetSeleniumTests(AdminWidgetSeleniumTestCase):
webdriver_class = 'selenium.webdriver.chrome.webdriver.WebDriver'
class HorizontalVerticalFilterSeleniumIETests(HorizontalVerticalFilterSeleniumFirefoxTests):
webdriver_class = 'selenium.webdriver.ie.webdriver.WebDriver'
@override_settings(ROOT_URLCONF='admin_widgets.urls')
class AdminRawIdWidgetSeleniumFirefoxTests(SeleniumDataMixin, AdminSeleniumWebDriverTestCase):
available_apps = ['admin_widgets'] + AdminSeleniumWebDriverTestCase.available_apps
webdriver_class = 'selenium.webdriver.firefox.webdriver.WebDriver'
def setUp(self): def setUp(self):
super(AdminRawIdWidgetSeleniumFirefoxTests, self).setUp() super(AdminRawIdWidgetSeleniumTests, self).setUp()
models.Band.objects.create(id=42, name='Bogey Blues') models.Band.objects.create(id=42, name='Bogey Blues')
models.Band.objects.create(id=98, name='Green Potatoes') models.Band.objects.create(id=98, name='Green Potatoes')
@ -1263,18 +1222,7 @@ class AdminRawIdWidgetSeleniumFirefoxTests(SeleniumDataMixin, AdminSeleniumWebDr
self.wait_for_value('#id_supporting_bands', '42,98') self.wait_for_value('#id_supporting_bands', '42,98')
class AdminRawIdWidgetSeleniumChromeTests(AdminRawIdWidgetSeleniumFirefoxTests): class RelatedFieldWidgetSeleniumTests(AdminWidgetSeleniumTestCase):
webdriver_class = 'selenium.webdriver.chrome.webdriver.WebDriver'
class AdminRawIdWidgetSeleniumIETests(AdminRawIdWidgetSeleniumFirefoxTests):
webdriver_class = 'selenium.webdriver.ie.webdriver.WebDriver'
@override_settings(ROOT_URLCONF='admin_widgets.urls')
class RelatedFieldWidgetSeleniumFirefoxTests(SeleniumDataMixin, AdminSeleniumWebDriverTestCase):
available_apps = ['admin_widgets'] + AdminSeleniumWebDriverTestCase.available_apps
webdriver_class = 'selenium.webdriver.firefox.webdriver.WebDriver'
def test_ForeignKey_using_to_field(self): def test_ForeignKey_using_to_field(self):
self.admin_login(username='super', password='secret', login_url='/') self.admin_login(username='super', password='secret', login_url='/')
@ -1321,11 +1269,3 @@ class RelatedFieldWidgetSeleniumFirefoxTests(SeleniumDataMixin, AdminSeleniumWeb
profiles = models.Profile.objects.all() profiles = models.Profile.objects.all()
self.assertEqual(len(profiles), 1) self.assertEqual(len(profiles), 1)
self.assertEqual(profiles[0].user.username, username_value) self.assertEqual(profiles[0].user.username, username_value)
class RelatedFieldWidgetSeleniumChromeTests(RelatedFieldWidgetSeleniumFirefoxTests):
webdriver_class = 'selenium.webdriver.chrome.webdriver.WebDriver'
class RelatedFieldWidgetSeleniumIETests(RelatedFieldWidgetSeleniumFirefoxTests):
webdriver_class = 'selenium.webdriver.ie.webdriver.WebDriver'

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import unicode_literals from __future__ import unicode_literals
from django.contrib.admin.tests import AdminSeleniumWebDriverTestCase from django.contrib.admin.tests import AdminSeleniumTestCase
from django.forms import ( from django.forms import (
CheckboxSelectMultiple, ClearableFileInput, RadioSelect, TextInput, CheckboxSelectMultiple, ClearableFileInput, RadioSelect, TextInput,
) )
@ -161,9 +161,9 @@ beatle J R Ringo False""")
@override_settings(ROOT_URLCONF='forms_tests.urls') @override_settings(ROOT_URLCONF='forms_tests.urls')
class LiveWidgetTests(AdminSeleniumWebDriverTestCase): class LiveWidgetTests(AdminSeleniumTestCase):
available_apps = ['forms_tests'] + AdminSeleniumWebDriverTestCase.available_apps available_apps = ['forms_tests'] + AdminSeleniumTestCase.available_apps
def test_textarea_trailing_newlines(self): def test_textarea_trailing_newlines(self):
""" """

View File

@ -1,4 +1,5 @@
#!/usr/bin/env python #!/usr/bin/env python
import argparse
import atexit import atexit
import copy import copy
import os import os
@ -7,7 +8,6 @@ import subprocess
import sys import sys
import tempfile import tempfile
import warnings import warnings
from argparse import ArgumentParser
import django import django
from django.apps import apps from django.apps import apps
@ -15,6 +15,7 @@ from django.conf import settings
from django.db import connection, connections from django.db import connection, connections
from django.test import TestCase, TransactionTestCase from django.test import TestCase, TransactionTestCase
from django.test.runner import default_test_processes from django.test.runner import default_test_processes
from django.test.selenium import SeleniumTestCaseBase
from django.test.utils import get_runner from django.test.utils import get_runner
from django.utils import six from django.utils import six
from django.utils._os import upath from django.utils._os import upath
@ -233,6 +234,20 @@ def actual_test_processes(parallel):
return parallel return parallel
class ActionSelenium(argparse.Action):
"""
Validate the comma-separated list of requested browsers.
"""
def __call__(self, parser, namespace, values, option_string=None):
browsers = values.split(',')
for browser in browsers:
try:
SeleniumTestCaseBase.import_webdriver(browser)
except ImportError:
raise argparse.ArgumentError(self, "Selenium browser specification '%s' is not valid." % browser)
setattr(namespace, self.dest, browsers)
def django_tests(verbosity, interactive, failfast, keepdb, reverse, def django_tests(verbosity, interactive, failfast, keepdb, reverse,
test_labels, debug_sql, parallel, tags, exclude_tags): test_labels, debug_sql, parallel, tags, exclude_tags):
state = setup(verbosity, test_labels, parallel) state = setup(verbosity, test_labels, parallel)
@ -360,7 +375,7 @@ def paired_tests(paired_test, options, test_labels, parallel):
if __name__ == "__main__": if __name__ == "__main__":
parser = ArgumentParser(description="Run the Django test suite.") parser = argparse.ArgumentParser(description="Run the Django test suite.")
parser.add_argument('modules', nargs='*', metavar='module', parser.add_argument('modules', nargs='*', metavar='module',
help='Optional path(s) to test modules; e.g. "i18n" or ' help='Optional path(s) to test modules; e.g. "i18n" or '
'"i18n.tests.TranslationTests.test_lazy_objects".') '"i18n.tests.TranslationTests.test_lazy_objects".')
@ -396,8 +411,8 @@ if __name__ == "__main__":
'LiveServerTestCase) is expected to run from. The default value ' 'LiveServerTestCase) is expected to run from. The default value '
'is localhost:8081-8179.') 'is localhost:8081-8179.')
parser.add_argument( parser.add_argument(
'--selenium', action='store_true', dest='selenium', default=False, '--selenium', dest='selenium', action=ActionSelenium, metavar='BROWSERS',
help='Run only the Selenium tests (equivalent to "--tag selenium").') help='A comma-separated list of browsers to run the Selenium tests against.')
parser.add_argument( parser.add_argument(
'--debug-sql', action='store_true', dest='debug_sql', default=False, '--debug-sql', action='store_true', dest='debug_sql', default=False,
help='Turn on the SQL query logger within tests.') help='Turn on the SQL query logger within tests.')
@ -438,11 +453,11 @@ if __name__ == "__main__":
os.environ['DJANGO_LIVE_TEST_SERVER_ADDRESS'] = options.liveserver os.environ['DJANGO_LIVE_TEST_SERVER_ADDRESS'] = options.liveserver
if options.selenium: if options.selenium:
os.environ['DJANGO_SELENIUM_TESTS'] = '1'
if not options.tags: if not options.tags:
options.tags = ['selenium'] options.tags = ['selenium']
elif 'selenium' not in options.tags: elif 'selenium' not in options.tags:
options.tags.append('selenium') options.tags.append('selenium')
SeleniumTestCaseBase.browsers = options.selenium
if options.bisect: if options.bisect:
bisect_tests(options.bisect, options, options.modules, options.parallel) bisect_tests(options.bisect, options, options.modules, options.parallel)

View File

@ -3,19 +3,16 @@ from __future__ import unicode_literals
import gettext import gettext
import json import json
import os
import unittest
from os import path from os import path
from django.conf import settings from django.conf import settings
from django.test import ( from django.test import (
LiveServerTestCase, SimpleTestCase, TestCase, modify_settings, SimpleTestCase, TestCase, modify_settings, override_settings,
override_settings, tag,
) )
from django.test.selenium import SeleniumTestCase
from django.urls import reverse from django.urls import reverse
from django.utils import six from django.utils import six
from django.utils._os import upath from django.utils._os import upath
from django.utils.module_loading import import_string
from django.utils.translation import LANGUAGE_SESSION_KEY, override from django.utils.translation import LANGUAGE_SESSION_KEY, override
from ..urls import locale_dir from ..urls import locale_dir
@ -265,35 +262,14 @@ class JsI18NTestsMultiPackage(SimpleTestCase):
'este texto de app3 debe ser traducido') 'este texto de app3 debe ser traducido')
skip_selenium = not os.environ.get('DJANGO_SELENIUM_TESTS', False)
@unittest.skipIf(skip_selenium, 'Selenium tests not requested')
@tag('selenium')
@override_settings(ROOT_URLCONF='view_tests.urls') @override_settings(ROOT_URLCONF='view_tests.urls')
class JavascriptI18nTests(LiveServerTestCase): class JavascriptI18nTests(SeleniumTestCase):
# The test cases use fixtures & translations from these apps. # The test cases use fixtures & translations from these apps.
available_apps = [ available_apps = [
'django.contrib.admin', 'django.contrib.auth', 'django.contrib.admin', 'django.contrib.auth',
'django.contrib.contenttypes', 'view_tests', 'django.contrib.contenttypes', 'view_tests',
] ]
webdriver_class = 'selenium.webdriver.firefox.webdriver.WebDriver'
@classmethod
def setUpClass(cls):
try:
cls.selenium = import_string(cls.webdriver_class)()
except Exception as e:
raise unittest.SkipTest('Selenium webdriver "%s" not installed or '
'not operational: %s' % (cls.webdriver_class, str(e)))
cls.selenium.implicitly_wait(10)
super(JavascriptI18nTests, cls).setUpClass()
@classmethod
def tearDownClass(cls):
cls.selenium.quit()
super(JavascriptI18nTests, cls).tearDownClass()
@override_settings(LANGUAGE_CODE='de') @override_settings(LANGUAGE_CODE='de')
def test_javascript_gettext(self): def test_javascript_gettext(self):
@ -321,11 +297,3 @@ class JavascriptI18nTests(LiveServerTestCase):
self.assertEqual(elem.text, 'il faut traduire cette chaîne de caractères de app1') self.assertEqual(elem.text, 'il faut traduire cette chaîne de caractères de app1')
elem = self.selenium.find_element_by_id('app2string') elem = self.selenium.find_element_by_id('app2string')
self.assertEqual(elem.text, 'il faut traduire cette chaîne de caractères de app2') self.assertEqual(elem.text, 'il faut traduire cette chaîne de caractères de app2')
class JavascriptI18nChromeTests(JavascriptI18nTests):
webdriver_class = 'selenium.webdriver.chrome.webdriver.WebDriver'
class JavascriptI18nIETests(JavascriptI18nTests):
webdriver_class = 'selenium.webdriver.ie.webdriver.WebDriver'