14027.3.4
by Jeroen Vermeulen
Automated import fixes and copyright updates. |
1 |
# Copyright 2009-2011 Canonical Ltd. This software is licensed under the
|
8687.15.15
by Karl Fogel
Add the copyright header block to files under lib/lp/bugs/. |
2 |
# GNU Affero General Public License version 3 (see the file LICENSE).
|
14235.4.7
by Ian Booth
Rename permission to launchpad.See and write tests |
3 |
from BeautifulSoup import BeautifulSoup |
4 |
from lp.registry.interfaces.person import PersonVisibility |
|
2770.1.40
by Guilherme Salgado
A few fixes Bjorn suggested and removing two unused templates. |
5 |
|
8971.26.2
by Gavin Panella
New property current_user_affected_js_status, and tests. |
6 |
__metaclass__ = type |
7 |
||
14128.3.21
by Aaron Bentley
Test the mustache rendering and placeholder. |
8 |
from contextlib import contextmanager |
14128.9.3
by Aaron Bentley
Provide bug age field, hidden by default. |
9 |
from datetime import ( |
10 |
datetime, |
|
11 |
timedelta, |
|
12 |
)
|
|
14128.3.21
by Aaron Bentley
Test the mustache rendering and placeholder. |
13 |
import re |
14186.3.12
by Ian Booth
When deleting the current bugtask, redirect to the default bug task afterwards, plus add tests |
14 |
import simplejson |
14128.3.47
by Aaron Bentley
Update tests. |
15 |
import urllib |
2770.1.40
by Guilherme Salgado
A few fixes Bjorn suggested and removing two unused templates. |
16 |
|
12792.8.1
by William Grant
Add failing test for BugActivityItem assignee escaping. |
17 |
from lazr.lifecycle.event import ObjectModifiedEvent |
14128.3.20
by Aaron Bentley
Ensure the json cache is populated appropriately. |
18 |
from lazr.restful.interfaces import IJSONRequestCache |
12792.8.1
by William Grant
Add failing test for BugActivityItem assignee escaping. |
19 |
from lazr.lifecycle.snapshot import Snapshot |
12075.3.3
by Gavin Panella
Factor out the selection of interesting activity. |
20 |
from pytz import UTC |
14128.3.21
by Aaron Bentley
Test the mustache rendering and placeholder. |
21 |
import soupmatchers |
11474.2.3
by Robert Collins
Add getSubscribersForPerson to IBug, permitting single query setup of bug index pages, which according to OOPS is about 1.2 seconds (but if we're underestimating could be more) and will make analysing performance on the page easier. |
22 |
from storm.store import Store |
14128.3.21
by Aaron Bentley
Test the mustache rendering and placeholder. |
23 |
from testtools.matchers import ( |
24 |
LessThan, |
|
25 |
Not, |
|
26 |
)
|
|
14027.3.4
by Jeroen Vermeulen
Automated import fixes and copyright updates. |
27 |
import transaction |
12799.1.2
by Ian Booth
Rework implementation and do unit tests instead of doc tests |
28 |
from zope.component import ( |
29 |
getMultiAdapter, |
|
30 |
getUtility, |
|
31 |
)
|
|
12792.8.1
by William Grant
Add failing test for BugActivityItem assignee escaping. |
32 |
from zope.event import notify |
33 |
from zope.interface import providedBy |
|
10680.2.1
by Abel Deuring
Add a new bug task status 'Expired'; the bug task expiration script sets expired bug tasks to this status; related adjustments to the bug task views |
34 |
from zope.security.proxy import removeSecurityProxy |
2770.1.40
by Guilherme Salgado
A few fixes Bjorn suggested and removing two unused templates. |
35 |
|
13752.3.9
by Graham Binns
The default batch size is now a config option. |
36 |
from canonical.config import config |
13955.1.7
by Graham Binns
Review changes for Jeroen. |
37 |
from canonical.database.constants import UTC_NOW |
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
38 |
from canonical.launchpad.ftests import ( |
39 |
ANONYMOUS, |
|
40 |
login, |
|
41 |
login_person, |
|
42 |
)
|
|
11655.1.1
by Brad Crittenden
Do not show bug information on a project group page with no projects that use Launchpad. |
43 |
from canonical.launchpad.testing.pages import find_tag_by_id |
11582.2.2
by Robert Collins
Probably broken, but takes 46 queries off of a baseline BugTask:+index. |
44 |
from canonical.launchpad.webapp import canonical_url |
14186.3.3
by Ian Booth
Implement code review changes |
45 |
from canonical.launchpad.webapp.authorization import clear_cache |
14186.4.1
by Ian Booth
Use ReturnToReferrerMixin for bugtask deletion view |
46 |
from canonical.launchpad.webapp.interfaces import ( |
47 |
ILaunchBag, |
|
48 |
ILaunchpadRoot, |
|
49 |
)
|
|
9521.3.1
by Tom Berger
patch from allenap |
50 |
from canonical.launchpad.webapp.servers import LaunchpadTestRequest |
12736.8.2
by William Grant
Test that BugTask:+index doesn't scale with attachments. |
51 |
from canonical.testing.layers import ( |
52 |
DatabaseFunctionalLayer, |
|
53 |
LaunchpadFunctionalLayer, |
|
54 |
)
|
|
13130.1.12
by Curtis Hovey
Sorted imports. |
55 |
from lp.app.interfaces.launchpad import ILaunchpadCelebrities |
13752.3.4
by Graham Binns
Added some basic tests. |
56 |
from lp.bugs.adapters.bugchange import BugTaskStatusChange |
10680.2.1
by Abel Deuring
Add a new bug task status 'Expired'; the bug task expiration script sets expired bug tasks to this status; related adjustments to the bug task views |
57 |
from lp.bugs.browser.bugtask import ( |
12792.8.1
by William Grant
Add failing test for BugActivityItem assignee escaping. |
58 |
BugActivityItem, |
14128.7.1
by Aaron Bentley
Control whether the bug_id is displayed using the mustache template. |
59 |
BugListingBatchNavigator, |
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
60 |
BugTaskEditView, |
14128.3.19
by Aaron Bentley
Test BugTaskListingItem.model |
61 |
BugTaskListingItem, |
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
62 |
BugTasksAndNominationsView, |
63 |
)
|
|
12075.3.3
by Gavin Panella
Factor out the selection of interesting activity. |
64 |
from lp.bugs.interfaces.bugactivity import IBugActivitySet |
12799.1.2
by Ian Booth
Rework implementation and do unit tests instead of doc tests |
65 |
from lp.bugs.interfaces.bugnomination import IBugNomination |
66 |
from lp.bugs.interfaces.bugtask import ( |
|
67 |
BugTaskStatus, |
|
68 |
IBugTask, |
|
69 |
IBugTaskSet, |
|
70 |
)
|
|
14174.2.10
by Ian Booth
Remove some unnecessary checks and refactor |
71 |
from lp.services.features.testing import FeatureFixture |
12075.3.3
by Gavin Panella
Factor out the selection of interesting activity. |
72 |
from lp.services.propertycache import get_property_cache |
12561.3.20
by Curtis Hovey
Reconstructed the BugTasksAndNominationsView.getTargetLinkTitle test. |
73 |
from lp.soyuz.interfaces.component import IComponentSet |
11655.1.1
by Brad Crittenden
Do not show bug information on a project group page with no projects that use Launchpad. |
74 |
from lp.testing import ( |
14128.3.21
by Aaron Bentley
Test the mustache rendering and placeholder. |
75 |
BrowserTestCase, |
12736.8.2
by William Grant
Test that BugTask:+index doesn't scale with attachments. |
76 |
celebrity_logged_in, |
14128.3.20
by Aaron Bentley
Ensure the json cache is populated appropriately. |
77 |
feature_flags, |
11655.1.1
by Brad Crittenden
Do not show bug information on a project group page with no projects that use Launchpad. |
78 |
person_logged_in, |
14128.3.20
by Aaron Bentley
Ensure the json cache is populated appropriately. |
79 |
set_feature_flag, |
11655.1.1
by Brad Crittenden
Do not show bug information on a project group page with no projects that use Launchpad. |
80 |
TestCaseWithFactory, |
81 |
)
|
|
11582.2.2
by Robert Collins
Probably broken, but takes 46 queries off of a baseline BugTask:+index. |
82 |
from lp.testing._webservice import QueryCollector |
12736.8.2
by William Grant
Test that BugTask:+index doesn't scale with attachments. |
83 |
from lp.testing.matchers import ( |
84 |
BrowsesWithQueryLimit, |
|
85 |
HasQueryCount, |
|
86 |
)
|
|
11474.2.1
by Robert Collins
Use sampledata constants. |
87 |
from lp.testing.sampledata import ( |
88 |
ADMIN_EMAIL, |
|
89 |
NO_PRIVILEGE_EMAIL, |
|
90 |
USER_EMAIL, |
|
91 |
)
|
|
11655.1.3
by Brad Crittenden
De-lint |
92 |
from lp.testing.views import create_initialized_view |
8971.26.2
by Gavin Panella
New property current_user_affected_js_status, and tests. |
93 |
|
94 |
||
14186.3.2
by Ian Booth
Add tests for delete view |
95 |
DELETE_BUGTASK_ENABLED = {u"disclosure.delete_bugtask.enabled": u"on"} |
96 |
||
97 |
||
11474.2.3
by Robert Collins
Add getSubscribersForPerson to IBug, permitting single query setup of bug index pages, which according to OOPS is about 1.2 seconds (but if we're underestimating could be more) and will make analysing performance on the page easier. |
98 |
class TestBugTaskView(TestCaseWithFactory): |
99 |
||
12736.8.2
by William Grant
Test that BugTask:+index doesn't scale with attachments. |
100 |
layer = LaunchpadFunctionalLayer |
11474.2.3
by Robert Collins
Add getSubscribersForPerson to IBug, permitting single query setup of bug index pages, which according to OOPS is about 1.2 seconds (but if we're underestimating could be more) and will make analysing performance on the page easier. |
101 |
|
102 |
def invalidate_caches(self, obj): |
|
103 |
store = Store.of(obj) |
|
104 |
# Make sure everything is in the database.
|
|
105 |
store.flush() |
|
106 |
# And invalidate the cache (not a reset, because that stops us using
|
|
107 |
# the domain objects)
|
|
108 |
store.invalidate() |
|
109 |
||
11582.2.2
by Robert Collins
Probably broken, but takes 46 queries off of a baseline BugTask:+index. |
110 |
def test_rendered_query_counts_constant_with_team_memberships(self): |
11474.2.3
by Robert Collins
Add getSubscribersForPerson to IBug, permitting single query setup of bug index pages, which according to OOPS is about 1.2 seconds (but if we're underestimating could be more) and will make analysing performance on the page easier. |
111 |
login(ADMIN_EMAIL) |
11618.1.1
by Deryck Hodge
Drive the query count down by two for bugtask+index, and fix up |
112 |
task = self.factory.makeBugTask() |
11582.2.2
by Robert Collins
Probably broken, but takes 46 queries off of a baseline BugTask:+index. |
113 |
person_no_teams = self.factory.makePerson(password='test') |
114 |
person_with_teams = self.factory.makePerson(password='test') |
|
11474.2.4
by Robert Collins
Testing tweaks per review. |
115 |
for _ in range(10): |
116 |
self.factory.makeTeam(members=[person_with_teams]) |
|
11474.2.3
by Robert Collins
Add getSubscribersForPerson to IBug, permitting single query setup of bug index pages, which according to OOPS is about 1.2 seconds (but if we're underestimating could be more) and will make analysing performance on the page easier. |
117 |
# count with no teams
|
11618.1.1
by Deryck Hodge
Drive the query count down by two for bugtask+index, and fix up |
118 |
url = canonical_url(task) |
11582.2.2
by Robert Collins
Probably broken, but takes 46 queries off of a baseline BugTask:+index. |
119 |
recorder = QueryCollector() |
120 |
recorder.register() |
|
121 |
self.addCleanup(recorder.unregister) |
|
11618.1.1
by Deryck Hodge
Drive the query count down by two for bugtask+index, and fix up |
122 |
self.invalidate_caches(task) |
11582.2.2
by Robert Collins
Probably broken, but takes 46 queries off of a baseline BugTask:+index. |
123 |
self.getUserBrowser(url, person_no_teams) |
11655.1.3
by Brad Crittenden
De-lint |
124 |
# This may seem large: it is; there is easily another 30% fat in
|
125 |
# there.
|
|
14189.6.9
by mbp at canonical
Add other_users_affected_count_with_dupes to get the right answers when the current user is affected by a dupe |
126 |
self.assertThat(recorder, HasQueryCount(LessThan(85))) |
11474.2.3
by Robert Collins
Add getSubscribersForPerson to IBug, permitting single query setup of bug index pages, which according to OOPS is about 1.2 seconds (but if we're underestimating could be more) and will make analysing performance on the page easier. |
127 |
count_with_no_teams = recorder.count |
11474.2.4
by Robert Collins
Testing tweaks per review. |
128 |
# count with many teams
|
11618.1.1
by Deryck Hodge
Drive the query count down by two for bugtask+index, and fix up |
129 |
self.invalidate_caches(task) |
11582.2.2
by Robert Collins
Probably broken, but takes 46 queries off of a baseline BugTask:+index. |
130 |
self.getUserBrowser(url, person_with_teams) |
11474.2.3
by Robert Collins
Add getSubscribersForPerson to IBug, permitting single query setup of bug index pages, which according to OOPS is about 1.2 seconds (but if we're underestimating could be more) and will make analysing performance on the page easier. |
131 |
# Allow an increase of one because storm bug 619017 causes additional
|
11474.2.4
by Robert Collins
Testing tweaks per review. |
132 |
# queries, revalidating things unnecessarily. An increase which is
|
133 |
# less than the number of new teams shows it is definitely not
|
|
134 |
# growing per-team.
|
|
11582.2.2
by Robert Collins
Probably broken, but takes 46 queries off of a baseline BugTask:+index. |
135 |
self.assertThat(recorder, HasQueryCount( |
11474.2.4
by Robert Collins
Testing tweaks per review. |
136 |
LessThan(count_with_no_teams + 3), |
11474.2.3
by Robert Collins
Add getSubscribersForPerson to IBug, permitting single query setup of bug index pages, which according to OOPS is about 1.2 seconds (but if we're underestimating could be more) and will make analysing performance on the page easier. |
137 |
))
|
138 |
||
12736.8.2
by William Grant
Test that BugTask:+index doesn't scale with attachments. |
139 |
def test_rendered_query_counts_constant_with_attachments(self): |
140 |
with celebrity_logged_in('admin'): |
|
141 |
browses_under_limit = BrowsesWithQueryLimit( |
|
14189.6.9
by mbp at canonical
Add other_users_affected_count_with_dupes to get the right answers when the current user is affected by a dupe |
142 |
88, self.factory.makePerson()) |
12736.8.2
by William Grant
Test that BugTask:+index doesn't scale with attachments. |
143 |
|
144 |
# First test with a single attachment.
|
|
145 |
task = self.factory.makeBugTask() |
|
146 |
self.factory.makeBugAttachment(bug=task.bug) |
|
147 |
self.assertThat(task, browses_under_limit) |
|
148 |
||
149 |
with celebrity_logged_in('admin'): |
|
150 |
# And now with 10.
|
|
151 |
task = self.factory.makeBugTask() |
|
152 |
self.factory.makeBugTask(bug=task.bug) |
|
153 |
for i in range(10): |
|
154 |
self.factory.makeBugAttachment(bug=task.bug) |
|
155 |
self.assertThat(task, browses_under_limit) |
|
156 |
||
13827.1.2
by Gary Poster
respond to review |
157 |
def makeLinkedBranchMergeProposal(self, sourcepackage, bug, owner): |
158 |
with person_logged_in(owner): |
|
159 |
f = self.factory |
|
160 |
target_branch = f.makePackageBranch( |
|
161 |
sourcepackage=sourcepackage, owner=owner) |
|
162 |
source_branch = f.makeBranchTargetBranch( |
|
163 |
target_branch.target, owner=owner) |
|
164 |
bug.linkBranch(source_branch, owner) |
|
165 |
return f.makeBranchMergeProposal( |
|
166 |
target_branch=target_branch, |
|
167 |
registrant=owner, |
|
168 |
source_branch=source_branch) |
|
169 |
||
13827.1.1
by Gary Poster
add optimization for bug page with many branches: this time for sure! |
170 |
def test_rendered_query_counts_reduced_with_branches(self): |
171 |
f = self.factory |
|
172 |
owner = f.makePerson() |
|
173 |
ds = f.makeDistroSeries() |
|
13827.1.2
by Gary Poster
respond to review |
174 |
bug = f.makeBug() |
13827.1.1
by Gary Poster
add optimization for bug page with many branches: this time for sure! |
175 |
sourcepackages = [ |
13827.1.2
by Gary Poster
respond to review |
176 |
f.makeSourcePackage(distroseries=ds, publish=True) |
177 |
for i in range(5)] |
|
13827.1.1
by Gary Poster
add optimization for bug page with many branches: this time for sure! |
178 |
for sp in sourcepackages: |
14027.3.1
by Jeroen Vermeulen
Fix lots of lint in recently-changed files. |
179 |
f.makeBugTask(bug=bug, owner=owner, target=sp) |
13827.1.2
by Gary Poster
respond to review |
180 |
url = canonical_url(bug.default_bugtask) |
13827.1.1
by Gary Poster
add optimization for bug page with many branches: this time for sure! |
181 |
recorder = QueryCollector() |
182 |
recorder.register() |
|
183 |
self.addCleanup(recorder.unregister) |
|
13827.1.2
by Gary Poster
respond to review |
184 |
self.invalidate_caches(bug.default_bugtask) |
13827.1.1
by Gary Poster
add optimization for bug page with many branches: this time for sure! |
185 |
self.getUserBrowser(url, owner) |
186 |
# At least 20 of these should be removed.
|
|
14280.2.6
by Steve Kowalik
Fix one last test failure. |
187 |
self.assertThat(recorder, HasQueryCount(LessThan(101))) |
13827.1.1
by Gary Poster
add optimization for bug page with many branches: this time for sure! |
188 |
count_with_no_branches = recorder.count |
13827.1.2
by Gary Poster
respond to review |
189 |
for sp in sourcepackages: |
190 |
self.makeLinkedBranchMergeProposal(sp, bug, owner) |
|
191 |
self.invalidate_caches(bug.default_bugtask) |
|
192 |
self.getUserBrowser(url, owner) # This triggers the query recorder. |
|
13827.1.1
by Gary Poster
add optimization for bug page with many branches: this time for sure! |
193 |
# Ideally this should be much fewer, but this tries to keep a win of
|
194 |
# removing more than half of these.
|
|
195 |
self.assertThat(recorder, HasQueryCount( |
|
14189.6.3
by mbp at canonical
Counting total affected users across dupes can be disabled by a feature flag. |
196 |
LessThan(count_with_no_branches + 46), |
13827.1.1
by Gary Poster
add optimization for bug page with many branches: this time for sure! |
197 |
))
|
198 |
||
12075.3.3
by Gavin Panella
Factor out the selection of interesting activity. |
199 |
def test_interesting_activity(self): |
200 |
# The interesting_activity property returns a tuple of interesting
|
|
201 |
# `BugActivityItem`s.
|
|
202 |
bug = self.factory.makeBug() |
|
203 |
view = create_initialized_view( |
|
204 |
bug.default_bugtask, name=u'+index', rootsite='bugs') |
|
205 |
||
206 |
def add_activity(what, old=None, new=None, message=None): |
|
207 |
getUtility(IBugActivitySet).new( |
|
208 |
bug, datetime.now(UTC), bug.owner, whatchanged=what, |
|
209 |
oldvalue=old, newvalue=new, message=message) |
|
210 |
del get_property_cache(view).interesting_activity |
|
211 |
||
212 |
# A fresh bug has no interesting activity.
|
|
213 |
self.assertEqual((), view.interesting_activity) |
|
214 |
||
215 |
# Some activity is not considered interesting.
|
|
216 |
add_activity("boring") |
|
217 |
self.assertEqual((), view.interesting_activity) |
|
218 |
||
219 |
# A description change is interesting.
|
|
220 |
add_activity("description") |
|
221 |
self.assertEqual(1, len(view.interesting_activity)) |
|
222 |
[activity] = view.interesting_activity |
|
223 |
self.assertEqual("description", activity.whatchanged) |
|
224 |
||
13402.4.1
by Graham Binns
I can't quite believe this, but the tests pass. |
225 |
def test_error_for_changing_target_with_invalid_status(self): |
226 |
# If a user moves a bug task with a restricted status (say,
|
|
227 |
# Triaged) to a target where they do not have permission to set
|
|
228 |
# that status, they will be unable to complete the retargeting
|
|
229 |
# and will instead receive an error in the UI.
|
|
230 |
person = self.factory.makePerson() |
|
231 |
product = self.factory.makeProduct( |
|
232 |
name='product1', owner=person, official_malone=True) |
|
233 |
with person_logged_in(person): |
|
234 |
product.setBugSupervisor(person, person) |
|
235 |
product_2 = self.factory.makeProduct( |
|
236 |
name='product2', official_malone=True) |
|
237 |
with person_logged_in(product_2.owner): |
|
238 |
product_2.setBugSupervisor(product_2.owner, product_2.owner) |
|
239 |
bug = self.factory.makeBug( |
|
240 |
product=product, owner=person) |
|
241 |
# We need to commit here, otherwise all the sample data we
|
|
242 |
# created gets destroyed when the transaction is rolled back.
|
|
243 |
transaction.commit() |
|
244 |
with person_logged_in(person): |
|
245 |
form_data = { |
|
13494.2.12
by William Grant
Fix test_bugtask. |
246 |
'%s.target' % product.name: 'product', |
247 |
'%s.target.product' % product.name: product_2.name, |
|
13402.4.1
by Graham Binns
I can't quite believe this, but the tests pass. |
248 |
'%s.status' % product.name: BugTaskStatus.TRIAGED.title, |
249 |
'%s.actions.save' % product.name: 'Save Changes', |
|
250 |
}
|
|
251 |
view = create_initialized_view( |
|
252 |
bug.default_bugtask, name=u'+editstatus', |
|
253 |
form=form_data) |
|
254 |
# The bugtask's target won't have changed, since an error
|
|
255 |
# happend. The error will be listed in the view.
|
|
13402.4.5
by Graham Binns
Tweaked according to Rob's requirements. |
256 |
self.assertEqual(1, len(view.errors)) |
13402.4.1
by Graham Binns
I can't quite believe this, but the tests pass. |
257 |
self.assertEqual(product, bug.default_bugtask.target) |
258 |
||
11474.2.3
by Robert Collins
Add getSubscribersForPerson to IBug, permitting single query setup of bug index pages, which according to OOPS is about 1.2 seconds (but if we're underestimating could be more) and will make analysing performance on the page easier. |
259 |
|
8971.26.2
by Gavin Panella
New property current_user_affected_js_status, and tests. |
260 |
class TestBugTasksAndNominationsView(TestCaseWithFactory): |
261 |
||
12561.3.12
by Curtis Hovey
Fixed broken view while rewriting a broken test. |
262 |
layer = DatabaseFunctionalLayer |
8971.26.2
by Gavin Panella
New property current_user_affected_js_status, and tests. |
263 |
|
264 |
def setUp(self): |
|
265 |
super(TestBugTasksAndNominationsView, self).setUp() |
|
11474.2.1
by Robert Collins
Use sampledata constants. |
266 |
login(ADMIN_EMAIL) |
8971.26.2
by Gavin Panella
New property current_user_affected_js_status, and tests. |
267 |
self.bug = self.factory.makeBug() |
9521.3.1
by Tom Berger
patch from allenap |
268 |
self.view = BugTasksAndNominationsView( |
269 |
self.bug, LaunchpadTestRequest()) |
|
8971.26.2
by Gavin Panella
New property current_user_affected_js_status, and tests. |
270 |
|
11582.2.5
by Robert Collins
Fix up test fallout. |
271 |
def refresh(self): |
11582.2.6
by Robert Collins
More tests not-quite-right. |
272 |
# The view caches, to see different scenarios, a refresh is needed.
|
11582.2.5
by Robert Collins
Fix up test fallout. |
273 |
self.view = BugTasksAndNominationsView( |
274 |
self.bug, LaunchpadTestRequest()) |
|
275 |
||
8971.26.2
by Gavin Panella
New property current_user_affected_js_status, and tests. |
276 |
def test_current_user_affected_status(self): |
277 |
self.failUnlessEqual( |
|
278 |
None, self.view.current_user_affected_status) |
|
11582.2.5
by Robert Collins
Fix up test fallout. |
279 |
self.bug.markUserAffected(self.view.user, True) |
280 |
self.refresh() |
|
8971.26.2
by Gavin Panella
New property current_user_affected_js_status, and tests. |
281 |
self.failUnlessEqual( |
282 |
True, self.view.current_user_affected_status) |
|
11582.2.5
by Robert Collins
Fix up test fallout. |
283 |
self.bug.markUserAffected(self.view.user, False) |
284 |
self.refresh() |
|
8971.26.2
by Gavin Panella
New property current_user_affected_js_status, and tests. |
285 |
self.failUnlessEqual( |
286 |
False, self.view.current_user_affected_status) |
|
287 |
||
288 |
def test_current_user_affected_js_status(self): |
|
289 |
self.failUnlessEqual( |
|
290 |
'null', self.view.current_user_affected_js_status) |
|
11582.2.5
by Robert Collins
Fix up test fallout. |
291 |
self.bug.markUserAffected(self.view.user, True) |
292 |
self.refresh() |
|
8971.26.2
by Gavin Panella
New property current_user_affected_js_status, and tests. |
293 |
self.failUnlessEqual( |
294 |
'true', self.view.current_user_affected_js_status) |
|
11582.2.5
by Robert Collins
Fix up test fallout. |
295 |
self.bug.markUserAffected(self.view.user, False) |
296 |
self.refresh() |
|
8971.26.2
by Gavin Panella
New property current_user_affected_js_status, and tests. |
297 |
self.failUnlessEqual( |
298 |
'false', self.view.current_user_affected_js_status) |
|
299 |
||
9521.3.1
by Tom Berger
patch from allenap |
300 |
def test_not_many_bugtasks(self): |
301 |
for count in range(10 - len(self.bug.bugtasks) - 1): |
|
302 |
self.factory.makeBugTask(bug=self.bug) |
|
303 |
self.view.initialize() |
|
304 |
self.failIf(self.view.many_bugtasks) |
|
305 |
row_view = self.view._getTableRowView( |
|
306 |
self.bug.default_bugtask, False, False) |
|
307 |
self.failIf(row_view.many_bugtasks) |
|
308 |
||
309 |
def test_many_bugtasks(self): |
|
310 |
for count in range(10 - len(self.bug.bugtasks)): |
|
311 |
self.factory.makeBugTask(bug=self.bug) |
|
312 |
self.view.initialize() |
|
313 |
self.failUnless(self.view.many_bugtasks) |
|
314 |
row_view = self.view._getTableRowView( |
|
315 |
self.bug.default_bugtask, False, False) |
|
316 |
self.failUnless(row_view.many_bugtasks) |
|
317 |
||
10015.1.2
by Gavin Panella
New view property other_users_affected_count. |
318 |
def test_other_users_affected_count(self): |
319 |
# The number of other users affected does not change when the
|
|
320 |
# logged-in user marked him or herself as affected or not.
|
|
321 |
self.failUnlessEqual( |
|
322 |
1, self.view.other_users_affected_count) |
|
11582.2.5
by Robert Collins
Fix up test fallout. |
323 |
self.bug.markUserAffected(self.view.user, True) |
324 |
self.refresh() |
|
10015.1.2
by Gavin Panella
New view property other_users_affected_count. |
325 |
self.failUnlessEqual( |
326 |
1, self.view.other_users_affected_count) |
|
11582.2.5
by Robert Collins
Fix up test fallout. |
327 |
self.bug.markUserAffected(self.view.user, False) |
328 |
self.refresh() |
|
10015.1.2
by Gavin Panella
New view property other_users_affected_count. |
329 |
self.failUnlessEqual( |
330 |
1, self.view.other_users_affected_count) |
|
331 |
||
332 |
def test_other_users_affected_count_other_users(self): |
|
333 |
# The number of other users affected only changes when other
|
|
334 |
# users mark themselves as affected.
|
|
335 |
self.failUnlessEqual( |
|
336 |
1, self.view.other_users_affected_count) |
|
337 |
other_user_1 = self.factory.makePerson() |
|
11582.2.5
by Robert Collins
Fix up test fallout. |
338 |
self.bug.markUserAffected(other_user_1, True) |
14189.6.9
by mbp at canonical
Add other_users_affected_count_with_dupes to get the right answers when the current user is affected by a dupe |
339 |
self.refresh() |
10015.1.2
by Gavin Panella
New view property other_users_affected_count. |
340 |
self.failUnlessEqual( |
341 |
2, self.view.other_users_affected_count) |
|
342 |
other_user_2 = self.factory.makePerson() |
|
11582.2.5
by Robert Collins
Fix up test fallout. |
343 |
self.bug.markUserAffected(other_user_2, True) |
14189.6.9
by mbp at canonical
Add other_users_affected_count_with_dupes to get the right answers when the current user is affected by a dupe |
344 |
self.refresh() |
10015.1.2
by Gavin Panella
New view property other_users_affected_count. |
345 |
self.failUnlessEqual( |
346 |
3, self.view.other_users_affected_count) |
|
11582.2.5
by Robert Collins
Fix up test fallout. |
347 |
self.bug.markUserAffected(other_user_1, False) |
14189.6.9
by mbp at canonical
Add other_users_affected_count_with_dupes to get the right answers when the current user is affected by a dupe |
348 |
self.refresh() |
10015.1.2
by Gavin Panella
New view property other_users_affected_count. |
349 |
self.failUnlessEqual( |
350 |
2, self.view.other_users_affected_count) |
|
11582.2.5
by Robert Collins
Fix up test fallout. |
351 |
self.bug.markUserAffected(self.view.user, True) |
352 |
self.refresh() |
|
10015.1.2
by Gavin Panella
New view property other_users_affected_count. |
353 |
self.failUnlessEqual( |
354 |
2, self.view.other_users_affected_count) |
|
355 |
||
14189.6.9
by mbp at canonical
Add other_users_affected_count_with_dupes to get the right answers when the current user is affected by a dupe |
356 |
def makeDuplicate(self): |
357 |
user2 = self.factory.makePerson() |
|
358 |
self.bug2 = self.factory.makeBug() |
|
359 |
self.bug2.markUserAffected(user2, True) |
|
360 |
self.assertEqual( |
|
361 |
2, self.bug2.users_affected_count) |
|
362 |
self.bug2.markAsDuplicate(self.bug) |
|
363 |
# After this there are three users already affected: the creators of
|
|
364 |
# the two bugs, plus user2. The current user is not yet affected by
|
|
365 |
# any of them.
|
|
366 |
||
367 |
def test_counts_user_unaffected(self): |
|
14189.6.3
by mbp at canonical
Counting total affected users across dupes can be disabled by a feature flag. |
368 |
self.useFixture(FeatureFixture( |
369 |
{'bugs.affected_count_includes_dupes.disabled': ''})) |
|
14189.6.9
by mbp at canonical
Add other_users_affected_count_with_dupes to get the right answers when the current user is affected by a dupe |
370 |
self.makeDuplicate() |
14189.6.3
by mbp at canonical
Counting total affected users across dupes can be disabled by a feature flag. |
371 |
self.assertEqual( |
372 |
3, self.view.total_users_affected_count) |
|
14189.6.9
by mbp at canonical
Add other_users_affected_count_with_dupes to get the right answers when the current user is affected by a dupe |
373 |
self.assertEqual( |
374 |
"This bug affects 3 people. Does this bug affect you?", |
|
375 |
self.view.affected_statement) |
|
376 |
self.assertEqual( |
|
377 |
"This bug affects 3 people", |
|
378 |
self.view.anon_affected_statement) |
|
379 |
self.assertEqual( |
|
380 |
self.view.other_users_affected_count, |
|
381 |
3) |
|
382 |
||
383 |
def test_counts_affected_by_duplicate(self): |
|
384 |
self.useFixture(FeatureFixture( |
|
385 |
{'bugs.affected_count_includes_dupes.disabled': ''})) |
|
386 |
self.makeDuplicate() |
|
387 |
# Now with you affected by the duplicate, but not the master.
|
|
388 |
self.bug2.markUserAffected(self.view.user, True) |
|
389 |
self.refresh() |
|
390 |
self.assertEqual( |
|
391 |
"This bug affects 3 people. Does this bug affect you?", |
|
392 |
self.view.affected_statement) |
|
393 |
self.assertEqual( |
|
394 |
"This bug affects 4 people", |
|
395 |
self.view.anon_affected_statement) |
|
396 |
self.assertEqual( |
|
397 |
self.view.other_users_affected_count, |
|
398 |
3) |
|
399 |
||
400 |
def test_counts_affected_by_master(self): |
|
401 |
self.useFixture(FeatureFixture( |
|
402 |
{'bugs.affected_count_includes_dupes.disabled': ''})) |
|
403 |
self.makeDuplicate() |
|
404 |
# And now with you also affected by the master.
|
|
405 |
self.bug.markUserAffected(self.view.user, True) |
|
406 |
self.refresh() |
|
407 |
self.assertEqual( |
|
408 |
"This bug affects you and 3 other people", |
|
409 |
self.view.affected_statement) |
|
410 |
self.assertEqual( |
|
411 |
"This bug affects 4 people", |
|
412 |
self.view.anon_affected_statement) |
|
413 |
self.assertEqual( |
|
414 |
self.view.other_users_affected_count, |
|
415 |
3) |
|
416 |
||
417 |
def test_counts_affected_by_duplicate_not_by_master(self): |
|
418 |
self.useFixture(FeatureFixture( |
|
419 |
{'bugs.affected_count_includes_dupes.disabled': ''})) |
|
420 |
self.makeDuplicate() |
|
421 |
self.bug2.markUserAffected(self.view.user, True) |
|
422 |
self.bug.markUserAffected(self.view.user, False) |
|
423 |
# You're not included in this count, even though you are affected by
|
|
424 |
# the dupe.
|
|
425 |
self.assertEqual( |
|
426 |
"This bug affects 3 people, but not you", |
|
427 |
self.view.affected_statement) |
|
428 |
# It would be reasonable for Anon to see this bug cluster affecting
|
|
429 |
# either 3 or 4 people. However at the moment the "No" answer on the
|
|
430 |
# master is more authoritative than the "Yes" on the dupe.
|
|
431 |
self.assertEqual( |
|
432 |
"This bug affects 3 people", |
|
433 |
self.view.anon_affected_statement) |
|
434 |
self.assertEqual( |
|
435 |
self.view.other_users_affected_count, |
|
436 |
3) |
|
14189.6.3
by mbp at canonical
Counting total affected users across dupes can be disabled by a feature flag. |
437 |
|
438 |
def test_total_users_affected_count_without_dupes(self): |
|
439 |
self.useFixture(FeatureFixture( |
|
440 |
{'bugs.affected_count_includes_dupes.disabled': 'on'})) |
|
14189.6.9
by mbp at canonical
Add other_users_affected_count_with_dupes to get the right answers when the current user is affected by a dupe |
441 |
self.makeDuplicate() |
14189.6.3
by mbp at canonical
Counting total affected users across dupes can be disabled by a feature flag. |
442 |
self.refresh() |
443 |
# Does not count the two users of bug2, so just 1.
|
|
444 |
self.assertEqual( |
|
445 |
1, self.view.total_users_affected_count) |
|
14189.6.4
by mbp at canonical
Also count across dupes in the anonymous affects statement; check both affectsmetoo statements |
446 |
self.assertEqual( |
447 |
"This bug affects 1 person. Does this bug affect you?", |
|
448 |
self.view.affected_statement) |
|
449 |
self.assertEqual( |
|
450 |
"This bug affects 1 person", |
|
451 |
self.view.anon_affected_statement) |
|
14189.6.6
by mbp at canonical
other_users_affected_count also counts across dupes to work with js |
452 |
self.assertEqual( |
453 |
1, |
|
454 |
self.view.other_users_affected_count) |
|
14189.6.3
by mbp at canonical
Counting total affected users across dupes can be disabled by a feature flag. |
455 |
|
10015.1.3
by Gavin Panella
New view property affected_statement. |
456 |
def test_affected_statement_no_one_affected(self): |
457 |
self.bug.markUserAffected(self.bug.owner, False) |
|
458 |
self.failUnlessEqual( |
|
459 |
0, self.view.other_users_affected_count) |
|
460 |
self.failUnlessEqual( |
|
461 |
"Does this bug affect you?", |
|
462 |
self.view.affected_statement) |
|
463 |
||
464 |
def test_affected_statement_only_you(self): |
|
465 |
self.view.context.markUserAffected(self.view.user, True) |
|
466 |
self.failUnless(self.bug.isUserAffected(self.view.user)) |
|
467 |
self.view.context.markUserAffected(self.bug.owner, False) |
|
468 |
self.failUnlessEqual( |
|
469 |
0, self.view.other_users_affected_count) |
|
470 |
self.failUnlessEqual( |
|
10015.1.7
by Gavin Panella
Remove trailing full-stops from the affected statement. |
471 |
"This bug affects you", |
10015.1.3
by Gavin Panella
New view property affected_statement. |
472 |
self.view.affected_statement) |
473 |
||
10015.1.8
by Gavin Panella
Make affected_statement different when a user has explicitly said they are not affected, and when they have not. |
474 |
def test_affected_statement_only_not_you(self): |
475 |
self.view.context.markUserAffected(self.view.user, False) |
|
476 |
self.failIf(self.bug.isUserAffected(self.view.user)) |
|
477 |
self.view.context.markUserAffected(self.bug.owner, False) |
|
478 |
self.failUnlessEqual( |
|
479 |
0, self.view.other_users_affected_count) |
|
480 |
self.failUnlessEqual( |
|
10015.1.10
by Gavin Panella
Say "doesn't" instead of "does not". |
481 |
"This bug doesn't affect you", |
10015.1.8
by Gavin Panella
Make affected_statement different when a user has explicitly said they are not affected, and when they have not. |
482 |
self.view.affected_statement) |
483 |
||
10015.1.3
by Gavin Panella
New view property affected_statement. |
484 |
def test_affected_statement_1_person_not_you(self): |
485 |
self.assertIs(None, self.bug.isUserAffected(self.view.user)) |
|
486 |
self.failUnlessEqual( |
|
487 |
1, self.view.other_users_affected_count) |
|
488 |
self.failUnlessEqual( |
|
489 |
"This bug affects 1 person. Does this bug affect you?", |
|
490 |
self.view.affected_statement) |
|
491 |
||
492 |
def test_affected_statement_1_person_and_you(self): |
|
493 |
self.view.context.markUserAffected(self.view.user, True) |
|
494 |
self.failUnless(self.bug.isUserAffected(self.view.user)) |
|
495 |
self.failUnlessEqual( |
|
496 |
1, self.view.other_users_affected_count) |
|
497 |
self.failUnlessEqual( |
|
10015.1.7
by Gavin Panella
Remove trailing full-stops from the affected statement. |
498 |
"This bug affects you and 1 other person", |
10015.1.3
by Gavin Panella
New view property affected_statement. |
499 |
self.view.affected_statement) |
500 |
||
10015.1.8
by Gavin Panella
Make affected_statement different when a user has explicitly said they are not affected, and when they have not. |
501 |
def test_affected_statement_1_person_and_not_you(self): |
502 |
self.view.context.markUserAffected(self.view.user, False) |
|
503 |
self.failIf(self.bug.isUserAffected(self.view.user)) |
|
504 |
self.failUnlessEqual( |
|
505 |
1, self.view.other_users_affected_count) |
|
506 |
self.failUnlessEqual( |
|
507 |
"This bug affects 1 person, but not you", |
|
508 |
self.view.affected_statement) |
|
509 |
||
10015.1.3
by Gavin Panella
New view property affected_statement. |
510 |
def test_affected_statement_more_than_1_person_not_you(self): |
511 |
self.assertIs(None, self.bug.isUserAffected(self.view.user)) |
|
512 |
other_user = self.factory.makePerson() |
|
513 |
self.view.context.markUserAffected(other_user, True) |
|
514 |
self.failUnlessEqual( |
|
515 |
2, self.view.other_users_affected_count) |
|
516 |
self.failUnlessEqual( |
|
517 |
"This bug affects 2 people. Does this bug affect you?", |
|
518 |
self.view.affected_statement) |
|
519 |
||
520 |
def test_affected_statement_more_than_1_person_and_you(self): |
|
521 |
self.view.context.markUserAffected(self.view.user, True) |
|
522 |
self.failUnless(self.bug.isUserAffected(self.view.user)) |
|
523 |
other_user = self.factory.makePerson() |
|
524 |
self.view.context.markUserAffected(other_user, True) |
|
525 |
self.failUnlessEqual( |
|
526 |
2, self.view.other_users_affected_count) |
|
527 |
self.failUnlessEqual( |
|
10015.1.7
by Gavin Panella
Remove trailing full-stops from the affected statement. |
528 |
"This bug affects you and 2 other people", |
10015.1.3
by Gavin Panella
New view property affected_statement. |
529 |
self.view.affected_statement) |
530 |
||
10015.1.8
by Gavin Panella
Make affected_statement different when a user has explicitly said they are not affected, and when they have not. |
531 |
def test_affected_statement_more_than_1_person_and_not_you(self): |
532 |
self.view.context.markUserAffected(self.view.user, False) |
|
533 |
self.failIf(self.bug.isUserAffected(self.view.user)) |
|
534 |
other_user = self.factory.makePerson() |
|
535 |
self.view.context.markUserAffected(other_user, True) |
|
536 |
self.failUnlessEqual( |
|
537 |
2, self.view.other_users_affected_count) |
|
538 |
self.failUnlessEqual( |
|
539 |
"This bug affects 2 people, but not you", |
|
540 |
self.view.affected_statement) |
|
541 |
||
10015.1.18
by Gavin Panella
New view property anon_affected_statement. |
542 |
def test_anon_affected_statement_no_one_affected(self): |
543 |
self.bug.markUserAffected(self.bug.owner, False) |
|
544 |
self.failUnlessEqual(0, self.bug.users_affected_count) |
|
545 |
self.assertIs(None, self.view.anon_affected_statement) |
|
546 |
||
547 |
def test_anon_affected_statement_1_user_affected(self): |
|
548 |
self.failUnlessEqual(1, self.bug.users_affected_count) |
|
549 |
self.failUnlessEqual( |
|
550 |
"This bug affects 1 person", |
|
551 |
self.view.anon_affected_statement) |
|
552 |
||
553 |
def test_anon_affected_statement_2_users_affected(self): |
|
554 |
self.view.context.markUserAffected(self.view.user, True) |
|
555 |
self.failUnlessEqual(2, self.bug.users_affected_count) |
|
556 |
self.failUnlessEqual( |
|
557 |
"This bug affects 2 people", |
|
558 |
self.view.anon_affected_statement) |
|
559 |
||
12561.3.19
by Curtis Hovey
Moved product and productseries getTargetLinkTitle tests to a unit test. |
560 |
def test_getTargetLinkTitle_product(self): |
561 |
# The target link title is always none for products.
|
|
562 |
target = self.factory.makeProduct() |
|
563 |
bug_task = self.factory.makeBugTask(bug=self.bug, target=target) |
|
12561.3.20
by Curtis Hovey
Reconstructed the BugTasksAndNominationsView.getTargetLinkTitle test. |
564 |
self.view.initialize() |
565 |
self.assertEqual(None, self.view.getTargetLinkTitle(bug_task.target)) |
|
12561.3.19
by Curtis Hovey
Moved product and productseries getTargetLinkTitle tests to a unit test. |
566 |
|
567 |
def test_getTargetLinkTitle_productseries(self): |
|
12561.3.20
by Curtis Hovey
Reconstructed the BugTasksAndNominationsView.getTargetLinkTitle test. |
568 |
# The target link title is always none for productseries.
|
12561.3.19
by Curtis Hovey
Moved product and productseries getTargetLinkTitle tests to a unit test. |
569 |
target = self.factory.makeProductSeries() |
570 |
bug_task = self.factory.makeBugTask(bug=self.bug, target=target) |
|
12561.3.20
by Curtis Hovey
Reconstructed the BugTasksAndNominationsView.getTargetLinkTitle test. |
571 |
self.view.initialize() |
572 |
self.assertEqual(None, self.view.getTargetLinkTitle(bug_task.target)) |
|
573 |
||
574 |
def test_getTargetLinkTitle_distribution(self): |
|
575 |
# The target link title is always none for distributions.
|
|
576 |
target = self.factory.makeDistribution() |
|
577 |
bug_task = self.factory.makeBugTask(bug=self.bug, target=target) |
|
578 |
self.view.initialize() |
|
579 |
self.assertEqual(None, self.view.getTargetLinkTitle(bug_task.target)) |
|
580 |
||
581 |
def test_getTargetLinkTitle_distroseries(self): |
|
582 |
# The target link title is always none for distroseries.
|
|
583 |
target = self.factory.makeDistroSeries() |
|
584 |
bug_task = self.factory.makeBugTask(bug=self.bug, target=target) |
|
585 |
self.view.initialize() |
|
586 |
self.assertEqual(None, self.view.getTargetLinkTitle(bug_task.target)) |
|
587 |
||
588 |
def test_getTargetLinkTitle_unpublished_distributionsourcepackage(self): |
|
589 |
# The target link title states that the package is not published
|
|
590 |
# in the current release.
|
|
591 |
distribution = self.factory.makeDistribution(name='boy') |
|
592 |
spn = self.factory.makeSourcePackageName('badger') |
|
593 |
component = getUtility(IComponentSet)['universe'] |
|
594 |
maintainer = self.factory.makePerson(name="jim") |
|
595 |
creator = self.factory.makePerson(name="tim") |
|
596 |
self.factory.makeSourcePackagePublishingHistory( |
|
597 |
distroseries=distribution.currentseries, version='2.0', |
|
598 |
component=component, sourcepackagename=spn, |
|
599 |
date_uploaded=datetime(2008, 7, 18, 10, 20, 30, tzinfo=UTC), |
|
600 |
maintainer=maintainer, creator=creator) |
|
601 |
target = distribution.getSourcePackage('badger') |
|
602 |
bug_task = self.factory.makeBugTask( |
|
603 |
bug=self.bug, target=target, publish=False) |
|
604 |
self.view.initialize() |
|
12561.3.21
by Curtis Hovey
Removed duplicate test. |
605 |
self.assertEqual({}, self.view.target_releases) |
12561.3.20
by Curtis Hovey
Reconstructed the BugTasksAndNominationsView.getTargetLinkTitle test. |
606 |
self.assertEqual( |
607 |
'No current release for this source package in Boy', |
|
608 |
self.view.getTargetLinkTitle(bug_task.target)) |
|
609 |
||
610 |
def test_getTargetLinkTitle_published_distributionsourcepackage(self): |
|
611 |
# The target link title states the information about the current
|
|
612 |
# package in the distro.
|
|
613 |
distribution = self.factory.makeDistribution(name='koi') |
|
614 |
distroseries = self.factory.makeDistroSeries( |
|
615 |
distribution=distribution) |
|
616 |
spn = self.factory.makeSourcePackageName('finch') |
|
617 |
component = getUtility(IComponentSet)['universe'] |
|
618 |
maintainer = self.factory.makePerson(name="jim") |
|
619 |
creator = self.factory.makePerson(name="tim") |
|
620 |
self.factory.makeSourcePackagePublishingHistory( |
|
621 |
distroseries=distroseries, version='2.0', |
|
622 |
component=component, sourcepackagename=spn, |
|
623 |
date_uploaded=datetime(2008, 7, 18, 10, 20, 30, tzinfo=UTC), |
|
624 |
maintainer=maintainer, creator=creator) |
|
625 |
target = distribution.getSourcePackage('finch') |
|
626 |
bug_task = self.factory.makeBugTask( |
|
627 |
bug=self.bug, target=target, publish=False) |
|
628 |
self.view.initialize() |
|
12561.3.21
by Curtis Hovey
Removed duplicate test. |
629 |
self.assertTrue( |
630 |
target in self.view.target_releases.keys()) |
|
12561.3.20
by Curtis Hovey
Reconstructed the BugTasksAndNominationsView.getTargetLinkTitle test. |
631 |
self.assertEqual( |
632 |
'Latest release: 2.0, uploaded to universe on '
|
|
633 |
'2008-07-18 10:20:30+00:00 by Tim (tim), maintained by Jim (jim)', |
|
634 |
self.view.getTargetLinkTitle(bug_task.target)) |
|
635 |
||
636 |
def test_getTargetLinkTitle_published_sourcepackage(self): |
|
637 |
# The target link title states the information about the current
|
|
638 |
# package in the distro.
|
|
639 |
distroseries = self.factory.makeDistroSeries() |
|
640 |
spn = self.factory.makeSourcePackageName('bunny') |
|
641 |
component = getUtility(IComponentSet)['universe'] |
|
642 |
maintainer = self.factory.makePerson(name="jim") |
|
643 |
creator = self.factory.makePerson(name="tim") |
|
644 |
self.factory.makeSourcePackagePublishingHistory( |
|
645 |
distroseries=distroseries, version='2.0', |
|
646 |
component=component, sourcepackagename=spn, |
|
647 |
date_uploaded=datetime(2008, 7, 18, 10, 20, 30, tzinfo=UTC), |
|
648 |
maintainer=maintainer, creator=creator) |
|
649 |
target = distroseries.getSourcePackage('bunny') |
|
650 |
bug_task = self.factory.makeBugTask( |
|
651 |
bug=self.bug, target=target, publish=False) |
|
652 |
self.view.initialize() |
|
12561.3.21
by Curtis Hovey
Removed duplicate test. |
653 |
self.assertTrue( |
654 |
target in self.view.target_releases.keys()) |
|
12561.3.20
by Curtis Hovey
Reconstructed the BugTasksAndNominationsView.getTargetLinkTitle test. |
655 |
self.assertEqual( |
656 |
'Latest release: 2.0, uploaded to universe on '
|
|
657 |
'2008-07-18 10:20:30+00:00 by Tim (tim), maintained by Jim (jim)', |
|
658 |
self.view.getTargetLinkTitle(bug_task.target)) |
|
12561.3.19
by Curtis Hovey
Moved product and productseries getTargetLinkTitle tests to a unit test. |
659 |
|
12799.1.2
by Ian Booth
Rework implementation and do unit tests instead of doc tests |
660 |
def _get_object_type(self, task_or_nomination): |
661 |
if IBugTask.providedBy(task_or_nomination): |
|
662 |
return "bugtask" |
|
663 |
elif IBugNomination.providedBy(task_or_nomination): |
|
664 |
return "nomination" |
|
665 |
else: |
|
666 |
return "unknown" |
|
667 |
||
668 |
def test_bugtask_listing_for_inactive_projects(self): |
|
669 |
# Bugtasks should only be listed for active projects.
|
|
670 |
||
671 |
product_foo = self.factory.makeProduct(name="foo") |
|
672 |
product_bar = self.factory.makeProduct(name="bar") |
|
673 |
foo_bug = self.factory.makeBug(product=product_foo) |
|
674 |
bugtask_set = getUtility(IBugTaskSet) |
|
13571.2.2
by William Grant
BugTaskSet.createTask now takes an IBugTarget, not a key. Blergh. |
675 |
bugtask_set.createTask(foo_bug, foo_bug.owner, product_bar) |
12799.1.2
by Ian Booth
Rework implementation and do unit tests instead of doc tests |
676 |
removeSecurityProxy(product_bar).active = False |
677 |
||
678 |
request = LaunchpadTestRequest() |
|
679 |
foo_bugtasks_and_nominations_view = getMultiAdapter( |
|
14186.3.8
by Ian Booth
Add ajax bug task delete functionality |
680 |
(foo_bug, request), name="+bugtasks-and-nominations-portal") |
12799.1.2
by Ian Booth
Rework implementation and do unit tests instead of doc tests |
681 |
foo_bugtasks_and_nominations_view.initialize() |
682 |
||
683 |
task_and_nomination_views = ( |
|
684 |
foo_bugtasks_and_nominations_view.getBugTaskAndNominationViews()) |
|
685 |
actual_results = [] |
|
686 |
for task_or_nomination_view in task_and_nomination_views: |
|
687 |
task_or_nomination = task_or_nomination_view.context |
|
688 |
actual_results.append(( |
|
689 |
self._get_object_type(task_or_nomination), |
|
690 |
task_or_nomination.status.title, |
|
691 |
task_or_nomination.target.bugtargetdisplayname)) |
|
692 |
# Only the one active project's task should be listed.
|
|
693 |
self.assertEqual([("bugtask", "New", "Foo")], actual_results) |
|
694 |
||
695 |
def test_listing_with_no_bugtasks(self): |
|
696 |
# Test the situation when there are no bugtasks to show.
|
|
697 |
||
698 |
product_foo = self.factory.makeProduct(name="foo") |
|
699 |
foo_bug = self.factory.makeBug(product=product_foo) |
|
700 |
removeSecurityProxy(product_foo).active = False |
|
701 |
||
702 |
request = LaunchpadTestRequest() |
|
703 |
foo_bugtasks_and_nominations_view = getMultiAdapter( |
|
14186.3.8
by Ian Booth
Add ajax bug task delete functionality |
704 |
(foo_bug, request), name="+bugtasks-and-nominations-portal") |
12799.1.2
by Ian Booth
Rework implementation and do unit tests instead of doc tests |
705 |
foo_bugtasks_and_nominations_view.initialize() |
706 |
||
707 |
task_and_nomination_views = ( |
|
708 |
foo_bugtasks_and_nominations_view.getBugTaskAndNominationViews()) |
|
709 |
self.assertEqual([], task_and_nomination_views) |
|
710 |
||
13543.10.1
by William Grant
Display a faded statusless row for the parents of orphaned bugtasks. |
711 |
def test_bugtarget_parent_shown_for_orphaned_series_tasks(self): |
712 |
# Test that a row is shown for the parent of a series task, even
|
|
713 |
# if the parent doesn't actually have a task.
|
|
714 |
series = self.factory.makeProductSeries() |
|
715 |
bug = self.factory.makeBug(series=series) |
|
716 |
self.assertEqual(2, len(bug.bugtasks)) |
|
717 |
new_prod = self.factory.makeProduct() |
|
718 |
bug.getBugTask(series.product).transitionToTarget(new_prod) |
|
719 |
||
720 |
view = create_initialized_view(bug, "+bugtasks-and-nominations-table") |
|
721 |
subviews = view.getBugTaskAndNominationViews() |
|
722 |
self.assertEqual([ |
|
723 |
(series.product, '+bugtasks-and-nominations-table-row'), |
|
724 |
(bug.getBugTask(series), '+bugtasks-and-nominations-table-row'), |
|
725 |
(bug.getBugTask(new_prod), '+bugtasks-and-nominations-table-row'), |
|
726 |
], [(v.context, v.__name__) for v in subviews]) |
|
727 |
||
728 |
content = subviews[0]() |
|
729 |
self.assertIn( |
|
730 |
'href="%s"' % canonical_url( |
|
731 |
series.product, path_only_if_possible=True), |
|
732 |
content) |
|
733 |
self.assertIn(series.product.displayname, content) |
|
734 |
||
14235.4.7
by Ian Booth
Rename permission to launchpad.See and write tests |
735 |
def test_bugtask_listing_for_private_assignees(self): |
736 |
# Private assignees are rendered in the bug portal view.
|
|
737 |
||
738 |
# Create a bugtask with a private assignee.
|
|
739 |
product_foo = self.factory.makeProduct(name="foo") |
|
740 |
foo_bug = self.factory.makeBug(product=product_foo) |
|
741 |
assignee = self.factory.makeTeam( |
|
742 |
name="assignee", |
|
743 |
visibility=PersonVisibility.PRIVATE) |
|
744 |
foo_bug.default_bugtask.transitionToAssignee(assignee) |
|
745 |
||
746 |
# Render the view.
|
|
747 |
request = LaunchpadTestRequest() |
|
748 |
any_person = self.factory.makePerson() |
|
749 |
login_person(any_person, request) |
|
750 |
foo_bugtasks_and_nominations_view = getMultiAdapter( |
|
751 |
(foo_bug, request), name="+bugtasks-and-nominations-portal") |
|
752 |
foo_bugtasks_and_nominations_view.initialize() |
|
753 |
task_and_nomination_views = ( |
|
754 |
foo_bugtasks_and_nominations_view.getBugTaskAndNominationViews()) |
|
755 |
getUtility(ILaunchBag).add(foo_bug.default_bugtask) |
|
756 |
self.assertEqual(1, len(task_and_nomination_views)) |
|
757 |
content = task_and_nomination_views[0]() |
|
758 |
||
759 |
# Check the result.
|
|
760 |
soup = BeautifulSoup(content) |
|
761 |
tag = soup.find('label', attrs={'for': "foo.assignee.assigned_to"}) |
|
762 |
tag_text = tag.renderContents().strip() |
|
763 |
self.assertEqual(assignee.unique_displayname, tag_text) |
|
764 |
||
2770.1.40
by Guilherme Salgado
A few fixes Bjorn suggested and removing two unused templates. |
765 |
|
14186.3.2
by Ian Booth
Add tests for delete view |
766 |
class TestBugTaskDeleteLinks(TestCaseWithFactory): |
14186.3.3
by Ian Booth
Implement code review changes |
767 |
""" Test that the delete icons/links are correctly rendered.
|
14186.3.2
by Ian Booth
Add tests for delete view |
768 |
|
769 |
Bug task deletion is protected by a feature flag.
|
|
770 |
"""
|
|
771 |
||
772 |
layer = DatabaseFunctionalLayer |
|
773 |
||
774 |
def test_cannot_delete_only_bugtask(self): |
|
775 |
# The last bugtask cannot be deleted.
|
|
776 |
bug = self.factory.makeBug() |
|
777 |
login_person(bug.owner) |
|
778 |
view = create_initialized_view( |
|
779 |
bug, name='+bugtasks-and-nominations-table') |
|
780 |
row_view = view._getTableRowView(bug.default_bugtask, False, False) |
|
781 |
self.assertFalse(row_view.user_can_delete_bugtask) |
|
782 |
del get_property_cache(row_view).user_can_delete_bugtask |
|
783 |
with FeatureFixture(DELETE_BUGTASK_ENABLED): |
|
784 |
self.assertFalse(row_view.user_can_delete_bugtask) |
|
785 |
||
786 |
def test_can_delete_bugtask_if_authorised(self): |
|
787 |
# The bugtask can be deleted if the user if authorised.
|
|
788 |
bug = self.factory.makeBug() |
|
789 |
bugtask = self.factory.makeBugTask(bug=bug) |
|
790 |
login_person(bugtask.owner) |
|
791 |
view = create_initialized_view( |
|
14186.3.3
by Ian Booth
Implement code review changes |
792 |
bug, name='+bugtasks-and-nominations-table', |
793 |
principal=bugtask.owner) |
|
14186.3.2
by Ian Booth
Add tests for delete view |
794 |
row_view = view._getTableRowView(bugtask, False, False) |
795 |
self.assertFalse(row_view.user_can_delete_bugtask) |
|
796 |
del get_property_cache(row_view).user_can_delete_bugtask |
|
14186.3.3
by Ian Booth
Implement code review changes |
797 |
clear_cache() |
14186.3.2
by Ian Booth
Add tests for delete view |
798 |
with FeatureFixture(DELETE_BUGTASK_ENABLED): |
799 |
self.assertTrue(row_view.user_can_delete_bugtask) |
|
800 |
||
801 |
def test_bugtask_delete_icon(self): |
|
802 |
# The bugtask delete icon is rendered correctly for those tasks the
|
|
803 |
# user is allowed to delete.
|
|
804 |
bug = self.factory.makeBug() |
|
14186.3.3
by Ian Booth
Implement code review changes |
805 |
bugtask_owner = self.factory.makePerson() |
806 |
bugtask = self.factory.makeBugTask(bug=bug, owner=bugtask_owner) |
|
14186.3.2
by Ian Booth
Add tests for delete view |
807 |
with FeatureFixture(DELETE_BUGTASK_ENABLED): |
14186.3.5
by Ian Booth
Figured out how to replace TestBrowser with create_view |
808 |
login_person(bugtask.owner) |
809 |
getUtility(ILaunchBag).add(bug.default_bugtask) |
|
810 |
view = create_initialized_view( |
|
811 |
bug, name='+bugtasks-and-nominations-table', |
|
812 |
principal=bugtask.owner) |
|
813 |
# We render the bug task table rows - there are 2 bug tasks.
|
|
814 |
subviews = view.getBugTaskAndNominationViews() |
|
815 |
self.assertEqual(2, len(subviews)) |
|
816 |
default_bugtask_contents = subviews[0]() |
|
817 |
bugtask_contents = subviews[1]() |
|
14186.3.2
by Ian Booth
Add tests for delete view |
818 |
# bugtask can be deleted because the user owns it.
|
819 |
delete_icon = find_tag_by_id( |
|
14186.3.5
by Ian Booth
Figured out how to replace TestBrowser with create_view |
820 |
bugtask_contents, 'bugtask-delete-task%d' % bugtask.id) |
14186.3.3
by Ian Booth
Implement code review changes |
821 |
delete_url = canonical_url( |
822 |
bugtask, rootsite='bugs', view_name='+delete') |
|
823 |
self.assertEqual(delete_url, delete_icon['href']) |
|
14186.3.2
by Ian Booth
Add tests for delete view |
824 |
# default_bugtask cannot be deleted.
|
825 |
delete_icon = find_tag_by_id( |
|
14186.3.5
by Ian Booth
Figured out how to replace TestBrowser with create_view |
826 |
default_bugtask_contents, |
14186.3.2
by Ian Booth
Add tests for delete view |
827 |
'bugtask-delete-task%d' % bug.default_bugtask.id) |
828 |
self.assertIsNone(delete_icon) |
|
829 |
||
14186.3.16
by Ian Booth
Add unit test |
830 |
def test_client_cache_contents(self): |
831 |
""" Test that the client cache contains the expected data.
|
|
832 |
||
14186.3.18
by Ian Booth
Lint and code review fixes |
833 |
The cache data is used by the Javascript to enable the delete
|
834 |
links to work as expected.
|
|
835 |
"""
|
|
14186.3.16
by Ian Booth
Add unit test |
836 |
bug = self.factory.makeBug() |
837 |
bugtask_owner = self.factory.makePerson() |
|
838 |
bugtask = self.factory.makeBugTask(bug=bug, owner=bugtask_owner) |
|
839 |
with FeatureFixture(DELETE_BUGTASK_ENABLED): |
|
840 |
login_person(bugtask.owner) |
|
841 |
getUtility(ILaunchBag).add(bug.default_bugtask) |
|
842 |
view = create_initialized_view( |
|
843 |
bug, name='+bugtasks-and-nominations-table', |
|
844 |
principal=bugtask.owner) |
|
845 |
view.render() |
|
846 |
cache = IJSONRequestCache(view.request) |
|
847 |
all_bugtask_data = cache.objects['bugtask_data'] |
|
848 |
||
849 |
def check_bugtask_data(bugtask, can_delete): |
|
850 |
self.assertIn(bugtask.id, all_bugtask_data) |
|
851 |
bugtask_data = all_bugtask_data[bugtask.id] |
|
852 |
self.assertEqual( |
|
853 |
'task%d' % bugtask.id, bugtask_data['form_row_id']) |
|
854 |
self.assertEqual( |
|
855 |
'tasksummary%d' % bugtask.id, bugtask_data['row_id']) |
|
856 |
self.assertEqual(can_delete, bugtask_data['user_can_delete']) |
|
857 |
||
858 |
check_bugtask_data(bug.default_bugtask, False) |
|
859 |
check_bugtask_data(bugtask, True) |
|
860 |
||
14186.3.2
by Ian Booth
Add tests for delete view |
861 |
|
862 |
class TestBugTaskDeleteView(TestCaseWithFactory): |
|
863 |
"""Test the bug task delete form."""
|
|
864 |
||
865 |
layer = DatabaseFunctionalLayer |
|
866 |
||
867 |
def test_delete_view_rendering(self): |
|
868 |
# Test the view rendering, including confirmation message, cancel url.
|
|
869 |
bug = self.factory.makeBug() |
|
870 |
bugtask = self.factory.makeBugTask(bug=bug) |
|
14186.4.1
by Ian Booth
Use ReturnToReferrerMixin for bugtask deletion view |
871 |
bug_url = canonical_url(bugtask.bug, rootsite='bugs') |
872 |
# Set up request so that the ReturnToReferrerMixin can correctly
|
|
873 |
# extra the referer url.
|
|
874 |
server_url = canonical_url( |
|
875 |
getUtility(ILaunchpadRoot), rootsite='bugs') |
|
876 |
extra = {'HTTP_REFERER': bug_url} |
|
14186.3.2
by Ian Booth
Add tests for delete view |
877 |
with FeatureFixture(DELETE_BUGTASK_ENABLED): |
878 |
login_person(bugtask.owner) |
|
879 |
view = create_initialized_view( |
|
14186.4.1
by Ian Booth
Use ReturnToReferrerMixin for bugtask deletion view |
880 |
bugtask, name='+delete', principal=bugtask.owner, |
881 |
server_url=server_url, **extra) |
|
14186.3.3
by Ian Booth
Implement code review changes |
882 |
contents = view.render() |
883 |
confirmation_message = find_tag_by_id( |
|
884 |
contents, 'confirmation-message') |
|
885 |
self.assertIsNotNone(confirmation_message) |
|
14186.4.1
by Ian Booth
Use ReturnToReferrerMixin for bugtask deletion view |
886 |
self.assertEqual(bug_url, view.cancel_url) |
14186.3.2
by Ian Booth
Add tests for delete view |
887 |
|
888 |
def test_delete_action(self): |
|
889 |
# Test that the delete action works as expected.
|
|
890 |
bug = self.factory.makeBug() |
|
891 |
bugtask = self.factory.makeBugTask(bug=bug) |
|
14186.3.7
by Ian Booth
Tweak cancel url and add delete icon to series targets |
892 |
target_name = bugtask.bugtargetdisplayname |
14186.3.2
by Ian Booth
Add tests for delete view |
893 |
with FeatureFixture(DELETE_BUGTASK_ENABLED): |
894 |
login_person(bugtask.owner) |
|
895 |
form = { |
|
896 |
'field.actions.delete_bugtask': 'Delete', |
|
897 |
}
|
|
898 |
view = create_initialized_view( |
|
899 |
bugtask, name='+delete', form=form, principal=bugtask.owner) |
|
900 |
self.assertEqual([bug.default_bugtask], bug.bugtasks) |
|
901 |
notifications = view.request.response.notifications |
|
902 |
self.assertEqual(1, len(notifications)) |
|
14186.3.7
by Ian Booth
Tweak cancel url and add delete icon to series targets |
903 |
expected = 'This bug no longer affects %s.' % target_name |
14186.3.2
by Ian Booth
Add tests for delete view |
904 |
self.assertEqual(expected, notifications[0].message) |
905 |
||
14307.1.1
by Ian Booth
Add extra test |
906 |
def test_delete_only_bugtask(self): |
907 |
# Test that the deleting the only bugtask results in an error message.
|
|
908 |
bug = self.factory.makeBug() |
|
909 |
with FeatureFixture(DELETE_BUGTASK_ENABLED): |
|
910 |
login_person(bug.owner) |
|
911 |
form = { |
|
912 |
'field.actions.delete_bugtask': 'Delete', |
|
913 |
}
|
|
914 |
view = create_initialized_view( |
|
915 |
bug.default_bugtask, name='+delete', form=form, |
|
916 |
principal=bug.owner) |
|
917 |
self.assertEqual([bug.default_bugtask], bug.bugtasks) |
|
918 |
notifications = view.request.response.notifications |
|
919 |
self.assertEqual(1, len(notifications)) |
|
920 |
expected = ('Cannot delete only bugtask affecting: %s.' |
|
921 |
% bug.default_bugtask.target.bugtargetdisplayname) |
|
922 |
self.assertEqual(expected, notifications[0].message) |
|
923 |
||
14186.3.18
by Ian Booth
Lint and code review fixes |
924 |
def _create_bugtask_to_delete(self): |
925 |
bug = self.factory.makeBug() |
|
926 |
bugtask = self.factory.makeBugTask(bug=bug) |
|
927 |
target_name = bugtask.bugtargetdisplayname |
|
928 |
bugtask_url = canonical_url(bugtask, rootsite='bugs') |
|
929 |
return bug, bugtask, target_name, bugtask_url |
|
930 |
||
14186.3.12
by Ian Booth
When deleting the current bugtask, redirect to the default bug task afterwards, plus add tests |
931 |
def test_ajax_delete_current_bugtask(self): |
932 |
# Test that deleting the current bugtask returns a JSON dict
|
|
933 |
# containing the URL of the bug's default task to redirect to.
|
|
14186.3.18
by Ian Booth
Lint and code review fixes |
934 |
bug, bugtask, target_name, bugtask_url = ( |
935 |
self._create_bugtask_to_delete()) |
|
14186.3.12
by Ian Booth
When deleting the current bugtask, redirect to the default bug task afterwards, plus add tests |
936 |
with FeatureFixture(DELETE_BUGTASK_ENABLED): |
937 |
login_person(bugtask.owner) |
|
938 |
# Set up the request so that we correctly simulate an XHR call
|
|
939 |
# from the URL of the bugtask we are deleting.
|
|
940 |
server_url = canonical_url( |
|
941 |
getUtility(ILaunchpadRoot), rootsite='bugs') |
|
942 |
extra = { |
|
943 |
'HTTP_X_REQUESTED_WITH': 'XMLHttpRequest', |
|
944 |
'HTTP_REFERER': bugtask_url, |
|
945 |
}
|
|
946 |
form = { |
|
947 |
'field.actions.delete_bugtask': 'Delete' |
|
948 |
}
|
|
949 |
view = create_initialized_view( |
|
950 |
bugtask, name='+delete', server_url=server_url, form=form, |
|
951 |
principal=bugtask.owner, **extra) |
|
952 |
result_data = simplejson.loads(view.render()) |
|
953 |
self.assertEqual([bug.default_bugtask], bug.bugtasks) |
|
954 |
notifications = simplejson.loads( |
|
955 |
view.request.response.getHeader('X-Lazr-Notifications')) |
|
956 |
self.assertEqual(1, len(notifications)) |
|
957 |
expected = 'This bug no longer affects %s.' % target_name |
|
958 |
self.assertEqual(expected, notifications[0][1]) |
|
959 |
self.assertEqual( |
|
14186.3.18
by Ian Booth
Lint and code review fixes |
960 |
'application/json', |
961 |
view.request.response.getHeader('content-type')) |
|
14186.3.12
by Ian Booth
When deleting the current bugtask, redirect to the default bug task afterwards, plus add tests |
962 |
expected_url = canonical_url(bug.default_bugtask, rootsite='bugs') |
963 |
self.assertEqual(dict(bugtask_url=expected_url), result_data) |
|
964 |
||
14307.1.1
by Ian Booth
Add extra test |
965 |
def test_ajax_delete_only_bugtask(self): |
966 |
# Test that deleting the only bugtask returns an empty JSON response
|
|
967 |
# with an error notification.
|
|
968 |
bug = self.factory.makeBug() |
|
969 |
with FeatureFixture(DELETE_BUGTASK_ENABLED): |
|
970 |
login_person(bug.owner) |
|
971 |
# Set up the request so that we correctly simulate an XHR call
|
|
972 |
# from the URL of the bugtask we are deleting.
|
|
973 |
server_url = canonical_url( |
|
974 |
getUtility(ILaunchpadRoot), rootsite='bugs') |
|
975 |
extra = { |
|
976 |
'HTTP_X_REQUESTED_WITH': 'XMLHttpRequest', |
|
977 |
}
|
|
978 |
form = { |
|
979 |
'field.actions.delete_bugtask': 'Delete' |
|
980 |
}
|
|
981 |
view = create_initialized_view( |
|
982 |
bug.default_bugtask, name='+delete', server_url=server_url, |
|
983 |
form=form, principal=bug.owner, **extra) |
|
984 |
result_data = simplejson.loads(view.render()) |
|
985 |
self.assertEqual([bug.default_bugtask], bug.bugtasks) |
|
986 |
notifications = simplejson.loads( |
|
987 |
view.request.response.getHeader('X-Lazr-Notifications')) |
|
988 |
self.assertEqual(1, len(notifications)) |
|
989 |
expected = ('Cannot delete only bugtask affecting: %s.' |
|
990 |
% bug.default_bugtask.target.bugtargetdisplayname) |
|
991 |
self.assertEqual(expected, notifications[0][1]) |
|
992 |
self.assertEqual( |
|
993 |
'application/json', |
|
994 |
view.request.response.getHeader('content-type')) |
|
995 |
self.assertEqual(None, result_data) |
|
996 |
||
14186.3.12
by Ian Booth
When deleting the current bugtask, redirect to the default bug task afterwards, plus add tests |
997 |
def test_ajax_delete_non_current_bugtask(self): |
998 |
# Test that deleting the non-current bugtask returns the new bugtasks
|
|
999 |
# table as HTML.
|
|
14186.3.18
by Ian Booth
Lint and code review fixes |
1000 |
bug, bugtask, target_name, bugtask_url = ( |
1001 |
self._create_bugtask_to_delete()) |
|
14186.3.12
by Ian Booth
When deleting the current bugtask, redirect to the default bug task afterwards, plus add tests |
1002 |
default_bugtask_url = canonical_url( |
1003 |
bug.default_bugtask, rootsite='bugs') |
|
1004 |
with FeatureFixture(DELETE_BUGTASK_ENABLED): |
|
1005 |
login_person(bugtask.owner) |
|
1006 |
# Set up the request so that we correctly simulate an XHR call
|
|
1007 |
# from the URL of the default bugtask, not the one we are
|
|
1008 |
# deleting.
|
|
1009 |
server_url = canonical_url( |
|
1010 |
getUtility(ILaunchpadRoot), rootsite='bugs') |
|
1011 |
extra = { |
|
1012 |
'HTTP_X_REQUESTED_WITH': 'XMLHttpRequest', |
|
1013 |
'HTTP_REFERER': default_bugtask_url, |
|
1014 |
}
|
|
1015 |
form = { |
|
1016 |
'field.actions.delete_bugtask': 'Delete' |
|
1017 |
}
|
|
1018 |
view = create_initialized_view( |
|
1019 |
bugtask, name='+delete', server_url=server_url, form=form, |
|
1020 |
principal=bugtask.owner, **extra) |
|
1021 |
result_html = view.render() |
|
1022 |
self.assertEqual([bug.default_bugtask], bug.bugtasks) |
|
1023 |
notifications = view.request.response.notifications |
|
1024 |
self.assertEqual(1, len(notifications)) |
|
1025 |
expected = 'This bug no longer affects %s.' % target_name |
|
1026 |
self.assertEqual(expected, notifications[0].message) |
|
1027 |
self.assertEqual( |
|
1028 |
view.request.response.getHeader('content-type'), 'text/html') |
|
1029 |
table = find_tag_by_id(result_html, 'affected-software') |
|
1030 |
self.assertIsNotNone(table) |
|
1031 |
[row] = table.tbody.findAll('tr', {'class': 'highlight'}) |
|
1032 |
target_link = row.find('a', {'class': 'sprite product'}) |
|
1033 |
self.assertIn( |
|
1034 |
bug.default_bugtask.bugtargetdisplayname, target_link) |
|
1035 |
||
14186.3.2
by Ian Booth
Add tests for delete view |
1036 |
|
14174.2.10
by Ian Booth
Remove some unnecessary checks and refactor |
1037 |
class TestBugTasksAndNominationsViewAlsoAffects(TestCaseWithFactory): |
1038 |
""" Tests the boolean methods on the view used to indicate whether the
|
|
14174.2.11
by Ian Booth
Fix implementation error for canAffect... methods |
1039 |
Also Affects... links should be allowed or not. Currently these
|
1040 |
restrictions are only used for private bugs. ie where body.private
|
|
1041 |
is true.
|
|
14174.2.10
by Ian Booth
Remove some unnecessary checks and refactor |
1042 |
|
1043 |
A feature flag is used to turn off the new restrictions. Each test
|
|
1044 |
is performed with and without the feature flag.
|
|
1045 |
"""
|
|
1046 |
||
1047 |
layer = DatabaseFunctionalLayer |
|
1048 |
||
1049 |
feature_flag = {'disclosure.allow_multipillar_private_bugs.enabled': 'on'} |
|
1050 |
||
1051 |
def _createView(self, bug): |
|
1052 |
request = LaunchpadTestRequest() |
|
1053 |
bugtasks_and_nominations_view = getMultiAdapter( |
|
14186.3.8
by Ian Booth
Add ajax bug task delete functionality |
1054 |
(bug, request), name="+bugtasks-and-nominations-portal") |
14174.2.10
by Ian Booth
Remove some unnecessary checks and refactor |
1055 |
return bugtasks_and_nominations_view |
1056 |
||
14174.2.11
by Ian Booth
Fix implementation error for canAffect... methods |
1057 |
def test_project_bug_cannot_affect_something_else(self): |
1058 |
# A bug affecting a project cannot also affect another project or
|
|
1059 |
# package.
|
|
14174.2.10
by Ian Booth
Remove some unnecessary checks and refactor |
1060 |
bug = self.factory.makeBug() |
1061 |
view = self._createView(bug) |
|
14174.2.11
by Ian Booth
Fix implementation error for canAffect... methods |
1062 |
self.assertFalse(view.canAddProjectTask()) |
1063 |
self.assertFalse(view.canAddPackageTask()) |
|
14174.2.10
by Ian Booth
Remove some unnecessary checks and refactor |
1064 |
with FeatureFixture(self.feature_flag): |
1065 |
self.assertTrue(view.canAddProjectTask()) |
|
1066 |
self.assertTrue(view.canAddPackageTask()) |
|
1067 |
||
14174.2.11
by Ian Booth
Fix implementation error for canAffect... methods |
1068 |
def test_distro_bug_cannot_affect_project(self): |
1069 |
# A bug affecting a distro cannot also affect another project but it
|
|
1070 |
# could affect another package.
|
|
14174.2.10
by Ian Booth
Remove some unnecessary checks and refactor |
1071 |
distro = self.factory.makeDistribution() |
1072 |
bug = self.factory.makeBug(distribution=distro) |
|
1073 |
view = self._createView(bug) |
|
14174.2.11
by Ian Booth
Fix implementation error for canAffect... methods |
1074 |
self.assertFalse(view.canAddProjectTask()) |
1075 |
self.assertTrue(view.canAddPackageTask()) |
|
1076 |
with FeatureFixture(self.feature_flag): |
|
1077 |
self.assertTrue(view.canAddProjectTask()) |
|
1078 |
self.assertTrue(view.canAddPackageTask()) |
|
1079 |
||
1080 |
def test_sourcepkg_bug_cannot_affect_project(self): |
|
1081 |
# A bug affecting a source pkg cannot also affect another project but
|
|
1082 |
# it could affect another package.
|
|
14174.2.10
by Ian Booth
Remove some unnecessary checks and refactor |
1083 |
distro = self.factory.makeDistribution() |
1084 |
distroseries = self.factory.makeDistroSeries(distribution=distro) |
|
1085 |
sp_name = self.factory.getOrMakeSourcePackageName() |
|
1086 |
self.factory.makeSourcePackage( |
|
1087 |
sourcepackagename=sp_name, distroseries=distroseries) |
|
1088 |
bug = self.factory.makeBug( |
|
14174.2.11
by Ian Booth
Fix implementation error for canAffect... methods |
1089 |
distribution=distro, sourcepackagename=sp_name) |
14174.2.10
by Ian Booth
Remove some unnecessary checks and refactor |
1090 |
view = self._createView(bug) |
1091 |
self.assertFalse(view.canAddProjectTask()) |
|
1092 |
self.assertTrue(view.canAddPackageTask()) |
|
1093 |
with FeatureFixture(self.feature_flag): |
|
1094 |
self.assertTrue(view.canAddProjectTask()) |
|
1095 |
self.assertTrue(view.canAddPackageTask()) |
|
1096 |
||
1097 |
||
10680.2.1
by Abel Deuring
Add a new bug task status 'Expired'; the bug task expiration script sets expired bug tasks to this status; related adjustments to the bug task views |
1098 |
class TestBugTaskEditViewStatusField(TestCaseWithFactory): |
1099 |
"""We show only those options as possible value in the status
|
|
1100 |
field that the user can select.
|
|
1101 |
"""
|
|
1102 |
||
12561.3.12
by Curtis Hovey
Fixed broken view while rewriting a broken test. |
1103 |
layer = DatabaseFunctionalLayer |
10680.2.1
by Abel Deuring
Add a new bug task status 'Expired'; the bug task expiration script sets expired bug tasks to this status; related adjustments to the bug task views |
1104 |
|
1105 |
def setUp(self): |
|
1106 |
super(TestBugTaskEditViewStatusField, self).setUp() |
|
10680.2.4
by Abel Deuring
replaced the no longer existing method factoy.makPersonNoCommit(9 by makePerson(); ensure that only project owners and supervisors can set a bugtask status to 'expired' |
1107 |
product_owner = self.factory.makePerson(name='product-owner') |
1108 |
bug_supervisor = self.factory.makePerson(name='bug-supervisor') |
|
1109 |
product = self.factory.makeProduct( |
|
10680.2.2
by Abel Deuring
implemented reviewer's comments |
1110 |
owner=product_owner, bug_supervisor=bug_supervisor) |
10680.2.1
by Abel Deuring
Add a new bug task status 'Expired'; the bug task expiration script sets expired bug tasks to this status; related adjustments to the bug task views |
1111 |
self.bug = self.factory.makeBug(product=product) |
1112 |
||
1113 |
def getWidgetOptionTitles(self, widget): |
|
1114 |
"""Return the titles of options of the given choice widget."""
|
|
1115 |
return [ |
|
1116 |
item.value.title for item in widget.field.vocabulary] |
|
1117 |
||
1118 |
def test_status_field_items_for_anonymous(self): |
|
1119 |
# Anonymous users see only the current value.
|
|
1120 |
login(ANONYMOUS) |
|
1121 |
view = BugTaskEditView( |
|
1122 |
self.bug.default_bugtask, LaunchpadTestRequest()) |
|
1123 |
view.initialize() |
|
1124 |
self.assertEqual( |
|
1125 |
['New'], self.getWidgetOptionTitles(view.form_fields['status'])) |
|
1126 |
||
1127 |
def test_status_field_items_for_ordinary_users(self): |
|
1128 |
# Ordinary users can set the status to all values except Won't fix,
|
|
1129 |
# Expired, Triaged, Unknown.
|
|
11474.2.1
by Robert Collins
Use sampledata constants. |
1130 |
login(NO_PRIVILEGE_EMAIL) |
10680.2.1
by Abel Deuring
Add a new bug task status 'Expired'; the bug task expiration script sets expired bug tasks to this status; related adjustments to the bug task views |
1131 |
view = BugTaskEditView( |
1132 |
self.bug.default_bugtask, LaunchpadTestRequest()) |
|
1133 |
view.initialize() |
|
1134 |
self.assertEqual( |
|
7675.718.1
by Abel Deuring
add a bug task status OPINION |
1135 |
['New', 'Incomplete', 'Opinion', 'Invalid', 'Confirmed', |
1136 |
'In Progress', 'Fix Committed', 'Fix Released'], |
|
10680.2.1
by Abel Deuring
Add a new bug task status 'Expired'; the bug task expiration script sets expired bug tasks to this status; related adjustments to the bug task views |
1137 |
self.getWidgetOptionTitles(view.form_fields['status'])) |
1138 |
||
1139 |
def test_status_field_privileged_persons(self): |
|
1140 |
# The bug target owner and the bug target supervisor can set
|
|
1141 |
# the status to any value except Unknown and Expired.
|
|
1142 |
for user in ( |
|
1143 |
self.bug.default_bugtask.pillar.owner, |
|
1144 |
self.bug.default_bugtask.pillar.bug_supervisor): |
|
1145 |
login_person(user) |
|
1146 |
view = BugTaskEditView( |
|
1147 |
self.bug.default_bugtask, LaunchpadTestRequest()) |
|
1148 |
view.initialize() |
|
1149 |
self.assertEqual( |
|
7675.718.1
by Abel Deuring
add a bug task status OPINION |
1150 |
['New', 'Incomplete', 'Opinion', 'Invalid', "Won't Fix", |
1151 |
'Confirmed', 'Triaged', 'In Progress', 'Fix Committed', |
|
1152 |
'Fix Released'], |
|
10680.2.1
by Abel Deuring
Add a new bug task status 'Expired'; the bug task expiration script sets expired bug tasks to this status; related adjustments to the bug task views |
1153 |
self.getWidgetOptionTitles(view.form_fields['status']), |
1154 |
'Unexpected set of settable status options for %s' |
|
1155 |
% user.name) |
|
1156 |
||
1157 |
def test_status_field_bug_task_in_status_unknown(self): |
|
1158 |
# If a bugtask has the status Unknown, this status is included
|
|
1159 |
# in the options.
|
|
10680.2.2
by Abel Deuring
implemented reviewer's comments |
1160 |
owner = self.bug.default_bugtask.pillar.owner |
1161 |
login_person(owner) |
|
1162 |
self.bug.default_bugtask.transitionToStatus( |
|
1163 |
BugTaskStatus.UNKNOWN, owner) |
|
11474.2.1
by Robert Collins
Use sampledata constants. |
1164 |
login(NO_PRIVILEGE_EMAIL) |
10680.2.1
by Abel Deuring
Add a new bug task status 'Expired'; the bug task expiration script sets expired bug tasks to this status; related adjustments to the bug task views |
1165 |
view = BugTaskEditView( |
1166 |
self.bug.default_bugtask, LaunchpadTestRequest()) |
|
1167 |
view.initialize() |
|
1168 |
self.assertEqual( |
|
7675.718.1
by Abel Deuring
add a bug task status OPINION |
1169 |
['New', 'Incomplete', 'Opinion', 'Invalid', 'Confirmed', |
1170 |
'In Progress', 'Fix Committed', 'Fix Released', 'Unknown'], |
|
10680.2.1
by Abel Deuring
Add a new bug task status 'Expired'; the bug task expiration script sets expired bug tasks to this status; related adjustments to the bug task views |
1171 |
self.getWidgetOptionTitles(view.form_fields['status'])) |
1172 |
||
1173 |
def test_status_field_bug_task_in_status_expired(self): |
|
10680.2.2
by Abel Deuring
implemented reviewer's comments |
1174 |
# If a bugtask has the status Expired, this status is included
|
10680.2.1
by Abel Deuring
Add a new bug task status 'Expired'; the bug task expiration script sets expired bug tasks to this status; related adjustments to the bug task views |
1175 |
# in the options.
|
13973.2.5
by Brad Crittenden
Version with lots of debugging junk |
1176 |
removeSecurityProxy(self.bug.default_bugtask)._status = ( |
10680.2.1
by Abel Deuring
Add a new bug task status 'Expired'; the bug task expiration script sets expired bug tasks to this status; related adjustments to the bug task views |
1177 |
BugTaskStatus.EXPIRED) |
11474.2.1
by Robert Collins
Use sampledata constants. |
1178 |
login(NO_PRIVILEGE_EMAIL) |
10680.2.1
by Abel Deuring
Add a new bug task status 'Expired'; the bug task expiration script sets expired bug tasks to this status; related adjustments to the bug task views |
1179 |
view = BugTaskEditView( |
1180 |
self.bug.default_bugtask, LaunchpadTestRequest()) |
|
1181 |
view.initialize() |
|
1182 |
self.assertEqual( |
|
7675.718.1
by Abel Deuring
add a bug task status OPINION |
1183 |
['New', 'Incomplete', 'Opinion', 'Invalid', 'Expired', |
1184 |
'Confirmed', 'In Progress', 'Fix Committed', 'Fix Released'], |
|
10680.2.1
by Abel Deuring
Add a new bug task status 'Expired'; the bug task expiration script sets expired bug tasks to this status; related adjustments to the bug task views |
1185 |
self.getWidgetOptionTitles(view.form_fields['status'])) |
1186 |
||
1187 |
||
10788.5.2
by Abel Deuring
ordinary users can (un)assign a bug task only to hemselves and their teams |
1188 |
class TestBugTaskEditViewAssigneeField(TestCaseWithFactory): |
1189 |
||
12561.3.12
by Curtis Hovey
Fixed broken view while rewriting a broken test. |
1190 |
layer = DatabaseFunctionalLayer |
10788.5.2
by Abel Deuring
ordinary users can (un)assign a bug task only to hemselves and their teams |
1191 |
|
1192 |
def setUp(self): |
|
1193 |
super(TestBugTaskEditViewAssigneeField, self).setUp() |
|
11435.6.11
by Deryck Hodge
Fix browser test. |
1194 |
self.owner = self.factory.makePerson() |
1195 |
self.product = self.factory.makeProduct(owner=self.owner) |
|
1196 |
self.bugtask = self.factory.makeBug( |
|
1197 |
product=self.product).default_bugtask |
|
10788.5.2
by Abel Deuring
ordinary users can (un)assign a bug task only to hemselves and their teams |
1198 |
|
11435.6.11
by Deryck Hodge
Fix browser test. |
1199 |
def test_assignee_vocabulary_regular_user_with_bug_supervisor(self): |
10788.5.2
by Abel Deuring
ordinary users can (un)assign a bug task only to hemselves and their teams |
1200 |
# For regular users, the assignee vocabulary is
|
11435.6.11
by Deryck Hodge
Fix browser test. |
1201 |
# AllUserTeamsParticipation if there is a bug supervisor defined.
|
1202 |
login_person(self.owner) |
|
1203 |
self.product.setBugSupervisor(self.owner, self.owner) |
|
11474.2.1
by Robert Collins
Use sampledata constants. |
1204 |
login(USER_EMAIL) |
10788.5.2
by Abel Deuring
ordinary users can (un)assign a bug task only to hemselves and their teams |
1205 |
view = BugTaskEditView(self.bugtask, LaunchpadTestRequest()) |
1206 |
view.initialize() |
|
1207 |
self.assertEqual( |
|
1208 |
'AllUserTeamsParticipation', |
|
1209 |
view.form_fields['assignee'].field.vocabularyName) |
|
1210 |
||
11435.6.11
by Deryck Hodge
Fix browser test. |
1211 |
def test_assignee_vocabulary_regular_user_without_bug_supervisor(self): |
1212 |
# For regular users, the assignee vocabulary is
|
|
1213 |
# ValidAssignee is there is not a bug supervisor defined.
|
|
1214 |
login_person(self.owner) |
|
1215 |
self.product.setBugSupervisor(None, self.owner) |
|
11474.2.7
by Robert Collins
Resolve conflicts with trunk. |
1216 |
login(USER_EMAIL) |
11435.6.11
by Deryck Hodge
Fix browser test. |
1217 |
view = BugTaskEditView(self.bugtask, LaunchpadTestRequest()) |
1218 |
view.initialize() |
|
1219 |
self.assertEqual( |
|
1220 |
'ValidAssignee', |
|
1221 |
view.form_fields['assignee'].field.vocabularyName) |
|
1222 |
||
10788.5.2
by Abel Deuring
ordinary users can (un)assign a bug task only to hemselves and their teams |
1223 |
def test_assignee_field_vocabulary_privileged_user(self): |
1224 |
# Privileged users, like the bug task target owner, can
|
|
1225 |
# assign anybody.
|
|
1226 |
login_person(self.bugtask.target.owner) |
|
1227 |
view = BugTaskEditView(self.bugtask, LaunchpadTestRequest()) |
|
1228 |
view.initialize() |
|
1229 |
self.assertEqual( |
|
1230 |
'ValidAssignee', |
|
1231 |
view.form_fields['assignee'].field.vocabularyName) |
|
1232 |
||
1233 |
||
12561.3.12
by Curtis Hovey
Fixed broken view while rewriting a broken test. |
1234 |
class TestBugTaskEditView(TestCaseWithFactory): |
12622.5.1
by Curtis Hovey
Always remove the bugtask milestone when retargeting the product. |
1235 |
"""Test the bug task edit form."""
|
12561.3.12
by Curtis Hovey
Fixed broken view while rewriting a broken test. |
1236 |
|
1237 |
layer = DatabaseFunctionalLayer |
|
1238 |
||
12599.4.2
by Leonard Richardson
Merge from trunk. |
1239 |
def test_retarget_already_exists_error(self): |
12561.3.12
by Curtis Hovey
Fixed broken view while rewriting a broken test. |
1240 |
user = self.factory.makePerson() |
1241 |
login_person(user) |
|
1242 |
ubuntu = getUtility(ILaunchpadCelebrities).ubuntu |
|
1243 |
dsp_1 = self.factory.makeDistributionSourcePackage( |
|
1244 |
distribution=ubuntu, sourcepackagename='mouse') |
|
13247.1.1
by Danilo Segan
Reapply bug 772754 fix with packagecopyjob changes removed. |
1245 |
self.factory.makeSourcePackagePublishingHistory( |
12561.3.12
by Curtis Hovey
Fixed broken view while rewriting a broken test. |
1246 |
distroseries=ubuntu.currentseries, |
1247 |
sourcepackagename=dsp_1.sourcepackagename) |
|
1248 |
bug_task_1 = self.factory.makeBugTask(target=dsp_1) |
|
1249 |
dsp_2 = self.factory.makeDistributionSourcePackage( |
|
1250 |
distribution=ubuntu, sourcepackagename='rabbit') |
|
13247.1.1
by Danilo Segan
Reapply bug 772754 fix with packagecopyjob changes removed. |
1251 |
self.factory.makeSourcePackagePublishingHistory( |
12561.3.12
by Curtis Hovey
Fixed broken view while rewriting a broken test. |
1252 |
distroseries=ubuntu.currentseries, |
1253 |
sourcepackagename=dsp_2.sourcepackagename) |
|
1254 |
bug_task_2 = self.factory.makeBugTask( |
|
1255 |
bug=bug_task_1.bug, target=dsp_2) |
|
1256 |
form = { |
|
1257 |
'ubuntu_rabbit.actions.save': 'Save Changes', |
|
1258 |
'ubuntu_rabbit.status': 'In Progress', |
|
1259 |
'ubuntu_rabbit.importance': 'High', |
|
1260 |
'ubuntu_rabbit.assignee.option': |
|
1261 |
'ubuntu_rabbit.assignee.assign_to_nobody', |
|
13494.2.13
by William Grant
Fix more test_bugtask. |
1262 |
'ubuntu_rabbit.target': 'package', |
1263 |
'ubuntu_rabbit.target.distribution': 'ubuntu', |
|
1264 |
'ubuntu_rabbit.target.package': 'mouse', |
|
12561.3.12
by Curtis Hovey
Fixed broken view while rewriting a broken test. |
1265 |
}
|
1266 |
view = create_initialized_view( |
|
12599.4.2
by Leonard Richardson
Merge from trunk. |
1267 |
bug_task_2, name='+editstatus', form=form, principal=user) |
12561.3.12
by Curtis Hovey
Fixed broken view while rewriting a broken test. |
1268 |
self.assertEqual(1, len(view.errors)) |
1269 |
self.assertEqual( |
|
13506.4.17
by William Grant
Fix test. |
1270 |
'A fix for this bug has already been requested for mouse in '
|
1271 |
'Ubuntu', |
|
12561.3.12
by Curtis Hovey
Fixed broken view while rewriting a broken test. |
1272 |
view.errors[0]) |
1273 |
||
12622.5.2
by Curtis Hovey
Added a test to verify that assiging a milestone during a retargeting is ignored. |
1274 |
def setUpRetargetMilestone(self): |
1275 |
"""Setup a bugtask with a milestone and a product to retarget to."""
|
|
12622.5.1
by Curtis Hovey
Always remove the bugtask milestone when retargeting the product. |
1276 |
first_product = self.factory.makeProduct(name='bunny') |
1277 |
with person_logged_in(first_product.owner): |
|
1278 |
first_product.official_malone = True |
|
1279 |
bug = self.factory.makeBug(product=first_product) |
|
1280 |
bug_task = bug.bugtasks[0] |
|
1281 |
milestone = self.factory.makeMilestone( |
|
1282 |
productseries=first_product.development_focus, name='1.0') |
|
1283 |
bug_task.transitionToMilestone(milestone, first_product.owner) |
|
1284 |
second_product = self.factory.makeProduct(name='duck') |
|
1285 |
with person_logged_in(second_product.owner): |
|
1286 |
second_product.official_malone = True |
|
12622.5.2
by Curtis Hovey
Added a test to verify that assiging a milestone during a retargeting is ignored. |
1287 |
return bug_task, second_product |
1288 |
||
1289 |
def test_retarget_product_with_milestone(self): |
|
1290 |
# Milestones are always cleared when retargeting a product bug task.
|
|
1291 |
bug_task, second_product = self.setUpRetargetMilestone() |
|
12622.5.1
by Curtis Hovey
Always remove the bugtask milestone when retargeting the product. |
1292 |
user = self.factory.makePerson() |
1293 |
login_person(user) |
|
1294 |
form = { |
|
1295 |
'bunny.status': 'In Progress', |
|
1296 |
'bunny.assignee.option': 'bunny.assignee.assign_to_nobody', |
|
13494.2.13
by William Grant
Fix more test_bugtask. |
1297 |
'bunny.target': 'product', |
1298 |
'bunny.target.product': 'duck', |
|
12622.5.1
by Curtis Hovey
Always remove the bugtask milestone when retargeting the product. |
1299 |
'bunny.actions.save': 'Save Changes', |
1300 |
}
|
|
1301 |
view = create_initialized_view( |
|
1302 |
bug_task, name='+editstatus', form=form) |
|
1303 |
self.assertEqual([], view.errors) |
|
1304 |
self.assertEqual(second_product, bug_task.target) |
|
1305 |
self.assertEqual(None, bug_task.milestone) |
|
12622.5.2
by Curtis Hovey
Added a test to verify that assiging a milestone during a retargeting is ignored. |
1306 |
notifications = view.request.response.notifications |
1307 |
self.assertEqual(1, len(notifications)) |
|
1308 |
expected = ('The Bunny 1.0 milestone setting has been removed') |
|
1309 |
self.assertTrue(notifications.pop().message.startswith(expected)) |
|
1310 |
||
1311 |
def test_retarget_product_and_assign_milestone(self): |
|
1312 |
# Milestones are always cleared when retargeting a product bug task.
|
|
1313 |
bug_task, second_product = self.setUpRetargetMilestone() |
|
1314 |
login_person(bug_task.target.owner) |
|
1315 |
milestone_id = bug_task.milestone.id |
|
1316 |
bug_task.transitionToMilestone(None, bug_task.target.owner) |
|
1317 |
form = { |
|
1318 |
'bunny.status': 'In Progress', |
|
1319 |
'bunny.assignee.option': 'bunny.assignee.assign_to_nobody', |
|
13494.2.13
by William Grant
Fix more test_bugtask. |
1320 |
'bunny.target': 'product', |
1321 |
'bunny.target.product': 'duck', |
|
12622.5.2
by Curtis Hovey
Added a test to verify that assiging a milestone during a retargeting is ignored. |
1322 |
'bunny.milestone': milestone_id, |
1323 |
'bunny.actions.save': 'Save Changes', |
|
1324 |
}
|
|
1325 |
view = create_initialized_view( |
|
1326 |
bug_task, name='+editstatus', form=form) |
|
1327 |
self.assertEqual([], view.errors) |
|
1328 |
self.assertEqual(second_product, bug_task.target) |
|
1329 |
self.assertEqual(None, bug_task.milestone) |
|
1330 |
notifications = view.request.response.notifications |
|
1331 |
self.assertEqual(1, len(notifications)) |
|
1332 |
expected = ('The milestone setting was ignored') |
|
1333 |
self.assertTrue(notifications.pop().message.startswith(expected)) |
|
12622.5.1
by Curtis Hovey
Always remove the bugtask milestone when retargeting the product. |
1334 |
|
13494.2.29
by William Grant
Fix helper. |
1335 |
def createNameChangingViewForSourcePackageTask(self, bug_task, new_name): |
13494.2.28
by William Grant
Test it a bit. |
1336 |
login_person(bug_task.owner) |
1337 |
form_prefix = '%s_%s_%s' % ( |
|
1338 |
bug_task.target.distroseries.distribution.name, |
|
1339 |
bug_task.target.distroseries.name, |
|
1340 |
bug_task.target.sourcepackagename.name) |
|
1341 |
form = { |
|
1342 |
form_prefix + '.sourcepackagename': new_name, |
|
1343 |
form_prefix + '.actions.save': 'Save Changes', |
|
1344 |
}
|
|
1345 |
view = create_initialized_view( |
|
1346 |
bug_task, name='+editstatus', form=form) |
|
1347 |
return view |
|
1348 |
||
1349 |
def test_retarget_sourcepackage(self): |
|
1350 |
# The sourcepackagename of a SourcePackage task can be changed.
|
|
1351 |
ds = self.factory.makeDistroSeries() |
|
1352 |
sp1 = self.factory.makeSourcePackage(distroseries=ds, publish=True) |
|
1353 |
sp2 = self.factory.makeSourcePackage(distroseries=ds, publish=True) |
|
1354 |
bug_task = self.factory.makeBugTask(target=sp1) |
|
1355 |
||
1356 |
view = self.createNameChangingViewForSourcePackageTask( |
|
1357 |
bug_task, sp2.sourcepackagename.name) |
|
1358 |
self.assertEqual([], view.errors) |
|
1359 |
self.assertEqual(sp2, bug_task.target) |
|
1360 |
notifications = view.request.response.notifications |
|
1361 |
self.assertEqual(0, len(notifications)) |
|
1362 |
||
1363 |
def test_retarget_sourcepackage_to_binary_name(self): |
|
1364 |
# The sourcepackagename of a SourcePackage task can be changed
|
|
1365 |
# to a binarypackagename, which gets mapped back to the source.
|
|
1366 |
ds = self.factory.makeDistroSeries() |
|
1367 |
das = self.factory.makeDistroArchSeries(distroseries=ds) |
|
1368 |
sp1 = self.factory.makeSourcePackage(distroseries=ds, publish=True) |
|
1369 |
# Now create a binary and its corresponding SourcePackage.
|
|
1370 |
bp = self.factory.makeBinaryPackagePublishingHistory( |
|
1371 |
distroarchseries=das) |
|
1372 |
bpr = bp.binarypackagerelease |
|
1373 |
spn = bpr.build.source_package_release.sourcepackagename |
|
1374 |
sp2 = self.factory.makeSourcePackage( |
|
1375 |
distroseries=ds, sourcepackagename=spn, publish=True) |
|
1376 |
bug_task = self.factory.makeBugTask(target=sp1) |
|
1377 |
||
1378 |
view = self.createNameChangingViewForSourcePackageTask( |
|
1379 |
bug_task, bpr.binarypackagename.name) |
|
1380 |
self.assertEqual([], view.errors) |
|
1381 |
self.assertEqual(sp2, bug_task.target) |
|
1382 |
notifications = view.request.response.notifications |
|
1383 |
self.assertEqual(1, len(notifications)) |
|
1384 |
expected = ( |
|
1385 |
"'%s' is a binary package. This bug has been assigned to its " |
|
1386 |
"source package '%s' instead." |
|
1387 |
% (bpr.binarypackagename.name, spn.name)) |
|
1388 |
self.assertTrue(notifications.pop().message.startswith(expected)) |
|
1389 |
||
1390 |
def test_retarget_sourcepackage_to_distroseries(self): |
|
1391 |
# A SourcePackage task can be changed to a DistroSeries one.
|
|
1392 |
ds = self.factory.makeDistroSeries() |
|
1393 |
sp = self.factory.makeSourcePackage(distroseries=ds, publish=True) |
|
1394 |
bug_task = self.factory.makeBugTask(target=sp) |
|
1395 |
||
1396 |
view = self.createNameChangingViewForSourcePackageTask( |
|
1397 |
bug_task, '') |
|
1398 |
self.assertEqual([], view.errors) |
|
1399 |
self.assertEqual(ds, bug_task.target) |
|
1400 |
notifications = view.request.response.notifications |
|
1401 |
self.assertEqual(0, len(notifications)) |
|
1402 |
||
14086.2.1
by Ian Booth
Handle case where private bugtask retargetted to new pillar becomes invisible |
1403 |
def test_retarget_private_bug(self): |
1404 |
# If a private bug is re-targetted such that the bug is no longer
|
|
1405 |
# visible to the user, they are redirected to the pillar's bug index
|
|
1406 |
# page with a suitable message. This corner case can occur when the
|
|
1407 |
# disclosure.private_bug_visibility_rules.enabled feature flag is on
|
|
1408 |
# and a bugtask is re-targetted to a pillar for which the user is not
|
|
1409 |
# authorised to see any private bugs.
|
|
1410 |
first_product = self.factory.makeProduct(name='bunny') |
|
1411 |
with person_logged_in(first_product.owner): |
|
1412 |
bug = self.factory.makeBug(product=first_product, private=True) |
|
1413 |
bug_task = bug.bugtasks[0] |
|
1414 |
second_product = self.factory.makeProduct(name='duck') |
|
1415 |
||
1416 |
# The first product owner can see the private bug. We will re-target
|
|
1417 |
# it to second_product where it will not be visible to that user.
|
|
1418 |
with person_logged_in(first_product.owner): |
|
1419 |
form = { |
|
1420 |
'bunny.target': 'product', |
|
1421 |
'bunny.target.product': 'duck', |
|
1422 |
'bunny.actions.save': 'Save Changes', |
|
1423 |
}
|
|
14205.1.5
by William Grant
Fix more tests to not rely on pillar owners. |
1424 |
with FeatureFixture({ |
1425 |
'disclosure.private_bug_visibility_rules.enabled': 'on'}): |
|
1426 |
view = create_initialized_view( |
|
1427 |
bug_task, name='+editstatus', form=form) |
|
14086.2.1
by Ian Booth
Handle case where private bugtask retargetted to new pillar becomes invisible |
1428 |
self.assertEqual( |
1429 |
canonical_url(bug_task.pillar, rootsite='bugs'), |
|
1430 |
view.next_url) |
|
1431 |
self.assertEqual([], view.errors) |
|
1432 |
self.assertEqual(second_product, bug_task.target) |
|
1433 |
notifications = view.request.response.notifications |
|
1434 |
self.assertEqual(1, len(notifications)) |
|
1435 |
expected = ('The bug you have just updated is now a private bug for') |
|
1436 |
self.assertTrue(notifications.pop().message.startswith(expected)) |
|
1437 |
||
1438 |
||
14423.2.7
by Curtis Hovey
Added missing test for persons. |
1439 |
class TestPersonBugs(TestCaseWithFactory): |
1440 |
"""Test the bugs overview page for distributions."""
|
|
1441 |
||
1442 |
layer = DatabaseFunctionalLayer |
|
1443 |
||
1444 |
def setUp(self): |
|
14423.2.8
by Curtis Hovey
Show the widget when there are subordinate structures. |
1445 |
super(TestPersonBugs, self).setUp() |
14423.2.7
by Curtis Hovey
Added missing test for persons. |
1446 |
self.target = self.factory.makePerson() |
1447 |
||
1448 |
def test_shouldShowStructuralSubscriberWidget(self): |
|
1449 |
view = create_initialized_view( |
|
1450 |
self.target, name=u'+bugs', rootsite='bugs') |
|
14423.2.8
by Curtis Hovey
Show the widget when there are subordinate structures. |
1451 |
self.assertTrue(view.shouldShowStructuralSubscriberWidget()) |
14423.2.7
by Curtis Hovey
Added missing test for persons. |
1452 |
|
14423.2.9
by Curtis Hovey
Provide informative labels for the bugtarget. |
1453 |
def test_structural_subscriber_label(self): |
1454 |
view = create_initialized_view( |
|
1455 |
self.target, name=u'+bugs', rootsite='bugs') |
|
1456 |
self.assertEqual( |
|
1457 |
'Project, distribution, package, or series subscriber', |
|
1458 |
view.structural_subscriber_label) |
|
1459 |
||
14423.2.7
by Curtis Hovey
Added missing test for persons. |
1460 |
|
14423.2.6
by Curtis Hovey
Added tests for the current behaviour. |
1461 |
class TestDistributionBugs(TestCaseWithFactory): |
1462 |
"""Test the bugs overview page for distributions."""
|
|
1463 |
||
1464 |
layer = DatabaseFunctionalLayer |
|
1465 |
||
1466 |
def setUp(self): |
|
1467 |
super(TestDistributionBugs, self).setUp() |
|
1468 |
self.target = self.factory.makeDistribution() |
|
1469 |
||
1470 |
def test_shouldShowStructuralSubscriberWidget(self): |
|
1471 |
view = create_initialized_view( |
|
1472 |
self.target, name=u'+bugs', rootsite='bugs') |
|
14423.2.8
by Curtis Hovey
Show the widget when there are subordinate structures. |
1473 |
self.assertTrue(view.shouldShowStructuralSubscriberWidget()) |
14423.2.6
by Curtis Hovey
Added tests for the current behaviour. |
1474 |
|
14423.2.9
by Curtis Hovey
Provide informative labels for the bugtarget. |
1475 |
def test_structural_subscriber_label(self): |
1476 |
view = create_initialized_view( |
|
1477 |
self.target, name=u'+bugs', rootsite='bugs') |
|
1478 |
self.assertEqual( |
|
1479 |
'Package, or series subscriber', view.structural_subscriber_label) |
|
1480 |
||
14423.2.6
by Curtis Hovey
Added tests for the current behaviour. |
1481 |
|
1482 |
class TestDistroSeriesBugs(TestCaseWithFactory): |
|
1483 |
"""Test the bugs overview page for distro series."""
|
|
1484 |
||
1485 |
layer = DatabaseFunctionalLayer |
|
1486 |
||
1487 |
def setUp(self): |
|
1488 |
super(TestDistroSeriesBugs, self).setUp() |
|
1489 |
self.target = self.factory.makeDistroSeries() |
|
1490 |
||
1491 |
def test_shouldShowStructuralSubscriberWidget(self): |
|
1492 |
view = create_initialized_view( |
|
1493 |
self.target, name=u'+bugs', rootsite='bugs') |
|
14423.2.8
by Curtis Hovey
Show the widget when there are subordinate structures. |
1494 |
self.assertTrue(view.shouldShowStructuralSubscriberWidget()) |
14423.2.6
by Curtis Hovey
Added tests for the current behaviour. |
1495 |
|
14423.2.9
by Curtis Hovey
Provide informative labels for the bugtarget. |
1496 |
def test_structural_subscriber_label(self): |
1497 |
view = create_initialized_view( |
|
1498 |
self.target, name=u'+bugs', rootsite='bugs') |
|
1499 |
self.assertEqual( |
|
1500 |
'Package subscriber', view.structural_subscriber_label) |
|
1501 |
||
14423.2.6
by Curtis Hovey
Added tests for the current behaviour. |
1502 |
|
1503 |
class TestDistributionSourcePackageBugs(TestCaseWithFactory): |
|
1504 |
"""Test the bugs overview page for distribution source packages."""
|
|
1505 |
||
1506 |
layer = DatabaseFunctionalLayer |
|
1507 |
||
1508 |
def setUp(self): |
|
1509 |
super(TestDistributionSourcePackageBugs, self).setUp() |
|
1510 |
self.target = self.factory.makeDistributionSourcePackage() |
|
1511 |
||
1512 |
def test_shouldShowStructuralSubscriberWidget(self): |
|
1513 |
view = create_initialized_view( |
|
1514 |
self.target, name=u'+bugs', rootsite='bugs') |
|
14423.2.8
by Curtis Hovey
Show the widget when there are subordinate structures. |
1515 |
self.assertFalse(view.shouldShowStructuralSubscriberWidget()) |
14423.2.6
by Curtis Hovey
Added tests for the current behaviour. |
1516 |
|
1517 |
||
1518 |
class TestDistroSeriesSourcePackageBugs(TestCaseWithFactory): |
|
1519 |
"""Test the bugs overview page for distro series source packages."""
|
|
1520 |
||
1521 |
layer = DatabaseFunctionalLayer |
|
1522 |
||
1523 |
def setUp(self): |
|
1524 |
super(TestDistroSeriesSourcePackageBugs, self).setUp() |
|
1525 |
self.target = self.factory.makeSourcePackage() |
|
1526 |
||
1527 |
def test_shouldShowStructuralSubscriberWidget(self): |
|
1528 |
view = create_initialized_view( |
|
1529 |
self.target, name=u'+bugs', rootsite='bugs') |
|
14423.2.8
by Curtis Hovey
Show the widget when there are subordinate structures. |
1530 |
self.assertFalse(view.shouldShowStructuralSubscriberWidget()) |
14423.2.6
by Curtis Hovey
Added tests for the current behaviour. |
1531 |
|
1532 |
||
1533 |
class TestProductBugs(TestCaseWithFactory): |
|
1534 |
"""Test the bugs overview page for projects."""
|
|
1535 |
||
1536 |
layer = DatabaseFunctionalLayer |
|
1537 |
||
1538 |
def setUp(self): |
|
1539 |
super(TestProductBugs, self).setUp() |
|
1540 |
self.target = self.factory.makeProduct() |
|
1541 |
||
1542 |
def test_shouldShowStructuralSubscriberWidget(self): |
|
1543 |
view = create_initialized_view( |
|
1544 |
self.target, name=u'+bugs', rootsite='bugs') |
|
14423.2.8
by Curtis Hovey
Show the widget when there are subordinate structures. |
1545 |
self.assertTrue(view.shouldShowStructuralSubscriberWidget()) |
14423.2.6
by Curtis Hovey
Added tests for the current behaviour. |
1546 |
|
14423.2.9
by Curtis Hovey
Provide informative labels for the bugtarget. |
1547 |
def test_structural_subscriber_label(self): |
1548 |
view = create_initialized_view( |
|
1549 |
self.target, name=u'+bugs', rootsite='bugs') |
|
1550 |
self.assertEqual( |
|
1551 |
'Series subscriber', view.structural_subscriber_label) |
|
1552 |
||
14423.2.6
by Curtis Hovey
Added tests for the current behaviour. |
1553 |
|
1554 |
class TestProductSeriesBugs(TestCaseWithFactory): |
|
1555 |
"""Test the bugs overview page for project series."""
|
|
1556 |
||
1557 |
layer = DatabaseFunctionalLayer |
|
1558 |
||
1559 |
def setUp(self): |
|
1560 |
super(TestProductSeriesBugs, self).setUp() |
|
1561 |
self.target = self.factory.makeProductSeries() |
|
1562 |
||
1563 |
def test_shouldShowStructuralSubscriberWidget(self): |
|
1564 |
view = create_initialized_view( |
|
1565 |
self.target, name=u'+bugs', rootsite='bugs') |
|
14423.2.8
by Curtis Hovey
Show the widget when there are subordinate structures. |
1566 |
self.assertFalse(view.shouldShowStructuralSubscriberWidget()) |
14423.2.6
by Curtis Hovey
Added tests for the current behaviour. |
1567 |
|
1568 |
||
11655.1.1
by Brad Crittenden
Do not show bug information on a project group page with no projects that use Launchpad. |
1569 |
class TestProjectGroupBugs(TestCaseWithFactory): |
14423.2.6
by Curtis Hovey
Added tests for the current behaviour. |
1570 |
"""Test the bugs overview page for project groups."""
|
11655.1.1
by Brad Crittenden
Do not show bug information on a project group page with no projects that use Launchpad. |
1571 |
|
12561.3.12
by Curtis Hovey
Fixed broken view while rewriting a broken test. |
1572 |
layer = DatabaseFunctionalLayer |
11655.1.1
by Brad Crittenden
Do not show bug information on a project group page with no projects that use Launchpad. |
1573 |
|
1574 |
def setUp(self): |
|
1575 |
super(TestProjectGroupBugs, self).setUp() |
|
1576 |
self.owner = self.factory.makePerson(name='bob') |
|
1577 |
self.projectgroup = self.factory.makeProject(name='container', |
|
1578 |
owner=self.owner) |
|
1579 |
||
1580 |
def makeSubordinateProduct(self, tracks_bugs_in_lp): |
|
1581 |
"""Create a new product and add it to the project group."""
|
|
1582 |
product = self.factory.makeProduct(official_malone=tracks_bugs_in_lp) |
|
1583 |
with person_logged_in(product.owner): |
|
1584 |
product.project = self.projectgroup |
|
1585 |
||
1586 |
def test_empty_project_group(self): |
|
1587 |
# An empty project group does not use Launchpad for bugs.
|
|
1588 |
view = create_initialized_view( |
|
1589 |
self.projectgroup, name=u'+bugs', rootsite='bugs') |
|
1590 |
self.assertFalse(self.projectgroup.hasProducts()) |
|
1591 |
self.assertFalse(view.should_show_bug_information) |
|
1592 |
||
1593 |
def test_project_group_with_subordinate_not_using_launchpad(self): |
|
1594 |
# A project group with all subordinates not using Launchpad
|
|
1595 |
# will itself be marked as not using Launchpad for bugs.
|
|
1596 |
self.makeSubordinateProduct(False) |
|
1597 |
self.assertTrue(self.projectgroup.hasProducts()) |
|
1598 |
view = create_initialized_view( |
|
1599 |
self.projectgroup, name=u'+bugs', rootsite='bugs') |
|
1600 |
self.assertFalse(view.should_show_bug_information) |
|
1601 |
||
1602 |
def test_project_group_with_subordinate_using_launchpad(self): |
|
1603 |
# A project group with one subordinate using Launchpad
|
|
1604 |
# will itself be marked as using Launchpad for bugs.
|
|
1605 |
self.makeSubordinateProduct(True) |
|
1606 |
self.assertTrue(self.projectgroup.hasProducts()) |
|
1607 |
view = create_initialized_view( |
|
1608 |
self.projectgroup, name=u'+bugs', rootsite='bugs') |
|
1609 |
self.assertTrue(view.should_show_bug_information) |
|
1610 |
||
1611 |
def test_project_group_with_mixed_subordinates(self): |
|
1612 |
# A project group with one or more subordinates using Launchpad
|
|
1613 |
# will itself be marked as using Launchpad for bugs.
|
|
1614 |
self.makeSubordinateProduct(False) |
|
1615 |
self.makeSubordinateProduct(True) |
|
1616 |
self.assertTrue(self.projectgroup.hasProducts()) |
|
1617 |
view = create_initialized_view( |
|
1618 |
self.projectgroup, name=u'+bugs', rootsite='bugs') |
|
1619 |
self.assertTrue(view.should_show_bug_information) |
|
1620 |
||
1621 |
def test_project_group_has_no_portlets_if_not_using_LP(self): |
|
11655.1.5
by Brad Crittenden
Post review fixes |
1622 |
# A project group that has no projects using Launchpad will not have
|
1623 |
# bug portlets.
|
|
11655.1.1
by Brad Crittenden
Do not show bug information on a project group page with no projects that use Launchpad. |
1624 |
self.makeSubordinateProduct(False) |
1625 |
view = create_initialized_view( |
|
1626 |
self.projectgroup, name=u'+bugs', rootsite='bugs', |
|
1627 |
current_request=True) |
|
1628 |
self.assertFalse(view.should_show_bug_information) |
|
1629 |
contents = view.render() |
|
1630 |
report_a_bug = find_tag_by_id(contents, 'bug-portlets') |
|
1631 |
self.assertIs(None, report_a_bug) |
|
1632 |
||
1633 |
def test_project_group_has_portlets_link_if_using_LP(self): |
|
11655.1.5
by Brad Crittenden
Post review fixes |
1634 |
# A project group that has projects using Launchpad will have a
|
1635 |
# portlets.
|
|
11655.1.1
by Brad Crittenden
Do not show bug information on a project group page with no projects that use Launchpad. |
1636 |
self.makeSubordinateProduct(True) |
1637 |
view = create_initialized_view( |
|
1638 |
self.projectgroup, name=u'+bugs', rootsite='bugs', |
|
1639 |
current_request=True) |
|
1640 |
self.assertTrue(view.should_show_bug_information) |
|
1641 |
contents = view.render() |
|
1642 |
report_a_bug = find_tag_by_id(contents, 'bug-portlets') |
|
1643 |
self.assertIsNot(None, report_a_bug) |
|
1644 |
||
11655.1.9
by Brad Crittenden
Add help link for configuring bugs |
1645 |
def test_project_group_has_help_link_if_not_using_LP(self): |
1646 |
# A project group that has no projects using Launchpad will have
|
|
1647 |
# a 'Getting started' help link.
|
|
1648 |
self.makeSubordinateProduct(False) |
|
1649 |
view = create_initialized_view( |
|
1650 |
self.projectgroup, name=u'+bugs', rootsite='bugs', |
|
1651 |
current_request=True) |
|
1652 |
contents = view.render() |
|
1653 |
help_link = find_tag_by_id(contents, 'getting-started-help') |
|
1654 |
self.assertIsNot(None, help_link) |
|
1655 |
||
1656 |
def test_project_group_has_no_help_link_if_using_LP(self): |
|
1657 |
# A project group that has no projects using Launchpad will not have
|
|
1658 |
# a 'Getting started' help link.
|
|
1659 |
self.makeSubordinateProduct(True) |
|
1660 |
view = create_initialized_view( |
|
1661 |
self.projectgroup, name=u'+bugs', rootsite='bugs', |
|
1662 |
current_request=True) |
|
1663 |
contents = view.render() |
|
1664 |
help_link = find_tag_by_id(contents, 'getting-started-help') |
|
1665 |
self.assertIs(None, help_link) |
|
12792.8.1
by William Grant
Add failing test for BugActivityItem assignee escaping. |
1666 |
|
14423.2.6
by Curtis Hovey
Added tests for the current behaviour. |
1667 |
def test_shouldShowStructuralSubscriberWidget(self): |
1668 |
view = create_initialized_view( |
|
1669 |
self.projectgroup, name=u'+bugs', rootsite='bugs') |
|
14423.2.8
by Curtis Hovey
Show the widget when there are subordinate structures. |
1670 |
self.assertTrue(view.shouldShowStructuralSubscriberWidget()) |
14423.2.6
by Curtis Hovey
Added tests for the current behaviour. |
1671 |
|
14423.2.9
by Curtis Hovey
Provide informative labels for the bugtarget. |
1672 |
def test_structural_subscriber_label(self): |
1673 |
view = create_initialized_view( |
|
1674 |
self.projectgroup, name=u'+bugs', rootsite='bugs') |
|
1675 |
self.assertEqual( |
|
1676 |
'Project or series subscriber', view.structural_subscriber_label) |
|
1677 |
||
12792.8.1
by William Grant
Add failing test for BugActivityItem assignee escaping. |
1678 |
|
1679 |
class TestBugActivityItem(TestCaseWithFactory): |
|
1680 |
||
1681 |
layer = DatabaseFunctionalLayer |
|
1682 |
||
1683 |
def setAttribute(self, obj, attribute, value): |
|
1684 |
obj_before_modification = Snapshot(obj, providing=providedBy(obj)) |
|
1685 |
setattr(removeSecurityProxy(obj), attribute, value) |
|
1686 |
notify(ObjectModifiedEvent( |
|
1687 |
obj, obj_before_modification, [attribute], |
|
1688 |
self.factory.makePerson())) |
|
1689 |
||
1690 |
def test_escapes_assignee(self): |
|
1691 |
with celebrity_logged_in('admin'): |
|
1692 |
task = self.factory.makeBugTask() |
|
1693 |
self.setAttribute( |
|
1694 |
task, 'assignee', |
|
1695 |
self.factory.makePerson(displayname="Foo &<>", name='foo')) |
|
1696 |
self.assertEquals( |
|
1697 |
"nobody → Foo &<> (foo)", |
|
1698 |
BugActivityItem(task.bug.activity[-1]).change_details) |
|
1699 |
||
1700 |
def test_escapes_title(self): |
|
1701 |
with celebrity_logged_in('admin'): |
|
1702 |
bug = self.factory.makeBug(title="foo") |
|
1703 |
self.setAttribute(bug, 'title', "bar &<>") |
|
1704 |
self.assertEquals( |
|
1705 |
"- foo<br />+ bar &<>", |
|
1706 |
BugActivityItem(bug.activity[-1]).change_details) |
|
13752.3.4
by Graham Binns
Added some basic tests. |
1707 |
|
1708 |
||
1709 |
class TestBugTaskBatchedCommentsAndActivityView(TestCaseWithFactory): |
|
1710 |
"""Tests for the BugTaskBatchedCommentsAndActivityView class."""
|
|
1711 |
||
1712 |
layer = LaunchpadFunctionalLayer |
|
1713 |
||
13955.1.3
by Graham Binns
Yay, fixed some test failures. |
1714 |
def _makeNoisyBug(self, comments_only=False, number_of_comments=10, |
1715 |
number_of_changes=10): |
|
13752.3.4
by Graham Binns
Added some basic tests. |
1716 |
"""Create and return a bug with a lot of comments and activity."""
|
13955.1.7
by Graham Binns
Review changes for Jeroen. |
1717 |
bug = self.factory.makeBug() |
13752.3.5
by Graham Binns
Somehow, I made that test pass. Wow. |
1718 |
with person_logged_in(bug.owner): |
1719 |
if not comments_only: |
|
13955.1.3
by Graham Binns
Yay, fixed some test failures. |
1720 |
for i in range(number_of_changes): |
13752.3.5
by Graham Binns
Somehow, I made that test pass. Wow. |
1721 |
change = BugTaskStatusChange( |
13955.1.7
by Graham Binns
Review changes for Jeroen. |
1722 |
bug.default_bugtask, UTC_NOW, |
13955.1.3
by Graham Binns
Yay, fixed some test failures. |
1723 |
bug.default_bugtask.product.owner, 'status', |
13752.3.5
by Graham Binns
Somehow, I made that test pass. Wow. |
1724 |
BugTaskStatus.NEW, BugTaskStatus.TRIAGED) |
1725 |
bug.addChange(change) |
|
14027.3.1
by Jeroen Vermeulen
Fix lots of lint in recently-changed files. |
1726 |
for i in range(number_of_comments): |
13752.3.5
by Graham Binns
Somehow, I made that test pass. Wow. |
1727 |
msg = self.factory.makeMessage( |
13955.1.7
by Graham Binns
Review changes for Jeroen. |
1728 |
owner=bug.owner, content="Message %i." % i) |
13752.3.5
by Graham Binns
Somehow, I made that test pass. Wow. |
1729 |
bug.linkMessage(msg, user=bug.owner) |
13752.3.4
by Graham Binns
Added some basic tests. |
1730 |
return bug |
1731 |
||
13955.1.7
by Graham Binns
Review changes for Jeroen. |
1732 |
def _assertThatUnbatchedAndBatchedActivityMatch(self, unbatched_activity, |
1733 |
batched_activity): |
|
1734 |
zipped_activity = zip( |
|
1735 |
unbatched_activity, batched_activity) |
|
1736 |
for index, items in enumerate(zipped_activity): |
|
1737 |
unbatched_item, batched_item = items |
|
1738 |
self.assertEqual( |
|
1739 |
unbatched_item['comment'].index, |
|
1740 |
batched_item['comment'].index, |
|
1741 |
"The comments at index %i don't match. Expected to see " |
|
1742 |
"comment %i, got comment %i instead." % |
|
1743 |
(index, unbatched_item['comment'].index, |
|
1744 |
batched_item['comment'].index)) |
|
1745 |
||
13752.3.4
by Graham Binns
Added some basic tests. |
1746 |
def test_offset(self): |
1747 |
# BugTaskBatchedCommentsAndActivityView.offset returns the
|
|
1748 |
# current offset being used to select a batch of bug comments
|
|
13955.1.2
by Graham Binns
Fixed the remaining test failures. Stick a knife in it, it's done (except for the icing of the review). |
1749 |
# and activity. If one is not specified, the offset will be the
|
1750 |
# view's visible_initial_comments count + 1 (so that comments
|
|
1751 |
# already shown on the page won't appear twice).
|
|
13752.3.4
by Graham Binns
Added some basic tests. |
1752 |
bug_task = self.factory.makeBugTask() |
1753 |
view = create_initialized_view(bug_task, '+batched-comments') |
|
14027.3.1
by Jeroen Vermeulen
Fix lots of lint in recently-changed files. |
1754 |
self.assertEqual(view.visible_initial_comments + 1, view.offset) |
13752.3.4
by Graham Binns
Added some basic tests. |
1755 |
view = create_initialized_view( |
1756 |
bug_task, '+batched-comments', form={'offset': 100}) |
|
1757 |
self.assertEqual(100, view.offset) |
|
1758 |
||
1759 |
def test_batch_size(self): |
|
1760 |
# BugTaskBatchedCommentsAndActivityView.batch_size returns the
|
|
1761 |
# current batch_size being used to select a batch of bug comments
|
|
13752.3.9
by Graham Binns
The default batch size is now a config option. |
1762 |
# and activity or the default configured batch size if one has
|
1763 |
# not been specified.
|
|
13752.3.4
by Graham Binns
Added some basic tests. |
1764 |
bug_task = self.factory.makeBugTask() |
1765 |
view = create_initialized_view(bug_task, '+batched-comments') |
|
13752.3.9
by Graham Binns
The default batch size is now a config option. |
1766 |
self.assertEqual( |
1767 |
config.malone.comments_list_default_batch_size, |
|
1768 |
view.batch_size) |
|
13752.3.4
by Graham Binns
Added some basic tests. |
1769 |
view = create_initialized_view( |
13752.3.5
by Graham Binns
Somehow, I made that test pass. Wow. |
1770 |
bug_task, '+batched-comments', form={'batch_size': 20}) |
1771 |
self.assertEqual(20, view.batch_size) |
|
1772 |
||
1773 |
def test_event_groups_only_returns_batch_size_results(self): |
|
1774 |
# BugTaskBatchedCommentsAndActivityView._event_groups will
|
|
1775 |
# return only batch_size results.
|
|
13955.1.3
by Graham Binns
Yay, fixed some test failures. |
1776 |
bug = self._makeNoisyBug(number_of_comments=20) |
1777 |
view = create_initialized_view( |
|
1778 |
bug.default_bugtask, '+batched-comments', |
|
1779 |
form={'batch_size': 10, 'offset': 1}) |
|
1780 |
self.assertEqual(10, len([group for group in view._event_groups])) |
|
1781 |
||
1782 |
def test_event_groups_excludes_visible_recent_comments(self): |
|
1783 |
# BugTaskBatchedCommentsAndActivityView._event_groups will
|
|
1784 |
# not return the last view comments - those covered by the
|
|
1785 |
# visible_recent_comments property.
|
|
13955.1.4
by Graham Binns
The tests... pass. I don't know what to do now. |
1786 |
bug = self._makeNoisyBug(number_of_comments=20, comments_only=True) |
13955.1.7
by Graham Binns
Review changes for Jeroen. |
1787 |
batched_view = create_initialized_view( |
13752.3.5
by Graham Binns
Somehow, I made that test pass. Wow. |
1788 |
bug.default_bugtask, '+batched-comments', |
13955.1.4
by Graham Binns
The tests... pass. I don't know what to do now. |
1789 |
form={'batch_size': 10, 'offset': 10}) |
13955.1.7
by Graham Binns
Review changes for Jeroen. |
1790 |
expected_length = 10 - batched_view.visible_recent_comments |
1791 |
actual_length = len([group for group in batched_view._event_groups]) |
|
13955.1.3
by Graham Binns
Yay, fixed some test failures. |
1792 |
self.assertEqual( |
13955.1.4
by Graham Binns
The tests... pass. I don't know what to do now. |
1793 |
expected_length, actual_length, |
1794 |
"Expected %i comments, got %i." % |
|
1795 |
(expected_length, actual_length)) |
|
13955.1.7
by Graham Binns
Review changes for Jeroen. |
1796 |
unbatched_view = create_initialized_view( |
1797 |
bug.default_bugtask, '+index', form={'comments': 'all'}) |
|
1798 |
self._assertThatUnbatchedAndBatchedActivityMatch( |
|
1799 |
unbatched_view.activity_and_comments[9:], |
|
1800 |
batched_view.activity_and_comments) |
|
13752.3.5
by Graham Binns
Somehow, I made that test pass. Wow. |
1801 |
|
1802 |
def test_activity_and_comments_matches_unbatched_version(self): |
|
1803 |
# BugTaskBatchedCommentsAndActivityView extends BugTaskView in
|
|
1804 |
# order to add the batching logic and reduce rendering
|
|
1805 |
# overheads. The results of activity_and_comments is the same
|
|
1806 |
# for both.
|
|
1807 |
# We create a bug with comments only so that we can test the
|
|
1808 |
# contents of activity_and_comments properly. Trying to test it
|
|
1809 |
# with multiply different datatypes is fragile at best.
|
|
13955.1.3
by Graham Binns
Yay, fixed some test failures. |
1810 |
bug = self._makeNoisyBug(comments_only=True, number_of_comments=20) |
13752.3.8
by Graham Binns
Fixed up test failures. |
1811 |
# We create a batched view with an offset of 0 so that all the
|
1812 |
# comments are returned.
|
|
13752.3.5
by Graham Binns
Somehow, I made that test pass. Wow. |
1813 |
batched_view = create_initialized_view( |
13752.3.8
by Graham Binns
Fixed up test failures. |
1814 |
bug.default_bugtask, '+batched-comments', |
13955.1.3
by Graham Binns
Yay, fixed some test failures. |
1815 |
{'offset': 5, 'batch_size': 10}) |
13752.3.5
by Graham Binns
Somehow, I made that test pass. Wow. |
1816 |
unbatched_view = create_initialized_view( |
13955.1.3
by Graham Binns
Yay, fixed some test failures. |
1817 |
bug.default_bugtask, '+index', form={'comments': 'all'}) |
1818 |
# It may look slightly confusing, but it's because the unbatched
|
|
1819 |
# view's activity_and_comments list is indexed from comment 1,
|
|
1820 |
# whereas the batched view indexes from zero for ease-of-coding.
|
|
1821 |
# Comment 0 is the original bug description and so is rarely
|
|
1822 |
# returned.
|
|
13955.1.7
by Graham Binns
Review changes for Jeroen. |
1823 |
self._assertThatUnbatchedAndBatchedActivityMatch( |
1824 |
unbatched_view.activity_and_comments[4:], |
|
1825 |
batched_view.activity_and_comments) |
|
14128.3.19
by Aaron Bentley
Test BugTaskListingItem.model |
1826 |
|
1827 |
||
14128.3.20
by Aaron Bentley
Ensure the json cache is populated appropriately. |
1828 |
def make_bug_task_listing_item(factory): |
1829 |
owner = factory.makePerson() |
|
1830 |
bug = factory.makeBug( |
|
1831 |
owner=owner, private=True, security_related=True) |
|
14174.2.15
by Ian Booth
Fix tests for private bugs and prepare makeBugTask for single pillar bugs |
1832 |
bugtask = bug.default_bugtask |
14128.3.20
by Aaron Bentley
Ensure the json cache is populated appropriately. |
1833 |
bug_task_set = getUtility(IBugTaskSet) |
1834 |
bug_badge_properties = bug_task_set.getBugTaskBadgeProperties( |
|
1835 |
[bugtask]) |
|
1836 |
badge_property = bug_badge_properties[bugtask] |
|
1837 |
return owner, BugTaskListingItem( |
|
1838 |
bugtask, |
|
1839 |
badge_property['has_branch'], |
|
1840 |
badge_property['has_specification'], |
|
1841 |
badge_property['has_patch'], |
|
1842 |
target_context=bugtask.target) |
|
1843 |
||
1844 |
||
14128.3.21
by Aaron Bentley
Test the mustache rendering and placeholder. |
1845 |
class TestBugTaskSearchListingView(BrowserTestCase): |
14128.3.20
by Aaron Bentley
Ensure the json cache is populated appropriately. |
1846 |
|
1847 |
layer = DatabaseFunctionalLayer |
|
1848 |
||
14128.3.21
by Aaron Bentley
Test the mustache rendering and placeholder. |
1849 |
client_listing = soupmatchers.Tag( |
1850 |
'client-listing', True, attrs={'id': 'client-listing'}) |
|
1851 |
||
14128.3.56
by Aaron Bentley
Get batch caching working. |
1852 |
def makeView(self, bugtask=None, size=None, memo=None, orderby=None, |
14317.1.16
by Deryck Hodge
Add test that should pass once field_visibility is set from a cookie. |
1853 |
forwards=True, cookie=None): |
14128.3.61
by Aaron Bentley
Update docs. |
1854 |
"""Make a BugTaskSearchListingView.
|
1855 |
||
1856 |
:param bugtask: The task to use for searching.
|
|
1857 |
:param size: The size of the batches. Required if forwards is False.
|
|
1858 |
:param memo: Batch identifier.
|
|
1859 |
:param orderby: The way to order the batch.
|
|
1860 |
:param forwards: If true, walk forwards from the memo. Else walk
|
|
1861 |
backwards.
|
|
1862 |
||
1863 |
"""
|
|
14128.3.47
by Aaron Bentley
Update tests. |
1864 |
query_vars = {} |
1865 |
if size is not None: |
|
14128.3.51
by Aaron Bentley
Fix lint. |
1866 |
query_vars['batch'] = size |
14128.3.47
by Aaron Bentley
Update tests. |
1867 |
if memo is not None: |
1868 |
query_vars['memo'] = memo |
|
14128.3.56
by Aaron Bentley
Get batch caching working. |
1869 |
if forwards: |
1870 |
query_vars['start'] = memo |
|
1871 |
else: |
|
1872 |
query_vars['start'] = int(memo) - size |
|
1873 |
if not forwards: |
|
1874 |
query_vars['direction'] = 'backwards' |
|
14128.3.47
by Aaron Bentley
Update tests. |
1875 |
query_string = urllib.urlencode(query_vars) |
14128.3.49
by Aaron Bentley
Get cache keys from model. |
1876 |
request = LaunchpadTestRequest( |
14317.1.16
by Deryck Hodge
Add test that should pass once field_visibility is set from a cookie. |
1877 |
QUERY_STRING=query_string, orderby=orderby, HTTP_COOKIE=cookie) |
14128.3.20
by Aaron Bentley
Ensure the json cache is populated appropriately. |
1878 |
if bugtask is None: |
1879 |
bugtask = self.factory.makeBugTask() |
|
14382.1.12
by Aaron Bentley
Fix and simplify tests. |
1880 |
view = getMultiAdapter((bugtask.target, request), name='+bugs') |
14128.3.20
by Aaron Bentley
Ensure the json cache is populated appropriately. |
1881 |
view.initialize() |
1882 |
return view |
|
1883 |
||
14128.3.21
by Aaron Bentley
Test the mustache rendering and placeholder. |
1884 |
@contextmanager
|
1885 |
def dynamic_listings(self): |
|
14128.3.61
by Aaron Bentley
Update docs. |
1886 |
"""Context manager to enable new bug listings."""
|
14128.3.21
by Aaron Bentley
Test the mustache rendering and placeholder. |
1887 |
with feature_flags(): |
1888 |
set_feature_flag(u'bugs.dynamic_bug_listings.enabled', u'on') |
|
1889 |
yield
|
|
1890 |
||
14128.3.20
by Aaron Bentley
Ensure the json cache is populated appropriately. |
1891 |
def test_mustache_model_missing_if_no_flag(self): |
1892 |
"""The IJSONRequestCache should contain mustache_model."""
|
|
1893 |
view = self.makeView() |
|
1894 |
cache = IJSONRequestCache(view.request) |
|
1895 |
self.assertIs(None, cache.objects.get('mustache_model')) |
|
1896 |
||
1897 |
def test_mustache_model_in_json(self): |
|
1898 |
"""The IJSONRequestCache should contain mustache_model.
|
|
1899 |
||
1900 |
mustache_model should contain bugtasks, the BugTaskListingItem.model
|
|
1901 |
for each BugTask.
|
|
1902 |
"""
|
|
1903 |
owner, item = make_bug_task_listing_item(self.factory) |
|
1904 |
self.useContext(person_logged_in(owner)) |
|
14128.3.21
by Aaron Bentley
Test the mustache rendering and placeholder. |
1905 |
with self.dynamic_listings(): |
14128.3.20
by Aaron Bentley
Ensure the json cache is populated appropriately. |
1906 |
view = self.makeView(item.bugtask) |
1907 |
cache = IJSONRequestCache(view.request) |
|
1908 |
bugtasks = cache.objects['mustache_model']['bugtasks'] |
|
1909 |
self.assertEqual(1, len(bugtasks)) |
|
14128.8.5
by Aaron Bentley
Fix failing test. |
1910 |
combined = dict(item.model) |
1911 |
combined.update(view.search().field_visibility) |
|
1912 |
self.assertEqual(combined, bugtasks[0]) |
|
14128.3.20
by Aaron Bentley
Ensure the json cache is populated appropriately. |
1913 |
|
14128.3.47
by Aaron Bentley
Update tests. |
1914 |
def test_no_next_prev_for_single_batch(self): |
1915 |
"""The IJSONRequestCache should contain data about ajacent batches.
|
|
1916 |
||
1917 |
mustache_model should contain bugtasks, the BugTaskListingItem.model
|
|
1918 |
for each BugTask.
|
|
1919 |
"""
|
|
1920 |
owner, item = make_bug_task_listing_item(self.factory) |
|
1921 |
self.useContext(person_logged_in(owner)) |
|
1922 |
with self.dynamic_listings(): |
|
1923 |
view = self.makeView(item.bugtask) |
|
1924 |
cache = IJSONRequestCache(view.request) |
|
1925 |
self.assertIs(None, cache.objects.get('next')) |
|
1926 |
self.assertIs(None, cache.objects.get('prev')) |
|
1927 |
||
1928 |
def test_next_for_multiple_batch(self): |
|
1929 |
"""The IJSONRequestCache should contain data about the next batch.
|
|
1930 |
||
1931 |
mustache_model should contain bugtasks, the BugTaskListingItem.model
|
|
1932 |
for each BugTask.
|
|
1933 |
"""
|
|
1934 |
task = self.factory.makeBugTask() |
|
14128.3.51
by Aaron Bentley
Fix lint. |
1935 |
self.factory.makeBugTask(target=task.target) |
14128.3.47
by Aaron Bentley
Update tests. |
1936 |
with self.dynamic_listings(): |
1937 |
view = self.makeView(task, size=1) |
|
1938 |
cache = IJSONRequestCache(view.request) |
|
1939 |
self.assertEqual({'memo': '1', 'start': 1}, cache.objects.get('next')) |
|
1940 |
||
1941 |
def test_prev_for_multiple_batch(self): |
|
1942 |
"""The IJSONRequestCache should contain data about the next batch.
|
|
1943 |
||
1944 |
mustache_model should contain bugtasks, the BugTaskListingItem.model
|
|
1945 |
for each BugTask.
|
|
1946 |
"""
|
|
1947 |
task = self.factory.makeBugTask() |
|
1948 |
task2 = self.factory.makeBugTask(target=task.target) |
|
1949 |
with self.dynamic_listings(): |
|
1950 |
view = self.makeView(task2, size=1, memo=1) |
|
1951 |
cache = IJSONRequestCache(view.request) |
|
1952 |
self.assertEqual({'memo': '1', 'start': 0}, cache.objects.get('prev')) |
|
1953 |
||
14382.1.5
by Aaron Bentley
view_name is included in BugTaskSearchListingView RequestCache. |
1954 |
def test_provides_view_name(self): |
1955 |
"""The IJSONRequestCache should provide the view's name."""
|
|
1956 |
self.useContext(self.dynamic_listings()) |
|
14382.1.12
by Aaron Bentley
Fix and simplify tests. |
1957 |
view = self.makeView() |
14382.1.5
by Aaron Bentley
view_name is included in BugTaskSearchListingView RequestCache. |
1958 |
cache = IJSONRequestCache(view.request) |
1959 |
self.assertEqual('+bugs', cache.objects['view_name']) |
|
14382.1.12
by Aaron Bentley
Fix and simplify tests. |
1960 |
person = self.factory.makePerson() |
14382.1.7
by Aaron Bentley
Simplify iter_view_registrations, fix tests. |
1961 |
commentview = getMultiAdapter( |
14382.1.12
by Aaron Bentley
Fix and simplify tests. |
1962 |
(person, LaunchpadTestRequest()), name='+commentedbugs') |
14382.1.5
by Aaron Bentley
view_name is included in BugTaskSearchListingView RequestCache. |
1963 |
commentview.initialize() |
1964 |
cache = IJSONRequestCache(commentview.request) |
|
1965 |
self.assertEqual('+commentedbugs', cache.objects['view_name']) |
|
1966 |
||
14128.3.49
by Aaron Bentley
Get cache keys from model. |
1967 |
def test_default_order_by(self): |
14128.3.61
by Aaron Bentley
Update docs. |
1968 |
"""order_by defaults to '-importance in JSONRequestCache"""
|
14128.3.49
by Aaron Bentley
Get cache keys from model. |
1969 |
task = self.factory.makeBugTask() |
1970 |
with self.dynamic_listings(): |
|
1971 |
view = self.makeView(task) |
|
1972 |
cache = IJSONRequestCache(view.request) |
|
1973 |
self.assertEqual('-importance', cache.objects['order_by']) |
|
1974 |
||
1975 |
def test_order_by_importance(self): |
|
14128.3.61
by Aaron Bentley
Update docs. |
1976 |
"""order_by follows query params in JSONRequestCache"""
|
14128.3.49
by Aaron Bentley
Get cache keys from model. |
1977 |
task = self.factory.makeBugTask() |
1978 |
with self.dynamic_listings(): |
|
1979 |
view = self.makeView(task, orderby='importance') |
|
1980 |
cache = IJSONRequestCache(view.request) |
|
1981 |
self.assertEqual('importance', cache.objects['order_by']) |
|
1982 |
||
14128.3.56
by Aaron Bentley
Get batch caching working. |
1983 |
def test_cache_has_all_batch_vars_defaults(self): |
14128.3.61
by Aaron Bentley
Update docs. |
1984 |
"""Cache has all the needed variables.
|
1985 |
||
1986 |
order_by, memo, start, forwards. These default to sane values.
|
|
1987 |
"""
|
|
14128.3.56
by Aaron Bentley
Get batch caching working. |
1988 |
task = self.factory.makeBugTask() |
1989 |
with self.dynamic_listings(): |
|
1990 |
view = self.makeView(task) |
|
1991 |
cache = IJSONRequestCache(view.request) |
|
1992 |
self.assertEqual('-importance', cache.objects['order_by']) |
|
1993 |
self.assertIs(None, cache.objects['memo']) |
|
1994 |
self.assertEqual(0, cache.objects['start']) |
|
1995 |
self.assertTrue(cache.objects['forwards']) |
|
14128.3.73
by Aaron Bentley
Fake merge of r14223 |
1996 |
self.assertEqual(1, cache.objects['total']) |
14128.3.56
by Aaron Bentley
Get batch caching working. |
1997 |
|
1998 |
def test_cache_has_all_batch_vars_specified(self): |
|
14128.3.61
by Aaron Bentley
Update docs. |
1999 |
"""Cache has all the needed variables.
|
2000 |
||
2001 |
order_by, memo, start, forwards. These are calculated appropriately.
|
|
2002 |
"""
|
|
14128.3.56
by Aaron Bentley
Get batch caching working. |
2003 |
task = self.factory.makeBugTask() |
2004 |
with self.dynamic_listings(): |
|
2005 |
view = self.makeView(task, memo=1, forwards=False, size=1) |
|
2006 |
cache = IJSONRequestCache(view.request) |
|
2007 |
self.assertEqual('1', cache.objects['memo']) |
|
2008 |
self.assertEqual(0, cache.objects['start']) |
|
2009 |
self.assertFalse(cache.objects['forwards']) |
|
14128.3.59
by Aaron Bentley
Fix last link to use correct start param. |
2010 |
self.assertEqual(0, cache.objects['last_start']) |
14128.3.56
by Aaron Bentley
Get batch caching working. |
2011 |
|
14128.7.18
by Aaron Bentley
Put field_visibility in model, rename set_field_visibility to change_fields. |
2012 |
def test_cache_field_visibility(self): |
14128.7.19
by Aaron Bentley
Update docs |
2013 |
"""Cache contains sane-looking field_visibility values."""
|
14128.7.18
by Aaron Bentley
Put field_visibility in model, rename set_field_visibility to change_fields. |
2014 |
task = self.factory.makeBugTask() |
2015 |
with self.dynamic_listings(): |
|
2016 |
view = self.makeView(task, memo=1, forwards=False, size=1) |
|
2017 |
cache = IJSONRequestCache(view.request) |
|
2018 |
field_visibility = cache.objects['field_visibility'] |
|
14385.1.1
by Aaron Bentley
Make bug title a mandatory field. |
2019 |
self.assertTrue(field_visibility['show_id']) |
14128.7.18
by Aaron Bentley
Put field_visibility in model, rename set_field_visibility to change_fields. |
2020 |
|
14317.1.25
by Deryck Hodge
Add test that cookie name is in cache. |
2021 |
def test_cache_cookie_name(self): |
2022 |
"""The cookie name should be in cache for js code access."""
|
|
2023 |
task = self.factory.makeBugTask() |
|
2024 |
with self.dynamic_listings(): |
|
2025 |
view = self.makeView(task, memo=1, forwards=False, size=1) |
|
2026 |
cache = IJSONRequestCache(view.request) |
|
2027 |
cookie_name = cache.objects['cbl_cookie_name'] |
|
2028 |
self.assertEqual('anon-buglist-fields', cookie_name) |
|
2029 |
||
14317.1.16
by Deryck Hodge
Add test that should pass once field_visibility is set from a cookie. |
2030 |
def test_cache_field_visibility_matches_cookie(self): |
2031 |
"""Cache contains cookie-matching values for field_visibiliy."""
|
|
2032 |
task = self.factory.makeBugTask() |
|
2033 |
cookie = ( |
|
14317.1.17
by Deryck Hodge
Update field_visibility if cookie is present. |
2034 |
'anon-buglist-fields=show_age=true&show_reporter=true'
|
14385.1.1
by Aaron Bentley
Make bug title a mandatory field. |
2035 |
'&show_id=true&show_bugtarget=true'
|
14317.1.16
by Deryck Hodge
Add test that should pass once field_visibility is set from a cookie. |
2036 |
'&show_milestone_name=true&show_last_updated=true'
|
2037 |
'&show_assignee=true&show_bug_heat=true&show_tags=true'
|
|
2038 |
'&show_importance=true&show_status=true') |
|
2039 |
with self.dynamic_listings(): |
|
2040 |
view = self.makeView( |
|
2041 |
task, memo=1, forwards=False, size=1, cookie=cookie) |
|
2042 |
cache = IJSONRequestCache(view.request) |
|
2043 |
field_visibility = cache.objects['field_visibility'] |
|
2044 |
self.assertTrue(field_visibility['show_tags']) |
|
2045 |
||
14385.1.1
by Aaron Bentley
Make bug title a mandatory field. |
2046 |
def test_exclude_unsupported_cookie_values(self): |
2047 |
"""Cookie values not present in defaults are ignored."""
|
|
2048 |
task = self.factory.makeBugTask() |
|
2049 |
cookie = ( |
|
2050 |
'anon-buglist-fields=show_age=true&show_reporter=true'
|
|
2051 |
'&show_id=true&show_bugtarget=true'
|
|
2052 |
'&show_milestone_name=true&show_last_updated=true'
|
|
2053 |
'&show_assignee=true&show_bug_heat=true&show_tags=true'
|
|
2054 |
'&show_importance=true&show_status=true&show_title=true') |
|
2055 |
with self.dynamic_listings(): |
|
2056 |
view = self.makeView( |
|
2057 |
task, memo=1, forwards=False, size=1, cookie=cookie) |
|
2058 |
cache = IJSONRequestCache(view.request) |
|
2059 |
field_visibility = cache.objects['field_visibility'] |
|
2060 |
self.assertNotIn('show_title', field_visibility) |
|
2061 |
||
2062 |
def test_add_defaults_to_cookie_values(self): |
|
2063 |
"""Where cookie values are missing, defaults are used"""
|
|
2064 |
task = self.factory.makeBugTask() |
|
2065 |
cookie = ( |
|
2066 |
'anon-buglist-fields=show_age=true&show_reporter=true'
|
|
2067 |
'&show_id=true&show_bugtarget=true'
|
|
2068 |
'&show_milestone_name=true&show_last_updated=true'
|
|
2069 |
'&show_assignee=true&show_bug_heat=true&show_tags=true'
|
|
2070 |
'&show_importance=true&show_title=true') |
|
2071 |
with self.dynamic_listings(): |
|
2072 |
view = self.makeView( |
|
2073 |
task, memo=1, forwards=False, size=1, cookie=cookie) |
|
2074 |
cache = IJSONRequestCache(view.request) |
|
2075 |
field_visibility = cache.objects['field_visibility'] |
|
2076 |
self.assertIn('show_status', field_visibility) |
|
2077 |
||
14241.3.83
by Deryck Hodge
Add test for adding field_visibility_defaults. |
2078 |
def test_cache_field_visibility_defaults(self): |
2079 |
"""Cache contains sane-looking field_visibility_defaults values."""
|
|
2080 |
task = self.factory.makeBugTask() |
|
2081 |
with self.dynamic_listings(): |
|
2082 |
view = self.makeView(task, memo=1, forwards=False, size=1) |
|
2083 |
cache = IJSONRequestCache(view.request) |
|
2084 |
field_visibility_defaults = cache.objects['field_visibility_defaults'] |
|
14385.1.1
by Aaron Bentley
Make bug title a mandatory field. |
2085 |
self.assertTrue(field_visibility_defaults['show_id']) |
14241.3.83
by Deryck Hodge
Add test for adding field_visibility_defaults. |
2086 |
|
14265.2.3
by Aaron Bentley
Obfuscate values for unauthenicated users. |
2087 |
def getBugtaskBrowser(self, title=None, no_login=False): |
14128.3.61
by Aaron Bentley
Update docs. |
2088 |
"""Return a browser for a new bugtask."""
|
14128.3.21
by Aaron Bentley
Test the mustache rendering and placeholder. |
2089 |
bugtask = self.factory.makeBugTask() |
2090 |
with person_logged_in(bugtask.target.owner): |
|
2091 |
bugtask.target.official_malone = True |
|
14265.2.3
by Aaron Bentley
Obfuscate values for unauthenicated users. |
2092 |
if title is not None: |
2093 |
bugtask.bug.title = title |
|
14128.3.23
by Aaron Bentley
Fix lint. |
2094 |
browser = self.getViewBrowser( |
14265.2.3
by Aaron Bentley
Obfuscate values for unauthenicated users. |
2095 |
bugtask.target, '+bugs', rootsite='bugs', no_login=no_login) |
14128.3.21
by Aaron Bentley
Test the mustache rendering and placeholder. |
2096 |
return bugtask, browser |
2097 |
||
2098 |
def assertHTML(self, browser, *tags, **kwargs): |
|
14128.3.61
by Aaron Bentley
Update docs. |
2099 |
"""Assert something about a browser's HTML."""
|
14128.3.21
by Aaron Bentley
Test the mustache rendering and placeholder. |
2100 |
matcher = soupmatchers.HTMLContains(*tags) |
2101 |
if kwargs.get('invert', False): |
|
2102 |
matcher = Not(matcher) |
|
2103 |
self.assertThat(browser.contents, matcher) |
|
2104 |
||
2105 |
@staticmethod
|
|
2106 |
def getBugNumberTag(bug_task): |
|
2107 |
"""Bug numbers with a leading hash are unique to new rendering."""
|
|
14128.3.23
by Aaron Bentley
Fix lint. |
2108 |
bug_number_re = re.compile(r'\#%d' % bug_task.bug.id) |
14241.3.70
by Deryck Hodge
Little markup fix in template and test to get broken test passing. |
2109 |
return soupmatchers.Tag('bugnumber', 'span', text=bug_number_re) |
14128.3.21
by Aaron Bentley
Test the mustache rendering and placeholder. |
2110 |
|
2111 |
def test_mustache_rendering_missing_if_no_flag(self): |
|
2112 |
"""If the flag is missing, then no mustache features appear."""
|
|
2113 |
bug_task, browser = self.getBugtaskBrowser() |
|
2114 |
number_tag = self.getBugNumberTag(bug_task) |
|
2115 |
self.assertHTML(browser, number_tag, invert=True) |
|
2116 |
self.assertHTML(browser, self.client_listing, invert=True) |
|
2117 |
||
2118 |
def test_mustache_rendering(self): |
|
2119 |
"""If the flag is present, then all mustache features appear."""
|
|
2120 |
with self.dynamic_listings(): |
|
2121 |
bug_task, browser = self.getBugtaskBrowser() |
|
2122 |
bug_number = self.getBugNumberTag(bug_task) |
|
14128.3.47
by Aaron Bentley
Update tests. |
2123 |
self.assertHTML(browser, self.client_listing, bug_number) |
14128.3.21
by Aaron Bentley
Test the mustache rendering and placeholder. |
2124 |
|
14265.2.3
by Aaron Bentley
Obfuscate values for unauthenicated users. |
2125 |
def test_mustache_rendering_obfuscation(self): |
14265.2.6
by Aaron Bentley
Update docs |
2126 |
"""For anonymous users, email addresses are obfuscated."""
|
14265.2.3
by Aaron Bentley
Obfuscate values for unauthenicated users. |
2127 |
with self.dynamic_listings(): |
2128 |
bug_task, browser = self.getBugtaskBrowser(title='a@example.com', |
|
2129 |
no_login=True) |
|
2130 |
self.assertNotIn('a@example.com', browser.contents) |
|
2131 |
||
14128.7.6
by Aaron Bentley
Allow bug target to be shown and hidden. |
2132 |
def getNavigator(self): |
14128.7.1
by Aaron Bentley
Control whether the bug_id is displayed using the mustache template. |
2133 |
request = LaunchpadTestRequest() |
14128.7.10
by Aaron Bentley
Support milestone_name as a field. |
2134 |
navigator = BugListingBatchNavigator([], request, [], 1) |
14128.7.1
by Aaron Bentley
Control whether the bug_id is displayed using the mustache template. |
2135 |
cache = IJSONRequestCache(request) |
14128.7.10
by Aaron Bentley
Support milestone_name as a field. |
2136 |
bugtask = { |
14128.9.3
by Aaron Bentley
Provide bug age field, hidden by default. |
2137 |
'age': 'age1', |
14128.9.1
by Aaron Bentley
Add assignee to bug fields. |
2138 |
'assignee': 'assignee1', |
14128.7.10
by Aaron Bentley
Support milestone_name as a field. |
2139 |
'bugtarget': 'bugtarget1', |
2140 |
'bugtarget_css': 'bugtarget_css1', |
|
2141 |
'bug_heat_html': 'bug_heat_html1', |
|
2142 |
'bug_url': 'bug_url1', |
|
14128.9.4
by Aaron Bentley
Add tags to listing. |
2143 |
'id': '3.14159', |
2144 |
'importance': 'importance1', |
|
2145 |
'importance_class': 'importance_class1', |
|
14128.9.6
by Aaron Bentley
Include last_updated in optional fields. |
2146 |
'last_updated': 'updated1', |
14128.9.4
by Aaron Bentley
Add tags to listing. |
2147 |
'milestone_name': 'milestone_name1', |
2148 |
'status': 'status1', |
|
14128.9.5
by Aaron Bentley
Add bug reporter, distinguish assignee from reporter with prefixes. |
2149 |
'reporter': 'reporter1', |
14128.9.4
by Aaron Bentley
Add tags to listing. |
2150 |
'tags': 'tags1', |
2151 |
'title': 'title1', |
|
14128.7.10
by Aaron Bentley
Support milestone_name as a field. |
2152 |
}
|
2153 |
bugtask.update(navigator.field_visibility) |
|
14128.7.1
by Aaron Bentley
Control whether the bug_id is displayed using the mustache template. |
2154 |
cache.objects['mustache_model'] = { |
14128.7.10
by Aaron Bentley
Support milestone_name as a field. |
2155 |
'bugtasks': [bugtask], |
14128.7.1
by Aaron Bentley
Control whether the bug_id is displayed using the mustache template. |
2156 |
}
|
2157 |
mustache_model = cache.objects['mustache_model'] |
|
14128.7.6
by Aaron Bentley
Allow bug target to be shown and hidden. |
2158 |
return navigator, mustache_model |
2159 |
||
2160 |
def test_hiding_bug_number(self): |
|
14128.8.4
by Aaron Bentley
Update docs. |
2161 |
"""Hiding a bug number makes it disappear from the page."""
|
14128.7.6
by Aaron Bentley
Allow bug target to be shown and hidden. |
2162 |
navigator, mustache_model = self.getNavigator() |
14128.7.1
by Aaron Bentley
Control whether the bug_id is displayed using the mustache template. |
2163 |
self.assertIn('3.14159', navigator.mustache) |
2164 |
mustache_model['bugtasks'][0]['show_id'] = False |
|
2165 |
self.assertNotIn('3.14159', navigator.mustache) |
|
14128.7.6
by Aaron Bentley
Allow bug target to be shown and hidden. |
2166 |
|
2167 |
def test_hiding_status(self): |
|
14128.8.4
by Aaron Bentley
Update docs. |
2168 |
"""Hiding status makes it disappear from the page."""
|
14128.7.6
by Aaron Bentley
Allow bug target to be shown and hidden. |
2169 |
navigator, mustache_model = self.getNavigator() |
14128.7.4
by Aaron Bentley
Make status and importance togglable. |
2170 |
self.assertIn('status1', navigator.mustache) |
2171 |
mustache_model['bugtasks'][0]['show_status'] = False |
|
2172 |
self.assertNotIn('status1', navigator.mustache) |
|
14128.7.6
by Aaron Bentley
Allow bug target to be shown and hidden. |
2173 |
|
2174 |
def test_hiding_importance(self): |
|
14128.8.4
by Aaron Bentley
Update docs. |
2175 |
"""Hiding importance removes the text and CSS."""
|
14128.7.6
by Aaron Bentley
Allow bug target to be shown and hidden. |
2176 |
navigator, mustache_model = self.getNavigator() |
14128.7.4
by Aaron Bentley
Make status and importance togglable. |
2177 |
self.assertIn('importance1', navigator.mustache) |
2178 |
self.assertIn('importance_class1', navigator.mustache) |
|
2179 |
mustache_model['bugtasks'][0]['show_importance'] = False |
|
2180 |
self.assertNotIn('importance1', navigator.mustache) |
|
2181 |
self.assertNotIn('importance_class1', navigator.mustache) |
|
14128.7.1
by Aaron Bentley
Control whether the bug_id is displayed using the mustache template. |
2182 |
|
14128.7.6
by Aaron Bentley
Allow bug target to be shown and hidden. |
2183 |
def test_hiding_bugtarget(self): |
14128.8.4
by Aaron Bentley
Update docs. |
2184 |
"""Hiding bugtarget removes the text and CSS."""
|
14128.7.6
by Aaron Bentley
Allow bug target to be shown and hidden. |
2185 |
navigator, mustache_model = self.getNavigator() |
2186 |
self.assertIn('bugtarget1', navigator.mustache) |
|
2187 |
self.assertIn('bugtarget_css1', navigator.mustache) |
|
2188 |
mustache_model['bugtasks'][0]['show_bugtarget'] = False |
|
2189 |
self.assertNotIn('bugtarget1', navigator.mustache) |
|
2190 |
self.assertNotIn('bugtarget_css1', navigator.mustache) |
|
2191 |
||
14128.7.8
by Aaron Bentley
Support hiding bug heat. |
2192 |
def test_hiding_bug_heat(self): |
14128.8.4
by Aaron Bentley
Update docs. |
2193 |
"""Hiding bug heat removes the html and CSS."""
|
14128.7.8
by Aaron Bentley
Support hiding bug heat. |
2194 |
navigator, mustache_model = self.getNavigator() |
2195 |
self.assertIn('bug_heat_html1', navigator.mustache) |
|
2196 |
self.assertIn('bug-heat-icons', navigator.mustache) |
|
2197 |
mustache_model['bugtasks'][0]['show_bug_heat'] = False |
|
2198 |
self.assertNotIn('bug_heat_html1', navigator.mustache) |
|
2199 |
self.assertNotIn('bug-heat-icons', navigator.mustache) |
|
14128.3.20
by Aaron Bentley
Ensure the json cache is populated appropriately. |
2200 |
|
14128.7.10
by Aaron Bentley
Support milestone_name as a field. |
2201 |
def test_hiding_milstone_name(self): |
14128.8.4
by Aaron Bentley
Update docs. |
2202 |
"""Showing milestone name shows the text."""
|
14128.7.10
by Aaron Bentley
Support milestone_name as a field. |
2203 |
navigator, mustache_model = self.getNavigator() |
2204 |
self.assertNotIn('milestone_name1', navigator.mustache) |
|
2205 |
mustache_model['bugtasks'][0]['show_milestone_name'] = True |
|
2206 |
self.assertIn('milestone_name1', navigator.mustache) |
|
2207 |
||
14128.9.1
by Aaron Bentley
Add assignee to bug fields. |
2208 |
def test_hiding_assignee(self): |
2209 |
"""Showing milestone name shows the text."""
|
|
2210 |
navigator, mustache_model = self.getNavigator() |
|
2211 |
self.assertIn('show_assignee', navigator.field_visibility) |
|
14128.9.5
by Aaron Bentley
Add bug reporter, distinguish assignee from reporter with prefixes. |
2212 |
self.assertNotIn('Assignee: assignee1', navigator.mustache) |
14128.9.1
by Aaron Bentley
Add assignee to bug fields. |
2213 |
mustache_model['bugtasks'][0]['show_assignee'] = True |
14128.9.5
by Aaron Bentley
Add bug reporter, distinguish assignee from reporter with prefixes. |
2214 |
self.assertIn('Assignee: assignee1', navigator.mustache) |
14128.9.1
by Aaron Bentley
Add assignee to bug fields. |
2215 |
|
14128.9.3
by Aaron Bentley
Provide bug age field, hidden by default. |
2216 |
def test_hiding_age(self): |
2217 |
"""Showing age shows the text."""
|
|
2218 |
navigator, mustache_model = self.getNavigator() |
|
2219 |
self.assertIn('show_age', navigator.field_visibility) |
|
2220 |
self.assertNotIn('age1', navigator.mustache) |
|
2221 |
mustache_model['bugtasks'][0]['show_age'] = True |
|
2222 |
self.assertIn('age1', navigator.mustache) |
|
2223 |
||
14128.9.4
by Aaron Bentley
Add tags to listing. |
2224 |
def test_hiding_tags(self): |
2225 |
"""Showing tags shows the text."""
|
|
2226 |
navigator, mustache_model = self.getNavigator() |
|
2227 |
self.assertIn('show_tags', navigator.field_visibility) |
|
2228 |
self.assertNotIn('tags1', navigator.mustache) |
|
2229 |
mustache_model['bugtasks'][0]['show_tags'] = True |
|
2230 |
self.assertIn('tags1', navigator.mustache) |
|
2231 |
||
14128.9.5
by Aaron Bentley
Add bug reporter, distinguish assignee from reporter with prefixes. |
2232 |
def test_hiding_reporter(self): |
2233 |
"""Showing reporter shows the text."""
|
|
2234 |
navigator, mustache_model = self.getNavigator() |
|
2235 |
self.assertIn('show_reporter', navigator.field_visibility) |
|
2236 |
self.assertNotIn('Reporter: reporter1', navigator.mustache) |
|
2237 |
mustache_model['bugtasks'][0]['show_reporter'] = True |
|
2238 |
self.assertIn('Reporter: reporter1', navigator.mustache) |
|
2239 |
||
14128.9.6
by Aaron Bentley
Include last_updated in optional fields. |
2240 |
def test_hiding_last_updated(self): |
2241 |
"""Showing last_updated shows the text."""
|
|
2242 |
navigator, mustache_model = self.getNavigator() |
|
2243 |
self.assertIn('show_last_updated', navigator.field_visibility) |
|
14424.3.6
by Aaron Bentley
Change capitalization of 'Last updated' |
2244 |
self.assertNotIn('Last updated updated1', navigator.mustache) |
14128.9.6
by Aaron Bentley
Include last_updated in optional fields. |
2245 |
mustache_model['bugtasks'][0]['show_last_updated'] = True |
14424.3.6
by Aaron Bentley
Change capitalization of 'Last updated' |
2246 |
self.assertIn('Last updated updated1', navigator.mustache) |
14128.9.6
by Aaron Bentley
Include last_updated in optional fields. |
2247 |
|
14128.7.9
by Aaron Bentley
Allow hiding bug title, linkify bug number. |
2248 |
|
14195.1.1
by Aaron Bentley
Fix template-escaping bug. |
2249 |
class TestBugListingBatchNavigator(TestCaseWithFactory): |
2250 |
||
2251 |
layer = DatabaseFunctionalLayer |
|
2252 |
||
2253 |
def test_mustache_listings_escaped(self): |
|
2254 |
"""Mustache template is encoded such that it has no unescaped tags."""
|
|
2255 |
navigator = BugListingBatchNavigator( |
|
2256 |
[], LaunchpadTestRequest(), [], 0) |
|
2257 |
self.assertNotIn('<', navigator.mustache_listings) |
|
2258 |
self.assertNotIn('>', navigator.mustache_listings) |
|
2259 |
||
2260 |
||
14128.3.19
by Aaron Bentley
Test BugTaskListingItem.model |
2261 |
class TestBugTaskListingItem(TestCaseWithFactory): |
2262 |
||
2263 |
layer = DatabaseFunctionalLayer |
|
2264 |
||
2265 |
def test_model(self): |
|
2266 |
"""Model contains expected fields with expected values."""
|
|
14128.3.20
by Aaron Bentley
Ensure the json cache is populated appropriately. |
2267 |
owner, item = make_bug_task_listing_item(self.factory) |
14128.3.19
by Aaron Bentley
Test BugTaskListingItem.model |
2268 |
with person_logged_in(owner): |
2269 |
model = item.model |
|
2270 |
self.assertEqual('Undecided', model['importance']) |
|
2271 |
self.assertEqual('importanceUNDECIDED', model['importance_class']) |
|
2272 |
self.assertEqual('New', model['status']) |
|
2273 |
self.assertEqual('statusNEW', model['status_class']) |
|
2274 |
self.assertEqual(item.bug.title, model['title']) |
|
2275 |
self.assertEqual(item.bug.id, model['id']) |
|
2276 |
self.assertEqual(canonical_url(item.bugtask), model['bug_url']) |
|
2277 |
self.assertEqual(item.bugtargetdisplayname, model['bugtarget']) |
|
2278 |
self.assertEqual('sprite product', model['bugtarget_css']) |
|
2279 |
self.assertEqual(item.bug_heat_html, model['bug_heat_html']) |
|
2280 |
self.assertEqual( |
|
2281 |
'<span alt="private" title="Private" class="sprite private">'
|
|
2282 |
' </span>', model['badges']) |
|
14128.7.10
by Aaron Bentley
Support milestone_name as a field. |
2283 |
self.assertEqual(None, model['milestone_name']) |
2284 |
item.bugtask.milestone = self.factory.makeMilestone( |
|
2285 |
product=item.bugtask.target) |
|
2286 |
milestone_name = item.milestone.displayname |
|
2287 |
self.assertEqual(milestone_name, item.model['milestone_name']) |
|
14128.9.1
by Aaron Bentley
Add assignee to bug fields. |
2288 |
|
2289 |
def test_model_assignee(self): |
|
2290 |
"""Model contains expected fields with expected values."""
|
|
2291 |
owner, item = make_bug_task_listing_item(self.factory) |
|
2292 |
with person_logged_in(owner): |
|
2293 |
self.assertIs(None, item.model['assignee']) |
|
2294 |
assignee = self.factory.makePerson(displayname='Example Person') |
|
2295 |
item.bugtask.transitionToAssignee(assignee) |
|
2296 |
self.assertEqual('Example Person', item.model['assignee']) |
|
2297 |
||
14128.9.4
by Aaron Bentley
Add tags to listing. |
2298 |
def test_model_age(self): |
14128.9.3
by Aaron Bentley
Provide bug age field, hidden by default. |
2299 |
"""Model contains bug age."""
|
2300 |
owner, item = make_bug_task_listing_item(self.factory) |
|
2301 |
with person_logged_in(owner): |
|
2302 |
item.bug.datecreated = datetime.now(UTC) - timedelta(3, 0, 0) |
|
2303 |
self.assertEqual('3 days old', item.model['age']) |
|
14128.9.4
by Aaron Bentley
Add tags to listing. |
2304 |
|
2305 |
def test_model_tags(self): |
|
2306 |
"""Model contains bug tags."""
|
|
2307 |
owner, item = make_bug_task_listing_item(self.factory) |
|
2308 |
with person_logged_in(owner): |
|
2309 |
item.bug.tags = ['tag1', 'tag2'] |
|
2310 |
self.assertEqual('tag1 tag2', item.model['tags']) |
|
14128.9.5
by Aaron Bentley
Add bug reporter, distinguish assignee from reporter with prefixes. |
2311 |
|
2312 |
def test_model_reporter(self): |
|
2313 |
"""Model contains bug reporter."""
|
|
2314 |
owner, item = make_bug_task_listing_item(self.factory) |
|
2315 |
with person_logged_in(owner): |
|
2316 |
self.assertEqual(owner.displayname, item.model['reporter']) |
|
14128.9.6
by Aaron Bentley
Include last_updated in optional fields. |
2317 |
|
2318 |
def test_model_last_updated_date_last_updated(self): |
|
2319 |
"""last_updated uses date_last_updated if newer."""
|
|
2320 |
owner, item = make_bug_task_listing_item(self.factory) |
|
2321 |
with person_logged_in(owner): |
|
2322 |
item.bug.date_last_updated = datetime(2001, 1, 1, tzinfo=UTC) |
|
2323 |
removeSecurityProxy(item.bug).date_last_message = datetime( |
|
2324 |
2000, 1, 1, tzinfo=UTC) |
|
2325 |
self.assertEqual( |
|
14128.9.10
by Aaron Bentley
Fix failing test. |
2326 |
'on 2001-01-01', item.model['last_updated']) |
14128.9.6
by Aaron Bentley
Include last_updated in optional fields. |
2327 |
|
2328 |
def test_model_last_updated_date_last_message(self): |
|
2329 |
"""last_updated uses date_last_message if newer."""
|
|
2330 |
owner, item = make_bug_task_listing_item(self.factory) |
|
2331 |
with person_logged_in(owner): |
|
2332 |
item.bug.date_last_updated = datetime(2000, 1, 1, tzinfo=UTC) |
|
2333 |
removeSecurityProxy(item.bug).date_last_message = datetime( |
|
2334 |
2001, 1, 1, tzinfo=UTC) |
|
2335 |
self.assertEqual( |
|
14128.9.10
by Aaron Bentley
Fix failing test. |
2336 |
'on 2001-01-01', item.model['last_updated']) |