changeset 447:8f46ba2f1b81

For #219, rework the polls views for a better flow. Added some tests for the views.
author Brian Neal <bgneal@gmail.com>
date Sat, 25 Jun 2011 23:23:20 +0000
parents 72ef6e809f79
children f55d7f08632f
files gpp/polls/tests/__init__.py gpp/polls/tests/view_tests.py gpp/polls/urls.py gpp/polls/views.py gpp/templates/polls/latest_poll_tag.html gpp/templates/polls/poll.html gpp/templates/polls/poll_detail.html gpp/templates/polls/poll_results.html gpp/templates/polls/poll_vote.html
diffstat 9 files changed, 262 insertions(+), 114 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gpp/polls/tests/__init__.py	Sat Jun 25 23:23:20 2011 +0000
@@ -0,0 +1,1 @@
+from view_tests import *
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gpp/polls/tests/view_tests.py	Sat Jun 25 23:23:20 2011 +0000
@@ -0,0 +1,178 @@
+"""
+Tests for the views in the polls application.
+
+"""
+import datetime
+
+from django.contrib.auth.models import User
+from django.test import TestCase
+from django.core.urlresolvers import reverse
+from polls.models import Poll, Choice
+
+
+class PollHomeTest(TestCase):
+
+    def setUp(self):
+        now = datetime.datetime.now()
+        Poll.objects.all().delete()
+
+        past = now - datetime.timedelta(7)
+        future = now + datetime.timedelta(7)
+
+        Poll.objects.create(start_date=past,
+                end_date=past,
+                is_enabled=True,
+                question='Old Poll')
+
+        active = Poll.objects.create(start_date=past,
+                end_date=future,
+                is_enabled=True,
+                question='Active Poll')
+
+        Poll.objects.create(start_date=future,
+                end_date=future,
+                is_enabled=True,
+                question='Future Poll')
+
+        Poll.objects.create(start_date=past,
+                end_date=future,
+                is_enabled=False,
+                question='Disabled Poll')
+
+        choice1 = Choice.objects.create(poll=active, choice="Yes")
+        choice2 = Choice.objects.create(poll=active, choice="Yes")
+
+        self.username = 'test_user'
+        self.pw = 'password'
+        self.user = User.objects.create_user(self.username, '', self.pw)
+        self.user.save()
+        self.assertTrue(self.client.login(username=self.username,
+            password=self.pw))
+
+    def tearDown(self):
+        self.client.logout()
+
+    def testHome(self):
+        response = self.client.get(reverse('polls-main'))
+
+        old = response.context['old_polls']
+        self.assertEqual(len(old), 1)
+        if old:
+            self.assertEqual(old[0].question, 'Old Poll')
+
+        current = response.context['current_polls']
+        self.assertEqual(len(current), 1)
+        if current:
+            self.assertEqual(current[0].question, 'Active Poll')
+
+        self.assertNotContains(response, 'Future Poll')
+        self.assertNotContains(response, 'Disabled Poll')
+
+    def testFuture(self):
+        poll = Poll.objects.get(question='Future Poll')
+        response = self.client.get(reverse('polls-detail', kwargs={'poll_id':
+            poll.id}))
+        self.assertEqual(response.status_code, 404)
+
+    def testDisabled(self):
+        poll = Poll.objects.get(question='Disabled Poll')
+        response = self.client.get(reverse('polls-detail', kwargs={'poll_id':
+            poll.id}))
+        self.assertEqual(response.status_code, 404)
+
+    def testOld(self):
+        poll = Poll.objects.get(question='Old Poll')
+        response = self.client.get(reverse('polls-detail', kwargs={'poll_id':
+            poll.id}))
+        self.assertEqual(response.status_code, 200)
+
+    def testActive(self):
+        poll = Poll.objects.get(question='Active Poll')
+        response = self.client.get(reverse('polls-detail', kwargs={'poll_id':
+            poll.id}))
+        self.assertEqual(response.status_code, 200)
+
+    def testVoteOld(self):
+        poll = Poll.objects.get(question='Old Poll')
+        response = self.client.get(reverse('polls-vote', kwargs={'poll_id':
+            poll.id}), follow=True)
+
+        self.assertEqual(len(response.redirect_chain), 1)
+        if response.redirect_chain:
+            self.assertEqual(response.redirect_chain[0][0],
+                    'http://testserver' + reverse('polls-detail', kwargs={'poll_id': poll.id}))
+            self.assertEqual(response.redirect_chain[0][1], 302)
+
+        self.assertEqual(response.status_code, 200)
+
+    def testVoteActive(self):
+        poll = Poll.objects.get(question='Active Poll')
+        response = self.client.get(reverse('polls-vote', kwargs={'poll_id':
+            poll.id}), follow=True)
+
+        self.assertEqual(len(response.redirect_chain), 0)
+        self.assertEqual(response.status_code, 200)
+
+    def testVoteFuture(self):
+        poll = Poll.objects.get(question='Future Poll')
+        response = self.client.get(reverse('polls-vote', kwargs={'poll_id':
+            poll.id}), follow=True)
+
+        self.assertEqual(len(response.redirect_chain), 1)
+        if response.redirect_chain:
+            self.assertEqual(response.redirect_chain[0][0],
+                'http://testserver' + reverse('polls-detail', kwargs={'poll_id': poll.id}))
+            self.assertEqual(response.redirect_chain[0][1], 302)
+
+        self.assertEqual(response.status_code, 404)
+
+    def testVoteDisabled(self):
+        poll = Poll.objects.get(question='Disabled Poll')
+        response = self.client.get(reverse('polls-vote', kwargs={'poll_id':
+            poll.id}), follow=True)
+
+        self.assertEqual(response.status_code, 404)
+
+    def testVoteActivePost(self):
+        self._voteTest()
+
+    def testDeleteVote(self):
+        poll = self._voteTest()
+
+        response = self.client.post(
+                reverse('polls-delete_vote'),
+                {'poll_id': poll.id},
+                follow=True)
+
+        self.assertEqual(len(response.redirect_chain), 1)
+        if response.redirect_chain:
+            self.assertEqual(response.redirect_chain[0][0],
+                'http://testserver' + reverse('polls-detail', kwargs={'poll_id': poll.id}))
+            self.assertEqual(response.redirect_chain[0][1], 302)
+
+        self.assertEqual(response.status_code, 200)
+
+        choice = Choice.objects.get(pk=1)
+        self.assertEqual(choice.votes, 0)
+        self.assertTrue(self.user not in choice.voters.all())
+
+    def _voteTest(self):
+        poll = Poll.objects.get(question='Active Poll')
+        response = self.client.post(
+                reverse('polls-vote', kwargs={'poll_id': poll.id}),
+                {'choices': 1},
+                follow=True)
+
+        self.assertEqual(len(response.redirect_chain), 1)
+        if response.redirect_chain:
+            self.assertEqual(response.redirect_chain[0][0],
+                'http://testserver' + reverse('polls-detail', kwargs={'poll_id': poll.id}))
+            self.assertEqual(response.redirect_chain[0][1], 302)
+
+        self.assertEqual(response.status_code, 200)
+
+        choice = Choice.objects.get(pk=1)
+        self.assertEqual(choice.votes, 1)
+        self.assertTrue(self.user in choice.voters.all())
+
+        return poll
--- a/gpp/polls/urls.py	Mon Jun 13 02:19:13 2011 +0000
+++ b/gpp/polls/urls.py	Sat Jun 25 23:23:20 2011 +0000
@@ -4,7 +4,6 @@
 urlpatterns = patterns('polls.views',
     url(r'^$', 'poll_index', name='polls-main'),
     url(r'^(?P<poll_id>\d+)/$', 'poll_detail', name='polls-detail'),
-    url(r'^(?P<poll_id>\d+)/results/$', 'poll_results', name='polls-results'),
     url(r'^(?P<poll_id>\d+)/vote/$', 'poll_vote', name='polls-vote'),
     url(r'^delete_vote/$', 'poll_delete_vote', name='polls-delete_vote'),
 )
--- a/gpp/polls/views.py	Mon Jun 13 02:19:13 2011 +0000
+++ b/gpp/polls/views.py	Sat Jun 25 23:23:20 2011 +0000
@@ -2,6 +2,8 @@
 Views for the polls application.
 
 """
+import datetime
+
 from django.shortcuts import render
 from django.contrib.auth.decorators import login_required
 from django.shortcuts import get_object_or_404
@@ -19,8 +21,8 @@
 
 def get_user_choice(user, poll):
     """
-    Return the Choice object the give user voted for from the given poll,
-    or None of no vote has been recorded (or the user is not authenticated.
+    Return the Choice object the given user voted for from the given poll,
+    or None if no vote has been recorded (or the user is not authenticated.
 
     """
     user_choice = None
@@ -45,11 +47,15 @@
 
 def poll_detail(request, poll_id):
     poll = get_object_or_404(Poll, pk=poll_id)
-    if not poll.is_enabled:
+    if not poll.is_enabled or poll.start_date > datetime.datetime.now():
         raise Http404
 
-    return render(request, 'polls/poll.html', {
+    total_votes, choices = poll.results()
+
+    return render(request, 'polls/poll_detail.html', {
         'poll': poll,
+        'total_votes': total_votes,
+        'choices': choices,
         'user_choice': get_user_choice(request.user, poll),
         })
 
@@ -61,15 +67,18 @@
     if not poll.is_enabled:
         raise Http404
     if not poll.is_open():
-        return HttpResponseRedirect(reverse('polls-results', args=[poll_id]))
+        return HttpResponseRedirect(reverse('polls-detail',
+                                            kwargs={'poll_id': poll_id}))
 
     user_choice = get_user_choice(request.user, poll)
 
     if request.method == "POST":
-        vote_form = VoteForm(poll, request.POST, user=request.user, user_choice=user_choice)
+        vote_form = VoteForm(poll, request.POST, user=request.user,
+                             user_choice=user_choice)
         if vote_form.is_valid():
             vote_form.save()
-            return HttpResponseRedirect(reverse('polls-results', args=[poll_id]))
+            return HttpResponseRedirect(reverse('polls-detail',
+                                                kwargs={'poll_id': poll_id}))
     else:
         vote_form = VoteForm(poll)
 
@@ -81,18 +90,6 @@
 
 #######################################################################
 
-def poll_results(request, poll_id):
-    poll = get_object_or_404(Poll, pk=poll_id)
-    total_votes, choices = poll.results()
-    return render(request, 'polls/poll_results.html', {
-        'poll': poll,
-        'total_votes': total_votes,
-        'choices': choices,
-        'user_choice': get_user_choice(request.user, poll),
-        })
-
-#######################################################################
-
 @require_POST
 @login_required
 def poll_delete_vote(request):
@@ -102,4 +99,4 @@
         Choice.objects.filter(id=user_choice.id).update(votes=F('votes') - 1)
         user_choice.voters.remove(request.user)
 
-    return HttpResponseRedirect(reverse('polls-results', args=[poll.id]))
+    return HttpResponseRedirect(reverse('polls-detail', kwargs={'poll_id': poll.id}))
--- a/gpp/templates/polls/latest_poll_tag.html	Mon Jun 13 02:19:13 2011 +0000
+++ b/gpp/templates/polls/latest_poll_tag.html	Sat Jun 25 23:23:20 2011 +0000
@@ -8,7 +8,7 @@
          <li>{{ choice.choice }}</li>
       {% endfor %}
    </ul>
-   <p>Go <a href="{% url 'polls-detail' poll_id=poll.id %}">see the results and vote</a>
+   <p>Go <a href="{{ poll.get_absolute_url }}">see the results and vote</a>
    or check out <a href="{% url 'polls-main' %}">more polls</a>.</p>
 </div>
 {% endif %}
--- a/gpp/templates/polls/poll.html	Mon Jun 13 02:19:13 2011 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,36 +0,0 @@
-{% extends 'base.html' %}
-{% load url from future %}
-{% load comment_tags %}
-{% block title %}Polls: {{ poll.question }}{% endblock %}
-{% block content %}
-<h2>Poll</h2>
-<h3>{{ poll.question }}</h3>
-<ul class="poll-detail">
-{% for choice in poll.choice_set.all %}
-   <li>{{ choice.choice }}</li>
-{% endfor %}
-</ul>
-{% if user_choice %}
-<p>You voted for &quot;{{ user_choice.choice }}&quot;.</p>
-{% endif %}
-{% get_comment_count for poll as comment_count %}
-<p>
-This poll has {{ poll.total_votes }} vote{{ poll.total_votes|pluralize }} and 
-<a href="{% url 'polls.views.poll_results' poll.id %}">{{ comment_count }} comment{{ comment_count|pluralize }}</a>.
-{% if poll.is_open %}
-Voting for this poll started on {{ poll.start_date|date:"F d, Y" }}. 
-   {% if poll.end_date %}
-      Voting will end on {{ poll.end_date|date:"F d, Y" }}.
-   {% endif %}
-{% else %}
-This poll ran from {{ poll.start_date|date:"F d, Y" }} to {{ poll.end_date|date:"F d, Y" }}.
-{% endif %}
-</p>
-<p class="poll-nav">
-<a href="{% url 'polls-results' poll.id %}">View Results &amp; Comments</a> 
-{% if poll.is_open and user.is_authenticated %}
-| <a href="{% url 'polls-vote' poll_id=poll.id %}">Vote</a>
-{% endif %}
-| <a href="{% url 'polls-main' %}">All Polls</a>
-</p>
-{% endblock %}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gpp/templates/polls/poll_detail.html	Sat Jun 25 23:23:20 2011 +0000
@@ -0,0 +1,63 @@
+{% extends 'base.html' %}
+{% load url from future %}
+{% load comment_tags %}
+{% load script_tags %}
+{% block title %}Poll Results: {{ poll.question }}{% endblock %}
+{% block custom_css %}
+<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}css/polls.css" />
+{% endblock %}
+{% block custom_js %}
+{% if poll.is_open %}
+<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}css/comments.css" />
+{% script_tags "markitup jquery-ui" %}
+<script type="text/javascript" src="{{ STATIC_URL }}js/comments.js"></script>
+{% endif %}
+{% endblock %}
+{% block content %}
+<h2>Polls</h2>
+<h3>Results for: {{ poll.question }}</h3>
+<dl class="poll-result">
+{% for choice in choices %}
+   <dt>{{ choice.choice }} - {{ choice.pct|floatformat }}% ({{ choice.votes }} vote{{ choice.votes|pluralize }})</dt>
+   <dd>
+      <div class="poll-percent" style="width: {{ choice.pct|floatformat:0 }}%; background-color: teal; color: white;">
+         <span>&nbsp;</span></div>
+   </dd>
+{% endfor %}
+</dl>
+<p><strong>{{ total_votes }} total vote{{ total_votes|pluralize }}.</strong></p>
+
+{% if user_choice %}
+<p>You voted for &quot;{{ user_choice.choice }}&quot;.</p>
+{% endif %}
+
+<p>
+{% if poll.is_open %}
+Voting for this poll started on {{ poll.start_date|date:"F d, Y" }}. 
+   {% if poll.end_date %}
+      Voting will end on {{ poll.end_date|date:"F d, Y" }}.
+   {% endif %}
+{% else %}
+This poll ran from {{ poll.start_date|date:"F d, Y" }} to {{ poll.end_date|date:"F d, Y" }}.
+{% endif %}
+</p>
+
+<p class="poll-nav">
+{% if poll.is_open and user.is_authenticated %}
+<a href="{% url 'polls-vote' poll_id=poll.id %}">Vote</a> 
+{% endif %}
+| <a href="{% url 'polls-main' %}">All Polls</a>
+</p>
+
+{% get_comment_count for poll as comment_count %}
+<p>This poll has <span id="comment-count">{{ comment_count }}</span> comment{{ comment_count|pluralize }}.</p>
+<hr />
+{% render_comment_list poll %}
+{% if poll.is_open %}
+<p>Leave a comment?</p>
+{% render_comment_form for poll %}
+{% else %}
+<p>Comments are closed for this poll. If you'd like to share your thoughts on this poll 
+with the site staff, you can <a href="{% url 'contact-form' %}">contact us directly</a>.</p>
+{% endif %}
+{% endblock %}
--- a/gpp/templates/polls/poll_results.html	Mon Jun 13 02:19:13 2011 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,53 +0,0 @@
-{% extends 'base.html' %}
-{% load url from future %}
-{% load comment_tags %}
-{% load script_tags %}
-{% block title %}Poll Results: {{ poll.question }}{% endblock %}
-{% block custom_css %}
-<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}css/polls.css" />
-<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}css/comments.css" />
-{% endblock %}
-{% block custom_js %}
-{% if poll.is_open %}
-{% script_tags "markitup jquery-ui" %}
-<script type="text/javascript" src="{{ STATIC_URL }}js/comments.js"></script>
-{% endif %}
-{% endblock %}
-{% block content %}
-<h2>Polls</h2>
-<h3>Results for: {{ poll.question }}</h3>
-<dl class="poll-result">
-{% for choice in choices %}
-   <dt>{{ choice.choice }} - {{ choice.pct|floatformat }}% ({{ choice.votes }} vote{{ choice.votes|pluralize }})</dt>
-   <dd>
-      <div class="poll-percent" style="width: {{ choice.pct|floatformat:0 }}%; background-color: teal; color: white;">
-         <span>&nbsp;</span></div>
-   </dd>
-{% endfor %}
-</dl>
-<p><strong>{{ total_votes }} total vote{{ total_votes|pluralize }}.</strong></p>
-
-{% if user_choice %}
-<p>You voted for &quot;{{ user_choice.choice }}&quot;.</p>
-{% endif %}
-
-<p class="poll-nav">
-<a href="{{ poll.get_absolute_url }}">Poll Details</a>
-{% if poll.is_open and user.is_authenticated %}
-| <a href="{% url 'polls-vote' poll_id=poll.id %}">Vote</a> 
-{% endif %}
-| <a href="{% url 'polls-main' %}">All Polls</a>
-</p>
-
-{% get_comment_count for poll as comment_count %}
-<p>This poll has <span id="comment-count">{{ comment_count }}</span> comment{{ comment_count|pluralize }}.</p>
-<hr />
-{% render_comment_list poll %}
-{% if poll.is_open %}
-<p>Leave a comment?</p>
-{% render_comment_form for poll %}
-{% else %}
-<p>Comments are closed for this poll. If you'd like to share your thoughts on this poll 
-with the site staff, you can <a href="{% url 'contact-form' %}">contact us directly</a>.</p>
-{% endif %}
-{% endblock %}
--- a/gpp/templates/polls/poll_vote.html	Mon Jun 13 02:19:13 2011 +0000
+++ b/gpp/templates/polls/poll_vote.html	Sat Jun 25 23:23:20 2011 +0000
@@ -24,9 +24,8 @@
    </form>
 {% endif %}
 <p class="poll-nav">
-<a href="{{ poll.get_absolute_url }}">Poll Details</a>
-| <a href="{% url 'polls-results' poll.id %}">View Results</a>
-| <a href="{% url 'polls-main' %}">All Polls</a>
+<a href="{{ poll.get_absolute_url }}">View results</a>
+| <a href="{% url 'polls-main' %}">All polls</a>
 </p>
 <p>This poll was published on {{ poll.start_date|date:"F d, Y" }}.</p>
 {% endblock %}