diff --git a/ietf/doc/tests_ballot.py b/ietf/doc/tests_ballot.py
index 810ee598f6..8420e411e2 100644
--- a/ietf/doc/tests_ballot.py
+++ b/ietf/doc/tests_ballot.py
@@ -25,7 +25,6 @@
from ietf.group.models import Group, Role
from ietf.group.factories import GroupFactory, RoleFactory, ReviewTeamFactory
from ietf.ipr.factories import HolderIprDisclosureFactory
-from ietf.name.models import BallotPositionName
from ietf.iesg.models import TelechatDate
from ietf.person.models import Person
from ietf.person.factories import PersonFactory, PersonalApiKeyFactory
@@ -37,9 +36,18 @@
class EditPositionTests(TestCase):
+
+ # N.B. This test needs to be rewritten to exercise all types of ballots (iesg, irsg, rsab)
+ # and test against the output of the mailtriggers instead of looking for hardcoded values
+ # in the To and CC results. See #7864
def test_edit_position(self):
ad = Person.objects.get(user__username="ad")
- draft = IndividualDraftFactory(ad=ad,stream_id='ietf')
+ draft = WgDraftFactory(
+ ad=ad,
+ stream_id="ietf",
+ notify="somebody@example.com",
+ group__acronym="mars",
+ )
ballot = create_ballot_if_not_open(None, draft, ad, 'approve')
url = urlreverse('ietf.doc.views_ballot.edit_position', kwargs=dict(name=draft.name,
ballot_id=ballot.pk))
@@ -55,11 +63,20 @@ def test_edit_position(self):
self.assertEqual(len(q('form textarea[name=comment]')), 1)
# vote
+ empty_outbox()
events_before = draft.docevent_set.count()
-
- r = self.client.post(url, dict(position="discuss",
- discuss=" This is a discussion test. \n ",
- comment=" This is a test. \n "))
+
+ r = self.client.post(
+ url,
+ dict(
+ position="discuss",
+ discuss=" This is a discussion test. \n ",
+ comment=" This is a test. \n ",
+ additional_cc="test298347@example.com",
+ cc_choices=["doc_notify", "doc_group_chairs"],
+ send_mail=1,
+ ),
+ )
self.assertEqual(r.status_code, 302)
pos = draft.latest_event(BallotPositionDocEvent, balloter=ad)
@@ -70,6 +87,22 @@ def test_edit_position(self):
self.assertTrue(pos.comment_time != None)
self.assertTrue("New position" in pos.desc)
self.assertEqual(draft.docevent_set.count(), events_before + 3)
+ self.assertEqual(len(outbox),1)
+ m = outbox[0]
+ self.assertTrue("COMMENT" in m['Subject'])
+ self.assertTrue("DISCUSS" in m['Subject'])
+ self.assertTrue(draft.name in m['Subject'])
+ self.assertTrue("This is a discussion test." in str(m))
+ self.assertTrue("This is a test" in str(m))
+ self.assertTrue("iesg@" in m['To'])
+ # cc_choice doc_group_chairs
+ self.assertTrue("mars-chairs@" in m['Cc'])
+ # cc_choice doc_notify
+ self.assertTrue("somebody@example.com" in m['Cc'])
+ # cc_choice doc_group_email_list was not selected
+ self.assertFalse(draft.group.list_email in m['Cc'])
+ # extra-cc
+ self.assertTrue("test298347@example.com" in m['Cc'])
# recast vote
events_before = draft.docevent_set.count()
@@ -230,64 +263,6 @@ def test_cannot_edit_position_as_pre_ad(self):
r = self.client.post(url, dict(position="discuss", discuss="Test discuss text"))
self.assertEqual(r.status_code, 403)
- # N.B. This test needs to be rewritten to exercise all types of ballots (iesg, irsg, rsab)
- # and test against the output of the mailtriggers instead of looking for hardcoded values
- # in the To and CC results. See #7864
- def test_send_ballot_comment(self):
- ad = Person.objects.get(user__username="ad")
- draft = WgDraftFactory(ad=ad,group__acronym='mars')
- draft.notify = "somebody@example.com"
- draft.save_with_history([DocEvent.objects.create(doc=draft, rev=draft.rev, type="changed_document", by=Person.objects.get(user__username="secretary"), desc="Test")])
-
- ballot = create_ballot_if_not_open(None, draft, ad, 'approve')
-
- BallotPositionDocEvent.objects.create(
- doc=draft, rev=draft.rev, type="changed_ballot_position",
- by=ad, balloter=ad, ballot=ballot, pos=BallotPositionName.objects.get(slug="discuss"),
- discuss="This draft seems to be lacking a clearer title?",
- discuss_time=timezone.now(),
- comment="Test!",
- comment_time=timezone.now())
-
- url = urlreverse('ietf.doc.views_ballot.send_ballot_comment', kwargs=dict(name=draft.name,
- ballot_id=ballot.pk))
- login_testing_unauthorized(self, "ad", url)
-
- # normal get
- r = self.client.get(url)
- self.assertEqual(r.status_code, 200)
- q = PyQuery(r.content)
- self.assertTrue(len(q('form input[name="extra_cc"]')) > 0)
-
- # send
- mailbox_before = len(outbox)
-
- r = self.client.post(url, dict(extra_cc="test298347@example.com", cc_choices=['doc_notify','doc_group_chairs']))
- self.assertEqual(r.status_code, 302)
-
- self.assertEqual(len(outbox), mailbox_before + 1)
- m = outbox[-1]
- self.assertTrue("COMMENT" in m['Subject'])
- self.assertTrue("DISCUSS" in m['Subject'])
- self.assertTrue(draft.name in m['Subject'])
- self.assertTrue("clearer title" in str(m))
- self.assertTrue("Test!" in str(m))
- self.assertTrue("iesg@" in m['To'])
- # cc_choice doc_group_chairs
- self.assertTrue("mars-chairs@" in m['Cc'])
- # cc_choice doc_notify
- self.assertTrue("somebody@example.com" in m['Cc'])
- # cc_choice doc_group_email_list was not selected
- self.assertFalse(draft.group.list_email in m['Cc'])
- # extra-cc
- self.assertTrue("test298347@example.com" in m['Cc'])
-
- r = self.client.post(url, dict(cc=""))
- self.assertEqual(r.status_code, 302)
- self.assertEqual(len(outbox), mailbox_before + 2)
- m = outbox[-1]
- self.assertTrue("iesg@" in m['To'])
- self.assertFalse(m['Cc'] and draft.group.list_email in m['Cc'])
class BallotWriteupsTests(TestCase):
diff --git a/ietf/doc/tests_draft.py b/ietf/doc/tests_draft.py
index ab33acebe6..4d262c5a2f 100644
--- a/ietf/doc/tests_draft.py
+++ b/ietf/doc/tests_draft.py
@@ -2,6 +2,7 @@
# -*- coding: utf-8 -*-
+import json
import os
import datetime
import io
@@ -11,7 +12,7 @@
from pathlib import Path
from pyquery import PyQuery
-from django.db.models import Q
+from django.db.models import Max, Q
from django.urls import reverse as urlreverse
from django.conf import settings
from django.utils import timezone
@@ -2391,3 +2392,77 @@ def test_editorial_metadata(self):
top_level_metadata_headings = q("tbody>tr>th:first-child").text()
self.assertNotIn("IESG", top_level_metadata_headings)
self.assertNotIn("IANA", top_level_metadata_headings)
+
+class BallotEmailAjaxTests(TestCase):
+ def test_ajax_build_position_email(self):
+ def _post_json(self, url, json_to_post):
+ r = self.client.post(
+ url, json.dumps(json_to_post), content_type="application/json"
+ )
+ self.assertEqual(r.status_code, 200)
+ return json.loads(r.content)
+
+ doc = WgDraftFactory()
+ ad = RoleFactory(
+ name_id="ad", group=doc.group, person__name="Some Areadirector"
+ ).person
+ url = urlreverse("ietf.doc.views_ballot.ajax_build_position_email")
+ login_testing_unauthorized(self, "secretary", url)
+ r = self.client.get(url)
+ self.assertEqual(r.status_code, 405)
+ response = _post_json(self, url, {})
+ self.assertFalse(response["success"])
+ self.assertEqual(response["errors"], ["post_data not provided"])
+ response = _post_json(self, url, {"dictis": "not empty"})
+ self.assertFalse(response["success"])
+ self.assertEqual(response["errors"], ["post_data not provided"])
+ response = _post_json(self, url, {"post_data": {}})
+ self.assertFalse(response["success"])
+ self.assertEqual(len(response["errors"]), 7)
+ response = _post_json(
+ self,
+ url,
+ {
+ "post_data": {
+ "discuss": "aaaaaa",
+ "comment": "bbbbbb",
+ "position": "discuss",
+ "balloter": Person.objects.aggregate(maxpk=Max("pk") + 1)["maxpk"],
+ "docname": "this-draft-does-not-exist",
+ "cc_choices": ["doc_group_mail_list"],
+ "additional_cc": "foo@example.com",
+ }
+ },
+ )
+ self.assertFalse(response["success"])
+ self.assertEqual(
+ response["errors"],
+ ["No person found matching balloter", "No document found matching docname"],
+ )
+ response = _post_json(
+ self,
+ url,
+ {
+ "post_data": {
+ "discuss": "aaaaaa",
+ "comment": "bbbbbb",
+ "position": "discuss",
+ "balloter": ad.pk,
+ "docname": doc.name,
+ "cc_choices": ["doc_group_mail_list"],
+ "additional_cc": "foo@example.com",
+ }
+ },
+ )
+ self.assertTrue(response["success"])
+ for snippet in [
+ "aaaaaa",
+ "bbbbbb",
+ "DISCUSS",
+ ad.plain_name(),
+ doc.name,
+ doc.group.list_email,
+ "foo@example.com",
+ ]:
+ self.assertIn(snippet, response["text"])
+
diff --git a/ietf/doc/tests_irsg_ballot.py b/ietf/doc/tests_irsg_ballot.py
index aa62d8aaf9..d96cf9dbef 100644
--- a/ietf/doc/tests_irsg_ballot.py
+++ b/ietf/doc/tests_irsg_ballot.py
@@ -355,28 +355,35 @@ def test_issue_ballot(self):
def test_take_and_email_position(self):
draft = RgDraftFactory()
ballot = IRSGBallotDocEventFactory(doc=draft)
- url = urlreverse('ietf.doc.views_ballot.edit_position', kwargs=dict(name=draft.name, ballot_id=ballot.pk)) + self.balloter
+ url = (
+ urlreverse(
+ "ietf.doc.views_ballot.edit_position",
+ kwargs=dict(name=draft.name, ballot_id=ballot.pk),
+ )
+ + self.balloter
+ )
empty_outbox()
login_testing_unauthorized(self, self.username, url)
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
- r = self.client.post(url, dict(position='yes', comment='oib239sb', send_mail='Save and send email'))
+ empty_outbox()
+ r = self.client.post(
+ url,
+ dict(
+ position="yes",
+ comment="oib239sb",
+ send_mail="Save and send email",
+ cc_choices=["doc_authors", "doc_group_chairs", "doc_group_mail_list"],
+ ),
+ )
self.assertEqual(r.status_code, 302)
e = draft.latest_event(BallotPositionDocEvent)
- self.assertEqual(e.pos.slug,'yes')
- self.assertEqual(e.comment, 'oib239sb')
-
- url = urlreverse('ietf.doc.views_ballot.send_ballot_comment', kwargs=dict(name=draft.name, ballot_id=ballot.pk)) + self.balloter
-
- r = self.client.get(url)
- self.assertEqual(r.status_code, 200)
-
- r = self.client.post(url, dict(cc_choices=['doc_authors','doc_group_chairs','doc_group_mail_list'], body="Stuff"))
- self.assertEqual(r.status_code, 302)
- self.assertEqual(len(outbox),1)
- self.assertNotIn('discuss-criteria', get_payload_text(outbox[0]))
+ self.assertEqual(e.pos.slug, "yes")
+ self.assertEqual(e.comment, "oib239sb")
+ self.assertEqual(len(outbox), 1)
+ self.assertNotIn("discuss-criteria", get_payload_text(outbox[0]))
def test_close_ballot(self):
draft = RgDraftFactory()
@@ -482,27 +489,31 @@ def test_cant_take_position_on_iesg_ballot(self):
def test_take_and_email_position(self):
draft = RgDraftFactory()
ballot = IRSGBallotDocEventFactory(doc=draft)
- url = urlreverse('ietf.doc.views_ballot.edit_position', kwargs=dict(name=draft.name, ballot_id=ballot.pk))
+ url = urlreverse(
+ "ietf.doc.views_ballot.edit_position",
+ kwargs=dict(name=draft.name, ballot_id=ballot.pk),
+ )
empty_outbox()
login_testing_unauthorized(self, self.username, url)
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
- r = self.client.post(url, dict(position='yes', comment='oib239sb', send_mail='Save and send email'))
+ r = self.client.post(
+ url,
+ dict(
+ position="yes",
+ comment="oib239sb",
+ send_mail="Save and send email",
+ cc_choices=["doc_authors", "doc_group_chairs", "doc_group_mail_list"],
+ ),
+ )
self.assertEqual(r.status_code, 302)
e = draft.latest_event(BallotPositionDocEvent)
- self.assertEqual(e.pos.slug,'yes')
- self.assertEqual(e.comment, 'oib239sb')
-
- url = urlreverse('ietf.doc.views_ballot.send_ballot_comment', kwargs=dict(name=draft.name, ballot_id=ballot.pk))
-
- r = self.client.get(url)
- self.assertEqual(r.status_code, 200)
-
- r = self.client.post(url, dict(cc_choices=['doc_authors','doc_group_chairs','doc_group_mail_list'], body="Stuff"))
+ self.assertEqual(e.pos.slug, "yes")
+ self.assertEqual(e.comment, "oib239sb")
self.assertEqual(r.status_code, 302)
- self.assertEqual(len(outbox),1)
+ self.assertEqual(len(outbox), 1)
class IESGMemberTests(TestCase):
diff --git a/ietf/doc/tests_rsab_ballot.py b/ietf/doc/tests_rsab_ballot.py
index 028f548232..9086106ba9 100644
--- a/ietf/doc/tests_rsab_ballot.py
+++ b/ietf/doc/tests_rsab_ballot.py
@@ -333,34 +333,19 @@ def test_take_and_email_position(self):
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
- r = self.client.post(
- url,
- dict(position="yes", comment="oib239sb", send_mail="Save and send email"),
- )
- self.assertEqual(r.status_code, 302)
- e = draft.latest_event(BallotPositionDocEvent)
- self.assertEqual(e.pos.slug, "yes")
- self.assertEqual(e.comment, "oib239sb")
-
- url = (
- urlreverse(
- "ietf.doc.views_ballot.send_ballot_comment",
- kwargs=dict(name=draft.name, ballot_id=ballot.pk),
- )
- + self.balloter
- )
-
- r = self.client.get(url)
- self.assertEqual(r.status_code, 200)
-
r = self.client.post(
url,
dict(
+ position="yes",
+ comment="oib239sb",
+ send_mail="Save and send email",
cc_choices=["doc_authors", "doc_group_chairs", "doc_group_mail_list"],
- body="Stuff",
),
)
self.assertEqual(r.status_code, 302)
+ e = draft.latest_event(BallotPositionDocEvent)
+ self.assertEqual(e.pos.slug, "yes")
+ self.assertEqual(e.comment, "oib239sb")
self.assertEqual(len(outbox), 1)
self.assertNotIn("discuss-criteria", get_payload_text(outbox[0]))
@@ -532,31 +517,19 @@ def test_take_and_email_position(self):
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
- r = self.client.post(
- url,
- dict(position="yes", comment="oib239sb", send_mail="Save and send email"),
- )
- self.assertEqual(r.status_code, 302)
- e = draft.latest_event(BallotPositionDocEvent)
- self.assertEqual(e.pos.slug, "yes")
- self.assertEqual(e.comment, "oib239sb")
-
- url = urlreverse(
- "ietf.doc.views_ballot.send_ballot_comment",
- kwargs=dict(name=draft.name, ballot_id=ballot.pk),
- )
-
- r = self.client.get(url)
- self.assertEqual(r.status_code, 200)
-
r = self.client.post(
url,
dict(
+ position="yes",
+ comment="oib239sb",
+ send_mail="Save and send email",
cc_choices=["doc_authors", "doc_group_chairs", "doc_group_mail_list"],
- body="Stuff",
),
)
self.assertEqual(r.status_code, 302)
+ e = draft.latest_event(BallotPositionDocEvent)
+ self.assertEqual(e.pos.slug, "yes")
+ self.assertEqual(e.comment, "oib239sb")
self.assertEqual(len(outbox), 1)
diff --git a/ietf/doc/urls.py b/ietf/doc/urls.py
index 7b444782d7..8e9c0569e2 100644
--- a/ietf/doc/urls.py
+++ b/ietf/doc/urls.py
@@ -93,6 +93,8 @@
url(r'^ballots/irsg/$', views_ballot.irsg_ballot_status),
url(r'^ballots/rsab/$', views_ballot.rsab_ballot_status),
+ url(r'^build-position-email/$', views_ballot.ajax_build_position_email),
+
url(r'^(?P
Ballot deferred by {{ ballot_deferred.by }} on {{ ballot_deferred.time|date:"Y-m-d" }}.