Ticket #2879: 2879.selenium-support.4.diff
File 2879.selenium-support.4.diff, 28.6 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 6b09be2..ed72df9 100644
a b DEFAULT_EXCEPTION_REPORTER_FILTER = 'django.views.debug.SafeExceptionReporterFil 560 560 # The name of the class to use to run the test suite 561 561 TEST_RUNNER = 'django.test.simple.DjangoTestSuiteRunner' 562 562 563 # For the live test server (e.g. used for running Selenium tests) 564 LIVE_TEST_SERVER_HOST = 'localhost' 565 LIVE_TEST_SERVER_PORT = 8080 566 567 # For Selenium 568 SELENIUM_SERVER_HOST = 'localhost' 569 SELENIUM_SERVER_PORT = 4444 570 SELENIUM_BROWSER = '*firefox' 571 563 572 ############ 564 573 # FIXTURES # 565 574 ############ -
django/test/__init__.py
diff --git a/django/test/__init__.py b/django/test/__init__.py index a3a03e3..b246821 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, SeleniumTestCase, 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..aebb30a 100644
a b 1 1 from __future__ import with_statement 2 2 3 import httplib 3 4 import re 4 5 import sys 5 6 from functools import wraps 6 7 from urlparse import urlsplit, urlunsplit 7 8 from xml.dom.minidom import parseString, Node 9 import select 10 import socket 11 import threading 8 12 9 13 from django.conf import settings 14 from django.contrib.staticfiles.handlers import StaticFilesHandler 10 15 from django.core import mail 11 16 from django.core.exceptions import ValidationError 17 from django.core.handlers.wsgi import WSGIHandler 12 18 from django.core.management import call_command 13 19 from django.core.signals import request_started 20 from django.core.servers.basehttp import (WSGIRequestHandler, WSGIServer, 21 WSGIServerException) 14 22 from django.core.urlresolvers import clear_url_caches 15 23 from django.core.validators import EMPTY_VALUES 16 24 from django.db import (transaction, connection, connections, DEFAULT_DB_ALIAS, … … from django.test.utils import (get_warnings_state, restore_warnings_state, 23 31 override_settings) 24 32 from django.utils import simplejson, unittest as ut2 25 33 from django.utils.encoding import smart_str 34 from django.utils.unittest import skipUnless 26 35 27 36 __all__ = ('DocTestRunner', 'OutputChecker', 'TestCase', 'TransactionTestCase', 28 37 'SimpleTestCase', 'skipIfDBFeature', 'skipUnlessDBFeature') … … def skipUnlessDBFeature(feature): 732 741 """ 733 742 return _deferredSkip(lambda: not getattr(connection.features, feature), 734 743 "Database doesn't support feature %s" % feature) 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 def log_message(*args): 752 pass 753 754 class StoppableWSGIServer(WSGIServer): 755 """ 756 The code in this class is borrowed from the `SocketServer.BaseServer` class 757 in Python 2.6. The important functionality here is that the server is non- 758 blocking and that it can be shut down at any moment. This is made possible 759 by the server regularly polling the socket and checking if it has been 760 asked to stop. 761 Note for the future: Once Django stops supporting Python 2.5, this class 762 can be removed as `WSGIServer` will have this ability to shutdown on 763 demand. 764 """ 765 766 def __init__(self, *args, **kwargs): 767 super(StoppableWSGIServer, self).__init__(*args, **kwargs) 768 self.__is_shut_down = threading.Event() 769 self.__serving = False 770 771 def serve_forever(self, poll_interval=0.5): 772 """Handle one request at a time until shutdown. 773 774 Polls for shutdown every poll_interval seconds. 775 """ 776 self.__serving = True 777 self.__is_shut_down.clear() 778 while self.__serving: 779 r, w, e = select.select([self], [], [], poll_interval) 780 if r: 781 self._handle_request_noblock() 782 self.__is_shut_down.set() 783 784 def shutdown(self): 785 """Stops the serve_forever loop. 786 787 Blocks until the loop has finished. This must be called while 788 serve_forever() is running in another thread, or it will 789 deadlock. 790 """ 791 self.__serving = False 792 self.__is_shut_down.wait() 793 794 def handle_request(self): 795 """Handle one request, possibly blocking. 796 """ 797 fd_sets = select.select([self], [], [], None) 798 if not fd_sets[0]: 799 return 800 self._handle_request_noblock() 801 802 def _handle_request_noblock(self): 803 """Handle one request, without blocking. 804 805 I assume that select.select has returned that the socket is 806 readable before this function was called, so there should be 807 no risk of blocking in get_request(). 808 """ 809 try: 810 request, client_address = self.get_request() 811 except socket.error: 812 return 813 if self.verify_request(request, client_address): 814 try: 815 self.process_request(request, client_address) 816 except Exception: 817 self.handle_error(request, client_address) 818 self.close_request(request) 819 820 class LiveServerThread(threading.Thread): 821 """ 822 Thread for running a live http server while the tests are running. 823 """ 824 825 def __init__(self, address, port, fixtures): 826 self.address = address 827 self.port = port 828 self.fixtures = fixtures 829 self.is_ready = threading.Event() 830 self.error = None 831 super(LiveServerThread, self).__init__() 832 833 def run(self): 834 """ 835 Sets up live server and database and loops over handling http requests. 836 """ 837 try: 838 # Instantiate and start the server 839 self.httpd = StoppableWSGIServer( 840 (self.address, self.port), QuietWSGIRequestHandler) 841 handler = StaticFilesHandler(WSGIHandler()) 842 self.httpd.set_app(handler) 843 844 # If the database is in memory we must reload the data in this new 845 # thread. 846 if (settings.DATABASES['default']['ENGINE'] == 'django.db.backends.sqlite3' or 847 settings.DATABASES['default']['TEST_NAME']): 848 connection.creation.create_test_db(0) 849 # Import the fixtures into the test database 850 if hasattr(self, 'fixtures'): 851 call_command('loaddata', *self.fixtures, 852 **{'verbosity': 0}) 853 self.is_ready.set() 854 self.httpd.serve_forever() 855 except WSGIServerException, e: 856 self.error = e 857 self.is_ready.set() 858 859 def join(self, timeout=None): 860 self.httpd.shutdown() 861 self.httpd.server_close() 862 super(LiveServerThread, self).join(timeout) 863 864 class LiveServerTestCase(TestCase): 865 """ 866 Does basically the same as TestCase but also launches a live http server in 867 a separate thread so that the tests may use another testing framework, such 868 as Selenium for example, instead of the built-in dummy client. 869 """ 870 871 fixtures = [] 872 873 def setUp(self): 874 # Launch the Django live server's thread 875 self.server_thread = LiveServerThread( 876 settings.LIVE_TEST_SERVER_HOST, 877 int(settings.LIVE_TEST_SERVER_PORT), 878 fixtures=self.fixtures) 879 self.server_thread.start() 880 super(LiveServerTestCase, self).setUp() 881 882 def tearDown(self): 883 # Terminate the Django server's thread 884 self.server_thread.join() 885 super(LiveServerTestCase, self).tearDown() 886 887 try: 888 # Check if the 'selenium' package is installed 889 from selenium import selenium 890 selenium_installed = True 891 except ImportError: 892 selenium_installed = False 893 894 # Check if the Selenium server is running 895 try: 896 conn = httplib.HTTPConnection(settings.SELENIUM_SERVER_HOST, 897 settings.SELENIUM_SERVER_PORT) 898 try: 899 conn.request("GET", "/selenium-server/driver/", '', {}) 900 finally: 901 conn.close() 902 selenium_server_running = True 903 except socket.error: 904 selenium_server_running = False 905 906 class SeleniumTestCase(LiveServerTestCase): 907 """ 908 Does basically the same as TestServerTestCase but also connects to the 909 Selenium server. The selenium client is then available with 910 'self.selenium'. The requirements are to have the 'selenium' installed in 911 the python path and to have the Selenium server running. If those 912 requirements are not filled then the tests will be skipped. 913 """ 914 915 def setUp(self): 916 super(SeleniumTestCase, self).setUp() 917 # Launch the Selenium server 918 selenium_browser_url = 'http://%s:%s' % ( 919 settings.LIVE_TEST_SERVER_HOST, settings.LIVE_TEST_SERVER_PORT) 920 self.selenium = selenium( 921 settings.SELENIUM_SERVER_HOST, 922 int(settings.SELENIUM_SERVER_PORT), 923 settings.SELENIUM_BROWSER, 924 selenium_browser_url) 925 self.selenium.start() 926 # Wait for the Django server to be ready 927 self.server_thread.is_ready.wait() 928 if self.server_thread.error: 929 raise self.server_thread.error 930 931 def tearDown(self): 932 super(SeleniumTestCase, self).tearDown() 933 self.selenium.stop() 934 935 SeleniumTestCase = skipUnless(selenium_installed, 936 'The \'selenium\' package isn\'t installed')(SeleniumTestCase) 937 SeleniumTestCase = skipUnless(selenium_server_running, 938 'Can\'t connect to the Selenium server using address %s and port %s' % ( 939 settings.SELENIUM_SERVER_HOST, settings.SELENIUM_SERVER_PORT) 940 )(SeleniumTestCase) 941 No newline at end of file -
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..cec408c 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 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 into your Python path and download the `Selenium server (>2.12.0)`_. The 131 Selenium server must then be started with the following command: 132 133 .. code-block:: bash 134 135 java -jar selenium-server-standalone-2.12.0.jar 136 137 If you're using linux, you may also run the tests in headless mode (i.e. with a 138 virtual display) using with the following command instead: 139 140 .. code-block:: bash 141 142 Xvfb :99 -ac & && DISPLAY=:99 java -jar selenium-server-standalone-2.12.0.jar 143 144 Then, run the tests normally, for example: 145 146 .. code-block:: bash 147 148 ./runtests.py --settings=test_sqlite admin_inlines 149 125 150 Running all the tests 126 151 ~~~~~~~~~~~~~~~~~~~~~ 127 152 … … dependencies: 135 160 * setuptools_ 136 161 * memcached_, plus a :ref:`supported Python binding <memcached>` 137 162 * gettext_ (:ref:`gettext_on_windows`) 163 * selenium_ plus the `Selenium server (>2.12.0)`_ 138 164 139 165 If you want to test the memcached cache backend, you'll also need to define 140 166 a :setting:`CACHES` setting that points at your memcached instance. … … associated tests will be skipped. 149 175 .. _setuptools: http://pypi.python.org/pypi/setuptools/ 150 176 .. _memcached: http://www.danga.com/memcached/ 151 177 .. _gettext: http://www.gnu.org/software/gettext/manual/gettext.html 178 .. _selenium: http://pypi.python.org/pypi/selenium 179 .. _Selenium server (>2.12.0): http://seleniumhq.org/download/ 152 180 153 181 .. _contrib-apps: 154 182 -
docs/ref/settings.txt
diff --git a/docs/ref/settings.txt b/docs/ref/settings.txt index 20366e3..16caf48 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: ``8080`` 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 … … Default: ``''`` (Empty string) 496 520 The port to use when connecting to the database. An empty string means the 497 521 default port. Not used with SQLite. 498 522 523 .. setting:: SELENIUM_SERVER_HOST 524 525 SELENIUM_SERVER_HOST 526 ~~~~~~~~~~~~~~~~~~~~ 527 528 Default: ``localhost`` 529 530 Host address where the Selenium server can be accessed. 531 532 .. setting:: SELENIUM_SERVER_PORT 533 534 SELENIUM_SERVER_PORT 535 ~~~~~~~~~~~~~~~~~~~~ 536 537 Default: ``4444`` 538 539 Port where the Selenium server can be accessed. 540 541 .. setting:: SELENIUM_BROWSER 542 543 SELENIUM_BROWSER 544 ~~~~~~~~~~~~~~~~ 545 546 Default: ``'*firefox'`` 547 548 Browser to be used when running Selenium tests. Note that the prefixing star 549 ('``*``') is required. Possible values include: 550 551 * ``'*firefox'`` 552 * ``'*googlechrome'`` 553 * ``'*safari'`` 554 * ``'*mock'`` 555 * ``'*firefoxproxy'`` 556 * ``'*pifirefox'`` 557 * ``'*chrome'`` 558 * ``'*iexploreproxy'`` 559 * ``'*iexplore'`` 560 * ``'*safariproxy'`` 561 * ``'*konqueror'`` 562 * ``'*firefox2'`` 563 * ``'*firefox3'`` 564 * ``'*firefoxchrome'`` 565 * ``'*piiexplore'`` 566 * ``'*opera'`` 567 * ``'*iehta'`` 568 * ``'*custom'`` 569 499 570 .. setting:: USER 500 571 501 572 USER -
docs/topics/testing.txt
diff --git a/docs/topics/testing.txt b/docs/topics/testing.txt index dc5bf7e..9b9ed1c 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 sections on 593 :class:`~django.test.testcases.LiveServerTestCase` and 594 :class:`~django.test.testcases.SeleniumTestCase`. 592 595 593 596 A comprehensive test suite should use a combination of both test types. 594 597 595 .. _Twill: http://twill.idyll.org/596 .. _Selenium: http://seleniumhq.org/597 598 598 Overview and a quick example 599 599 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 600 600 … … set up, execute and tear down the test suite. 1828 1828 those options will be added to the list of command-line options that 1829 1829 the :djadmin:`test` command can use. 1830 1830 1831 Live test server 1832 ---------------- 1833 1834 .. currentmodule:: django.test 1835 1836 .. versionadded::1.4 1837 1838 .. class:: LiveServerTestCase() 1839 1840 ``LiveServerTestCase`` does basically the same as 1841 :class:`~django.test.TestCase` with one extra thing: it launches a live Django 1842 server in the background on setup, and shuts it down on teardown. This allows 1843 to use other automated test clients than the 1844 :ref:`Django dummy client <test-client>` such as, for example, the Selenium_ or 1845 Windmill_ clients. 1846 1847 You may control which host and port the live server will run at with 1848 respectively the :setting:`LIVE_TEST_SERVER_HOST` and 1849 :setting:`LIVE_TEST_SERVER_PORT` settings. 1850 1851 Fixtures defined with the :attr:`~TestCase.fixtures` class attribute will get 1852 loaded at the beginning of each test if you require some initial data to be 1853 present. 1854 1855 See the section on :class:`SeleniumTestCase` for a concrete example of how 1856 ``LiveServerTestCase`` can be used. 1857 1858 Selenium tests 1859 -------------- 1860 1861 .. versionadded::1.4 1862 1863 .. class:: SeleniumTestCase() 1864 1865 Django provides out-of-the box support for Selenium_ tests with the 1866 ``SeleniumTestCase`` class. Django itself uses it in its own test suite for 1867 some ``contrib.admin`` tests. 1868 1869 ``SeleniumTestCase`` inherits from :class:`LiveServerTestCase`, which means 1870 that a live server is available for the duration of each test. That live server 1871 can then be accessed by the Selenium client to execute a series of functional 1872 tests inside a browser, simulating a real user's actions. 1873 1874 To get started with Selenium tests, your environment needs to satisfy a number 1875 of requirements: 1876 1877 * You must install the `selenium package`_ into your Python path: 1878 1879 .. code-block:: bash 1880 1881 pip install selenium 1882 1883 * You must download the `Selenium server (>2.12.0)`_, and then start it with 1884 the following command: 1885 1886 .. code-block:: bash 1887 1888 java -jar selenium-server-standalone-2.12.0.jar 1889 1890 If you'd like to run the selenium server at a different port than the 1891 standard one (i.e. 4444) you may do so as follows: 1892 1893 .. code-block:: bash 1894 1895 java -jar selenium-server-standalone-2.12.0.jar -port 1234 1896 1897 * If the selenium server isn't running at the standard host address or port, 1898 you need to provide the exact details using respectively the 1899 :setting:`SELENIUM_SERVER_HOST` and :setting:`SELENIUM_SERVER_PORT` 1900 settings. 1901 1902 If those requirements are not satisfied, then the tests will be skipped. 1903 1904 By the default, the tests are run in Firefox_. If you do not have Firefox 1905 installed or simply wish to run the tests in another browser, you may do so by 1906 changing the :setting:`SELENIUM_BROWSER` setting. 1907 1908 Once your environment is set up, you may start writing your Selenium tests. 1909 Here's an example of how to control the Selenium client (accessible via 1910 ``self.selenium``): 1911 1912 .. code-block:: python 1913 1914 from django.test import SeleniumTestCase 1915 1916 class MySeleniumTests(SeleniumTestCase): 1917 1918 fixtures = ['user-data.json'] 1919 1920 def test_login(self): 1921 self.selenium.open('/login/') 1922 self.selenium.type('username', username) 1923 self.selenium.type('password', password) 1924 self.selenium.click("//input[@value='Log in']") 1925 1926 This is just a tiny fraction of what the Selenium client can do. Check out the 1927 `full reference`_ for more details. 1928 1929 .. _Windmill: http://www.getwindmill.com/ 1930 .. _Selenium: http://seleniumhq.org/ 1931 .. _selenium package: http://pypi.python.org/pypi/selenium 1932 .. _Selenium server (>2.12.0): http://seleniumhq.org/download/ 1933 .. _full reference: http://selenium.googlecode.com/svn/trunk/docs/api/py/selenium/selenium.selenium.html 1934 .. _Firefox: http://www.mozilla.com/firefox/ 1831 1935 1832 1936 Attributes 1833 1937 ~~~~~~~~~~ 1834 1938 1835 1836 1939 .. attribute:: DjangoTestSuiteRunner.option_list 1837 1940 1838 1941 .. 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..942ece6 100644
a b from django.test import TestCase 7 7 8 8 # local test models 9 9 from .admin import InnerInline 10 from django.test.testcases import SeleniumTestCase 10 11 from .models import (Holder, Inner, Holder2, Inner2, Holder3, Inner3, Person, 11 12 OutfitItem, Fashionista, Teacher, Parent, Child, Author, Book) 12 13 … … class TestInlinePermissions(TestCase): 380 381 self.assertContains(response, 'value="4" id="id_inner2_set-TOTAL_FORMS"') 381 382 self.assertContains(response, '<input type="hidden" name="inner2_set-0-id" value="%i"' % self.inner2_id) 382 383 self.assertContains(response, 'id="id_inner2_set-0-DELETE"') 384 385 386 class SeleniumTests(SeleniumTestCase): 387 fixtures = ['admin-views-users.xml'] 388 urls = "regressiontests.admin_inlines.urls" 389 390 def admin_login(self, username, password): 391 """ 392 Helper function to log into the admin. 393 """ 394 self.selenium.open('/admin/') 395 self.selenium.type('username', username) 396 self.selenium.type('password', password) 397 self.selenium.click("//input[@value='Log in']") 398 self.selenium.wait_for_page_to_load(3000) 399 400 def test_add_inlines(self): 401 """ 402 Ensure that the "Add another XXX" link correctly adds items to the 403 inline form. 404 """ 405 self.admin_login(username='super', password='secret') 406 self.selenium.open('/admin/admin_inlines/titlecollection/add/') 407 408 # Check that there's only one inline to start with and that it has the 409 # correct ID. 410 self.failUnlessEqual(self.selenium.get_css_count( 411 'css=#title_set-group table tr.dynamic-title_set'), 1) 412 self.failUnless(self.selenium.get_attribute( 413 'css=.dynamic-title_set:nth-of-type(1)@id'), 'title_set-0') 414 self.failUnless(self.selenium.is_element_present( 415 'css=form#titlecollection_form tr.dynamic-title_set#title_set-0 input[name=title_set-0-title1]')) 416 self.failUnless(self.selenium.is_element_present( 417 'css=form#titlecollection_form tr.dynamic-title_set#title_set-0 input[name=title_set-0-title2]')) 418 419 # Add an inline 420 self.selenium.click("link=Add another Title") 421 422 # Check that the inline has been added, that it has the right id, and 423 # that it contains the right fields. 424 self.failUnlessEqual(self.selenium.get_css_count( 425 'css=#title_set-group table tr.dynamic-title_set'), 2) 426 self.failUnless(self.selenium.get_attribute( 427 'css=.dynamic-title_set:nth-of-type(2)@id'), 'title_set-1') 428 self.failUnless(self.selenium.is_element_present( 429 'css=form#titlecollection_form tr.dynamic-title_set#title_set-1 input[name=title_set-1-title1]')) 430 self.failUnless(self.selenium.is_element_present( 431 'css=form#titlecollection_form tr.dynamic-title_set#title_set-1 input[name=title_set-1-title2]')) 432 433 # Let's add another one to be sure 434 self.selenium.click("link=Add another Title") 435 self.failUnlessEqual(self.selenium.get_css_count( 436 'css=#title_set-group table tr.dynamic-title_set'), 3) 437 self.failUnless(self.selenium.get_attribute( 438 'css=.dynamic-title_set:nth-of-type(3)@id'), 'title_set-2') 439 self.failUnless(self.selenium.is_element_present( 440 'css=form#titlecollection_form tr.dynamic-title_set#title_set-2 input[name=title_set-2-title1]')) 441 self.failUnless(self.selenium.is_element_present( 442 'css=form#titlecollection_form tr.dynamic-title_set#title_set-2 input[name=title_set-2-title2]')) 443 444 def test_delete_inlines(self): 445 self.admin_login(username='super', password='secret') 446 self.selenium.open('/admin/admin_inlines/titlecollection/add/') 447 448 # Add a few inlines 449 self.selenium.click("link=Add another Title") 450 self.selenium.click("link=Add another Title") 451 self.selenium.click("link=Add another Title") 452 self.selenium.click("link=Add another Title") 453 self.failUnlessEqual(self.selenium.get_css_count( 454 'css=#title_set-group table tr.dynamic-title_set'), 5) 455 self.failUnless(self.selenium.is_element_present( 456 'css=form#titlecollection_form tr.dynamic-title_set#title_set-0')) 457 self.failUnless(self.selenium.is_element_present( 458 'css=form#titlecollection_form tr.dynamic-title_set#title_set-1')) 459 self.failUnless(self.selenium.is_element_present( 460 'css=form#titlecollection_form tr.dynamic-title_set#title_set-2')) 461 self.failUnless(self.selenium.is_element_present( 462 'css=form#titlecollection_form tr.dynamic-title_set#title_set-3')) 463 self.failUnless(self.selenium.is_element_present( 464 'css=form#titlecollection_form tr.dynamic-title_set#title_set-4')) 465 466 # Click on a few delete buttons 467 self.selenium.click( 468 'css=form#titlecollection_form tr.dynamic-title_set#title_set-1 td.delete a') 469 self.selenium.click( 470 'css=form#titlecollection_form tr.dynamic-title_set#title_set-2 td.delete a') 471 # Verify that they're gone and that the IDs have been re-sequenced 472 self.failUnlessEqual(self.selenium.get_css_count( 473 'css=#title_set-group table tr.dynamic-title_set'), 3) 474 self.failUnless(self.selenium.is_element_present( 475 'css=form#titlecollection_form tr.dynamic-title_set#title_set-0')) 476 self.failUnless(self.selenium.is_element_present( 477 'css=form#titlecollection_form tr.dynamic-title_set#title_set-1')) 478 self.failUnless(self.selenium.is_element_present( 479 'css=form#titlecollection_form tr.dynamic-title_set#title_set-2')) 480 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 08a1a59..c0704d5 100644
a b from django.core.files.storage import default_storage 11 11 from django.core.files.uploadedfile import SimpleUploadedFile 12 12 from django.db.models import DateField 13 13 from django.test import TestCase as DjangoTestCase 14 from django.test.testcases import SeleniumTestCase 14 15 from django.utils import translation 15 16 from django.utils.html import conditional_escape 16 17 from django.utils.unittest import TestCase … … class RelatedFieldWidgetWrapperTests(DjangoTestCase): 372 373 # Used to fail with a name error. 373 374 w = widgets.RelatedFieldWidgetWrapper(w, rel, widget_admin_site) 374 375 self.assertFalse(w.can_add_related) 376 377 378 class SeleniumTests(SeleniumTestCase): 379 fixtures = ['admin-widgets-users.xml'] 380 urls = "regressiontests.admin_widgets.urls" 381 382 def admin_login(self, username, password): 383 """ 384 Helper function to log into the admin. 385 """ 386 self.selenium.open('/') 387 self.selenium.type('username', username) 388 self.selenium.type('password', password) 389 self.selenium.click("//input[@value='Log in']") 390 self.selenium.wait_for_page_to_load(3000) 391 392 def get_css_value(self, selector, attribute): 393 """ 394 Helper function that returns the value for the CSS attribute of an 395 DOM element specified by the given selector. 396 """ 397 return self.selenium.get_eval( 398 'selenium.browserbot.getCurrentWindow().django' 399 '.jQuery("%s").css("%s")' % (selector, attribute)) 400 401 def test_show_hide_date_time_picker_widgets(self): 402 """ 403 Ensure that pressing the ESC key closes the date and time picker 404 widgets. 405 Refs #17064. 406 """ 407 self.admin_login(username='super', password='secret') 408 # Open a page that has a date and time picker widgets 409 self.selenium.open('/admin_widgets/member/add/') 410 411 # First, with the date picker widget --------------------------------- 412 # Check that the date picker is hidden 413 self.assertEqual( 414 self.get_css_value('#calendarbox0', 'display'), 'none') 415 # Click the calendar icon 416 self.selenium.click('id=calendarlink0') 417 # Check that the date picker is visible 418 self.assertEqual( 419 self.get_css_value('#calendarbox0', 'display'), 'block') 420 # Press the ESC key 421 self.selenium.key_up('css=html', '27') 422 # Check that the date picker is hidden again 423 self.assertEqual( 424 self.get_css_value('#calendarbox0', 'display'), 'none') 425 426 # Then, with the time picker widget ---------------------------------- 427 # Check that the time picker is hidden 428 self.assertEqual( 429 self.get_css_value('#clockbox0', 'display'), 'none') 430 # Click the time icon 431 self.selenium.click('id=clocklink0') 432 # Check that the time picker is visible 433 self.assertEqual( 434 self.get_css_value('#clockbox0', 'display'), 'block') 435 # Press the ESC key 436 self.selenium.key_up('css=html', '27') 437 # Check that the time picker is hidden again 438 self.assertEqual( 439 self.get_css_value('#clockbox0', 'display'), 'none') 440 No newline at end of file