Merge lp:~wallyworld/launchpad/rename-private-team-795771 into lp:launchpad
- rename-private-team-795771
- Merge into devel
Status: | Merged |
---|---|
Approved by: | Curtis Hovey |
Approved revision: | no longer in the source branch. |
Merged at revision: | 13813 |
Proposed branch: | lp:~wallyworld/launchpad/rename-private-team-795771 |
Merge into: | lp:launchpad |
Diff against target: |
465 lines (+181/-202) 4 files modified
lib/lp/registry/browser/team.py (+9/-15) lib/lp/registry/browser/tests/test_team_view.py (+168/-6) lib/lp/registry/stories/team/xx-team-edit.txt (+0/-180) lib/lp/testing/factory.py (+4/-1) |
To merge this branch: | bzr merge lp:~wallyworld/launchpad/rename-private-team-795771 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Curtis Hovey (community) | code | Approve | |
Steve Kowalik (community) | code | Needs Fixing | |
Review via email: mp+72516@code.launchpad.net |
Commit message
[r=sinzui][bug=795771] Allow private teams to be renamed
Description of the change
Private teams can be renamed.
== Implementation ==
Remove the private team check in setUpWidgets of TeamEditView
== Tests ==
Write some, there were none.
TestTeamEditView
test_
test_
test_
test_
== Lint ==
Checking for conflicts and issues in changed files.
Linting changed files:
lib/lp/
lib/lp/
Robert Collins (lifeless) wrote : | # |
Ian Booth (wallyworld) wrote : | # |
What's there is how the code was originally written, just with one check
for is_private removed. It's not possible to reject for a reason other
than a mailing list or ppa so the assert is not strictly required. The
code below will work but does change the text output if both a ppa and
mailing list exist (a bit more verbose).
I'll change it as suggested and modify any tests accordingly.
On 23/08/11 18:04, Robert Collins wrote:
> + if not has_mailing_list:
> + reason = 'has a PPA'
> + elif not has_ppa:
> + reason = 'has a mailing list'
>
> This made me blink.
>
> Perhaps:
> reasons = []
> if has_mailing_list:
> reasons.append('has a mailing list')
> if has_ppa:
> reasons.append('has a PPA')
> assert reasons, 'rejecting but no reasons found!'
> reason = ' and '.join(reasons)
>
Steve Kowalik (stevenk) wrote : | # |
At the moment you only check that the widget is editable and then check the error conditions. Please add an end-to-end test that confirms a private team can be renamed.
Ian Booth (wallyworld) wrote : | # |
I removed the entire xx-team-edit doc test (replaced with unit tests).
End-end rename test is added, not just for private teams but also teams
with purged mailing lists.
On 24/08/11 11:11, Steve Kowalik wrote:
> Review: Needs Fixing code
> At the moment you only check that the widget is editable and then check the error conditions. Please add an end-to-end test that confirms a private team can be renamed.
Curtis Hovey (sinzui) wrote : | # |
I am very wary of converting stories into unittests. Most are predicated on issues
that are relevant. A story only needs to show the happy path that demonstrates
that the user can perform a goal. In this case I believe only one browser test
is needs to show that a user can navigate from the team page to the edit page
and submit a change to be returned to the team page.
Do not use TestBrowser (an integration test) to test what a view
does (unittest). Browser tests do not document form contracts or directly
exercise the lines in the view it claims to be testing.
test_team_
It is testing that base-layout-macros, which I am pretty sure is not telling
us anything more than the 100 other tests like this. I image a test that
realley exercises that the view does soemthing like this:
def test_team_
# If we try to use a name which is already in use, we'll get an error
# message explaining it.
owner = self.factory.
team = self.factory.
form = {
# May need more field to document that actual contract the
# for requires.
}
view = create_
The page rendering has nothing to do with permission. This is a zope
secuirty rule on a view. Never test with "mark" you do not know why
permission is given. Test that the team owner has permission and other
users do not. Also this ancient test is unaware that registry-experts
is the celebrity that actually does most of the administration today.
from canonical.
def test_edit_
# Only an administrator (as well as the team owner) of a team can
# change the details of that team.
person = self.factory.
team = self.factory.
view = create_view(team, '+edit')
Ian Booth (wallyworld) wrote : | # |
On 25/08/11 00:53, Curtis Hovey wrote:
> Review: Needs Fixing test
> I am very wary of converting stories into unittests. Most are predicated on issues
> that are relevant. A story only needs to show the happy path that demonstrates
> that the user can perform a goal. In this case I believe only one browser test
> is needs to show that a user can navigate from the team page to the edit page
> and submit a change to be returned to the team page.
>
> Do not use TestBrowser (an integration test) to test what a view
> does (unittest). Browser tests do not document form contracts or directly
> exercise the lines in the view it claims to be testing.
>
I used the browser because I needed the end-end behaviour to test the
saving of a private team as requested in a review comment. The other
uses were from porting the story code and I didn't know how to do it
using the view. Many thanks for providing the examples to show how.
Ian Booth (wallyworld) wrote : | # |
> > Do not use TestBrowser (an integration test) to test what a view
> > does (unittest). Browser tests do not document form contracts or directly
> > exercise the lines in the view it claims to be testing.
> >
>
> I used the browser because I needed the end-end behaviour to test the
> saving of a private team as requested in a review comment. The other
> uses were from porting the story code and I didn't know how to do it
> using the view. Many thanks for providing the examples to show how.
Al tests except the end-end test now only use the view, not the browser.
Preview Diff
1 | === modified file 'lib/lp/registry/browser/team.py' |
2 | --- lib/lp/registry/browser/team.py 2011-08-07 04:05:52 +0000 |
3 | +++ lib/lp/registry/browser/team.py 2011-08-25 01:46:27 +0000 |
4 | @@ -259,10 +259,9 @@ |
5 | has_mailing_list = ( |
6 | mailing_list is not None and |
7 | mailing_list.status != MailingListStatus.PURGED) |
8 | - is_private = self.context.visibility == PersonVisibility.PRIVATE |
9 | has_ppa = self.context.archive is not None |
10 | |
11 | - block_renaming = (has_mailing_list or is_private or has_ppa) |
12 | + block_renaming = (has_mailing_list or has_ppa) |
13 | if block_renaming: |
14 | # This makes the field's widget display (i.e. read) only. |
15 | self.form_fields['name'].for_display = True |
16 | @@ -273,17 +272,12 @@ |
17 | # read-only mode if necessary. |
18 | if block_renaming: |
19 | # Group the read-only mode reasons in textual form. |
20 | - # Private teams can't be associated with mailing lists |
21 | - # or PPAs yet, so it's a dominant condition. |
22 | - if is_private: |
23 | - reason = 'is private' |
24 | - else: |
25 | - if not has_mailing_list: |
26 | - reason = 'has a PPA' |
27 | - elif not has_ppa: |
28 | - reason = 'has a mailing list' |
29 | - else: |
30 | - reason = 'has a mailing list and a PPA' |
31 | + reasons = [] |
32 | + if has_mailing_list: |
33 | + reasons.append('has a mailing list') |
34 | + if has_ppa: |
35 | + reasons.append('has a PPA') |
36 | + reason = ' and '.join(reasons) |
37 | self.widgets['name'].hint = _( |
38 | 'This team cannot be renamed because it %s.' % reason) |
39 | |
40 | @@ -549,7 +543,7 @@ |
41 | already been approved or declined. This can only happen |
42 | through bypassing the UI. |
43 | """ |
44 | - mailing_list = getUtility(IMailingListSet).get(self.context.name) |
45 | + getUtility(IMailingListSet).get(self.context.name) |
46 | if self.getListInState(MailingListStatus.REGISTERED) is None: |
47 | self.addError("This application can't be cancelled.") |
48 | |
49 | @@ -985,7 +979,7 @@ |
50 | failed_names = [person.displayname for person in failed_joins] |
51 | failed_list = ", ".join(failed_names) |
52 | |
53 | - mapping = dict( this_team=target_team.displayname, |
54 | + mapping = dict(this_team=target_team.displayname, |
55 | failed_list=failed_list) |
56 | |
57 | if len(failed_joins) == 1: |
58 | |
59 | === modified file 'lib/lp/registry/browser/tests/test_team_view.py' |
60 | --- lib/lp/registry/browser/tests/test_team_view.py 2010-10-04 19:50:45 +0000 |
61 | +++ lib/lp/registry/browser/tests/test_team_view.py 2011-08-25 01:46:27 +0000 |
62 | @@ -8,16 +8,28 @@ |
63 | __metaclass__ = type |
64 | |
65 | import transaction |
66 | - |
67 | -from canonical.testing.layers import DatabaseFunctionalLayer |
68 | - |
69 | -from lp.registry.interfaces.person import TeamSubscriptionPolicy |
70 | - |
71 | +from zope.security.proxy import removeSecurityProxy |
72 | + |
73 | +from canonical.launchpad.webapp.authorization import check_permission |
74 | +from canonical.launchpad.webapp.publisher import canonical_url |
75 | +from canonical.testing.layers import ( |
76 | + DatabaseFunctionalLayer, |
77 | + LaunchpadFunctionalLayer, |
78 | + ) |
79 | +from lp.registry.interfaces.mailinglist import MailingListStatus |
80 | +from lp.registry.interfaces.person import ( |
81 | + PersonVisibility, |
82 | + TeamSubscriptionPolicy, |
83 | + TeamMembershipRenewalPolicy) |
84 | from lp.testing import ( |
85 | login_person, |
86 | + person_logged_in, |
87 | TestCaseWithFactory, |
88 | ) |
89 | -from lp.testing.views import create_initialized_view |
90 | +from lp.testing.views import ( |
91 | + create_view, |
92 | + create_initialized_view, |
93 | + ) |
94 | |
95 | |
96 | class TestProposedTeamMembersEditView(TestCaseWithFactory): |
97 | @@ -141,3 +153,153 @@ |
98 | failed = (self.a_team, self.b_team) |
99 | successful = (self.c_team, self.d_team) |
100 | self.acceptTeam(self.super_team, successful, failed) |
101 | + |
102 | + |
103 | +class TestTeamEditView(TestCaseWithFactory): |
104 | + |
105 | + layer = LaunchpadFunctionalLayer |
106 | + |
107 | + def test_cannot_rename_team_with_ppa(self): |
108 | + # A team with a ppa cannot be renamed. |
109 | + owner = self.factory.makePerson() |
110 | + team = self.factory.makeTeam(owner=owner) |
111 | + removeSecurityProxy(team).archive = self.factory.makeArchive() |
112 | + with person_logged_in(owner): |
113 | + view = create_initialized_view(team, name="+edit") |
114 | + self.assertTrue(view.form_fields['name'].for_display) |
115 | + self.assertEqual( |
116 | + 'This team cannot be renamed because it has a PPA.', |
117 | + view.widgets['name'].hint) |
118 | + |
119 | + def test_cannot_rename_team_with_active_mailinglist(self): |
120 | + # Because renaming mailing lists is non-trivial in Mailman 2.1, |
121 | + # renaming teams with mailing lists is prohibited. |
122 | + owner = self.factory.makePerson() |
123 | + team = self.factory.makeTeam(owner=owner) |
124 | + self.factory.makeMailingList(team, owner) |
125 | + with person_logged_in(owner): |
126 | + view = create_initialized_view(team, name="+edit") |
127 | + self.assertTrue(view.form_fields['name'].for_display) |
128 | + self.assertEqual( |
129 | + 'This team cannot be renamed because it has a mailing list.', |
130 | + view.widgets['name'].hint) |
131 | + |
132 | + def test_can_rename_team_with_purged_mailinglist(self): |
133 | + # A team with a mailing list which is purged can be renamed. |
134 | + owner = self.factory.makePerson() |
135 | + team = self.factory.makeTeam(owner=owner) |
136 | + team_list = self.factory.makeMailingList(team, owner) |
137 | + team_list.deactivate() |
138 | + team_list.transitionToStatus(MailingListStatus.INACTIVE) |
139 | + team_list.purge() |
140 | + with person_logged_in(owner): |
141 | + view = create_initialized_view(team, name="+edit") |
142 | + self.assertFalse(view.form_fields['name'].for_display) |
143 | + |
144 | + def test_cannot_rename_team_with_multiple_reasons(self): |
145 | + # Since public teams can have mailing lists and PPAs simultaneously, |
146 | + # there will be scenarios where more than one of these conditions are |
147 | + # actually blocking the team to be renamed. |
148 | + owner = self.factory.makePerson() |
149 | + team = self.factory.makeTeam(owner=owner) |
150 | + self.factory.makeMailingList(team, owner) |
151 | + removeSecurityProxy(team).archive = self.factory.makeArchive() |
152 | + with person_logged_in(owner): |
153 | + view = create_initialized_view(team, name="+edit") |
154 | + self.assertTrue(view.form_fields['name'].for_display) |
155 | + self.assertEqual( |
156 | + ('This team cannot be renamed because it has a mailing list ' |
157 | + 'and has a PPA.'), |
158 | + view.widgets['name'].hint) |
159 | + |
160 | + def test_edit_team_view_permission(self): |
161 | + # Only an administrator or the team owner of a team can |
162 | + # change the details of that team. |
163 | + person = self.factory.makePerson() |
164 | + owner = self.factory.makePerson() |
165 | + team = self.factory.makeTeam(owner=owner) |
166 | + view = create_view(team, '+edit') |
167 | + login_person(person) |
168 | + self.assertFalse(check_permission('launchpad.Edit', view)) |
169 | + login_person(owner) |
170 | + self.assertTrue(check_permission('launchpad.Edit', view)) |
171 | + |
172 | + def test_edit_team_view_data(self): |
173 | + # The edit view renders the team's details correctly. |
174 | + owner = self.factory.makePerson() |
175 | + team = self.factory.makeTeam( |
176 | + name="team", displayname='A Team', |
177 | + description="A great team", owner=owner, |
178 | + subscription_policy=TeamSubscriptionPolicy.MODERATED) |
179 | + with person_logged_in(owner): |
180 | + view = create_initialized_view(team, name="+edit") |
181 | + self.assertEqual('team', view.widgets['name']._data) |
182 | + self.assertEqual( |
183 | + 'A Team', view.widgets['displayname']._data) |
184 | + self.assertEqual( |
185 | + 'A great team', view.widgets['teamdescription']._data) |
186 | + self.assertEqual( |
187 | + TeamSubscriptionPolicy.MODERATED, |
188 | + view.widgets['subscriptionpolicy']._data) |
189 | + self.assertEqual( |
190 | + TeamMembershipRenewalPolicy.NONE, |
191 | + view.widgets['renewal_policy']._data) |
192 | + self.assertIsNone(view.widgets['defaultrenewalperiod']._data) |
193 | + |
194 | + def test_edit_team_view_save(self): |
195 | + # A team can be edited and saved, including a name change, even if it |
196 | + # is a private team and has a purged mailing list. |
197 | + owner = self.factory.makePerson() |
198 | + team = self.factory.makeTeam( |
199 | + name="team", displayname='A Team', |
200 | + description="A great team", owner=owner, |
201 | + visibility=PersonVisibility.PRIVATE, |
202 | + subscription_policy=TeamSubscriptionPolicy.MODERATED) |
203 | + |
204 | + with person_logged_in(owner): |
205 | + team_list = self.factory.makeMailingList(team, owner) |
206 | + team_list.deactivate() |
207 | + team_list.transitionToStatus(MailingListStatus.INACTIVE) |
208 | + team_list.purge() |
209 | + url = canonical_url(team) |
210 | + browser = self.getUserBrowser(url, user=owner) |
211 | + browser.getLink('Change details').click() |
212 | + browser.getControl('Name', index=0).value = 'ubuntuteam' |
213 | + browser.getControl('Display Name').value = 'Ubuntu Team' |
214 | + browser.getControl('Team Description').value = '' |
215 | + browser.getControl('Restricted Team').selected = True |
216 | + browser.getControl('Save').click() |
217 | + |
218 | + # We're now redirected to the team's home page, which is now on a |
219 | + # different URL since we changed its name. |
220 | + self.assertEqual('http://launchpad.dev/~ubuntuteam', browser.url) |
221 | + |
222 | + # Check the values again. |
223 | + browser.getLink('Change details').click() |
224 | + self.assertEqual( |
225 | + 'ubuntuteam', browser.getControl('Name', index=0).value) |
226 | + self.assertEqual( |
227 | + 'Ubuntu Team', browser.getControl('Display Name', index=0).value) |
228 | + self.assertEqual( |
229 | + '', browser.getControl('Team Description', index=0).value) |
230 | + self.assertTrue( |
231 | + browser.getControl('Restricted Team', index=0).selected) |
232 | + |
233 | + def test_team_name_already_used(self): |
234 | + # If we try to use a name which is already in use, we'll get an error |
235 | + # message explaining it. |
236 | + |
237 | + self.factory.makeTeam(name="existing") |
238 | + owner = self.factory.makePerson() |
239 | + team = self.factory.makeTeam(name="team", owner=owner) |
240 | + |
241 | + form = { |
242 | + 'field.name': 'existing', |
243 | + 'field.actions.save': 'Save', |
244 | + } |
245 | + login_person(owner) |
246 | + view = create_initialized_view(team, '+edit', form=form) |
247 | + self.assertEqual(1, len(view.errors)) |
248 | + self.assertEqual( |
249 | + 'existing is already in use by another person or team.', |
250 | + view.errors[0].doc()) |
251 | |
252 | === removed file 'lib/lp/registry/stories/team/xx-team-edit.txt' |
253 | --- lib/lp/registry/stories/team/xx-team-edit.txt 2011-08-07 08:04:38 +0000 |
254 | +++ lib/lp/registry/stories/team/xx-team-edit.txt 1970-01-01 00:00:00 +0000 |
255 | @@ -1,180 +0,0 @@ |
256 | -Editing team details |
257 | -==================== |
258 | - |
259 | -Only an administrator (as well as the team owner) of a team can change the |
260 | -details of that team. |
261 | - |
262 | - >>> user_browser.open('http://launchpad.dev/~ubuntu-team/+edit') |
263 | - Traceback (most recent call last): |
264 | - ... |
265 | - Unauthorized:... |
266 | - |
267 | - >>> browser.addHeader('Authorization', 'Basic mark@example.com:test') |
268 | - >>> browser.open('http://launchpad.dev/~ubuntu-team') |
269 | - >>> browser.getLink('Change details').click() |
270 | - >>> browser.url |
271 | - 'http://launchpad.dev/~ubuntu-team/+edit' |
272 | - >>> browser.getControl('Name', index=0).value |
273 | - 'ubuntu-team' |
274 | - >>> browser.getControl('Display Name').value |
275 | - 'Ubuntu Team' |
276 | - >>> browser.getControl('Team Description').value |
277 | - 'This Team is responsible for the Ubuntu Distribution' |
278 | - >>> browser.getControl('Moderated Team').selected |
279 | - True |
280 | - >>> browser.getControl('invite them to apply for renewal').selected |
281 | - True |
282 | - >>> print browser.getControl('Renewal period').value |
283 | - |
284 | -Now we'll change some values. |
285 | - |
286 | - >>> browser.getControl('Name', index=0).value = 'ubuntuteam' |
287 | - >>> browser.getControl('Display Name').value = 'The Ubuntu Team' |
288 | - >>> browser.getControl('Team Description').value = '' |
289 | - >>> browser.getControl('Restricted Team').selected = True |
290 | - |
291 | - >>> browser.getControl('Save').click() |
292 | - |
293 | -We're now redirected to the team's home page, which is now on a different |
294 | -URL since we changed its name. |
295 | - |
296 | - >>> browser.url |
297 | - 'http://launchpad.dev/~ubuntuteam' |
298 | - >>> browser.getLink('Change details').click() |
299 | - |
300 | - >>> browser.url |
301 | - 'http://launchpad.dev/~ubuntuteam/+edit' |
302 | - >>> browser.getControl('Name', index=0).value |
303 | - 'ubuntuteam' |
304 | - >>> browser.getControl('Display Name').value |
305 | - 'The Ubuntu Team' |
306 | - >>> browser.getControl('Team Description').value |
307 | - '' |
308 | - >>> browser.getControl('Restricted Team').selected |
309 | - True |
310 | - |
311 | -If we try to use a name which is already in use, we'll get an error |
312 | -message explaining it. |
313 | - |
314 | - >>> browser.getControl('Name', index=0).value = 'mark' |
315 | - >>> browser.getControl('Save').click() |
316 | - |
317 | - >>> browser.url |
318 | - 'http://launchpad.dev/%7Eubuntuteam/+edit' |
319 | - >>> for tag in find_tags_by_class(browser.contents, 'message'): |
320 | - ... print extract_text(tag) |
321 | - There is 1 error. |
322 | - mark is already in use by another person or team. |
323 | - |
324 | - |
325 | -Teams with mailing lists may not be renamed |
326 | -------------------------------------------- |
327 | - |
328 | -Because renaming mailing lists is non-trivial in Mailman 2.1, renaming teams |
329 | -with mailing lists is prohibited. This is done by making the 'name' field of |
330 | -such team pages read-only. |
331 | - |
332 | - >>> browser.open('http://launchpad.dev/~landscape-developers') |
333 | - >>> browser.getLink(url='+mailinglist').click() |
334 | - >>> browser.getControl('Create new Mailing List').click() |
335 | - |
336 | - # Approval of mailing lists is not yet exposed through the web. |
337 | - >>> from lp.registry.model.mailinglist import MailingListSet |
338 | - >>> from canonical.launchpad.ftests import login, logout |
339 | - >>> login('foo.bar@canonical.com') |
340 | - >>> mailing_list = MailingListSet().get('landscape-developers') |
341 | - >>> mailing_list.syncUpdate() |
342 | - >>> logout() |
343 | - |
344 | -Because the 'name' field is read-only, it's not a control that we can get |
345 | -a hold of to change. |
346 | - |
347 | - >>> browser.getLink('Change details').click() |
348 | - >>> browser.getControl(name='field.name').value |
349 | - Traceback (most recent call last): |
350 | - ... |
351 | - LookupError: name 'field.name' |
352 | - |
353 | -The reason why the field is immutable is displayed in the field |
354 | -description. |
355 | - |
356 | - >>> print extract_text( |
357 | - ... first_tag_by_class(browser.contents, 'form')) |
358 | - Name: landscape-developers |
359 | - This team cannot be renamed because it has a mailing list. |
360 | - ... |
361 | - |
362 | -Of course, teams without mailing lists still have an editable name field. |
363 | - |
364 | - >>> print MailingListSet().get('guadamen') |
365 | - None |
366 | - >>> browser.open('http://launchpad.dev/~guadamen') |
367 | - >>> browser.getLink('Change details').click() |
368 | - >>> browser.getControl(name='field.name').value |
369 | - 'guadamen' |
370 | - |
371 | - |
372 | -Private teams may not be renamed |
373 | --------------------------------- |
374 | - |
375 | - >>> # Create the necessary teams. |
376 | - >>> login('foo.bar@canonical.com') |
377 | - >>> from lp.registry.interfaces.person import PersonVisibility |
378 | - >>> owner = factory.makePerson(name='team-owner') |
379 | - >>> priv_team = factory.makeTeam(name='private-team', |
380 | - ... owner=owner, |
381 | - ... visibility=PersonVisibility.PRIVATE) |
382 | - >>> logout() |
383 | - |
384 | - >>> browser.open('http://launchpad.dev/~private-team') |
385 | - >>> browser.getLink('Change details').click() |
386 | - >>> browser.getControl(name='field.name').value |
387 | - Traceback (most recent call last): |
388 | - ... |
389 | - LookupError: name 'field.name' |
390 | - |
391 | -The reason why the field is immutable is displayed in the field |
392 | -description. |
393 | - |
394 | - >>> print extract_text( |
395 | - ... first_tag_by_class(browser.contents, 'form')) |
396 | - Name: private-team |
397 | - This team cannot be renamed because it is private. |
398 | - ... |
399 | - |
400 | - |
401 | -Multiple conditions blocking team renaming |
402 | ------------------------------------------- |
403 | - |
404 | -Since public teams can have mailing lists and PPAs simultaneously, |
405 | -there will be scenarios where more than one of these conditions are |
406 | -actually blocking the team to be renamed. |
407 | - |
408 | -The Landscape Developers team already has a mailing list, we will |
409 | -also activate its PPA. |
410 | - |
411 | - >>> browser.open('http://launchpad.dev/~landscape-developers') |
412 | - >>> browser.getLink('Create a new PPA').click() |
413 | - >>> browser.getControl( |
414 | - ... name="field.displayname").value = 'Devel PPA' |
415 | - >>> browser.getControl(name="field.accepted").value = True |
416 | - >>> browser.getControl( |
417 | - ... name="field.description").value = 'Hoohay for Team PPA.' |
418 | - >>> browser.getControl("Activate").click() |
419 | - |
420 | -Back in the team edit form, the 'name' field remains read-only and has |
421 | -a complete explanation about why it cannot be modified. |
422 | - |
423 | - >>> browser.getLink('Overview').click() |
424 | - >>> browser.getLink('Change details').click() |
425 | - |
426 | - >>> browser.getControl(name='field.name').value |
427 | - Traceback (most recent call last): |
428 | - ... |
429 | - LookupError: name 'field.name' |
430 | - |
431 | - >>> print extract_text( |
432 | - ... first_tag_by_class(browser.contents, 'form')) |
433 | - Name: landscape-developers |
434 | - This team cannot be renamed because it has a mailing list and a PPA. |
435 | - ... |
436 | |
437 | === modified file 'lib/lp/testing/factory.py' |
438 | --- lib/lp/testing/factory.py 2011-08-16 09:14:07 +0000 |
439 | +++ lib/lp/testing/factory.py 2011-08-25 01:46:27 +0000 |
440 | @@ -755,6 +755,7 @@ |
441 | address, person, email_status, account) |
442 | |
443 | def makeTeam(self, owner=None, displayname=None, email=None, name=None, |
444 | + description=None, |
445 | subscription_policy=TeamSubscriptionPolicy.OPEN, |
446 | visibility=None, members=None): |
447 | """Create and return a new, arbitrary Team. |
448 | @@ -764,6 +765,7 @@ |
449 | :type owner: `IPerson` or string |
450 | :param displayname: The team's display name. If not given we'll use |
451 | the auto-generated name. |
452 | + :param description: Team team's description. |
453 | :type string: |
454 | :param email: The email address to use as the team's contact address. |
455 | :type email: string |
456 | @@ -789,7 +791,8 @@ |
457 | displayname = SPACE.join( |
458 | word.capitalize() for word in name.split('-')) |
459 | team = getUtility(IPersonSet).newTeam( |
460 | - owner, name, displayname, subscriptionpolicy=subscription_policy) |
461 | + owner, name, displayname, teamdescription=description, |
462 | + subscriptionpolicy=subscription_policy) |
463 | if visibility is not None: |
464 | # Visibility is normally restricted to launchpad.Commercial, so |
465 | # removing the security proxy as we don't care here. |
+ if not has_mailing_list:
+ reason = 'has a PPA'
+ elif not has_ppa:
+ reason = 'has a mailing list'
This made me blink.
Perhaps: append( 'has a mailing list') append( 'has a PPA')
reasons = []
if has_mailing_list:
reasons.
if has_ppa:
reasons.
assert reasons, 'rejecting but no reasons found!'
reason = ' and '.join(reasons)