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

File 2879.selenium-support.7.diff, 32.1 KB (added by julien, 3 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..8a09604
    - +  
     1from django.test import LiveServerTestCase
     2from django.utils.importlib import import_module
     3from django.utils.unittest import SkipTest
     4
     5class AdminSeleniumWebDriverTestCase(LiveServerTestCase):
     6
     7    webdriver_class = 'selenium.webdriver.firefox.webdriver.WebDriver'
     8
     9    def setUp(self):
     10        try:
     11            # Import and start the WebDriver class.
     12            module, attr = self.webdriver_class.rsplit('.', 1)
     13            mod = import_module(module)
     14            WebDriver = getattr(mod, attr)
     15            self.selenium = WebDriver()
     16        except Exception:
     17            raise SkipTest('Selenium webdriver "%s" not installed or not '
     18                           'operational.' % self.webdriver_class)
     19        super(AdminSeleniumWebDriverTestCase, self).setUp()
     20
     21    def tearDown(self):
     22        super(AdminSeleniumWebDriverTestCase, self).tearDown()
     23        if hasattr(self, 'selenium'):
     24            self.selenium.quit()
     25
     26    def admin_login(self, username, password, login_url='/admin/'):
     27        """
     28        Helper function to log into the admin.
     29        """
     30        self.selenium.get('%s%s' % (self.live_server_url, login_url))
     31        username_input = self.selenium.find_element_by_name("username")
     32        username_input.send_keys(username)
     33        password_input = self.selenium.find_element_by_name("password")
     34        password_input.send_keys(password)
     35        self.selenium.find_element_by_xpath('//input[@value="Log in"]').click()
     36
     37    def get_css_value(self, selector, attribute):
     38        """
     39        Helper function that returns the value for the CSS attribute of an
     40        DOM element specified by the given selector. Uses the jQuery that ships
     41        with Django.
     42        """
     43        return self.selenium.execute_script(
     44            'return django.jQuery("%s").css("%s")' % (selector, attribute))
     45 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..4e69b48 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    def setUp(self):
     881        for conn in connections.all():
     882            if (conn.settings_dict['ENGINE'] == 'django.db.backends.sqlite3'
     883                and conn.settings_dict['NAME'] == ':memory:'):
     884                raise ut2.SkipTest('In-memory sqlite databases are not '
     885                    'supported by LiveServerTestCase because the memory '
     886                    'cannot be shared between threads.')
     887
     888        # Launch the Django live server's thread
     889        self.server_thread = LiveServerThread(
     890            settings.LIVE_TEST_SERVER_HOST,
     891            int(settings.LIVE_TEST_SERVER_PORT))
     892        self.server_thread.start()
     893
     894        # Wait for the Django server to be ready
     895        self.server_thread.is_ready.wait()
     896        if self.server_thread.error:
     897            raise self.server_thread.error
     898
     899        super(LiveServerTestCase, self).setUp()
     900
     901    def tearDown(self):
     902        # Terminate the Django server's thread
     903        self.server_thread.join()
     904        super(LiveServerTestCase, self).tearDown()
  • 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/tests.py

    diff --git a/tests/regressiontests/admin_inlines/tests.py b/tests/regressiontests/admin_inlines/tests.py
    index c2e3bbc..d4f8f67 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
    class TestInlinePermissions(TestCase): 
    380381        self.assertContains(response, 'value="4" id="id_inner2_set-TOTAL_FORMS"')
    381382        self.assertContains(response, '<input type="hidden" name="inner2_set-0-id" value="%i"' % self.inner2_id)
    382383        self.assertContains(response, 'id="id_inner2_set-0-DELETE"')
     384
     385class SeleniumFirefoxTests(AdminSeleniumWebDriverTestCase):
     386    webdriver_class = 'selenium.webdriver.firefox.webdriver.WebDriver'
     387    fixtures = ['admin-views-users.xml']
     388    urls = "regressiontests.admin_inlines.urls"
     389
     390    def test_add_inlines(self):
     391        """
     392        Ensure that the "Add another XXX" link correctly adds items to the
     393        inline form.
     394        """
     395        self.admin_login(username='super', password='secret')
     396        self.selenium.get('%s%s' % (self.live_server_url,
     397            '/admin/admin_inlines/titlecollection/add/'))
     398
     399        # Check that there's only one inline to start with and that it has the
     400        # correct ID.
     401        self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector(
     402            '#title_set-group table tr.dynamic-title_set')), 1)
     403        self.failUnlessEqual(self.selenium.find_element_by_css_selector(
     404            '.dynamic-title_set:nth-of-type(1)').get_attribute('id'),
     405            'title_set-0')
     406        self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector(
     407            'form#titlecollection_form tr.dynamic-title_set#title_set-0 input[name=title_set-0-title1]')), 1)
     408        self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector(
     409            'form#titlecollection_form tr.dynamic-title_set#title_set-0 input[name=title_set-0-title2]')), 1)
     410
     411        # Add an inline
     412        self.selenium.find_element_by_link_text('Add another Title').click()
     413
     414        # Check that the inline has been added, that it has the right id, and
     415        # that it contains the right fields.
     416        self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector(
     417            '#title_set-group table tr.dynamic-title_set')), 2)
     418        self.failUnlessEqual(self.selenium.find_element_by_css_selector(
     419            '.dynamic-title_set:nth-of-type(2)').get_attribute('id'), 'title_set-1')
     420        self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector(
     421            'form#titlecollection_form tr.dynamic-title_set#title_set-1 input[name=title_set-1-title1]')), 1)
     422        self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector(
     423            'form#titlecollection_form tr.dynamic-title_set#title_set-1 input[name=title_set-1-title2]')), 1)
     424
     425        # Let's add another one to be sure
     426        self.selenium.find_element_by_link_text('Add another Title').click()
     427        self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector(
     428            '#title_set-group table tr.dynamic-title_set')), 3)
     429        self.failUnlessEqual(self.selenium.find_element_by_css_selector(
     430            '.dynamic-title_set:nth-of-type(3)').get_attribute('id'), 'title_set-2')
     431        self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector(
     432            'form#titlecollection_form tr.dynamic-title_set#title_set-2 input[name=title_set-2-title1]')), 1)
     433        self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector(
     434            'form#titlecollection_form tr.dynamic-title_set#title_set-2 input[name=title_set-2-title2]')), 1)
     435
     436    def test_delete_inlines(self):
     437        self.admin_login(username='super', password='secret')
     438        self.selenium.get('%s%s' % (self.live_server_url,
     439            '/admin/admin_inlines/titlecollection/add/'))
     440
     441        # Add a few inlines
     442        self.selenium.find_element_by_link_text('Add another Title').click()
     443        self.selenium.find_element_by_link_text('Add another Title').click()
     444        self.selenium.find_element_by_link_text('Add another Title').click()
     445        self.selenium.find_element_by_link_text('Add another Title').click()
     446        self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector(
     447            '#title_set-group table tr.dynamic-title_set')), 5)
     448        self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector(
     449            'form#titlecollection_form tr.dynamic-title_set#title_set-0')), 1)
     450        self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector(
     451            'form#titlecollection_form tr.dynamic-title_set#title_set-1')), 1)
     452        self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector(
     453            'form#titlecollection_form tr.dynamic-title_set#title_set-2')), 1)
     454        self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector(
     455            'form#titlecollection_form tr.dynamic-title_set#title_set-3')), 1)
     456        self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector(
     457            'form#titlecollection_form tr.dynamic-title_set#title_set-4')), 1)
     458
     459        # Click on a few delete buttons
     460        self.selenium.find_element_by_css_selector(
     461            'form#titlecollection_form tr.dynamic-title_set#title_set-1 td.delete a').click()
     462        self.selenium.find_element_by_css_selector(
     463            'form#titlecollection_form tr.dynamic-title_set#title_set-2 td.delete a').click()
     464        # Verify that they're gone and that the IDs have been re-sequenced
     465        self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector(
     466            '#title_set-group table tr.dynamic-title_set')), 3)
     467        self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector(
     468            'form#titlecollection_form tr.dynamic-title_set#title_set-0')), 1)
     469        self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector(
     470            'form#titlecollection_form tr.dynamic-title_set#title_set-1')), 1)
     471        self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector(
     472            'form#titlecollection_form tr.dynamic-title_set#title_set-2')), 1)
     473
     474class SeleniumInternetExplorerTests(SeleniumFirefoxTests):
     475    webdriver_class = 'selenium.webdriver.ie.webdriver.WebDriver'
     476 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/live_server/fixtures/testdata.json

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

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

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

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

    diff --git a/tests/regressiontests/live_server/tests.py b/tests/regressiontests/live_server/tests.py
    new file mode 100644
    index 0000000..67acf5d
    - +  
     1import os
     2import urllib2
     3
     4from django.conf import settings
     5from django.test import LiveServerTestCase
     6from django.test.utils import override_settings
     7
     8from .models import Person
     9
     10
     11TEST_ROOT = os.path.dirname(__file__)
     12TEST_SETTINGS = {
     13    'MEDIA_URL': '/media/',
     14    'MEDIA_ROOT': os.path.join(TEST_ROOT, 'media'),
     15    'STATIC_URL': '/static/',
     16    'STATIC_ROOT': os.path.join(TEST_ROOT, 'static'),
     17}
     18
     19
     20class LiveServerTestBase(LiveServerTestCase):
     21    urls = 'regressiontests.live_server.urls'
     22    fixtures = ['testdata.json']
     23
     24    def urlopen(self, url):
     25        base = 'http://%s:%s' % (settings.LIVE_TEST_SERVER_HOST,
     26                                 settings.LIVE_TEST_SERVER_PORT)
     27        return urllib2.urlopen(base + url)
     28
     29
     30class TestViews(LiveServerTestBase):
     31    def test_404(self):
     32        """
     33        Ensure that the LiveServerTestCase serves 404s.
     34        """
     35        try:
     36            self.urlopen('/')
     37        except urllib2.HTTPError, err:
     38            self.assertEquals(err.code, 404, 'Expected 404 response')
     39        else:
     40            self.fail('Expected 404 response')
     41
     42    def test_view(self):
     43        """
     44        Ensure that the LiveServerTestCase serves views.
     45        """
     46        f = self.urlopen('/example_view/')
     47        self.assertEquals(f.read(), 'example view')
     48
     49    def test_static_file(self):
     50        """
     51        Ensure that the LiveServerTestCase serves static files.
     52        """
     53        f = self.urlopen('/static/example_file.txt')
     54        self.assertEquals(f.read(), 'example file\n')
     55
     56    def test_media_files(self):
     57        """
     58        Ensure that the LiveServerTestCase serves media files.
     59        """
     60        f = self.urlopen('/media/example_media_file.txt')
     61        self.assertEquals(f.read(), 'example media file\n')
     62
     63TestViews = override_settings(**TEST_SETTINGS)(TestViews)
     64
     65
     66class TestDatabase(LiveServerTestBase):
     67
     68    def test_fixtures_loaded(self):
     69        """
     70        Ensure that fixtures are properly loaded and visible to the
     71        live server thread.
     72        """
     73        import pdb; pdb.set_trace()
     74        f = self.urlopen('/model_view/')
     75        self.assertEquals(f.read().splitlines(), ['jane', 'robert'])
     76
     77    def test_database_writes(self):
     78        """
     79        Ensure that data written to the database by a view can be read.
     80        """
     81        self.urlopen('/create_model_instance/')
     82        names = [person.name for person in Person.objects.all()]
     83        self.assertEquals(names, ['jane', 'robert', 'emily'])
  • new file tests/regressiontests/live_server/urls.py

    diff --git a/tests/regressiontests/live_server/urls.py b/tests/regressiontests/live_server/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/live_server/views.py

    diff --git a/tests/regressiontests/live_server/views.py b/tests/regressiontests/live_server/views.py
    new file mode 100644
    index 0000000..bfc7513
    - +  
     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    import pdb; pdb.set_trace()
     12    return HttpResponse('\n'.join([person.name for person in people]))
     13
     14
     15def create_model_instance(request):
     16    person = Person(name='emily')
     17    person.save()
     18    return HttpResponse('')
Back to Top