Merge lp:~thumper/launchpad/bug-branch-proposal-view into lp:launchpad

Proposed by Tim Penhey
Status: Merged
Merged at revision: not available
Proposed branch: lp:~thumper/launchpad/bug-branch-proposal-view
Merge into: lp:launchpad
Prerequisite: lp:~thumper/launchpad/project-sourcecode
Diff against target: 640 lines (+233/-57)
10 files modified
lib/lp/code/browser/branchmergeproposal.py (+19/-1)
lib/lp/code/browser/configure.zcml (+10/-5)
lib/lp/code/browser/tests/test_branchmergeproposal.py (+55/-17)
lib/lp/code/model/branchmergeproposal.py (+11/-6)
lib/lp/code/stories/branches/xx-branch-merge-proposals.txt (+40/-27)
lib/lp/code/templates/branch-index.pt (+4/-0)
lib/lp/code/templates/branch-pending-merges.pt (+1/-0)
lib/lp/code/templates/branchmergeproposal-link-summary.pt (+2/-1)
lib/lp/code/templates/branchmergeproposal-vote-summary.pt (+49/-0)
lib/lp/code/tests/helpers.py (+42/-0)
To merge this branch: bzr merge lp:~thumper/launchpad/bug-branch-proposal-view
Reviewer Review Type Date Requested Status
Barry Warsaw (community) ui* Approve
Paul Hummer (community) ui code Approve
Review via email: mp+14736@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Tim Penhey (thumper) wrote :

Trying the prerequisite branch bit.

This branch shows more merge proposal details on the branch page.

 browser/branch.py | 1

Drive by pep-8 cleanup

 browser/branchmergeproposal.py | 20 ++++++

Define a title for the status link based on the approver/rejecter.

 browser/configure.zcml | 15 +++--

New view, and renamed an old one to be more consistent.

 browser/tests/test_branchmergeproposal.py | 72 +++++++++++++++++++------

Cleaned up a test before I realised it was the wrong one.
Added a test for the status title in the view above.

 model/branchmergeproposal.py | 17 +++--

This change is just adding the ability to pass the review date through to the function for better testing.

 stories/branches/xx-branch-merge-proposals.txt | 67 +++++++++++++----------

Move some object creation out into a helper function.
Change the explicit BMP urls to use a recorded value.
Add a section at the end to show how merge proposals look on branch pages.

 templates/branch-index.pt | 4 +

Some page based styles.

 templates/branch-pending-merges.pt | 1

Add in the vote summary.

 templates/branchmergeproposal-link-summary.pt | 3 -

Add the title to the status anchor.

 templates/branchmergeproposal-vote-summary.pt | 49 +++++++++++++++++

Render the details.

 tests/helpers.py | 42 ++++++++++++++

Create some standard people/projects for the tests.
 11 files changed, 234 insertions(+), 57 deletions(-)

This branch only has the change on the branch page, but I'm expecting to have it on the bug page as well in a follow on branch.

Revision history for this message
Paul Hummer (rockstar) :
review: Approve (ui code)
Revision history for this message
Barry Warsaw (barry) :
review: Approve (ui*)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/code/browser/branchmergeproposal.py'
2--- lib/lp/code/browser/branchmergeproposal.py 2009-11-10 22:57:42 +0000
3+++ lib/lp/code/browser/branchmergeproposal.py 2009-11-18 00:28:15 +0000
4@@ -60,7 +60,8 @@
5 from canonical.launchpad.webapp.breadcrumb import Breadcrumb
6 from canonical.launchpad.webapp.interfaces import IPrimaryContext
7 from canonical.launchpad.webapp.menu import NavigationMenu
8-from canonical.launchpad.webapp.tales import FormattersAPI
9+from canonical.launchpad.webapp.tales import (
10+ DateTimeFormatterAPI, FormattersAPI)
11 from canonical.widgets.lazrjs import (
12 TextAreaEditorWidget, vocabulary_to_choice_edit_items)
13
14@@ -138,6 +139,23 @@
15 }
16 return friendly_texts[self.context.queue_status]
17
18+ @property
19+ def status_title(self):
20+ """The title for the status text.
21+
22+ Only set if the status is approved or rejected.
23+ """
24+ result = ''
25+ if self.context.queue_status in (
26+ BranchMergeProposalStatus.CODE_APPROVED,
27+ BranchMergeProposalStatus.REJECTED
28+ ):
29+ formatter = DateTimeFormatterAPI(self.context.date_reviewed)
30+ result = '%s %s' % (
31+ self.context.reviewer.displayname,
32+ formatter.displaydate())
33+ return result
34+
35
36 class BranchMergeProposalMenuMixin:
37 """Mixin class for merge proposal menus."""
38
39=== modified file 'lib/lp/code/browser/configure.zcml'
40--- lib/lp/code/browser/configure.zcml 2009-11-13 20:12:17 +0000
41+++ lib/lp/code/browser/configure.zcml 2009-11-18 00:28:15 +0000
42@@ -184,13 +184,18 @@
43 name="+pagelet-summary"
44 template="../templates/branchmergeproposal-pagelet-summary.pt"/>
45 </browser:pages>
46- <browser:page
47- name="+votes"
48+ <browser:pages
49 for="lp.code.interfaces.branchmergeproposal.IBranchMergeProposal"
50 class="lp.code.browser.branchmergeproposal.BranchMergeProposalVoteView"
51 facet="branches"
52- permission="launchpad.View"
53- template="../templates/branchmergeproposal-votes.pt"/>
54+ permission="launchpad.View">
55+ <browser:page
56+ name="+votes"
57+ template="../templates/branchmergeproposal-votes.pt"/>
58+ <browser:page
59+ name="+vote-summary"
60+ template="../templates/branchmergeproposal-vote-summary.pt"/>
61+ </browser:pages>
62 <browser:pages
63 for="lp.code.interfaces.branchmergeproposal.IBranchMergeProposal"
64 class="lp.code.browser.branchmergeproposal.BranchMergeProposalEditView"
65@@ -276,7 +281,7 @@
66 class="lp.code.browser.branchmergeproposal.BranchMergeCandidateView"
67 facet="branches"
68 permission="zope.Public"
69- template="../templates/branch-merge-link-summary.pt"/>
70+ template="../templates/branchmergeproposal-link-summary.pt"/>
71 <browser:page
72 name="+pagelet-subscribers"
73 for="lp.code.interfaces.branchmergeproposal.IBranchMergeProposal"
74
75=== modified file 'lib/lp/code/browser/tests/test_branchmergeproposal.py'
76--- lib/lp/code/browser/tests/test_branchmergeproposal.py 2009-11-01 23:13:09 +0000
77+++ lib/lp/code/browser/tests/test_branchmergeproposal.py 2009-11-18 00:28:15 +0000
78@@ -7,10 +7,11 @@
79
80 __metaclass__ = type
81
82-from datetime import timedelta
83+from datetime import datetime, timedelta
84 from difflib import unified_diff
85 import unittest
86
87+import pytz
88 import transaction
89 from zope.component import getMultiAdapter
90 from zope.security.interfaces import Unauthorized
91@@ -66,6 +67,7 @@
92 link = menu.add_comment()
93 self.assertTrue(menu.add_comment().enabled)
94
95+
96 class TestDecoratedCodeReviewVoteReference(TestCaseWithFactory):
97
98 layer = DatabaseFunctionalLayer
99@@ -459,13 +461,6 @@
100 self.bmp = self.factory.makeBranchMergeProposal(registrant=self.user)
101 login_person(self.user)
102
103- def _createView(self):
104- # Construct the view and initialize it.
105- view = BranchMergeProposalView(
106- self.bmp, LaunchpadTestRequest())
107- view.initialize()
108- return view
109-
110 def makeTeamReview(self):
111 owner = self.bmp.source_branch.owner
112 review_team = self.factory.makeTeam()
113@@ -477,7 +472,7 @@
114 albert = self.factory.makePerson()
115 albert.join(review.reviewer)
116 login_person(albert)
117- view = self._createView()
118+ view = create_initialized_view(self.bmp, '+index')
119 view.claim_action.success({'review_id': review.id})
120 self.assertEqual(albert, review.reviewer)
121
122@@ -486,13 +481,13 @@
123 review = self.makeTeamReview()
124 albert = self.factory.makePerson()
125 login_person(albert)
126- view = self._createView()
127+ view = create_initialized_view(self.bmp, '+index')
128 self.assertRaises(Unauthorized, view.claim_action.success,
129 {'review_id': review.id})
130
131 def test_preview_diff_text_with_no_diff(self):
132 """review_diff should be None when there is no context.review_diff."""
133- view = self._createView()
134+ view = create_initialized_view(self.bmp, '+index')
135 self.assertIs(None, view.preview_diff_text)
136
137 def test_review_diff_utf8(self):
138@@ -502,8 +497,9 @@
139 diff = StaticDiff.acquireFromText('x', 'y', diff_bytes)
140 transaction.commit()
141 self.bmp.review_diff = diff
142+ view = create_initialized_view(self.bmp, '+index')
143 self.assertEqual(diff_bytes.decode('utf-8'),
144- self._createView().preview_diff_text)
145+ view.preview_diff_text)
146
147 def test_review_diff_all_chars(self):
148 """review_diff should work on diffs containing all possible bytes."""
149@@ -512,8 +508,9 @@
150 diff = StaticDiff.acquireFromText('x', 'y', diff_bytes)
151 transaction.commit()
152 self.bmp.review_diff = diff
153+ view = create_initialized_view(self.bmp, '+index')
154 self.assertEqual(diff_bytes.decode('windows-1252', 'replace'),
155- self._createView().preview_diff_text)
156+ view.preview_diff_text)
157
158 def addReviewDiff(self):
159 review_diff_bytes = ''.join(unified_diff('', 'review'))
160@@ -532,27 +529,31 @@
161 def test_preview_diff_prefers_preview_diff(self):
162 """The preview will be used for BMP with both a review and preview."""
163 preview_diff = self.addBothDiffs()
164- self.assertEqual(preview_diff, self._createView().preview_diff)
165+ view = create_initialized_view(self.bmp, '+index')
166+ self.assertEqual(preview_diff, view.preview_diff)
167
168 def test_preview_diff_uses_review_diff(self):
169 """The review diff will be used if there is no preview."""
170 review_diff = self.addReviewDiff()
171+ view = create_initialized_view(self.bmp, '+index')
172 self.assertEqual(review_diff.diff,
173- self._createView().preview_diff)
174+ view.preview_diff)
175
176 def test_review_diff_text_prefers_preview_diff(self):
177 """The preview will be used for BMP with both a review and preview."""
178 preview_diff = self.addBothDiffs()
179 transaction.commit()
180+ view = create_initialized_view(self.bmp, '+index')
181 self.assertEqual(
182- preview_diff.text, self._createView().preview_diff_text)
183+ preview_diff.text, view.preview_diff_text)
184
185 def test_linked_bugs_excludes_mutual_bugs(self):
186 """List bugs that are linked to the source only."""
187 bug = self.factory.makeBug()
188 self.bmp.source_branch.linkBug(bug, self.bmp.registrant)
189 self.bmp.target_branch.linkBug(bug, self.bmp.registrant)
190- self.assertEqual([], self._createView().linked_bugs)
191+ view = create_initialized_view(self.bmp, '+index')
192+ self.assertEqual([], view.linked_bugs)
193
194
195 class TestBranchMergeProposalChangeStatusOptions(TestCaseWithFactory):
196@@ -692,5 +693,42 @@
197 self.assertEqual(u'\u2615', diff_attachment.diff_text)
198
199
200+class TestBranchMergeCandidateView(TestCaseWithFactory):
201+ """Test the status title for the view."""
202+
203+ layer = DatabaseFunctionalLayer
204+
205+ def test_needs_review_title(self):
206+ # No title is set for a proposal needing review.
207+ bmp = self.factory.makeBranchMergeProposal(
208+ set_state=BranchMergeProposalStatus.NEEDS_REVIEW)
209+ view = create_initialized_view(bmp, '+link-summary')
210+ self.assertEqual('', view.status_title)
211+
212+ def test_approved_shows_reviewer(self):
213+ # If the proposal is approved, the approver is shown in the title
214+ # along with when they approved it.
215+ bmp = self.factory.makeBranchMergeProposal()
216+ owner = bmp.target_branch.owner
217+ login_person(bmp.target_branch.owner)
218+ owner.displayname = 'Eric'
219+ bmp.approveBranch(owner, 'some-rev', datetime(
220+ year=2008, month=9, day=10, tzinfo=pytz.UTC))
221+ view = create_initialized_view(bmp, '+link-summary')
222+ self.assertEqual('Eric on 2008-09-10', view.status_title)
223+
224+ def test_rejected_shows_reviewer(self):
225+ # If the proposal is rejected, the approver is shown in the title
226+ # along with when they approved it.
227+ bmp = self.factory.makeBranchMergeProposal()
228+ owner = bmp.target_branch.owner
229+ login_person(bmp.target_branch.owner)
230+ owner.displayname = 'Eric'
231+ bmp.rejectBranch(owner, 'some-rev', datetime(
232+ year=2008, month=9, day=10, tzinfo=pytz.UTC))
233+ view = create_initialized_view(bmp, '+link-summary')
234+ self.assertEqual('Eric on 2008-09-10', view.status_title)
235+
236+
237 def test_suite():
238 return unittest.TestLoader().loadTestsFromName(__name__)
239
240=== modified file 'lib/lp/code/model/branchmergeproposal.py'
241--- lib/lp/code/model/branchmergeproposal.py 2009-10-05 14:17:48 +0000
242+++ lib/lp/code/model/branchmergeproposal.py 2009-11-18 00:28:15 +0000
243@@ -361,7 +361,8 @@
244 # or superseded, then it is valid to be merged.
245 return (self.queue_status not in FINAL_STATES)
246
247- def _reviewProposal(self, reviewer, next_state, revision_id):
248+ def _reviewProposal(self, reviewer, next_state, revision_id,
249+ _date_reviewed=None):
250 """Set the proposal to one of the two review statuses."""
251 # Check the reviewer can review the code for the target branch.
252 old_state = self.queue_status
253@@ -371,21 +372,25 @@
254 self._transitionToState(next_state, reviewer)
255 # Record the reviewer
256 self.reviewer = reviewer
257- self.date_reviewed = UTC_NOW
258+ if _date_reviewed is None:
259+ _date_reviewed = UTC_NOW
260+ self.date_reviewed = _date_reviewed
261 # Record the reviewed revision id
262 self.reviewed_revision_id = revision_id
263 notify(BranchMergeProposalStatusChangeEvent(
264 self, reviewer, old_state, next_state))
265
266- def approveBranch(self, reviewer, revision_id):
267+ def approveBranch(self, reviewer, revision_id, _date_reviewed=None):
268 """See `IBranchMergeProposal`."""
269 self._reviewProposal(
270- reviewer, BranchMergeProposalStatus.CODE_APPROVED, revision_id)
271+ reviewer, BranchMergeProposalStatus.CODE_APPROVED, revision_id,
272+ _date_reviewed)
273
274- def rejectBranch(self, reviewer, revision_id):
275+ def rejectBranch(self, reviewer, revision_id, _date_reviewed=None):
276 """See `IBranchMergeProposal`."""
277 self._reviewProposal(
278- reviewer, BranchMergeProposalStatus.REJECTED, revision_id)
279+ reviewer, BranchMergeProposalStatus.REJECTED, revision_id,
280+ _date_reviewed)
281
282 def enqueue(self, queuer, revision_id):
283 """See `IBranchMergeProposal`."""
284
285=== modified file 'lib/lp/code/stories/branches/xx-branch-merge-proposals.txt'
286--- lib/lp/code/stories/branches/xx-branch-merge-proposals.txt 2009-10-28 19:31:47 +0000
287+++ lib/lp/code/stories/branches/xx-branch-merge-proposals.txt 2009-11-18 00:28:15 +0000
288@@ -29,6 +29,8 @@
289 >>> _unused = branch.subscribe(subscriber,
290 ... BranchSubscriptionNotificationLevel.NOEMAIL, None,
291 ... CodeReviewNotificationLevel.FULL)
292+ >>> from lp.code.tests.helpers import make_erics_fooix_project
293+ >>> locals().update(make_erics_fooix_project(factory))
294 >>> logout()
295
296 Any logged in user can register a merge proposal. Registering
297@@ -68,7 +70,8 @@
298
299 Registering the merge proposal takes the user to the new merge proposal.
300
301- >>> print nopriv_browser.url
302+ >>> klingon_proposal = nopriv_browser.url
303+ >>> print klingon_proposal
304 http://code.launchpad.dev/~name12/gnome-terminal/klingon/+merge/...
305
306 The summary reflects the selected target and prerequisite.
307@@ -97,9 +100,6 @@
308 ... 'Add more <b>mojo</b>')
309 >>> nopriv_browser.getControl('Update').click()
310
311- >>> print nopriv_browser.url
312- http://code.launchpad.dev/~name12/gnome-terminal/klingon/+merge/1
313-
314 >>> print_tag_with_id(nopriv_browser.contents, 'edit-description')
315 Commit Message
316 Add more &lt;b&gt;mojo&lt;/b&gt;
317@@ -113,7 +113,6 @@
318 for the source_branch.
319
320 >>> login('foo.bar@canonical.com')
321- >>> eric = factory.makePerson(email="eric@example.com", password="test")
322 >>> bmp = factory.makeBranchMergeProposal(registrant=eric)
323 >>> bmp_url = canonical_url(bmp)
324 >>> branch_url = canonical_url(bmp.source_branch)
325@@ -128,14 +127,13 @@
326
327 >>> sample_browser = setupBrowser(auth="Basic test@canonical.com:test")
328
329+
330 Requesting reviews
331 ------------------
332
333 You can request a review of a merge proposal.
334
335- >>> sample_browser.open(
336- ... 'http://code.launchpad.dev/~name12/gnome-terminal/klingon/+merge/1'
337- ... )
338+ >>> sample_browser.open(klingon_proposal)
339 >>> sample_browser.getLink('Request a review').click()
340 >>> sample_browser.getControl('Reviewer').value = 'mark'
341 >>> sample_browser.getControl('Review type').value = 'first'
342@@ -169,7 +167,7 @@
343 Reviewer Review Type Date Requested Status
344 Sample Person Pending [Review]
345 Mark Shuttleworth second ... ago Pending
346- Review via email: mp+1@code.launchpad.dev
347+ Review via email: mp+...@code.launchpad.dev
348 Request another review
349
350
351@@ -178,16 +176,14 @@
352
353 People not logged in cannot perform reviews.
354
355- >>> anon_browser.open('http://code.launchpad.dev/~name12/gnome-terminal'
356- ... '/klingon/+merge/1')
357+ >>> anon_browser.open(klingon_proposal)
358 >>> link = anon_browser.getLink('[Review]')
359 Traceback (most recent call last):
360 LinkNotFoundError
361
362 People who are logged in can perform reviews.
363
364- >>> nopriv_browser.open('http://code.launchpad.dev/~name12/gnome-terminal'
365- ... '/klingon/+merge/1')
366+ >>> nopriv_browser.open(klingon_proposal)
367 >>> nopriv_browser.getLink('Add a review or comment').click()
368 >>> nopriv_browser.getControl(name='field.comment').value = "Don't like it"
369 >>> nopriv_browser.getControl(name='field.vote').getControl(
370@@ -201,8 +197,7 @@
371
372 People can claim reviews for teams of which they are a member.
373
374- >>> sample_browser.open('http://code.launchpad.dev/~name12/gnome-terminal'
375- ... '/klingon/+merge/1')
376+ >>> sample_browser.open(klingon_proposal)
377 >>> sample_browser.getLink('Request another review').click()
378 >>> sample_browser.getControl('Reviewer').value = 'hwdb-team'
379 >>> sample_browser.getControl('Review type').value = 'claimable'
380@@ -213,8 +208,7 @@
381 Reviewer Review Type Date Requested Status...
382 HWDB Team claimable ... ago Pending ...
383 >>> foobar_browser = setupBrowser(auth="Basic foo.bar@canonical.com:test")
384- >>> foobar_browser.open('http://code.launchpad.dev/~name12/gnome-terminal'
385- ... '/klingon/+merge/1')
386+ >>> foobar_browser.open(klingon_proposal)
387 >>> foobar_browser.getControl('Claim review').click()
388 >>> pending = find_tag_by_id(
389 ... foobar_browser.contents, 'code-review-votes')
390@@ -230,8 +224,7 @@
391 >>> foobar_browser.getLink('Reassign').click()
392 >>> foobar_browser.getControl('Reviewer').value = 'hwdb-team'
393 >>> foobar_browser.getControl('Reassign').click()
394- >>> foobar_browser.open('http://code.launchpad.dev/~name12/gnome-terminal'
395- ... '/klingon/+merge/1')
396+ >>> foobar_browser.open(klingon_proposal)
397 >>> pending = find_tag_by_id(
398 ... foobar_browser.contents, 'code-review-votes')
399
400@@ -248,8 +241,7 @@
401 When a branch has been merged into the target branch, the proposal should
402 be marked as merged.
403
404- >>> nopriv_browser.open(
405- ... 'http://code.launchpad.dev/~name12/gnome-terminal/klingon/+merge/1')
406+ >>> nopriv_browser.open(klingon_proposal)
407
408 The edit icon at the end of the status allows the user to edit the status.
409
410@@ -264,8 +256,6 @@
411 >>> nopriv_browser.getControl(name='field.queue_status').displayValue = (
412 ... ['Merged'])
413 >>> nopriv_browser.getControl('Change Status').click()
414- >>> print nopriv_browser.url
415- http://code.launchpad.dev/~name12/gnome-terminal/klingon/+merge/1
416
417 The proposal is now shown as have being merged.
418
419@@ -282,6 +272,7 @@
420 >>> print extract_text(find_tag_by_id(
421 ... sample_browser.contents, 'landing-targets'))
422 Merged into lp://dev/~name12/gnome-terminal/main
423+ ...
424 >>> sample_browser.getLink('Merged').click()
425 >>> print_summary(sample_browser)
426 Status: Merged
427@@ -303,6 +294,7 @@
428 >>> print extract_text(find_tag_by_id(
429 ... sample_browser.contents, 'landing-targets'))
430 Merged into lp://dev/~name12/gnome-terminal/main at revision 42
431+ ...
432
433
434 Resubmitting proposals
435@@ -315,9 +307,7 @@
436 branches but with the state set to work-in-progress.
437
438 >>> login('foo.bar@canonical.com')
439- >>> fooix = factory.makeProduct(name='fooix')
440- >>> target = factory.makeProductBranch(product=fooix, owner=eric)
441- >>> bmp = factory.makeBranchMergeProposal(target_branch=target)
442+ >>> bmp = factory.makeBranchMergeProposal(target_branch=trunk)
443 >>> bmp_url = canonical_url(bmp)
444 >>> logout()
445 >>> eric_browser.open(bmp_url)
446@@ -538,7 +528,6 @@
447 Bug #...: Bug for linking (Undecided &ndash; New)
448
449
450-
451 Target branch edge cases
452 ------------------------
453
454@@ -626,3 +615,27 @@
455 >>> print extract_text(get_review_diff())
456 Preview Diff
457 Empty
458+
459+
460+Merge proposal details shown on the branch page
461+-----------------------------------------------
462+
463+A branch that has a merge proposal, but no requested reviews shows this on the
464+branch page.
465+
466+ >>> nopriv_browser.open('http://code.launchpad.dev/~fred/fooix/feature')
467+ >>> nopriv_browser.getLink('Propose for merging').click()
468+ >>> nopriv_browser.getControl('Reviewer').value = ''
469+ >>> nopriv_browser.getControl('Propose Merge').click()
470+ >>> nopriv_browser.getLink('lp://dev/~fred/fooix/feature').click()
471+ >>> print_tag_with_id(nopriv_browser.contents, 'landing-targets')
472+ Ready for review for merging into lp://dev/fooix
473+ No reviews requested
474+
475+If there are reviews either pending or completed these are also shown.
476+
477+ >>> nopriv_browser.open('http://code.launchpad.dev/~fred/fooix/proposed')
478+ >>> print_tag_with_id(nopriv_browser.contents, 'landing-targets')
479+ Ready for review for merging into lp://dev/fooix
480+ Eric the Viking: Pending (code) requested ... ago
481+ Diff: 47 lines
482
483=== modified file 'lib/lp/code/templates/branch-index.pt'
484--- lib/lp/code/templates/branch-index.pt 2009-10-11 02:33:30 +0000
485+++ lib/lp/code/templates/branch-index.pt 2009-11-18 00:28:15 +0000
486@@ -25,6 +25,10 @@
487 #download-url dd span.branch-url, #upload-url dd span.branch-url {
488 font-weight: normal;
489 }
490+ dl.reviews dd {
491+ padding-left: 2em;
492+ margin-bottom: 0;
493+ }
494
495 </style>
496 <script type="text/javascript"
497
498=== modified file 'lib/lp/code/templates/branch-pending-merges.pt'
499--- lib/lp/code/templates/branch-pending-merges.pt 2009-09-18 21:20:42 +0000
500+++ lib/lp/code/templates/branch-pending-merges.pt 2009-11-18 00:28:15 +0000
501@@ -36,6 +36,7 @@
502 <div>
503 <tal:merge-fragment tal:replace="structure mergeproposal/@@+link-summary" />
504 </div>
505+ <tal:merge-fragment tal:replace="structure mergeproposal/@@+vote-summary" />
506 </tal:landing-candidates>
507 </div>
508
509
510=== renamed file 'lib/lp/code/templates/branch-merge-link-summary.pt' => 'lib/lp/code/templates/branchmergeproposal-link-summary.pt'
511--- lib/lp/code/templates/branch-merge-link-summary.pt 2009-09-22 16:31:42 +0000
512+++ lib/lp/code/templates/branchmergeproposal-link-summary.pt 2009-11-18 00:28:15 +0000
513@@ -4,7 +4,8 @@
514 xmlns:i18n="http://xml.zope.org/namespaces/i18n">
515
516 <img src="/@@/merge-proposal-icon" />
517- <a tal:attributes="href context/fmt:url"
518+ <a tal:attributes="href context/fmt:url;
519+ title view/status_title"
520 tal:content="view/friendly_text">Approved</a>
521 <tal:for-merging condition="not: context/queue_status/enumvalue:MERGED">
522 for merging
523
524=== added file 'lib/lp/code/templates/branchmergeproposal-vote-summary.pt'
525--- lib/lp/code/templates/branchmergeproposal-vote-summary.pt 1970-01-01 00:00:00 +0000
526+++ lib/lp/code/templates/branchmergeproposal-vote-summary.pt 2009-11-18 00:28:15 +0000
527@@ -0,0 +1,49 @@
528+<tal:root
529+ xmlns:tal="http://xml.zope.org/namespaces/tal"
530+ xmlns:metal="http://xml.zope.org/namespaces/metal"
531+ xmlns:i18n="http://xml.zope.org/namespaces/i18n">
532+
533+ <tal:comment condition="nothing">
534+ <!--
535+ Yet again we are bitten by white space issues, so some tags in this
536+ template have closing brackets on following lines, and not breaks
537+ between some tags.
538+ -->
539+ </tal:comment>
540+
541+<dl class="reviews">
542+ <dd tal:condition="not: view/reviews">
543+ No reviews requested
544+ </dd>
545+ <dd tal:repeat="review view/current_reviews">
546+ <tal:reviewer replace="structure review/reviewer/fmt:link:mainsite">
547+ Eric the Reviewer</tal:reviewer
548+ ><tal:community condition="not: review/trusted">
549+ (community)</tal:community>:
550+ <span tal:attributes="class string:vote${review/comment/vote/name}"
551+ tal:content="review/status_text">
552+ Approved
553+ </span>
554+ <tal:vote-tags condition="review/review_type_str">
555+ (<tal:tag replace="review/review_type_str"/>)
556+ </tal:vote-tags>
557+ <tal:date replace="review/date_of_comment/fmt:displaydate" />
558+ </dd>
559+ <dd tal:repeat="review view/requested_reviews"
560+ tal:attributes="id string:review-${review/reviewer/name}">
561+ <tal:reviewer
562+ tal:replace="structure review/reviewer/fmt:link:mainsite" />:
563+
564+ <span class="votePENDING">Pending</span>
565+ <tal:vote-tags condition="review/review_type_str">
566+ (<tal:tag replace="review/review_type_str"/>)
567+ </tal:vote-tags>
568+ requested
569+ <tal:date replace="review/date_requested/fmt:approximatedate"/>
570+ </dd>
571+ <dd tal:condition="context/preview_diff">
572+ Diff: <tal:diff replace="structure context/preview_diff/fmt:link"/>
573+ </dd>
574+
575+</dl>
576+</tal:root>
577
578=== modified file 'lib/lp/code/tests/helpers.py'
579--- lib/lp/code/tests/helpers.py 2009-10-26 18:40:04 +0000
580+++ lib/lp/code/tests/helpers.py 2009-11-18 00:28:15 +0000
581@@ -6,10 +6,12 @@
582 __metaclass__ = type
583 __all__ = [
584 'make_linked_package_branch',
585+ 'make_erics_fooix_project',
586 ]
587
588
589 from datetime import timedelta
590+from difflib import unified_diff
591 from itertools import count
592
593 from zope.component import getUtility
594@@ -23,6 +25,46 @@
595 from lp.testing import time_counter
596
597
598+def make_erics_fooix_project(factory):
599+ """Make Eric, the Fooix project, and some branches.
600+
601+ :return: a dict of objects to put into local scope.
602+ """
603+ result = {}
604+ eric = factory.makePerson(
605+ name='eric', displayname='Eric the Viking',
606+ email='eric@example.com', password='test')
607+ fooix = factory.makeProduct(
608+ name='fooix', displayname='Fooix', owner=eric)
609+ trunk = factory.makeProductBranch(
610+ owner=eric, product=fooix, name='trunk')
611+ removeSecurityProxy(fooix.development_focus).branch = trunk
612+ # Development is done by Fred.
613+ fred = factory.makePerson(
614+ name='fred', displayname='Fred Flintstone',
615+ email='fred@example.com', password='test')
616+ feature = factory.makeProductBranch(
617+ owner=fred, product=fooix, name='feature')
618+ proposed = factory.makeProductBranch(
619+ owner=fred, product=fooix, name='proposed')
620+ bmp = proposed.addLandingTarget(
621+ registrant=fred, target_branch=trunk, needs_review=True,
622+ review_requests=[(eric, 'code')])
623+ # And fake a diff.
624+ naked_bmp = removeSecurityProxy(bmp)
625+ preview = removeSecurityProxy(bmp.updatePreviewDiff(
626+ ''.join(unified_diff('', 'random content')), u'rev-a', u'rev-b'))
627+ naked_bmp.source_branch.last_scanned_id = preview.source_revision_id
628+ naked_bmp.target_branch.last_scanned_id = preview.target_revision_id
629+ preview.diff_lines_count = 47
630+ preview.added_lines_count = 7
631+ preview.remvoed_lines_count = 13
632+ preview.diffstat = {'file1': (3, 8), 'file2': (4, 5)}
633+ return {
634+ 'eric': eric, 'fooix': fooix, 'trunk':trunk, 'feature': feature,
635+ 'proposed': proposed, 'fred': fred}
636+
637+
638 def make_linked_package_branch(factory, distribution=None,
639 sourcepackagename=None):
640 """Make a new package branch and make it official."""