~launchpad-pqm/launchpad/devel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
Start out by verifying the members page is sane.

    >>> browser.open('http://launchpad.dev/~ubuntu-team/+members')
    >>> 'Ubuntu Team' in browser.contents
    True

Let's take a look at Colin's subscription page. Colin is an
administrator and his subscription never expires.

    >>> browser.addHeader('Authorization', 'Basic foo.bar@canonical.com:test')
    >>> browser.reload()
    >>> url = '/~ubuntu-team/+member/kamion'
    >>> browser.getLink(url=url).click()

    >>> print browser.title
    Colin Watson's membership : ...Ubuntu Team... team
    >>> "Active member" in browser.contents
    True
    >>> browser.getControl(name='admin').value
    ['yes']

    >>> browser.getControl(name='expires').value
    ['never']

Post an incomplete date and remove his administrator status.

There is some TestBrowser voodoo at work here. The datepicker widget now
allows you to render it disabled, which is neat because in the compound
widget that we have made it shows more clearly when the date is relevant and
when it is not if we disable it when it is not relevant (when the expiration
is set to "Never" with the radio button).

The Zope TestBrowser is smart enough to detect that the widget was rendered
with the input disabled, but not smart enough to play out the JavaScript
which would enable the input when the radio button was clicked to indicate
that a specific expiration date was desired. There is also no TestBrowser
way to "enable" the input. So, we have to reach into the guts of the
TestBrowser to manually re-enable the input. That's what the
control.mech_control.disabled=False stuff is.

    >>> browser.getControl(name='admin').value = ['no']
    >>> browser.getControl(name='expires').value = ['date']
    >>> expiry = browser.getControl(name='membership.expirationdate')
    >>> expiry.mech_control.disabled = False # control may have been disabled
    >>> expiry.value = 'ssdf'
    >>> browser.getControl('Change').click()

We get a nice error message

    >>> for tag in find_tags_by_class(browser.contents, 'message'):
    ...     print tag.renderContents()
    Invalid expiration: Invalid date value

Give up on change, nothing should have changed with Colin:

    >>> from zope.component import getUtility
    >>> from lp.registry.interfaces.person import IPersonSet
    >>> from lp.registry.interfaces.teammembership import ITeamMembershipSet
    >>> from canonical.launchpad.ftests import login, logout, ANONYMOUS
    >>> login(ANONYMOUS)
    >>> personset = getUtility(IPersonSet)
    >>> teammembershipset = getUtility(ITeamMembershipSet)
    >>> ubuntu_team = personset.getByName('ubuntu-team')
    >>> kamion = personset.getByName('kamion')
    >>> kamion_membership = teammembershipset.getByPersonAndTeam(
    ...     kamion, ubuntu_team)
    >>> kamion_membership.status.title
    'Administrator'
    >>> print kamion_membership.dateexpires
    None
    >>> logout()


Now revoke Colin's administrator status and make him expire in November
next year -- successfully.

    >>> from datetime import datetime, timedelta
    >>> expire_date = datetime.utcnow() + timedelta(days=365)

    >>> browser.getControl(name='admin').value = ['no']
    >>> browser.getControl(name='expires').value = ['date']
    >>> expiry = browser.getControl(name='membership.expirationdate')
    >>> expiry.mech_control.disabled = False # control may have been disabled
    >>> expiry.value = expire_date.strftime('%Y-%m-%d')
    >>> browser.getControl(name='comment').value = 'Arfie'
    >>> browser.getControl('Change').click()

We're redirected to the +members page

    >>> browser.url
    'http://launchpad.dev/~ubuntu-team/+members'

    >>> login(ANONYMOUS)
    >>> kamion_membership = teammembershipset.getByPersonAndTeam(
    ...     kamion, ubuntu_team)
    >>> print kamion_membership.status.title
    Approved
    >>> kamion_membership.dateexpires.date() == expire_date.date()
    True
    >>> print kamion_membership.last_change_comment
    Arfie
    >>> logout()

If we revisit Colin's membership page we'll see the comment field is
pre-populated with the last comment.

    >>> url = '/~ubuntu-team/+member/kamion'
    >>> browser.getLink(url=url).click()
    >>> # Do not use 'print' for the following test as it will eliminate potential
    >>> # leading and trailing whitespace, which we don't want.
    >>> browser.getControl(name='comment').value
    'Arfie'

Jeff is also an administrator and his subscription never expires, but he can
demote himself. He starts by paging though the memberships, then he deselects
the administrator control on the membership page.

    >>> jdub_browser = setupBrowser(auth='Basic jeff.waugh@ubuntulinux.com:jdub')
    >>> jdub_browser.open('http://launchpad.dev/~ubuntu-team/+members')
    >>> jdub_browser.getLink('Next').click()
    >>> url = '/~ubuntu-team/+member/jdub'
    >>> jdub_browser.getLink(url=url).click()

    >>> print jdub_browser.title
    Jeff Waugh's membership : ...Ubuntu Team... team
    >>> "Active member" in jdub_browser.contents
    True

    >>> jdub_browser.getControl(name='admin').value = ['no']
    >>> jdub_browser.getControl('Change').click()

    >>> jdub_browser.url
    'http://launchpad.dev/~ubuntu-team/+members'

    >>> login(ANONYMOUS)
    >>> jdub = personset.getByName('jdub')
    >>> jdub_membership = teammembershipset.getByPersonAndTeam(
    ...     jdub, ubuntu_team)
    >>> jdub_membership.status.title
    'Approved'
    >>> logout()

Sample person had his membership declined to the guadamen group. Test
that the page works and that foo.bar@canonical can access it.

    >>> browser.open('http://launchpad.dev/~guadamen/+member/name12/')

    >>> 'Declined member' in browser.contents
    True

Dave Miller is a proposed member in Ubuntu Gnome Team.
If two people try to accept him as a member at the same time, the first one
should succeed and the second one receive a nice error message.

    >>> browser.open('http://launchpad.dev/~name18/+members')
    >>> url = '/~name18/+member/justdave'
    >>> browser.getLink(url=url).click()

    >>> second_browser = setupBrowser(auth='Basic foo.bar@canonical.com:test')
    >>> second_browser.open('http://launchpad.dev' + url)

Approve the membership in the first browser.

    >>> browser.getControl('Approve').click()

We're redirected to the members page.

    >>> browser.url
    'http://launchpad.dev/~name18/+members'

    >>> login(ANONYMOUS)
    >>> dave = personset.getByName('justdave')
    >>> ubuntu_gnome_team = personset.getByName('name18')
    >>> dave_membership = teammembershipset.getByPersonAndTeam(
    ...     dave, ubuntu_gnome_team)
    >>> dave_membership.status.title
    'Approved'
    >>> logout()


But in the second browser with the stale data we get an error message:

    >>> second_browser.getControl('Approve').click()
    >>> message = (
    ...     'The membership request for Dave Miller has already been processed')
    >>> message in second_browser.contents
    True

An admin can see the former members of the team.

    >>> browser.open('http://launchpad.dev/~name18/+members')
    >>> print extract_text(
    ...     find_tag_by_id(browser.contents, 'inactivemembers'))
    Name    Joined in   Status...

Other users cannot see the former members of the team.

    >>> user_browser.open('http://launchpad.dev/~name18/+members')
    >>> print find_tag_by_id(user_browser.contents, 'inactivemembers')
    None


Team Participation page
=======================

The team participation page shows the team in which a person is a direct
member, as well as the teams in which they are an indirect member.

Kiko has not joined any teams:

    >>> anon_browser.open('http://launchpad.dev/~kiko/+participation')
    >>> print extract_text(
    ...     find_tag_by_id(anon_browser.contents, 'no-participation'))
    Christian Reis has not yet joined any teams.
    >>> print find_tag_by_id(anon_browser.contents, 'participation')
    None

Sample Person has both direct and indirect memberships:

    >>> anon_browser.open('http://launchpad.dev/~name12/+participation')
    >>> content = find_main_content(anon_browser.contents)
    >>> print find_tag_by_id(content, 'no-participation')
    None

    >>> print extract_text(
    ...     find_tag_by_id(content, 'participation'))
    Team                  Joined      Role    Via                 Mailing List
    HWDB Team             2009-07-09  Member  —              —
    Landscape Developers  2006-07-11  Owner   —              —
    Launchpad Users       2008-11-26  Owner   —              —
    Ubuntu Gnome Team     —     Member  Warty Security Team  —
    Warty Security Team   2007-01-26  Member  —              —

User can see links to register teams and change their mailing list
subscriptions on their own participation page.

    >>> print find_tag_by_id(content, 'participation-actions')
    None

    >>> user_browser.open('http://launchpad.dev/~no-priv/+participation')
    >>> actions = find_tag_by_id(
    ...     user_browser.contents, 'participation-actions')
    >>> print extract_text(actions)
    Register a team
    Change mailing list subscriptions

    >>> user_browser.getLink('Register a team')
    <Link ... url='http://.../people/+newteam'>
    >>> user_browser.getLink('Change mailing list subscriptions')
    <Link ... url='http://.../~no-priv/+editemails'>

Teams also have a participation page, but it does not include a mailing
list column.

    >>> admin_browser.open('http://launchpad.dev/~admins/+participation')
    >>> print extract_text(
    ...     find_tag_by_id(admin_browser.contents, 'participation'))
    Team                  Joined      Role    Via
    Mailing List Experts  2007-10-04  Owner   &mdash;