Ticket #2879: 2879.selenium-support.3.diff
File 2879.selenium-support.3.diff, 24.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..0ed3773 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_ADDRESS = 'localhost' 565 LIVE_TEST_SERVER_PORT = 8080 566 567 # For Selenium 568 SELENIUM_SERVER_ADDRESS = 'localhost' 569 SELENIUM_SERVER_PORT = 4444 570 SELENIUM_BROWSER = '*firefox' 571 563 572 ############ 564 573 # FIXTURES # 565 574 ############ -
django/test/testcases.py
diff --git a/django/test/testcases.py b/django/test/testcases.py index ee22ac2..30eab11 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_ADDRESS, 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_ADDRESS, 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_ADDRESS, settings.LIVE_TEST_SERVER_PORT) 920 self.selenium = selenium( 921 settings.SELENIUM_SERVER_ADDRESS, 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_ADDRESS, 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..87a7d7f 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 the 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 You may also run the tests headless (i.e. with a virtual display). On Linux: 138 139 Xvfb :99 -ac & && DISPLAY=:99 java -jar selenium-server-standalone-2.12.0.jar 140 141 *TODO: Running headless on the Mac and Windows...* 142 143 Then, run the tests normally, for example: 144 145 .. code-block:: bash 146 147 ./runtests.py --settings=test_sqlite admin_inlines 148 125 149 Running all the tests 126 150 ~~~~~~~~~~~~~~~~~~~~~ 127 151 … … dependencies: 135 159 * setuptools_ 136 160 * memcached_, plus a :ref:`supported Python binding <memcached>` 137 161 * gettext_ (:ref:`gettext_on_windows`) 162 * selenium_ plus the `Selenium server (>2.12.0)`_ 138 163 139 164 If you want to test the memcached cache backend, you'll also need to define 140 165 a :setting:`CACHES` setting that points at your memcached instance. … … associated tests will be skipped. 149 174 .. _setuptools: http://pypi.python.org/pypi/setuptools/ 150 175 .. _memcached: http://www.danga.com/memcached/ 151 176 .. _gettext: http://www.gnu.org/software/gettext/manual/gettext.html 177 .. _selenium: http://pypi.python.org/pypi/selenium 178 .. _Selenium server (>2.12.0): http://seleniumhq.org/download/ 152 179 153 180 .. _contrib-apps: 154 181 -
docs/ref/settings.txt
diff --git a/docs/ref/settings.txt b/docs/ref/settings.txt index 20366e3..5fed888 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 LIVE_TEST_SERVER_ADDRESS 199 ~~~~~~~~~~~~~~~~~~~~~~~~ 200 201 Default: ``'localhost'`` 202 203 TODO: Description... 204 205 LIVE_TEST_SERVER_PORT 206 ~~~~~~~~~~~~~~~~~~~~~ 207 208 Default: ``8080`` 209 210 TODO: Description... 211 198 212 .. setting:: CACHES-LOCATION 199 213 200 214 LOCATION … … default port. Not used with SQLite. 498 512 499 513 .. setting:: USER 500 514 515 SELENIUM_SERVER_ADDRESS 516 ~~~~~~~~~~~~~~~~~~~~~~~ 517 518 Default: ``localhost`` 519 520 Address where the Selenium server can be accessed. 521 522 SELENIUM_SERVER_PORT 523 ~~~~~~~~~~~~~~~~~~~~ 524 525 Default: ``4444`` 526 527 Port where the Selenium server can be accessed. 528 529 SELENIUM_BROWSER 530 ~~~~~~~~~~~~~~~~ 531 532 Default: ``'*firefox'`` 533 534 Browser to be used when running Selenium tests. Note that the prefixing star 535 ('``*``') is required. Possible values include: 536 537 * ``'*firefox'`` 538 * ``'*googlechrome'`` 539 * ``'*safari'`` 540 * ``'*mock'`` 541 * ``'*firefoxproxy'`` 542 * ``'*pifirefox'`` 543 * ``'*chrome'`` 544 * ``'*iexploreproxy'`` 545 * ``'*iexplore'`` 546 * ``'*safariproxy'`` 547 * ``'*konqueror'`` 548 * ``'*firefox2'`` 549 * ``'*firefox3'`` 550 * ``'*firefoxchrome'`` 551 * ``'*piiexplore'`` 552 * ``'*opera'`` 553 * ``'*iehta'`` 554 * ``'*custom'`` 555 501 556 USER 502 557 ~~~~ 503 558 -
docs/topics/testing.txt
diff --git a/docs/topics/testing.txt b/docs/topics/testing.txt index dc5bf7e..47638f3 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.testcases 1835 1836 .. versionadded::1.4 1837 1838 .. class:: LiveServerTestCase() 1839 1840 **This is a stub** 1841 1842 ``LiveServerTestCase`` does basically the same as ``TestCase`` but also 1843 launches a live http server in a separate thread so that the tests may use 1844 another testing framework, such as Selenium_ or Windmill_ for example, instead 1845 of the built-in :ref:`dummy client <test-client>`. 1846 1847 Selenium 1848 -------- 1849 1850 .. versionadded::1.4 1851 1852 .. class:: SeleniumTestCase() 1853 1854 **This is a stub** 1855 1856 Django provides out-of-the box support for Selenium_, as Django itself uses it 1857 in its own test suite for the admin. 1858 1859 TODO: 1860 1861 * settings 1862 * requirements 1863 * code sample 1864 1865 .. _Windmill: http://www.getwindmill.com/ 1866 .. _Selenium: http://seleniumhq.org/ 1831 1867 1832 1868 Attributes 1833 1869 ~~~~~~~~~~ -
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