summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorJulien Phalip <jphalip@gmail.com>2011-12-22 08:33:58 +0000
committerJulien Phalip <jphalip@gmail.com>2011-12-22 08:33:58 +0000
commit2f02a05ffb45be68b4164b4785ff1826833150a3 (patch)
treed51f7454aeb97a5c35b3045d5d5413691aaf1d00 /tests
parent45e3dff5ac697f16829697bc2a899eaeac8986ea (diff)
Fixed #2879 -- Added support for the integration with Selenium and other in-browser testing frameworks. Also added the first Selenium tests for `contrib.admin`. Many thanks to everyone for their contributions and feedback: Mikeal Rogers, Dirk Datzert, mir, Simon G., Almad, Russell Keith-Magee, Denis Golomazov, devin, robertrv, andrewbadr, Idan Gazit, voidspace, Tom Christie, hjwp2, Adam Nelson, Jannis Leidel, Anssi Kääriäinen, Preston Holmes, Bruno Renié and Jacob Kaplan-Moss.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@17241 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Diffstat (limited to 'tests')
-rw-r--r--tests/regressiontests/admin_inlines/admin.py5
-rw-r--r--tests/regressiontests/admin_inlines/models.py10
-rw-r--r--tests/regressiontests/admin_inlines/tests.py106
-rw-r--r--tests/regressiontests/admin_scripts/tests.py45
-rw-r--r--tests/regressiontests/admin_widgets/tests.py50
-rw-r--r--tests/regressiontests/servers/fixtures/testdata.json16
-rw-r--r--tests/regressiontests/servers/media/example_media_file.txt1
-rw-r--r--tests/regressiontests/servers/models.py5
-rw-r--r--tests/regressiontests/servers/static/example_static_file.txt1
-rw-r--r--tests/regressiontests/servers/tests.py151
-rw-r--r--tests/regressiontests/servers/urls.py12
-rw-r--r--tests/regressiontests/servers/views.py17
-rwxr-xr-xtests/runtests.py62
13 files changed, 461 insertions, 20 deletions
diff --git a/tests/regressiontests/admin_inlines/admin.py b/tests/regressiontests/admin_inlines/admin.py
index 4edd361d09..508f30223f 100644
--- a/tests/regressiontests/admin_inlines/admin.py
+++ b/tests/regressiontests/admin_inlines/admin.py
@@ -109,6 +109,10 @@ class SottoCapoInline(admin.TabularInline):
model = SottoCapo
+class ProfileInline(admin.TabularInline):
+ model = Profile
+ extra = 1
+
site.register(TitleCollection, inlines=[TitleInline])
# Test bug #12561 and #12778
# only ModelAdmin media
@@ -124,3 +128,4 @@ site.register(Fashionista, inlines=[InlineWeakness])
site.register(Holder4, Holder4Admin)
site.register(Author, AuthorAdmin)
site.register(CapoFamiglia, inlines=[ConsigliereInline, SottoCapoInline])
+site.register(ProfileCollection, inlines=[ProfileInline]) \ No newline at end of file
diff --git a/tests/regressiontests/admin_inlines/models.py b/tests/regressiontests/admin_inlines/models.py
index 748280d8ab..f2add00288 100644
--- a/tests/regressiontests/admin_inlines/models.py
+++ b/tests/regressiontests/admin_inlines/models.py
@@ -136,3 +136,13 @@ class Consigliere(models.Model):
class SottoCapo(models.Model):
name = models.CharField(max_length=100)
capo_famiglia = models.ForeignKey(CapoFamiglia, related_name='+')
+
+# Other models
+
+class ProfileCollection(models.Model):
+ pass
+
+class Profile(models.Model):
+ collection = models.ForeignKey(ProfileCollection, blank=True, null=True)
+ first_name = models.CharField(max_length=100)
+ last_name = models.CharField(max_length=100) \ No newline at end of file
diff --git a/tests/regressiontests/admin_inlines/tests.py b/tests/regressiontests/admin_inlines/tests.py
index c2e3bbc397..7b417d8995 100644
--- a/tests/regressiontests/admin_inlines/tests.py
+++ b/tests/regressiontests/admin_inlines/tests.py
@@ -1,5 +1,6 @@
from __future__ import absolute_import
+from django.contrib.admin.tests import AdminSeleniumWebDriverTestCase
from django.contrib.admin.helpers import InlineAdminForm
from django.contrib.auth.models import User, Permission
from django.contrib.contenttypes.models import ContentType
@@ -8,7 +9,8 @@ from django.test import TestCase
# local test models
from .admin import InnerInline
from .models import (Holder, Inner, Holder2, Inner2, Holder3, Inner3, Person,
- OutfitItem, Fashionista, Teacher, Parent, Child, Author, Book)
+ OutfitItem, Fashionista, Teacher, Parent, Child, Author, Book, Profile,
+ ProfileCollection)
class TestInline(TestCase):
@@ -380,3 +382,105 @@ class TestInlinePermissions(TestCase):
self.assertContains(response, 'value="4" id="id_inner2_set-TOTAL_FORMS"')
self.assertContains(response, '<input type="hidden" name="inner2_set-0-id" value="%i"' % self.inner2_id)
self.assertContains(response, 'id="id_inner2_set-0-DELETE"')
+
+class SeleniumFirefoxTests(AdminSeleniumWebDriverTestCase):
+ webdriver_class = 'selenium.webdriver.firefox.webdriver.WebDriver'
+ fixtures = ['admin-views-users.xml']
+ urls = "regressiontests.admin_inlines.urls"
+
+ def test_add_inlines(self):
+ """
+ Ensure that the "Add another XXX" link correctly adds items to the
+ inline form.
+ """
+ self.admin_login(username='super', password='secret')
+ self.selenium.get('%s%s' % (self.live_server_url,
+ '/admin/admin_inlines/profilecollection/add/'))
+
+ # Check that there's only one inline to start with and that it has the
+ # correct ID.
+ self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector(
+ '#profile_set-group table tr.dynamic-profile_set')), 1)
+ self.failUnlessEqual(self.selenium.find_element_by_css_selector(
+ '.dynamic-profile_set:nth-of-type(1)').get_attribute('id'),
+ 'profile_set-0')
+ self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector(
+ 'form#profilecollection_form tr.dynamic-profile_set#profile_set-0 input[name=profile_set-0-first_name]')), 1)
+ self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector(
+ 'form#profilecollection_form tr.dynamic-profile_set#profile_set-0 input[name=profile_set-0-last_name]')), 1)
+
+ # Add an inline
+ self.selenium.find_element_by_link_text('Add another Profile').click()
+
+ # Check that the inline has been added, that it has the right id, and
+ # that it contains the right fields.
+ self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector(
+ '#profile_set-group table tr.dynamic-profile_set')), 2)
+ self.failUnlessEqual(self.selenium.find_element_by_css_selector(
+ '.dynamic-profile_set:nth-of-type(2)').get_attribute('id'), 'profile_set-1')
+ self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector(
+ 'form#profilecollection_form tr.dynamic-profile_set#profile_set-1 input[name=profile_set-1-first_name]')), 1)
+ self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector(
+ 'form#profilecollection_form tr.dynamic-profile_set#profile_set-1 input[name=profile_set-1-last_name]')), 1)
+
+ # Let's add another one to be sure
+ self.selenium.find_element_by_link_text('Add another Profile').click()
+ self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector(
+ '#profile_set-group table tr.dynamic-profile_set')), 3)
+ self.failUnlessEqual(self.selenium.find_element_by_css_selector(
+ '.dynamic-profile_set:nth-of-type(3)').get_attribute('id'), 'profile_set-2')
+ self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector(
+ 'form#profilecollection_form tr.dynamic-profile_set#profile_set-2 input[name=profile_set-2-first_name]')), 1)
+ self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector(
+ 'form#profilecollection_form tr.dynamic-profile_set#profile_set-2 input[name=profile_set-2-last_name]')), 1)
+
+ # Enter some data and click 'Save'
+ self.selenium.find_element_by_name('profile_set-0-first_name').send_keys('0 first name 1')
+ self.selenium.find_element_by_name('profile_set-0-last_name').send_keys('0 last name 2')
+ self.selenium.find_element_by_name('profile_set-1-first_name').send_keys('1 first name 1')
+ self.selenium.find_element_by_name('profile_set-1-last_name').send_keys('1 last name 2')
+ self.selenium.find_element_by_name('profile_set-2-first_name').send_keys('2 first name 1')
+ self.selenium.find_element_by_name('profile_set-2-last_name').send_keys('2 last name 2')
+ self.selenium.find_element_by_xpath('//input[@value="Save"]').click()
+
+ # Check that the objects have been created in the database
+ self.assertEqual(ProfileCollection.objects.all().count(), 1)
+ self.assertEqual(Profile.objects.all().count(), 3)
+
+ def test_delete_inlines(self):
+ self.admin_login(username='super', password='secret')
+ self.selenium.get('%s%s' % (self.live_server_url,
+ '/admin/admin_inlines/profilecollection/add/'))
+
+ # Add a few inlines
+ self.selenium.find_element_by_link_text('Add another Profile').click()
+ self.selenium.find_element_by_link_text('Add another Profile').click()
+ self.selenium.find_element_by_link_text('Add another Profile').click()
+ self.selenium.find_element_by_link_text('Add another Profile').click()
+ self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector(
+ '#profile_set-group table tr.dynamic-profile_set')), 5)
+ self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector(
+ 'form#profilecollection_form tr.dynamic-profile_set#profile_set-0')), 1)
+ self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector(
+ 'form#profilecollection_form tr.dynamic-profile_set#profile_set-1')), 1)
+ self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector(
+ 'form#profilecollection_form tr.dynamic-profile_set#profile_set-2')), 1)
+ self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector(
+ 'form#profilecollection_form tr.dynamic-profile_set#profile_set-3')), 1)
+ self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector(
+ 'form#profilecollection_form tr.dynamic-profile_set#profile_set-4')), 1)
+
+ # Click on a few delete buttons
+ self.selenium.find_element_by_css_selector(
+ 'form#profilecollection_form tr.dynamic-profile_set#profile_set-1 td.delete a').click()
+ self.selenium.find_element_by_css_selector(
+ 'form#profilecollection_form tr.dynamic-profile_set#profile_set-2 td.delete a').click()
+ # Verify that they're gone and that the IDs have been re-sequenced
+ self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector(
+ '#profile_set-group table tr.dynamic-profile_set')), 3)
+ self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector(
+ 'form#profilecollection_form tr.dynamic-profile_set#profile_set-0')), 1)
+ self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector(
+ 'form#profilecollection_form tr.dynamic-profile_set#profile_set-1')), 1)
+ self.failUnlessEqual(len(self.selenium.find_elements_by_css_selector(
+ 'form#profilecollection_form tr.dynamic-profile_set#profile_set-2')), 1)
diff --git a/tests/regressiontests/admin_scripts/tests.py b/tests/regressiontests/admin_scripts/tests.py
index 11beddb7e0..72d47adce1 100644
--- a/tests/regressiontests/admin_scripts/tests.py
+++ b/tests/regressiontests/admin_scripts/tests.py
@@ -13,6 +13,7 @@ import re
from django import conf, bin, get_version
from django.conf import settings
+from django.test.simple import DjangoTestSuiteRunner
from django.utils import unittest
@@ -1058,6 +1059,50 @@ class ManageValidate(AdminScriptTestCase):
self.assertOutput(out, '0 errors found')
+class CustomTestRunner(DjangoTestSuiteRunner):
+
+ def __init__(self, *args, **kwargs):
+ assert 'liveserver' not in kwargs
+ super(CustomTestRunner, self).__init__(*args, **kwargs)
+
+ def run_tests(self, test_labels, extra_tests=None, **kwargs):
+ pass
+
+class ManageTestCommand(AdminScriptTestCase):
+ def setUp(self):
+ from django.core.management.commands.test import Command as TestCommand
+ self.cmd = TestCommand()
+
+ def test_liveserver(self):
+ """
+ Ensure that the --liveserver option sets the environment variable
+ correctly.
+ Refs #2879.
+ """
+
+ # Backup original state
+ address_predefined = 'DJANGO_LIVE_TEST_SERVER_ADDRESS' in os.environ
+ old_address = os.environ.get('DJANGO_LIVE_TEST_SERVER_ADDRESS')
+
+ self.cmd.handle(verbosity=0, testrunner='regressiontests.admin_scripts.tests.CustomTestRunner')
+
+ # Original state hasn't changed
+ self.assertEqual('DJANGO_LIVE_TEST_SERVER_ADDRESS' in os.environ, address_predefined)
+ self.assertEqual(os.environ.get('DJANGO_LIVE_TEST_SERVER_ADDRESS'), old_address)
+
+ self.cmd.handle(verbosity=0, testrunner='regressiontests.admin_scripts.tests.CustomTestRunner',
+ liveserver='blah')
+
+ # Variable was correctly set
+ self.assertEqual(os.environ['DJANGO_LIVE_TEST_SERVER_ADDRESS'], 'blah')
+
+ # Restore original state
+ if address_predefined:
+ os.environ['DJANGO_LIVE_TEST_SERVER_ADDRESS'] = old_address
+ else:
+ del os.environ['DJANGO_LIVE_TEST_SERVER_ADDRESS']
+
+
class ManageRunserver(AdminScriptTestCase):
def setUp(self):
from django.core.management.commands.runserver import BaseRunserverCommand
diff --git a/tests/regressiontests/admin_widgets/tests.py b/tests/regressiontests/admin_widgets/tests.py
index 37fa7bc8ef..e28df32577 100644
--- a/tests/regressiontests/admin_widgets/tests.py
+++ b/tests/regressiontests/admin_widgets/tests.py
@@ -7,6 +7,7 @@ from django import forms
from django.conf import settings
from django.contrib import admin
from django.contrib.admin import widgets
+from django.contrib.admin.tests import AdminSeleniumWebDriverTestCase
from django.core.files.storage import default_storage
from django.core.files.uploadedfile import SimpleUploadedFile
from django.db.models import DateField
@@ -407,3 +408,52 @@ class RelatedFieldWidgetWrapperTests(DjangoTestCase):
# Used to fail with a name error.
w = widgets.RelatedFieldWidgetWrapper(w, rel, widget_admin_site)
self.assertFalse(w.can_add_related)
+
+
+class SeleniumFirefoxTests(AdminSeleniumWebDriverTestCase):
+ webdriver_class = 'selenium.webdriver.firefox.webdriver.WebDriver'
+ fixtures = ['admin-widgets-users.xml']
+ urls = "regressiontests.admin_widgets.urls"
+
+ def test_show_hide_date_time_picker_widgets(self):
+ """
+ Ensure that pressing the ESC key closes the date and time picker
+ widgets.
+ Refs #17064.
+ """
+ from selenium.webdriver.common.keys import Keys
+
+ self.admin_login(username='super', password='secret', login_url='/')
+ # Open a page that has a date and time picker widgets
+ self.selenium.get('%s%s' % (self.live_server_url,
+ '/admin_widgets/member/add/'))
+
+ # First, with the date picker widget ---------------------------------
+ # Check that the date picker is hidden
+ self.assertEqual(
+ self.get_css_value('#calendarbox0', 'display'), 'none')
+ # Click the calendar icon
+ self.selenium.find_element_by_id('calendarlink0').click()
+ # Check that the date picker is visible
+ self.assertEqual(
+ self.get_css_value('#calendarbox0', 'display'), 'block')
+ # Press the ESC key
+ self.selenium.find_element_by_tag_name('html').send_keys([Keys.ESCAPE])
+ # Check that the date picker is hidden again
+ self.assertEqual(
+ self.get_css_value('#calendarbox0', 'display'), 'none')
+
+ # Then, with the time picker widget ----------------------------------
+ # Check that the time picker is hidden
+ self.assertEqual(
+ self.get_css_value('#clockbox0', 'display'), 'none')
+ # Click the time icon
+ self.selenium.find_element_by_id('clocklink0').click()
+ # Check that the time picker is visible
+ self.assertEqual(
+ self.get_css_value('#clockbox0', 'display'), 'block')
+ # Press the ESC key
+ self.selenium.find_element_by_tag_name('html').send_keys([Keys.ESCAPE])
+ # Check that the time picker is hidden again
+ self.assertEqual(
+ self.get_css_value('#clockbox0', 'display'), 'none')
diff --git a/tests/regressiontests/servers/fixtures/testdata.json b/tests/regressiontests/servers/fixtures/testdata.json
new file mode 100644
index 0000000000..d81b2253d2
--- /dev/null
+++ b/tests/regressiontests/servers/fixtures/testdata.json
@@ -0,0 +1,16 @@
+[
+ {
+ "pk": 1,
+ "model": "servers.person",
+ "fields": {
+ "name": "jane"
+ }
+ },
+ {
+ "pk": 2,
+ "model": "servers.person",
+ "fields": {
+ "name": "robert"
+ }
+ }
+] \ No newline at end of file
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 0000000000..dd2dda94e3
--- /dev/null
+++ b/tests/regressiontests/servers/media/example_media_file.txt
@@ -0,0 +1 @@
+example media file
diff --git a/tests/regressiontests/servers/models.py b/tests/regressiontests/servers/models.py
index e69de29bb2..6e1414ae19 100644
--- a/tests/regressiontests/servers/models.py
+++ b/tests/regressiontests/servers/models.py
@@ -0,0 +1,5 @@
+from django.db import models
+
+
+class Person(models.Model):
+ name = models.CharField(max_length=256)
diff --git a/tests/regressiontests/servers/static/example_static_file.txt b/tests/regressiontests/servers/static/example_static_file.txt
new file mode 100644
index 0000000000..b5d8342212
--- /dev/null
+++ b/tests/regressiontests/servers/static/example_static_file.txt
@@ -0,0 +1 @@
+example static file
diff --git a/tests/regressiontests/servers/tests.py b/tests/regressiontests/servers/tests.py
index b9d24ba571..ff49c74b9d 100644
--- a/tests/regressiontests/servers/tests.py
+++ b/tests/regressiontests/servers/tests.py
@@ -3,13 +3,17 @@ Tests for django.core.servers.
"""
import os
from urlparse import urljoin
+import urllib2
import django
from django.conf import settings
-from django.test import TestCase
+from django.core.exceptions import ImproperlyConfigured
+from django.test import TestCase, LiveServerTestCase
from django.core.handlers.wsgi import WSGIHandler
-from django.core.servers.basehttp import AdminMediaHandler
+from django.core.servers.basehttp import AdminMediaHandler, WSGIServerException
+from django.test.utils import override_settings
+from .models import Person
class AdminMediaHandlerTests(TestCase):
@@ -68,3 +72,146 @@ class AdminMediaHandlerTests(TestCase):
continue
self.fail('URL: %s should have caused a ValueError exception.'
% url)
+
+
+TEST_ROOT = os.path.dirname(__file__)
+TEST_SETTINGS = {
+ 'MEDIA_URL': '/media/',
+ 'MEDIA_ROOT': os.path.join(TEST_ROOT, 'media'),
+ 'STATIC_URL': '/static/',
+ 'STATIC_ROOT': os.path.join(TEST_ROOT, 'static'),
+}
+
+
+class LiveServerBase(LiveServerTestCase):
+ urls = 'regressiontests.servers.urls'
+ fixtures = ['testdata.json']
+
+ @classmethod
+ def setUpClass(cls):
+ # Override settings
+ cls.settings_override = override_settings(**TEST_SETTINGS)
+ cls.settings_override.enable()
+ super(LiveServerBase, cls).setUpClass()
+
+ @classmethod
+ def tearDownClass(cls):
+ # Restore original settings
+ cls.settings_override.disable()
+ super(LiveServerBase, cls).tearDownClass()
+
+ def urlopen(self, url):
+ server_address = os.environ.get(
+ 'DJANGO_LIVE_TEST_SERVER_ADDRESS', 'localhost:8081')
+ base = 'http://%s' % server_address
+ return urllib2.urlopen(base + url)
+
+
+class LiveServerAddress(LiveServerBase):
+ """
+ Ensure that the address set in the environment variable is valid.
+ Refs #2879.
+ """
+
+ @classmethod
+ def setUpClass(cls):
+ # Backup original environment variable
+ address_predefined = 'DJANGO_LIVE_TEST_SERVER_ADDRESS' in os.environ
+ old_address = os.environ.get('DJANGO_LIVE_TEST_SERVER_ADDRESS')
+
+ # Just the host is not accepted
+ os.environ['DJANGO_LIVE_TEST_SERVER_ADDRESS'] = 'localhost'
+ try:
+ super(LiveServerAddress, cls).setUpClass()
+ raise Exception("The line above should have raised an exception")
+ except ImproperlyConfigured:
+ pass
+
+ # The host must be valid
+ os.environ['DJANGO_LIVE_TEST_SERVER_ADDRESS'] = 'blahblahblah:8081'
+ try:
+ super(LiveServerAddress, cls).setUpClass()
+ raise Exception("The line above should have raised an exception")
+ except WSGIServerException:
+ pass
+
+ # If contrib.staticfiles isn't configured properly, the exception
+ # should bubble up to the main thread.
+ old_STATIC_URL = TEST_SETTINGS['STATIC_URL']
+ TEST_SETTINGS['STATIC_URL'] = None
+ os.environ['DJANGO_LIVE_TEST_SERVER_ADDRESS'] = 'localhost:8081'
+ try:
+ super(LiveServerAddress, cls).setUpClass()
+ raise Exception("The line above should have raised an exception")
+ except ImproperlyConfigured:
+ pass
+ TEST_SETTINGS['STATIC_URL'] = old_STATIC_URL
+
+ # Restore original environment variable
+ if address_predefined:
+ os.environ['DJANGO_LIVE_TEST_SERVER_ADDRESS'] = old_address
+ else:
+ del os.environ['DJANGO_LIVE_TEST_SERVER_ADDRESS']
+
+ def test_test_test(self):
+ # Intentionally empty method so that the test is picked up by the
+ # test runner and the overriden setUpClass() method is executed.
+ pass
+
+class LiveServerViews(LiveServerBase):
+ def test_404(self):
+ """
+ Ensure that the LiveServerTestCase serves 404s.
+ Refs #2879.
+ """
+ try:
+ self.urlopen('/')
+ except urllib2.HTTPError, err:
+ self.assertEquals(err.code, 404, 'Expected 404 response')
+ else:
+ self.fail('Expected 404 response')
+
+ def test_view(self):
+ """
+ Ensure that the LiveServerTestCase serves views.
+ Refs #2879.
+ """
+ f = self.urlopen('/example_view/')
+ self.assertEquals(f.read(), 'example view')
+
+ def test_static_files(self):
+ """
+ Ensure that the LiveServerTestCase serves static files.
+ Refs #2879.
+ """
+ f = self.urlopen('/static/example_static_file.txt')
+ self.assertEquals(f.read(), 'example static file\n')
+
+ def test_media_files(self):
+ """
+ Ensure that the LiveServerTestCase serves media files.
+ Refs #2879.
+ """
+ f = self.urlopen('/media/example_media_file.txt')
+ self.assertEquals(f.read(), 'example media file\n')
+
+
+class LiveServerDatabase(LiveServerBase):
+
+ def test_fixtures_loaded(self):
+ """
+ Ensure that fixtures are properly loaded and visible to the
+ live server thread.
+ Refs #2879.
+ """
+ f = self.urlopen('/model_view/')
+ self.assertEquals(f.read().splitlines(), ['jane', 'robert'])
+
+ def test_database_writes(self):
+ """
+ Ensure that data written to the database by a view can be read.
+ Refs #2879.
+ """
+ self.urlopen('/create_model_instance/')
+ names = [person.name for person in Person.objects.all()]
+ self.assertEquals(names, ['jane', 'robert', 'emily'])
diff --git a/tests/regressiontests/servers/urls.py b/tests/regressiontests/servers/urls.py
new file mode 100644
index 0000000000..c8ca1ac7a4
--- /dev/null
+++ b/tests/regressiontests/servers/urls.py
@@ -0,0 +1,12 @@
+from __future__ import absolute_import
+
+from django.conf.urls import patterns, url
+
+from . import views
+
+
+urlpatterns = patterns('',
+ url(r'^example_view/$', views.example_view),
+ url(r'^model_view/$', views.model_view),
+ url(r'^create_model_instance/$', views.create_model_instance),
+) \ No newline at end of file
diff --git a/tests/regressiontests/servers/views.py b/tests/regressiontests/servers/views.py
new file mode 100644
index 0000000000..94a4f2d9ef
--- /dev/null
+++ b/tests/regressiontests/servers/views.py
@@ -0,0 +1,17 @@
+from django.http import HttpResponse
+from .models import Person
+
+
+def example_view(request):
+ return HttpResponse('example view')
+
+
+def model_view(request):
+ people = Person.objects.all()
+ return HttpResponse('\n'.join([person.name for person in people]))
+
+
+def create_model_instance(request):
+ person = Person(name='emily')
+ person.save()
+ return HttpResponse('') \ No newline at end of file
diff --git a/tests/runtests.py b/tests/runtests.py
index c38c4c2202..73c35a50c1 100755
--- a/tests/runtests.py
+++ b/tests/runtests.py
@@ -49,7 +49,10 @@ def geodjango(settings):
def get_test_modules():
modules = []
- for loc, dirpath in (MODEL_TESTS_DIR_NAME, MODEL_TEST_DIR), (REGRESSION_TESTS_DIR_NAME, REGRESSION_TEST_DIR), (CONTRIB_DIR_NAME, CONTRIB_DIR):
+ for loc, dirpath in (
+ (MODEL_TESTS_DIR_NAME, MODEL_TEST_DIR),
+ (REGRESSION_TESTS_DIR_NAME, REGRESSION_TEST_DIR),
+ (CONTRIB_DIR_NAME, CONTRIB_DIR)):
for f in os.listdir(dirpath):
if (f.startswith('__init__') or
f.startswith('.') or
@@ -150,7 +153,8 @@ def django_tests(verbosity, interactive, failfast, test_labels):
settings.TEST_RUNNER = 'django.test.simple.DjangoTestSuiteRunner'
TestRunner = get_runner(settings)
- test_runner = TestRunner(verbosity=verbosity, interactive=interactive, failfast=failfast)
+ test_runner = TestRunner(verbosity=verbosity, interactive=interactive,
+ failfast=failfast)
failures = test_runner.run_tests(test_labels, extra_tests=extra_tests)
teardown(state)
@@ -175,7 +179,8 @@ def bisect_tests(bisection_label, options, test_labels):
except ValueError:
pass
- subprocess_args = [sys.executable, __file__, '--settings=%s' % options.settings]
+ subprocess_args = [
+ sys.executable, __file__, '--settings=%s' % options.settings]
if options.failfast:
subprocess_args.append('--failfast')
if options.verbosity:
@@ -235,7 +240,8 @@ def paired_tests(paired_test, options, test_labels):
except ValueError:
pass
- subprocess_args = [sys.executable, __file__, '--settings=%s' % options.settings]
+ subprocess_args = [
+ sys.executable, __file__, '--settings=%s' % options.settings]
if options.failfast:
subprocess_args.append('--failfast')
if options.verbosity:
@@ -244,7 +250,8 @@ def paired_tests(paired_test, options, test_labels):
subprocess_args.append('--noinput')
for i, label in enumerate(test_labels):
- print '***** %d of %d: Check test pairing with %s' % (i+1, len(test_labels), label)
+ print '***** %d of %d: Check test pairing with %s' % (
+ i+1, len(test_labels), label)
failures = subprocess.call(subprocess_args + [label, paired_test])
if failures:
print '***** Found problem pair with',label
@@ -257,19 +264,36 @@ if __name__ == "__main__":
from optparse import OptionParser
usage = "%prog [options] [module module module ...]"
parser = OptionParser(usage=usage)
- parser.add_option('-v','--verbosity', action='store', dest='verbosity', default='1',
+ parser.add_option(
+ '-v','--verbosity', action='store', dest='verbosity', default='1',
type='choice', choices=['0', '1', '2', '3'],
- help='Verbosity level; 0=minimal output, 1=normal output, 2=all output')
- parser.add_option('--noinput', action='store_false', dest='interactive', default=True,
+ help='Verbosity level; 0=minimal output, 1=normal output, 2=all '
+ 'output')
+ parser.add_option(
+ '--noinput', action='store_false', dest='interactive', default=True,
help='Tells Django to NOT prompt the user for input of any kind.')
- parser.add_option('--failfast', action='store_true', dest='failfast', default=False,
- help='Tells Django to stop running the test suite after first failed test.')
- parser.add_option('--settings',
- help='Python path to settings module, e.g. "myproject.settings". If this isn\'t provided, the DJANGO_SETTINGS_MODULE environment variable will be used.')
- parser.add_option('--bisect', action='store', dest='bisect', default=None,
- help="Bisect the test suite to discover a test that causes a test failure when combined with the named test.")
- parser.add_option('--pair', action='store', dest='pair', default=None,
- help="Run the test suite in pairs with the named test to find problem pairs.")
+ parser.add_option(
+ '--failfast', action='store_true', dest='failfast', default=False,
+ help='Tells Django to stop running the test suite after first failed '
+ 'test.')
+ parser.add_option(
+ '--settings',
+ help='Python path to settings module, e.g. "myproject.settings". If '
+ 'this isn\'t provided, the DJANGO_SETTINGS_MODULE environment '
+ 'variable will be used.')
+ parser.add_option(
+ '--bisect', action='store', dest='bisect', default=None,
+ help='Bisect the test suite to discover a test that causes a test '
+ 'failure when combined with the named test.')
+ parser.add_option(
+ '--pair', action='store', dest='pair', default=None,
+ help='Run the test suite in pairs with the named test to find problem '
+ 'pairs.')
+ parser.add_option(
+ '--liveserver', action='store', dest='liveserver', default=None,
+ help='Overrides the default address where the live server (used with '
+ 'LiveServerTestCase) is expected to run from. The default value '
+ 'is localhost:8081.'),
options, args = parser.parse_args()
if options.settings:
os.environ['DJANGO_SETTINGS_MODULE'] = options.settings
@@ -279,11 +303,15 @@ if __name__ == "__main__":
else:
options.settings = os.environ['DJANGO_SETTINGS_MODULE']
+ if options.liveserver is not None:
+ os.environ['DJANGO_LIVE_TEST_SERVER_ADDRESS'] = options.liveserver
+
if options.bisect:
bisect_tests(options.bisect, options, args)
elif options.pair:
paired_tests(options.pair, options, args)
else:
- failures = django_tests(int(options.verbosity), options.interactive, options.failfast, args)
+ failures = django_tests(int(options.verbosity), options.interactive,
+ options.failfast, args)
if failures:
sys.exit(bool(failures))