Ticket #2879: 2879.selenium-support.8.diff

File 2879.selenium-support.8.diff, 35.8 KB (added by Julien Phalip, 13 years ago)
  • django/conf/global_settings.py

    diff --git a/django/conf/global_settings.py b/django/conf/global_settings.py
    index 0aee63d..da9d030 100644
    a b DEFAULT_EXCEPTION_REPORTER_FILTER = 'django.views.debug.SafeExceptionReporterFil  
    565565# The name of the class to use to run the test suite
    566566TEST_RUNNER = 'django.test.simple.DjangoTestSuiteRunner'
    567567
     568# For the live test server (e.g. used for running Selenium tests)
     569LIVE_TEST_SERVER_HOST = 'localhost'
     570LIVE_TEST_SERVER_PORT = 8081
     571
    568572############
    569573# FIXTURES #
    570574############
  • new file django/contrib/admin/tests.py

    diff --git a/django/contrib/admin/tests.py b/django/contrib/admin/tests.py
    new file mode 100644
    index 0000000..bd93899
    - +  
     1import sys
     2
     3from django.test import LiveServerTestCase
     4from django.utils.importlib import import_module
     5from django.utils.unittest import SkipTest
     6
     7class AdminSeleniumWebDriverTestCase(LiveServerTestCase):
     8
     9    webdriver_class = 'selenium.webdriver.firefox.webdriver.WebDriver'
     10
     11    def setUp(self):
     12        if sys.version_info < (2, 6):
     13            raise SkipTest('Selenium Webdriver does not support Python < 2.6.')
     14        try:
     15            # Import and start the WebDriver class.
     16            module, attr = self.webdriver_class.rsplit('.', 1)
     17            mod = import_module(module)
     18            WebDriver = getattr(mod, attr)
     19            self.selenium = WebDriver()
     20        except Exception:
     21            raise SkipTest('Selenium webdriver "%s" not installed or not '
     22                           'operational.' % self.webdriver_class)
     23        super(AdminSeleniumWebDriverTestCase, self).setUp()
     24
     25    def tearDown(self):
     26        super(AdminSeleniumWebDriverTestCase, self).tearDown()
     27        if hasattr(self, 'selenium'):
     28            self.selenium.quit()
     29
     30    def admin_login(self, username, password, login_url='/admin/'):
     31        """
     32        Helper function to log into the admin.
     33        """
     34        self.selenium.get('%s%s' % (self.live_server_url, login_url))
     35        username_input = self.selenium.find_element_by_name("username")
     36        username_input.send_keys(username)
     37        password_input = self.selenium.find_element_by_name("password")
     38        password_input.send_keys(password)
     39        self.selenium.find_element_by_xpath('//input[@value="Log in"]').click()
     40
     41    def get_css_value(self, selector, attribute):
     42        """
     43        Helper function that returns the value for the CSS attribute of an
     44        DOM element specified by the given selector. Uses the jQuery that ships
     45        with Django.
     46        """
     47        return self.selenium.execute_script(
     48            'return django.jQuery("%s").css("%s")' % (selector, attribute))
     49 No newline at end of file
  • django/test/__init__.py

    diff --git a/django/test/__init__.py b/django/test/__init__.py
    index a3a03e3..21a4841 100644
    a b Django Unit Test and Doctest framework.  
    44
    55from django.test.client import Client, RequestFactory
    66from django.test.testcases import (TestCase, TransactionTestCase,
    7         SimpleTestCase, skipIfDBFeature, skipUnlessDBFeature)
     7    SimpleTestCase, LiveServerTestCase, skipIfDBFeature,
     8    skipUnlessDBFeature)
    89from django.test.utils import Approximate
  • django/test/testcases.py

    diff --git a/django/test/testcases.py b/django/test/testcases.py
    index ee22ac2..3a13d26 100644
    a b import sys  
    55from functools import wraps
    66from urlparse import urlsplit, urlunsplit
    77from xml.dom.minidom import parseString, Node
     8import select
     9import socket
     10import threading
    811
    912from django.conf import settings
     13from django.contrib.staticfiles.handlers import StaticFilesHandler
    1014from django.core import mail
    1115from django.core.exceptions import ValidationError
     16from django.core.handlers.wsgi import WSGIHandler
    1217from django.core.management import call_command
    1318from django.core.signals import request_started
     19from django.core.servers.basehttp import (WSGIRequestHandler, WSGIServer,
     20    WSGIServerException)
    1421from django.core.urlresolvers import clear_url_caches
    1522from django.core.validators import EMPTY_VALUES
    1623from django.db import (transaction, connection, connections, DEFAULT_DB_ALIAS,
    from django.test.utils import (get_warnings_state, restore_warnings_state,  
    2330    override_settings)
    2431from django.utils import simplejson, unittest as ut2
    2532from django.utils.encoding import smart_str
     33from django.views.static import serve
    2634
    2735__all__ = ('DocTestRunner', 'OutputChecker', 'TestCase', 'TransactionTestCase',
    2836           'SimpleTestCase', 'skipIfDBFeature', 'skipUnlessDBFeature')
    def skipUnlessDBFeature(feature):  
    732740    """
    733741    return _deferredSkip(lambda: not getattr(connection.features, feature),
    734742                         "Database doesn't support feature %s" % feature)
     743
     744class QuietWSGIRequestHandler(WSGIRequestHandler):
     745    """
     746    Just a regular WSGIRequestHandler except it doesn't log to the standard
     747    output any of the requests received, so as to not clutter the output for
     748    the tests' results.
     749    """
     750    def log_message(*args):
     751        pass
     752
     753class StoppableWSGIServer(WSGIServer):
     754    """
     755    The code in this class is borrowed from the `SocketServer.BaseServer` class
     756    in Python 2.6. The important functionality here is that the server is non-
     757    blocking and that it can be shut down at any moment. This is made possible
     758    by the server regularly polling the socket and checking if it has been
     759    asked to stop.
     760    Note for the future: Once Django stops supporting Python 2.5, this class
     761    can be removed as `WSGIServer` will have this ability to shutdown on
     762    demand.
     763    """
     764
     765    def __init__(self, *args, **kwargs):
     766        super(StoppableWSGIServer, self).__init__(*args, **kwargs)
     767        self.__is_shut_down = threading.Event()
     768        self.__serving = False
     769
     770    def serve_forever(self, poll_interval=0.5):
     771        """Handle one request at a time until shutdown.
     772
     773        Polls for shutdown every poll_interval seconds.
     774        """
     775        self.__serving = True
     776        self.__is_shut_down.clear()
     777        while self.__serving:
     778            r, w, e = select.select([self], [], [], poll_interval)
     779            if r:
     780                self._handle_request_noblock()
     781        self.__is_shut_down.set()
     782
     783    def shutdown(self):
     784        """Stops the serve_forever loop.
     785
     786        Blocks until the loop has finished. This must be called while
     787        serve_forever() is running in another thread, or it will
     788        deadlock.
     789        """
     790        self.__serving = False
     791        self.__is_shut_down.wait()
     792
     793    def handle_request(self):
     794        """Handle one request, possibly blocking.
     795        """
     796        fd_sets = select.select([self], [], [], None)
     797        if not fd_sets[0]:
     798            return
     799        self._handle_request_noblock()
     800
     801    def _handle_request_noblock(self):
     802        """Handle one request, without blocking.
     803
     804        I assume that select.select has returned that the socket is
     805        readable before this function was called, so there should be
     806        no risk of blocking in get_request().
     807        """
     808        try:
     809            request, client_address = self.get_request()
     810        except socket.error:
     811            return
     812        if self.verify_request(request, client_address):
     813            try:
     814                self.process_request(request, client_address)
     815            except Exception:
     816                self.handle_error(request, client_address)
     817                self.close_request(request)
     818
     819class MediaFilesHandler(StaticFilesHandler):
     820    """
     821    Handler for serving the media files.
     822    """
     823
     824    def get_base_dir(self):
     825        return settings.MEDIA_ROOT
     826
     827    def get_base_url(self):
     828        return settings.MEDIA_URL
     829
     830    def serve(self, request):
     831        return serve(request, self.file_path(request.path),
     832            document_root=self.get_base_dir())
     833
     834class LiveServerThread(threading.Thread):
     835    """
     836    Thread for running a live http server while the tests are running.
     837    """
     838
     839    def __init__(self, address, port):
     840        self.address = address
     841        self.port = port
     842        self.is_ready = threading.Event()
     843        self.error = None
     844        super(LiveServerThread, self).__init__()
     845
     846    def run(self):
     847        """
     848        Sets up live server and database and loops over handling http requests.
     849        """
     850        try:
     851            # Instantiate and start the server
     852            self.httpd = StoppableWSGIServer(
     853                (self.address, self.port), QuietWSGIRequestHandler)
     854            handler = StaticFilesHandler(MediaFilesHandler(WSGIHandler()))
     855            self.httpd.set_app(handler)
     856            self.is_ready.set()
     857            self.httpd.serve_forever()
     858        except WSGIServerException, e:
     859            self.error = e
     860            self.is_ready.set()
     861
     862    def join(self, timeout=None):
     863        self.httpd.shutdown()
     864        self.httpd.server_close()
     865        super(LiveServerThread, self).join(timeout)
     866
     867class LiveServerTestCase(TransactionTestCase):
     868    """
     869    Does basically the same as TransactionTestCase but also launches a live
     870    http server in a separate thread so that the tests may use another testing
     871    framework, such as Selenium for example, instead of the built-in dummy
     872    client.
     873    """
     874
     875    @property
     876    def live_server_url(self):
     877        return 'http://%s:%s' % (settings.LIVE_TEST_SERVER_HOST,
     878                                 settings.LIVE_TEST_SERVER_PORT)
     879
     880    @classmethod
     881    def setUpClass(cls):
     882        for conn in connections.all():
     883            if (conn.settings_dict['ENGINE'] == 'django.db.backends.sqlite3'
     884                and conn.settings_dict['NAME'] == ':memory:'):
     885                raise ut2.SkipTest('In-memory sqlite databases are not '
     886                    'supported by LiveServerTestCase because the memory '
     887                    'cannot be shared between threads.')
     888
     889        # Launch the Django live server's thread
     890        cls.server_thread = LiveServerThread(
     891            settings.LIVE_TEST_SERVER_HOST,
     892            int(settings.LIVE_TEST_SERVER_PORT))
     893        cls.server_thread.start()
     894
     895        # Wait for the Django server to be ready
     896        cls.server_thread.is_ready.wait()
     897        if cls.server_thread.error:
     898            raise cls.server_thread.error
     899
     900        super(LiveServerTestCase, cls).setUpClass()
     901
     902    @classmethod
     903    def tearDownClass(cls):
     904        # Terminate the Django server's thread
     905        cls.server_thread.join()
     906        super(LiveServerTestCase, cls).tearDownClass()
  • docs/internals/contributing/writing-code/unit-tests.txt

    diff --git a/docs/internals/contributing/writing-code/unit-tests.txt b/docs/internals/contributing/writing-code/unit-tests.txt
    index 5ec09fe..baa20a1 100644
    a b Going beyond that, you can specify an individual test method like this:  
    122122
    123123    ./runtests.py --settings=path.to.settings i18n.TranslationTests.test_lazy_objects
    124124
     125Running the Selenium tests
     126~~~~~~~~~~~~~~~~~~~~~~~~~~
     127
     128Some admin tests require Selenium 2 to work via a real Web browser. To allow
     129those tests to run and not be skipped, you must install the selenium_ package
     130(version > 2.13) into your Python path.
     131
     132Then, run the tests normally, for example:
     133
     134.. code-block:: bash
     135
     136    ./runtests.py --settings=test_sqlite admin_inlines
     137
    125138Running all the tests
    126139~~~~~~~~~~~~~~~~~~~~~
    127140
    dependencies:  
    135148*  setuptools_
    136149*  memcached_, plus a :ref:`supported Python binding <memcached>`
    137150*  gettext_ (:ref:`gettext_on_windows`)
     151*  selenium_
    138152
    139153If you want to test the memcached cache backend, you'll also need to define
    140154a :setting:`CACHES` setting that points at your memcached instance.
    associated tests will be skipped.  
    149163.. _setuptools: http://pypi.python.org/pypi/setuptools/
    150164.. _memcached: http://www.danga.com/memcached/
    151165.. _gettext: http://www.gnu.org/software/gettext/manual/gettext.html
     166.. _selenium: http://pypi.python.org/pypi/selenium
    152167
    153168.. _contrib-apps:
    154169
  • docs/ref/settings.txt

    diff --git a/docs/ref/settings.txt b/docs/ref/settings.txt
    index a35d99a..89f2c47 100644
    a b all cache keys used by the Django server.  
    195195
    196196See the :ref:`cache documentation <cache_key_prefixing>` for more information.
    197197
     198.. setting:: LIVE_TEST_SERVER_HOST
     199
     200LIVE_TEST_SERVER_HOST
     201~~~~~~~~~~~~~~~~~~~~~
     202
     203Default: ``'localhost'``
     204
     205Controls the host address at which the live test server gets started when using
     206a :class:`~django.test.LiveServerTestCase`.
     207
     208See also: :setting:`LIVE_TEST_SERVER_PORT`
     209
     210.. setting:: LIVE_TEST_SERVER_PORT
     211
     212LIVE_TEST_SERVER_PORT
     213~~~~~~~~~~~~~~~~~~~~~
     214
     215Default: ``8081``
     216
     217Controls the port at which the live test server gets started when using
     218a :class:`~django.test.LiveServerTestCase`.
     219
     220See also: :setting:`LIVE_TEST_SERVER_HOST`
     221
    198222.. setting:: CACHES-LOCATION
    199223
    200224LOCATION
  • docs/topics/testing.txt

    diff --git a/docs/topics/testing.txt b/docs/topics/testing.txt
    index 181b4ff..fdf4e81 100644
    a b Some of the things you can do with the test client are:  
    580580* Test that a given request is rendered by a given Django template, with
    581581  a template context that contains certain values.
    582582
    583 Note that the test client is not intended to be a replacement for Twill_,
     583Note that the test client is not intended to be a replacement for Windmill_,
    584584Selenium_, or other "in-browser" frameworks. Django's test client has
    585585a different focus. In short:
    586586
    587587* Use Django's test client to establish that the correct view is being
    588588  called and that the view is collecting the correct context data.
    589589
    590 * Use in-browser frameworks such as Twill and Selenium to test *rendered*
    591   HTML and the *behavior* of Web pages, namely JavaScript functionality.
     590* Use in-browser frameworks such as Windmill_ and Selenium_ to test *rendered*
     591  HTML and the *behavior* of Web pages, namely JavaScript functionality. Django
     592  also provides special support for those frameworks; see the section on
     593  :class:`~django.test.LiveServerTestCase` for more details.
    592594
    593595A comprehensive test suite should use a combination of both test types.
    594596
    595 .. _Twill: http://twill.idyll.org/
    596 .. _Selenium: http://seleniumhq.org/
    597 
    598597Overview and a quick example
    599598~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    600599
    set up, execute and tear down the test suite.  
    18301829    those options will be added to the list of command-line options that
    18311830    the :djadmin:`test` command can use.
    18321831
     1832Live test server
     1833----------------
     1834
     1835.. versionadded:: 1.4
     1836
     1837.. currentmodule:: django.test
     1838
     1839.. class:: LiveServerTestCase()
     1840
     1841``LiveServerTestCase`` does basically the same as
     1842:class:`~django.test.TransactionTestCase` with one extra thing: it launches a
     1843live Django server in the background on setup, and shuts it down on teardown.
     1844This allows to use other automated test clients than the
     1845:ref:`Django dummy client <test-client>` such as, for example, the Selenium_ or
     1846Windmill_ clients, to execute a series of functional tests inside a browser and
     1847simulate a real user's actions.
     1848
     1849You may control which host and port the live server will run at with
     1850respectively the :setting:`LIVE_TEST_SERVER_HOST` and
     1851:setting:`LIVE_TEST_SERVER_PORT` settings. The full server url can then be
     1852accessed during the tests with ``self.live_server_url``.
     1853
     1854To demonstrate how to use ``LiveServerTestCase``, let's write a simple Selenium
     1855test. First of all, you need to install the `selenium package`_ into your
     1856Python path:
     1857
     1858.. code-block:: bash
     1859
     1860   pip install selenium
     1861
     1862Then, add the following code to one of your app's tests module (for example:
     1863``myapp/tests.py``):
     1864
     1865.. code-block:: python
     1866
     1867    from django.test import LiveServerTestCase
     1868    from selenium.webdriver.firefox.webdriver import WebDriver
     1869
     1870    class MySeleniumTests(LiveServerTestCase):
     1871        fixtures = ['user-data.json']
     1872
     1873        def setUp(self):
     1874            self.selenium = WebDriver()
     1875            super(MySeleniumTests, self).setUp()
     1876
     1877        def tearDown(self):
     1878            super(MySeleniumTests, self).tearDown()
     1879            self.selenium.quit()
     1880
     1881        def test_login(self):
     1882            self.selenium.get('%s%s' % (self.live_server_url, '/login/'))
     1883            username_input = self.selenium.find_element_by_name("username")
     1884            username_input.send_keys('myuser')
     1885            password_input = self.selenium.find_element_by_name("password")
     1886            password_input.send_keys('secret')
     1887            self.selenium.find_element_by_xpath('//input[@value="Log in"]').click()
     1888
     1889Finally, you may run the test as follows:
     1890
     1891.. code-block:: bash
     1892
     1893    ./manage.py test myapp.MySeleniumTests.test_login
     1894
     1895This example will automatically open Firefox then go to the login page, enter
     1896the credentials and press the "Log in" button. Selenium offers other drivers in
     1897case you do not have Firefox installed or wish to use another browser. The
     1898example above is just a tiny fraction of what the Selenium client can do; check
     1899out the `full reference`_ for more details.
     1900
     1901.. _Windmill: http://www.getwindmill.com/
     1902.. _Selenium: http://seleniumhq.org/
     1903.. _selenium package: http://pypi.python.org/pypi/selenium
     1904.. _full reference: http://readthedocs.org/docs/selenium-python/en/latest/api.html
     1905.. _Firefox: http://www.mozilla.com/firefox/
    18331906
    18341907Attributes
    18351908~~~~~~~~~~
    18361909
    1837 
    18381910.. attribute:: DjangoTestSuiteRunner.option_list
    18391911
    18401912    .. versionadded:: 1.4
  • tests/regressiontests/admin_inlines/admin.py

    diff --git a/tests/regressiontests/admin_inlines/admin.py b/tests/regressiontests/admin_inlines/admin.py
    index 4edd361..508f302 100644
    a b class SottoCapoInline(admin.TabularInline):  
    109109    model = SottoCapo
    110110
    111111
     112class ProfileInline(admin.TabularInline):
     113    model = Profile
     114    extra = 1
     115
    112116site.register(TitleCollection, inlines=[TitleInline])
    113117# Test bug #12561 and #12778
    114118# only ModelAdmin media
    site.register(Fashionista, inlines=[InlineWeakness])  
    124128site.register(Holder4, Holder4Admin)
    125129site.register(Author, AuthorAdmin)
    126130site.register(CapoFamiglia, inlines=[ConsigliereInline, SottoCapoInline])
     131site.register(ProfileCollection, inlines=[ProfileInline])
     132 No newline at end of file
  • tests/regressiontests/admin_inlines/models.py

    diff --git a/tests/regressiontests/admin_inlines/models.py b/tests/regressiontests/admin_inlines/models.py
    index 748280d..f2add00 100644
    a b class Consigliere(models.Model):  
    136136class SottoCapo(models.Model):
    137137    name = models.CharField(max_length=100)
    138138    capo_famiglia = models.ForeignKey(CapoFamiglia, related_name='+')
     139
     140# Other models
     141
     142class ProfileCollection(models.Model):
     143    pass
     144
     145class Profile(models.Model):
     146    collection = models.ForeignKey(ProfileCollection, blank=True, null=True)
     147    first_name = models.CharField(max_length=100)
     148    last_name = models.CharField(max_length=100)
     149 No newline at end of file
  • tests/regressiontests/admin_inlines/tests.py

    diff --git a/tests/regressiontests/admin_inlines/tests.py b/tests/regressiontests/admin_inlines/tests.py
    index c2e3bbc..8a053dc 100644
    a b  
    11from __future__ import absolute_import
    22
     3from django.contrib.admin.tests import AdminSeleniumWebDriverTestCase
    34from django.contrib.admin.helpers import InlineAdminForm
    45from django.contrib.auth.models import User, Permission
    56from django.contrib.contenttypes.models import ContentType
    from django.test import TestCase  
    89# local test models
    910from .admin import InnerInline
    1011from .models import (Holder, Inner, Holder2, Inner2, Holder3, Inner3, Person,
    11     OutfitItem, Fashionista, Teacher, Parent, Child, Author, Book)
     12    OutfitItem, Fashionista, Teacher, Parent, Child, Author, Book, Profile,
     13    ProfileCollection)
    1214
    1315
    1416class TestInline(TestCase):
    class TestInlinePermissions(TestCase):  
    380382        self.assertContains(response, 'value="4" id="id_inner2_set-TOTAL_FORMS"')
    381383        self.assertContains(response, '<input type="hidden" name="inner2_set-0-id" value="%i"' % self.inner2_id)
    382384        self.assertContains(response, 'id="id_inner2_set-0-DELETE"')
     385
     386class SeleniumFirefoxTests(AdminSeleniumWebDriverTestCase):
     387    webdriver_class = 'selenium.webdriver.firefox.webdriver.WebDriver'
     388    fixtures = ['admin-views-users.xml']
     389    urls = "regressiontests.admin_inlines.urls"
     390
     391    def test_add_inlines(self):
     392        """
     393        Ensure that the "Add another XXX" link correctly adds items to the
     394        inline form.
     395        """
     396        self.admin_login(username='super', password='secret')
     397        self.selenium.get('%s%s' % (self.live_server_url,
     398            '/admin/admin_inlines/profilecollection/add/'))
     399
     400        # Check that there's only one inline to start with and that it has the
     401        # correct ID.
     402        self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector(
     403            '#profile_set-group table tr.dynamic-profile_set')), 1)
     404        self.failUnlessEqual(self.selenium.find_element_by_css_selector(
     405            '.dynamic-profile_set:nth-of-type(1)').get_attribute('id'),
     406            'profile_set-0')
     407        self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector(
     408            'form#profilecollection_form tr.dynamic-profile_set#profile_set-0 input[name=profile_set-0-first_name]')), 1)
     409        self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector(
     410            'form#profilecollection_form tr.dynamic-profile_set#profile_set-0 input[name=profile_set-0-last_name]')), 1)
     411
     412        # Add an inline
     413        self.selenium.find_element_by_link_text('Add another Profile').click()
     414
     415        # Check that the inline has been added, that it has the right id, and
     416        # that it contains the right fields.
     417        self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector(
     418            '#profile_set-group table tr.dynamic-profile_set')), 2)
     419        self.failUnlessEqual(self.selenium.find_element_by_css_selector(
     420            '.dynamic-profile_set:nth-of-type(2)').get_attribute('id'), 'profile_set-1')
     421        self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector(
     422            'form#profilecollection_form tr.dynamic-profile_set#profile_set-1 input[name=profile_set-1-first_name]')), 1)
     423        self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector(
     424            'form#profilecollection_form tr.dynamic-profile_set#profile_set-1 input[name=profile_set-1-last_name]')), 1)
     425
     426        # Let's add another one to be sure
     427        self.selenium.find_element_by_link_text('Add another Profile').click()
     428        self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector(
     429            '#profile_set-group table tr.dynamic-profile_set')), 3)
     430        self.failUnlessEqual(self.selenium.find_element_by_css_selector(
     431            '.dynamic-profile_set:nth-of-type(3)').get_attribute('id'), 'profile_set-2')
     432        self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector(
     433            'form#profilecollection_form tr.dynamic-profile_set#profile_set-2 input[name=profile_set-2-first_name]')), 1)
     434        self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector(
     435            'form#profilecollection_form tr.dynamic-profile_set#profile_set-2 input[name=profile_set-2-last_name]')), 1)
     436
     437        # Enter some data and click 'Save'
     438        self.selenium.find_element_by_name('profile_set-0-first_name').send_keys('0 first name 1')
     439        self.selenium.find_element_by_name('profile_set-0-last_name').send_keys('0 last name 2')
     440        self.selenium.find_element_by_name('profile_set-1-first_name').send_keys('1 first name 1')
     441        self.selenium.find_element_by_name('profile_set-1-last_name').send_keys('1 last name 2')
     442        self.selenium.find_element_by_name('profile_set-2-first_name').send_keys('2 first name 1')
     443        self.selenium.find_element_by_name('profile_set-2-last_name').send_keys('2 last name 2')
     444        self.selenium.find_element_by_xpath('//input[@value="Save"]').click()
     445
     446        # Check that the objects have been created in the database
     447        self.assertEqual(ProfileCollection.objects.all().count(), 1)
     448        self.assertEqual(Profile.objects.all().count(), 3)
     449
     450    def test_delete_inlines(self):
     451        self.admin_login(username='super', password='secret')
     452        self.selenium.get('%s%s' % (self.live_server_url,
     453            '/admin/admin_inlines/profilecollection/add/'))
     454
     455        # Add a few inlines
     456        self.selenium.find_element_by_link_text('Add another Profile').click()
     457        self.selenium.find_element_by_link_text('Add another Profile').click()
     458        self.selenium.find_element_by_link_text('Add another Profile').click()
     459        self.selenium.find_element_by_link_text('Add another Profile').click()
     460        self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector(
     461            '#profile_set-group table tr.dynamic-profile_set')), 5)
     462        self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector(
     463            'form#profilecollection_form tr.dynamic-profile_set#profile_set-0')), 1)
     464        self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector(
     465            'form#profilecollection_form tr.dynamic-profile_set#profile_set-1')), 1)
     466        self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector(
     467            'form#profilecollection_form tr.dynamic-profile_set#profile_set-2')), 1)
     468        self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector(
     469            'form#profilecollection_form tr.dynamic-profile_set#profile_set-3')), 1)
     470        self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector(
     471            'form#profilecollection_form tr.dynamic-profile_set#profile_set-4')), 1)
     472
     473        # Click on a few delete buttons
     474        self.selenium.find_element_by_css_selector(
     475            'form#profilecollection_form tr.dynamic-profile_set#profile_set-1 td.delete a').click()
     476        self.selenium.find_element_by_css_selector(
     477            'form#profilecollection_form tr.dynamic-profile_set#profile_set-2 td.delete a').click()
     478        # Verify that they're gone and that the IDs have been re-sequenced
     479        self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector(
     480            '#profile_set-group table tr.dynamic-profile_set')), 3)
     481        self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector(
     482            'form#profilecollection_form tr.dynamic-profile_set#profile_set-0')), 1)
     483        self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector(
     484            'form#profilecollection_form tr.dynamic-profile_set#profile_set-1')), 1)
     485        self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector(
     486            'form#profilecollection_form tr.dynamic-profile_set#profile_set-2')), 1)
     487
     488class SeleniumInternetExplorerTests(SeleniumFirefoxTests):
     489    webdriver_class = 'selenium.webdriver.ie.webdriver.WebDriver'
     490 No newline at end of file
  • tests/regressiontests/admin_widgets/tests.py

    diff --git a/tests/regressiontests/admin_widgets/tests.py b/tests/regressiontests/admin_widgets/tests.py
    index 37fa7bc..ecf3165 100644
    a b from django import forms  
    77from django.conf import settings
    88from django.contrib import admin
    99from django.contrib.admin import widgets
     10from django.contrib.admin.tests import AdminSeleniumWebDriverTestCase
    1011from django.core.files.storage import default_storage
    1112from django.core.files.uploadedfile import SimpleUploadedFile
    1213from django.db.models import DateField
    class RelatedFieldWidgetWrapperTests(DjangoTestCase):  
    407408        # Used to fail with a name error.
    408409        w = widgets.RelatedFieldWidgetWrapper(w, rel, widget_admin_site)
    409410        self.assertFalse(w.can_add_related)
     411
     412
     413class SeleniumFirefoxTests(AdminSeleniumWebDriverTestCase):
     414    webdriver_class = 'selenium.webdriver.firefox.webdriver.WebDriver'
     415    fixtures = ['admin-widgets-users.xml']
     416    urls = "regressiontests.admin_widgets.urls"
     417
     418    def test_show_hide_date_time_picker_widgets(self):
     419        """
     420        Ensure that pressing the ESC key closes the date and time picker
     421        widgets.
     422        Refs #17064.
     423        """
     424        from selenium.webdriver.common.keys import Keys
     425
     426        self.admin_login(username='super', password='secret', login_url='/')
     427        # Open a page that has a date and time picker widgets
     428        self.selenium.get('%s%s' % (self.live_server_url,
     429            '/admin_widgets/member/add/'))
     430
     431        # First, with the date picker widget ---------------------------------
     432        # Check that the date picker is hidden
     433        self.assertEqual(
     434            self.get_css_value('#calendarbox0', 'display'), 'none')
     435        # Click the calendar icon
     436        self.selenium.find_element_by_id('calendarlink0').click()
     437        # Check that the date picker is visible
     438        self.assertEqual(
     439            self.get_css_value('#calendarbox0', 'display'), 'block')
     440        # Press the ESC key
     441        self.selenium.find_element_by_tag_name('html').send_keys([Keys.ESCAPE])
     442        # Check that the date picker is hidden again
     443        self.assertEqual(
     444            self.get_css_value('#calendarbox0', 'display'), 'none')
     445
     446        # Then, with the time picker widget ----------------------------------
     447        # Check that the time picker is hidden
     448        self.assertEqual(
     449            self.get_css_value('#clockbox0', 'display'), 'none')
     450        # Click the time icon
     451        self.selenium.find_element_by_id('clocklink0').click()
     452        # Check that the time picker is visible
     453        self.assertEqual(
     454            self.get_css_value('#clockbox0', 'display'), 'block')
     455        # Press the ESC key
     456        self.selenium.find_element_by_tag_name('html').send_keys([Keys.ESCAPE])
     457        # Check that the time picker is hidden again
     458        self.assertEqual(
     459            self.get_css_value('#clockbox0', 'display'), 'none')
     460
     461class SeleniumInternetExplorerTests(SeleniumFirefoxTests):
     462    webdriver_class = 'selenium.webdriver.ie.webdriver.WebDriver'
     463 No newline at end of file
  • new file tests/regressiontests/servers/fixtures/testdata.json

    diff --git a/tests/regressiontests/servers/fixtures/testdata.json b/tests/regressiontests/servers/fixtures/testdata.json
    new file mode 100644
    index 0000000..d81b225
    - +  
     1[
     2  {
     3    "pk": 1,
     4    "model": "servers.person",
     5    "fields": {
     6      "name": "jane"
     7    }
     8  },
     9  {
     10    "pk": 2,
     11    "model": "servers.person",
     12    "fields": {
     13      "name": "robert"
     14    }
     15  }
     16]
     17 No newline at end of file
  • new file tests/regressiontests/servers/media/example_media_file.txt

    diff --git a/tests/regressiontests/servers/media/example_media_file.txt b/tests/regressiontests/servers/media/example_media_file.txt
    new file mode 100644
    index 0000000..dd2dda9
    - +  
     1example media file
  • tests/regressiontests/servers/models.py

    diff --git a/tests/regressiontests/servers/models.py b/tests/regressiontests/servers/models.py
    index e69de29..6e1414a 100644
    a b  
     1from django.db import models
     2
     3
     4class Person(models.Model):
     5    name = models.CharField(max_length=256)
  • new file tests/regressiontests/servers/static/example_file.txt

    diff --git a/tests/regressiontests/servers/static/example_file.txt b/tests/regressiontests/servers/static/example_file.txt
    new file mode 100644
    index 0000000..5f1cfce
    - +  
     1example file
  • tests/regressiontests/servers/tests.py

    diff --git a/tests/regressiontests/servers/tests.py b/tests/regressiontests/servers/tests.py
    index b9d24ba..04450a9 100644
    a b Tests for django.core.servers.  
    33"""
    44import os
    55from urlparse import urljoin
     6import urllib2
    67
    78import django
    89from django.conf import settings
    9 from django.test import TestCase
     10from django.test import TestCase, LiveServerTestCase
    1011from django.core.handlers.wsgi import WSGIHandler
    1112from django.core.servers.basehttp import AdminMediaHandler
     13from django.test.utils import override_settings
    1214
     15from .models import Person
    1316
    1417class AdminMediaHandlerTests(TestCase):
    1518
    class AdminMediaHandlerTests(TestCase):  
    6871                continue
    6972            self.fail('URL: %s should have caused a ValueError exception.'
    7073                      % url)
     74
     75
     76TEST_ROOT = os.path.dirname(__file__)
     77TEST_SETTINGS = {
     78    'MEDIA_URL': '/media/',
     79    'MEDIA_ROOT': os.path.join(TEST_ROOT, 'media'),
     80    'STATIC_URL': '/static/',
     81    'STATIC_ROOT': os.path.join(TEST_ROOT, 'static'),
     82}
     83
     84
     85class LiveServerBase(LiveServerTestCase):
     86    urls = 'regressiontests.servers.urls'
     87    fixtures = ['testdata.json']
     88
     89    @classmethod
     90    def setUpClass(cls):
     91        cls.settings_override = override_settings(**TEST_SETTINGS)
     92        cls.settings_override.enable()
     93        super(LiveServerBase, cls).setUpClass()
     94
     95    @classmethod
     96    def tearDownClass(cls):
     97        cls.settings_override.disable()
     98        super(LiveServerBase, cls).tearDownClass()
     99
     100    def urlopen(self, url):
     101        base = 'http://%s:%s' % (settings.LIVE_TEST_SERVER_HOST,
     102                                 settings.LIVE_TEST_SERVER_PORT)
     103        return urllib2.urlopen(base + url)
     104
     105
     106class LiveServerViews(LiveServerBase):
     107    def test_404(self):
     108        """
     109        Ensure that the LiveServerTestCase serves 404s.
     110        """
     111        try:
     112            self.urlopen('/')
     113        except urllib2.HTTPError, err:
     114            self.assertEquals(err.code, 404, 'Expected 404 response')
     115        else:
     116            self.fail('Expected 404 response')
     117
     118    def test_view(self):
     119        """
     120        Ensure that the LiveServerTestCase serves views.
     121        """
     122        f = self.urlopen('/example_view/')
     123        self.assertEquals(f.read(), 'example view')
     124
     125    def test_static_files(self):
     126        """
     127        Ensure that the LiveServerTestCase serves static files.
     128        """
     129        f = self.urlopen('/static/example_file.txt')
     130        self.assertEquals(f.read(), 'example file\n')
     131
     132    def test_media_files(self):
     133        """
     134        Ensure that the LiveServerTestCase serves media files.
     135        """
     136        f = self.urlopen('/media/example_media_file.txt')
     137        self.assertEquals(f.read(), 'example media file\n')
     138
     139
     140class LiveServerDatabase(LiveServerBase):
     141
     142    def test_fixtures_loaded(self):
     143        """
     144        Ensure that fixtures are properly loaded and visible to the
     145        live server thread.
     146        """
     147        f = self.urlopen('/model_view/')
     148        self.assertEquals(f.read().splitlines(), ['jane', 'robert'])
     149
     150    def test_database_writes(self):
     151        """
     152        Ensure that data written to the database by a view can be read.
     153        """
     154        self.urlopen('/create_model_instance/')
     155        names = [person.name for person in Person.objects.all()]
     156        self.assertEquals(names, ['jane', 'robert', 'emily'])
  • new file tests/regressiontests/servers/urls.py

    diff --git a/tests/regressiontests/servers/urls.py b/tests/regressiontests/servers/urls.py
    new file mode 100644
    index 0000000..83f757f
    - +  
     1from __future__ import absolute_import
     2
     3from django.conf.urls import patterns, url
     4
     5from . import views
     6
     7
     8urlpatterns = patterns('',
     9    url(r'^example_view/$', views.example_view),
     10    url(r'^model_view/$', views.model_view),
     11    url(r'^create_model_instance/$', views.create_model_instance),
     12)
  • new file tests/regressiontests/servers/views.py

    diff --git a/tests/regressiontests/servers/views.py b/tests/regressiontests/servers/views.py
    new file mode 100644
    index 0000000..42fafe0
    - +  
     1from django.http import HttpResponse
     2from .models import Person
     3
     4
     5def example_view(request):
     6    return HttpResponse('example view')
     7
     8
     9def model_view(request):
     10    people = Person.objects.all()
     11    return HttpResponse('\n'.join([person.name for person in people]))
     12
     13
     14def create_model_instance(request):
     15    person = Person(name='emily')
     16    person.save()
     17    return HttpResponse('')
Back to Top