Ticket #2879: 2879.selenium-support.9.diff
File 2879.selenium-support.9.diff, 42.8 KB (added by , 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 565 565 # The name of the class to use to run the test suite 566 566 TEST_RUNNER = 'django.test.simple.DjangoTestSuiteRunner' 567 567 568 # For the live test server (e.g. used for running Selenium tests) 569 LIVE_TEST_SERVER_HOST = 'localhost' 570 LIVE_TEST_SERVER_PORT = 8081 571 568 572 ############ 569 573 # FIXTURES # 570 574 ############ -
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
- + 1 import sys 2 3 from django.test import LiveServerTestCase 4 from django.utils.importlib import import_module 5 from django.utils.unittest import SkipTest 6 7 class 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/db/__init__.py
diff --git a/django/db/__init__.py b/django/db/__init__.py index 8395468..e3fcbbd 100644
a b 1 from threading import local 1 2 from django.conf import settings 2 3 from django.core import signals 3 4 from django.core.exceptions import ImproperlyConfigured … … router = ConnectionRouter(settings.DATABASE_ROUTERS) 24 25 # by the PostgreSQL backends. 25 26 # we load all these up for backwards compatibility, you should use 26 27 # connections['default'] instead. 27 connection = connections[DEFAULT_DB_ALIAS] 28 class DefaultConnectionProxy(local): 29 """ 30 Thread-local proxy for the default connection. 31 """ 32 def __getattr__(self, item): 33 return getattr(connections[DEFAULT_DB_ALIAS], item) 34 def __setattr__(self, name, value): 35 return setattr(connections[DEFAULT_DB_ALIAS], name, value) 36 connection = DefaultConnectionProxy() 28 37 backend = load_backend(connection.settings_dict['ENGINE']) 29 38 30 39 # Register an event that closes the database connection -
django/db/backends/__init__.py
diff --git a/django/db/backends/__init__.py b/django/db/backends/__init__.py index f2bde84..0f4fc31 100644
a b try: 2 2 import thread 3 3 except ImportError: 4 4 import dummy_thread as thread 5 from threading import local6 5 from contextlib import contextmanager 7 6 8 7 from django.conf import settings … … from django.utils.importlib import import_module 13 12 from django.utils.timezone import is_aware 14 13 15 14 16 class BaseDatabaseWrapper( local):15 class BaseDatabaseWrapper(object): 17 16 """ 18 17 Represents a database connection. 19 18 """ -
django/db/utils.py
diff --git a/django/db/utils.py b/django/db/utils.py index f0c13e3..41ad6df 100644
a b 1 1 import os 2 from threading import local 2 3 3 4 from django.conf import settings 4 5 from django.core.exceptions import ImproperlyConfigured … … class ConnectionDoesNotExist(Exception): 50 51 class ConnectionHandler(object): 51 52 def __init__(self, databases): 52 53 self.databases = databases 53 self._connections = {}54 self._connections = local() 54 55 55 56 def ensure_defaults(self, alias): 56 57 """ … … class ConnectionHandler(object): 73 74 conn.setdefault(setting, None) 74 75 75 76 def __getitem__(self, alias): 76 if alias in self._connections:77 return self._connections[alias]77 if hasattr(self._connections, alias): 78 return getattr(self._connections, alias) 78 79 79 80 self.ensure_defaults(alias) 80 81 db = self.databases[alias] 81 82 backend = load_backend(db['ENGINE']) 82 83 conn = backend.DatabaseWrapper(db, alias) 83 se lf._connections[alias] = conn84 setattr(self._connections, alias, conn) 84 85 return conn 85 86 87 def __setitem__(self, key, value): 88 setattr(self._connections, key, value) 89 86 90 def __iter__(self): 87 91 return iter(self.databases) 88 92 -
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. 4 4 5 5 from django.test.client import Client, RequestFactory 6 6 from django.test.testcases import (TestCase, TransactionTestCase, 7 SimpleTestCase, skipIfDBFeature, skipUnlessDBFeature) 7 SimpleTestCase, LiveServerTestCase, skipIfDBFeature, 8 skipUnlessDBFeature) 8 9 from django.test.utils import Approximate -
django/test/testcases.py
diff --git a/django/test/testcases.py b/django/test/testcases.py index ee22ac2..c2344d4 100644
a b import sys 5 5 from functools import wraps 6 6 from urlparse import urlsplit, urlunsplit 7 7 from xml.dom.minidom import parseString, Node 8 import select 9 import socket 10 import threading 8 11 9 12 from django.conf import settings 13 from django.contrib.staticfiles.handlers import StaticFilesHandler 10 14 from django.core import mail 11 15 from django.core.exceptions import ValidationError 16 from django.core.handlers.wsgi import WSGIHandler 12 17 from django.core.management import call_command 13 18 from django.core.signals import request_started 19 from django.core.servers.basehttp import (WSGIRequestHandler, WSGIServer, 20 WSGIServerException) 14 21 from django.core.urlresolvers import clear_url_caches 15 22 from django.core.validators import EMPTY_VALUES 16 23 from django.db import (transaction, connection, connections, DEFAULT_DB_ALIAS, … … from django.test.utils import (get_warnings_state, restore_warnings_state, 23 30 override_settings) 24 31 from django.utils import simplejson, unittest as ut2 25 32 from django.utils.encoding import smart_str 33 from django.views.static import serve 26 34 27 35 __all__ = ('DocTestRunner', 'OutputChecker', 'TestCase', 'TransactionTestCase', 28 36 'SimpleTestCase', 'skipIfDBFeature', 'skipUnlessDBFeature') … … def skipUnlessDBFeature(feature): 732 740 """ 733 741 return _deferredSkip(lambda: not getattr(connection.features, feature), 734 742 "Database doesn't support feature %s" % feature) 743 744 class 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 753 class 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 819 class 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 834 class 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, connections_override=None): 840 self.address = address 841 self.port = port 842 self.is_ready = threading.Event() 843 self.error = None 844 self.connections_override = connections_override 845 super(LiveServerThread, self).__init__() 846 847 def run(self): 848 """ 849 Sets up live server and database and loops over handling http requests. 850 """ 851 if self.connections_override: 852 from django.db import connections 853 for alias, conn in self.connections_override.items(): 854 connections[alias] = conn 855 try: 856 # Instantiate and start the server 857 self.httpd = StoppableWSGIServer( 858 (self.address, self.port), QuietWSGIRequestHandler) 859 handler = StaticFilesHandler(MediaFilesHandler(WSGIHandler())) 860 self.httpd.set_app(handler) 861 self.is_ready.set() 862 self.httpd.serve_forever() 863 except WSGIServerException, e: 864 self.error = e 865 self.is_ready.set() 866 867 def join(self, timeout=None): 868 self.httpd.shutdown() 869 self.httpd.server_close() 870 super(LiveServerThread, self).join(timeout) 871 872 class LiveServerTestCase(TransactionTestCase): 873 """ 874 Does basically the same as TransactionTestCase but also launches a live 875 http server in a separate thread so that the tests may use another testing 876 framework, such as Selenium for example, instead of the built-in dummy 877 client. 878 Note that it inherits from TransactionTestCase instead of TestCase because 879 the threads do not share the same connection's cursor (unless if using 880 in-memory sqlite). 881 """ 882 883 @property 884 def live_server_url(self): 885 return 'http://%s:%s' % (settings.LIVE_TEST_SERVER_HOST, 886 settings.LIVE_TEST_SERVER_PORT) 887 888 @classmethod 889 def setUpClass(cls): 890 connections_override = {} 891 for conn in connections.all(): 892 if (conn.settings_dict['ENGINE'] == 'django.db.backends.sqlite3' 893 and conn.settings_dict['NAME'] == ':memory:'): 894 connections_override[conn.alias] = conn 895 896 # Launch the Django live server's thread 897 cls.server_thread = LiveServerThread( 898 settings.LIVE_TEST_SERVER_HOST, 899 int(settings.LIVE_TEST_SERVER_PORT), 900 connections_override) 901 cls.server_thread.start() 902 903 # Wait for the Django server to be ready 904 cls.server_thread.is_ready.wait() 905 if cls.server_thread.error: 906 raise cls.server_thread.error 907 908 super(LiveServerTestCase, cls).setUpClass() 909 910 @classmethod 911 def tearDownClass(cls): 912 # Terminate the Django server's thread 913 cls.server_thread.join() 914 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: 122 122 123 123 ./runtests.py --settings=path.to.settings i18n.TranslationTests.test_lazy_objects 124 124 125 Running the Selenium tests 126 ~~~~~~~~~~~~~~~~~~~~~~~~~~ 127 128 Some admin tests require Selenium 2 to work via a real Web browser. To allow 129 those tests to run and not be skipped, you must install the selenium_ package 130 (version > 2.13) into your Python path. 131 132 Then, run the tests normally, for example: 133 134 .. code-block:: bash 135 136 ./runtests.py --settings=test_sqlite admin_inlines 137 125 138 Running all the tests 126 139 ~~~~~~~~~~~~~~~~~~~~~ 127 140 … … dependencies: 135 148 * setuptools_ 136 149 * memcached_, plus a :ref:`supported Python binding <memcached>` 137 150 * gettext_ (:ref:`gettext_on_windows`) 151 * selenium_ 138 152 139 153 If you want to test the memcached cache backend, you'll also need to define 140 154 a :setting:`CACHES` setting that points at your memcached instance. … … associated tests will be skipped. 149 163 .. _setuptools: http://pypi.python.org/pypi/setuptools/ 150 164 .. _memcached: http://www.danga.com/memcached/ 151 165 .. _gettext: http://www.gnu.org/software/gettext/manual/gettext.html 166 .. _selenium: http://pypi.python.org/pypi/selenium 152 167 153 168 .. _contrib-apps: 154 169 -
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. 195 195 196 196 See the :ref:`cache documentation <cache_key_prefixing>` for more information. 197 197 198 .. setting:: LIVE_TEST_SERVER_HOST 199 200 LIVE_TEST_SERVER_HOST 201 ~~~~~~~~~~~~~~~~~~~~~ 202 203 Default: ``'localhost'`` 204 205 Controls the host address at which the live test server gets started when using 206 a :class:`~django.test.LiveServerTestCase`. 207 208 See also: :setting:`LIVE_TEST_SERVER_PORT` 209 210 .. setting:: LIVE_TEST_SERVER_PORT 211 212 LIVE_TEST_SERVER_PORT 213 ~~~~~~~~~~~~~~~~~~~~~ 214 215 Default: ``8081`` 216 217 Controls the port at which the live test server gets started when using 218 a :class:`~django.test.LiveServerTestCase`. 219 220 See also: :setting:`LIVE_TEST_SERVER_HOST` 221 198 222 .. setting:: CACHES-LOCATION 199 223 200 224 LOCATION -
docs/topics/testing.txt
diff --git a/docs/topics/testing.txt b/docs/topics/testing.txt index 181b4ff..f5044b3 100644
a b Some of the things you can do with the test client are: 580 580 * Test that a given request is rendered by a given Django template, with 581 581 a template context that contains certain values. 582 582 583 Note that the test client is not intended to be a replacement for Twill_,583 Note that the test client is not intended to be a replacement for Windmill_, 584 584 Selenium_, or other "in-browser" frameworks. Django's test client has 585 585 a different focus. In short: 586 586 587 587 * Use Django's test client to establish that the correct view is being 588 588 called and that the view is collecting the correct context data. 589 589 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. 592 594 593 595 A comprehensive test suite should use a combination of both test types. 594 596 595 .. _Twill: http://twill.idyll.org/596 .. _Selenium: http://seleniumhq.org/597 598 597 Overview and a quick example 599 598 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 600 599 … … set up, execute and tear down the test suite. 1830 1829 those options will be added to the list of command-line options that 1831 1830 the :djadmin:`test` command can use. 1832 1831 1832 Live 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 1843 live Django server in the background on setup, and shuts it down on teardown. 1844 This allows to use other automated test clients than the 1845 :ref:`Django dummy client <test-client>` such as, for example, the Selenium_ or 1846 Windmill_ clients, to execute a series of functional tests inside a browser and 1847 simulate a real user's actions. 1848 1849 You may control which host and port the live server will run at with 1850 respectively the :setting:`LIVE_TEST_SERVER_HOST` and 1851 :setting:`LIVE_TEST_SERVER_PORT` settings. The full server url can then be 1852 accessed during the tests with ``self.live_server_url``. 1853 1854 To demonstrate how to use ``LiveServerTestCase``, let's write a simple Selenium 1855 test. First of all, you need to install the `selenium package`_ into your 1856 Python path: 1857 1858 .. code-block:: bash 1859 1860 pip install selenium 1861 1862 Then, 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 1889 Finally, you may run the test as follows: 1890 1891 .. code-block:: bash 1892 1893 ./manage.py test myapp.MySeleniumTests.test_login 1894 1895 This example will automatically open Firefox then go to the login page, enter 1896 the credentials and press the "Log in" button. Selenium offers other drivers in 1897 case you do not have Firefox installed or wish to use another browser. The 1898 example above is just a tiny fraction of what the Selenium client can do; check 1899 out 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/ 1906 1907 .. note:: 1908 1909 When running tests with an in-memory SQLite database, the same database 1910 connection needs to be shared between the thread in which the tested code 1911 is run and the thread in which the test case is run. By default, pysqlite 1912 forbids connections being shared between multiple threads. To allow this 1913 behavior you need to set the ``check_same_thread`` option in the 1914 :setting:`DATABASES` setting to ``False``. 1915 1916 .. code-block:: python 1917 1918 DATABASES = { 1919 'default': { 1920 'ENGINE': 'django.db.backends.sqlite3', 1921 'OPTIONS': { 1922 'check_same_thread': False, 1923 } 1924 } 1925 } 1833 1926 1834 1927 Attributes 1835 1928 ~~~~~~~~~~ 1836 1929 1837 1838 1930 .. attribute:: DjangoTestSuiteRunner.option_list 1839 1931 1840 1932 .. 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): 109 109 model = SottoCapo 110 110 111 111 112 class ProfileInline(admin.TabularInline): 113 model = Profile 114 extra = 1 115 112 116 site.register(TitleCollection, inlines=[TitleInline]) 113 117 # Test bug #12561 and #12778 114 118 # only ModelAdmin media … … site.register(Fashionista, inlines=[InlineWeakness]) 124 128 site.register(Holder4, Holder4Admin) 125 129 site.register(Author, AuthorAdmin) 126 130 site.register(CapoFamiglia, inlines=[ConsigliereInline, SottoCapoInline]) 131 site.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): 136 136 class SottoCapo(models.Model): 137 137 name = models.CharField(max_length=100) 138 138 capo_famiglia = models.ForeignKey(CapoFamiglia, related_name='+') 139 140 # Other models 141 142 class ProfileCollection(models.Model): 143 pass 144 145 class 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..7b417d8 100644
a b 1 1 from __future__ import absolute_import 2 2 3 from django.contrib.admin.tests import AdminSeleniumWebDriverTestCase 3 4 from django.contrib.admin.helpers import InlineAdminForm 4 5 from django.contrib.auth.models import User, Permission 5 6 from django.contrib.contenttypes.models import ContentType … … from django.test import TestCase 8 9 # local test models 9 10 from .admin import InnerInline 10 11 from .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) 12 14 13 15 14 16 class TestInline(TestCase): … … class TestInlinePermissions(TestCase): 380 382 self.assertContains(response, 'value="4" id="id_inner2_set-TOTAL_FORMS"') 381 383 self.assertContains(response, '<input type="hidden" name="inner2_set-0-id" value="%i"' % self.inner2_id) 382 384 self.assertContains(response, 'id="id_inner2_set-0-DELETE"') 385 386 class 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) -
tests/regressiontests/admin_widgets/tests.py
diff --git a/tests/regressiontests/admin_widgets/tests.py b/tests/regressiontests/admin_widgets/tests.py index 37fa7bc..e28df32 100644
a b from django import forms 7 7 from django.conf import settings 8 8 from django.contrib import admin 9 9 from django.contrib.admin import widgets 10 from django.contrib.admin.tests import AdminSeleniumWebDriverTestCase 10 11 from django.core.files.storage import default_storage 11 12 from django.core.files.uploadedfile import SimpleUploadedFile 12 13 from django.db.models import DateField … … class RelatedFieldWidgetWrapperTests(DjangoTestCase): 407 408 # Used to fail with a name error. 408 409 w = widgets.RelatedFieldWidgetWrapper(w, rel, widget_admin_site) 409 410 self.assertFalse(w.can_add_related) 411 412 413 class 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') -
tests/regressiontests/backends/tests.py
diff --git a/tests/regressiontests/backends/tests.py b/tests/regressiontests/backends/tests.py index 936f010..be83a9a 100644
a b 3 3 from __future__ import with_statement, absolute_import 4 4 5 5 import datetime 6 import threading 6 7 7 8 from django.conf import settings 8 9 from django.core.management.color import no_style … … class FkConstraintsTests(TransactionTestCase): 446 447 connection.check_constraints() 447 448 finally: 448 449 transaction.rollback() 450 451 class ThreadTests(TestCase): 452 453 def test_default_connection_thread_local(self): 454 """ 455 Ensure that the default connection (i.e. django.db.connection) is 456 different for each thread. 457 Refs #17258. 458 """ 459 connections_set = set() 460 connection.cursor() 461 connections_set.add(connection.connection) 462 def runner(): 463 from django.db import connection 464 connection.cursor() 465 connections_set.add(connection.connection) 466 for x in xrange(2): 467 t = threading.Thread(target=runner) 468 t.start() 469 t.join() 470 self.assertEquals(len(connections_set), 3) 471 472 def test_connections_thread_local(self): 473 """ 474 Ensure that the connections are different for each thread. 475 Refs #17258. 476 """ 477 connections_set = set() 478 for conn in connections.all(): 479 connections_set.add(conn) 480 def runner(): 481 from django.db import connections 482 for conn in connections.all(): 483 connections_set.add(conn) 484 for x in xrange(2): 485 t = threading.Thread(target=runner) 486 t.start() 487 t.join() 488 self.assertEquals(len(connections_set), 6) 489 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
- + 1 example 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 1 from django.db import models 2 3 4 class 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
- + 1 example 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. 3 3 """ 4 4 import os 5 5 from urlparse import urljoin 6 import urllib2 6 7 7 8 import django 8 9 from django.conf import settings 9 from django.test import TestCase 10 from django.test import TestCase, LiveServerTestCase 10 11 from django.core.handlers.wsgi import WSGIHandler 11 12 from django.core.servers.basehttp import AdminMediaHandler 13 from django.test.utils import override_settings 12 14 15 from .models import Person 13 16 14 17 class AdminMediaHandlerTests(TestCase): 15 18 … … class AdminMediaHandlerTests(TestCase): 68 71 continue 69 72 self.fail('URL: %s should have caused a ValueError exception.' 70 73 % url) 74 75 76 TEST_ROOT = os.path.dirname(__file__) 77 TEST_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 85 class 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 106 class 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 140 class 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
- + 1 from __future__ import absolute_import 2 3 from django.conf.urls import patterns, url 4 5 from . import views 6 7 8 urlpatterns = 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
- + 1 from django.http import HttpResponse 2 from .models import Person 3 4 5 def example_view(request): 6 return HttpResponse('example view') 7 8 9 def model_view(request): 10 people = Person.objects.all() 11 return HttpResponse('\n'.join([person.name for person in people])) 12 13 14 def create_model_instance(request): 15 person = Person(name='emily') 16 person.save() 17 return HttpResponse('') -
tests/test_sqlite.py
diff --git a/tests/test_sqlite.py b/tests/test_sqlite.py index de8bf93..5af754c 100644
a b 1 1 # This is an example test settings file for use with the Django test suite. 2 2 # 3 3 # The 'sqlite3' backend requires only the ENGINE setting (an in- 4 # memory database will be used). All other backends will require a 5 # NAME and potentially authentication information. See the 6 # following section in the docs for more information: 4 # memory database will be used) and the 'check_same_thread' option set to False 5 # so that the in-memory database is shared between threads (which is required 6 # for running Selenium tests). All other backends will require a NAME and 7 # potentially authentication information. See the following section in the docs 8 # for more information: 7 9 # 8 # http ://docs.djangoproject.com/en/dev/internals/contributing/#unit-tests10 # https://docs.djangoproject.com/en/dev/internals/contributing/writing-code/unit-tests/ 9 11 # 10 12 # The different databases that Django supports behave differently in certain 11 13 # situations, so it is recommended to run the test suite against as many … … 14 16 15 17 DATABASES = { 16 18 'default': { 17 'ENGINE': 'django.db.backends.sqlite3' 19 'ENGINE': 'django.db.backends.sqlite3', 20 'OPTIONS': { 21 'check_same_thread': False, 22 }, 18 23 }, 19 24 'other': { 20 25 'ENGINE': 'django.db.backends.sqlite3', 26 'OPTIONS': { 27 'check_same_thread': False, 28 }, 21 29 } 22 30 }