Ticket #2879: 2879.selenium-support.11.diff
File 2879.selenium-support.11.diff, 38.2 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/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..d872087 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 745 class QuietWSGIRequestHandler(WSGIRequestHandler): 746 """ 747 Just a regular WSGIRequestHandler except it doesn't log to the standard 748 output any of the requests received, so as to not clutter the output for 749 the tests' results. 750 """ 751 752 def log_message(*args): 753 pass 754 755 756 class _ImprovedEvent(threading._Event): 757 """ 758 Does the same as `threading.Event` except it overrides the wait() method 759 with some code borrowed from Python 2.7 to return the set state of the 760 event (see: http://hg.python.org/cpython/rev/b5aa8aa78c0f/). This allows 761 to know whether the wait() method exited normally or because of the 762 timeout. This class can be removed when Django supports only Python >= 2.7. 763 """ 764 765 def wait(self, timeout=None): 766 self._Event__cond.acquire() 767 try: 768 if not self._Event__flag: 769 self._Event__cond.wait(timeout) 770 return self._Event__flag 771 finally: 772 self._Event__cond.release() 773 774 775 class StoppableWSGIServer(WSGIServer): 776 """ 777 The code in this class is borrowed from the `SocketServer.BaseServer` class 778 in Python 2.6. The important functionality here is that the server is non- 779 blocking and that it can be shut down at any moment. This is made possible 780 by the server regularly polling the socket and checking if it has been 781 asked to stop. 782 Note for the future: Once Django stops supporting Python 2.6, this class 783 can be removed as `WSGIServer` will have this ability to shutdown on 784 demand and will not require the use of the _ImprovedEvent class whose code 785 is borrowed from Python 2.7. 786 """ 787 788 def __init__(self, *args, **kwargs): 789 super(StoppableWSGIServer, self).__init__(*args, **kwargs) 790 self.__is_shut_down = _ImprovedEvent() 791 self.__serving = False 792 793 def serve_forever(self, poll_interval=0.5): 794 """Handle one request at a time until shutdown. 795 796 Polls for shutdown every poll_interval seconds. 797 """ 798 self.__serving = True 799 self.__is_shut_down.clear() 800 while self.__serving: 801 r, w, e = select.select([self], [], [], poll_interval) 802 if r: 803 self._handle_request_noblock() 804 self.__is_shut_down.set() 805 806 def shutdown(self): 807 """Stops the serve_forever loop. 808 809 Blocks until the loop has finished. This must be called while 810 serve_forever() is running in another thread, or it will 811 deadlock. 812 """ 813 self.__serving = False 814 if not self.__is_shut_down.wait(2): 815 raise RuntimeError( 816 "Failed to shutdown the live test server in 2 seconds. The " 817 "server might be stuck or generating a slow response.") 818 819 def handle_request(self): 820 """Handle one request, possibly blocking. 821 """ 822 fd_sets = select.select([self], [], [], None) 823 if not fd_sets[0]: 824 return 825 self._handle_request_noblock() 826 827 def _handle_request_noblock(self): 828 """Handle one request, without blocking. 829 830 I assume that select.select has returned that the socket is 831 readable before this function was called, so there should be 832 no risk of blocking in get_request(). 833 """ 834 try: 835 request, client_address = self.get_request() 836 except socket.error: 837 return 838 if self.verify_request(request, client_address): 839 try: 840 self.process_request(request, client_address) 841 except Exception: 842 self.handle_error(request, client_address) 843 self.close_request(request) 844 845 846 class MediaFilesHandler(StaticFilesHandler): 847 """ 848 Handler for serving the media files. 849 """ 850 851 def get_base_dir(self): 852 return settings.MEDIA_ROOT 853 854 def get_base_url(self): 855 return settings.MEDIA_URL 856 857 def serve(self, request): 858 return serve(request, self.file_path(request.path), 859 document_root=self.get_base_dir()) 860 861 862 class LiveServerThread(threading.Thread): 863 """ 864 Thread for running a live http server while the tests are running. 865 """ 866 867 def __init__(self, address, port, connections_override=None): 868 self.address = address 869 self.port = port 870 self.is_ready = threading.Event() 871 self.error = None 872 self.connections_override = connections_override 873 super(LiveServerThread, self).__init__() 874 875 def run(self): 876 """ 877 Sets up live server and database and loops over handling http requests. 878 """ 879 if self.connections_override: 880 from django.db import connections 881 # Override this thread's database connections with the ones 882 # provided by the main thread. 883 for alias, conn in self.connections_override.items(): 884 connections[alias] = conn 885 try: 886 # Instantiate and start the server 887 self.httpd = StoppableWSGIServer( 888 (self.address, self.port), QuietWSGIRequestHandler) 889 handler = StaticFilesHandler(MediaFilesHandler(WSGIHandler())) 890 self.httpd.set_app(handler) 891 self.is_ready.set() 892 self.httpd.serve_forever() 893 except WSGIServerException, e: 894 self.error = e 895 self.is_ready.set() 896 897 def join(self, timeout=None): 898 self.httpd.shutdown() 899 self.httpd.server_close() 900 super(LiveServerThread, self).join(timeout) 901 902 903 class LiveServerTestCase(TransactionTestCase): 904 """ 905 Does basically the same as TransactionTestCase but also launches a live 906 http server in a separate thread so that the tests may use another testing 907 framework, such as Selenium for example, instead of the built-in dummy 908 client. 909 Note that it inherits from TransactionTestCase instead of TestCase because 910 the threads do not share the same connection (unless if using in-memory 911 sqlite) and each thread needs to commit all their transactions so that the 912 other thread can see the changes. 913 """ 914 915 @property 916 def live_server_url(self): 917 return 'http://%s:%s' % (settings.LIVE_TEST_SERVER_HOST, 918 settings.LIVE_TEST_SERVER_PORT) 919 920 @classmethod 921 def setUpClass(cls): 922 connections_override = {} 923 for conn in connections.all(): 924 # If using in-memory sqlite databases, pass the connections to 925 # the server thread. 926 if (conn.settings_dict['ENGINE'] == 'django.db.backends.sqlite3' 927 and conn.settings_dict['NAME'] == ':memory:'): 928 # Explicitly enable thread-shareability for this connection 929 conn.allow_thread_sharing = True 930 connections_override[conn.alias] = conn 931 932 # Launch the Django live server's thread 933 cls.server_thread = LiveServerThread( 934 settings.LIVE_TEST_SERVER_HOST, 935 int(settings.LIVE_TEST_SERVER_PORT), 936 connections_override) 937 cls.server_thread.daemon = True 938 cls.server_thread.start() 939 940 # Wait for the Django server to be ready 941 cls.server_thread.is_ready.wait() 942 if cls.server_thread.error: 943 raise cls.server_thread.error 944 945 super(LiveServerTestCase, cls).setUpClass() 946 947 @classmethod 948 def tearDownClass(cls): 949 # Terminate the Django server's thread 950 cls.server_thread.join() 951 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..275ee15 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, Firefox and Python >= 2.6 to work via a 129 real Web browser. To allow those tests to run and not be skipped, you must 130 install the selenium_ package (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_ (if also using Python >= 2.6) 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/releases/1.4.txt
diff --git a/docs/releases/1.4.txt b/docs/releases/1.4.txt index f614dee..d5e9cab 100644
a b before the release of Django 1.4. 40 40 What's new in Django 1.4 41 41 ======================== 42 42 43 Support for in-browser testing frameworks 44 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 45 46 Django 1.4 now includes built-in support for in-browser testing frameworks such 47 as Selenium or Windmill thanks to the :class:`django.test.LiveServerTestCase` 48 base class, allowing you to test the interactions between your site's front and 49 back ends more comprehensively. See the 50 :class:`documentation<django.test.LiveServerTestCase>` for more details and 51 concrete examples. 52 43 53 ``SELECT FOR UPDATE`` support 44 54 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 45 55 -
docs/topics/testing.txt
diff --git a/docs/topics/testing.txt b/docs/topics/testing.txt index 181b4ff..7a6efcd 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 feature: it launches a 1843 live Django server in the background on setup, and shuts it down on teardown. 1844 This allows the use of automated test clients other 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 setUpClass(self): 1874 self.selenium = WebDriver() 1875 super(MySeleniumTests, self).setUpClass() 1876 1877 def tearDownClass(self): 1878 super(MySeleniumTests, self).tearDownClass() 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 1833 1907 1834 1908 Attributes 1835 1909 ~~~~~~~~~~ 1836 1910 1837 1838 1911 .. attribute:: DjangoTestSuiteRunner.option_list 1839 1912 1840 1913 .. 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') -
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..c8ca1ac
- + 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 ) 13 No newline at end of file -
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..94a4f2d
- + 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('') 18 No newline at end of file