Merge lp:~allenap/launchpad/me-too-ajax-bug-361097 into lp:launchpad
- me-too-ajax-bug-361097
- Merge into devel
Status: | Merged | ||||
---|---|---|---|---|---|
Merged at revision: | not available | ||||
Proposed branch: | lp:~allenap/launchpad/me-too-ajax-bug-361097 | ||||
Merge into: | lp:launchpad | ||||
Diff against target: | None lines | ||||
To merge this branch: | bzr merge lp:~allenap/launchpad/me-too-ajax-bug-361097 | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Martin Albisetti (community) | ui | Approve | |
Michael Nelson (community) | ui | Approve | |
Canonical Launchpad Engineering | Pending | ||
Review via email: mp+9510@code.launchpad.net |
Commit message
Description of the change
Gavin Panella (allenap) wrote : | # |
Michael Nelson (michael.nelson) wrote : | # |
As per our IRC conversation, ui=me* - my only remaining question was regarding there being two icons for the one action once a user marks the bug as affecting them, which you were going to ask Martin about.
<noodles775> allenap: Looks great and works really well. I've got a few questions with the wording though (but I'm no expert there).
<noodles775> Was the wording discussed, or what were your thoughts when you chose the wording?
<noodles775> (on the actual overlay I mean)
<allenap> noodles775: I'll be with you soon; on the phone.
<allenap> noodles775: I didn't talk to anyone about the wording. I looked at the various me_too movies in https:/
<allenap> noodles775: I was also limited a little by ChoiceSource where the displayed value in the overlay is the same as the displayed value in the <... class="value"> area.
<allenap> noodles775: But I think I should see if I can change it to display "Does this bug affect you?" if the user has not voted in either direction.
<noodles775> The link? Yes that would be nice.
<allenap> noodles775: Okay, I've pushed a fix for that.
<noodles775> allenap: so am I right that the issue you had is that you cannot have the values of the choices displaying as 'affects me too', and hence you had to change the heading/intro of the overlay to 'Does this bug affect you?'?
<allenap> noodles775: Yes, that's right, unless I add some hackery to munge the displayed value (which might not be that hard actually...)
<adeuring1> gmb. thanks
<allenap> noodles775: I might modify ChangeSource to allow alternate display values.
<noodles775> allenap: is it possible to update the ChoiceSource/widget to all...
<noodles775> allenap: :)
<noodles775> Yes, I think it might be worthwhile doing that, as it seems strange asking 'Does this bug affect you?' after the user has just clicked on the link.
<allenap> noodles775: Okay, I'll do that. But it seems like you're overall happy with it. Shall I aim for https:/
<noodles775> allenap: Indeed - it looks and feels great to use! And yes, I'd recommend going for exactly what me_too.swf shows.
<allenap> noodles775: Okay, I'll bother you again later for a final sign off then. Thank you!
<noodles775> allenap: If it's not possible or too difficult to update ChangeSource, I wonder how it would feel without the heading in the overlay, but just the two options "This bug affects me too"/...
<allenap> noodles775: I'll give that a go too.
<allenap> noodles775: Hi there. Continuing on from the UI review earlier, I have a fix for showing different ChoiceSource/
<noodles775_> allenap: Great updates on the 'This bug doesn't affect me' feature!
<allenap> noodles775_: Cool, thanks :)
<noodles775_> allenap: is there an mp for it?
<noodles775_> allenap: some small thoughts that I had while using it (not so important, but might be easy):
<allenap> noodles775_: Not yet, bu...
Martin Albisetti (beuno) wrote : | # |
Hi Gavin,
This branch gets us a step closer to AJAX-zen, thanks for working on it.
As discussed on IRC, it feels more natural to me to phrase the options in the pop-up as a question, along the lines of: "Does this bug affect you?" "Yes, it affects me/No, it doesn't affect me".
The second issue, which is more of an open-ended question, is if the edit icon should be on the right instead of on the left, since you're editing that object (think about titles, for example). I'm inclined to prefer it on the right, but I will leave it to you to make that call.
Preview Diff
1 | === modified file 'lib/canonical/launchpad/javascript/bugs/bugtask-index.js' |
2 | --- lib/canonical/launchpad/javascript/bugs/bugtask-index.js 2009-07-29 11:13:40 +0000 |
3 | +++ lib/canonical/launchpad/javascript/bugs/bugtask-index.js 2009-07-31 13:10:26 +0000 |
4 | @@ -1308,6 +1308,116 @@ |
5 | } |
6 | }; |
7 | |
8 | +/** |
9 | + * Set up the "me too" selection. |
10 | + * |
11 | + * Called once, on load, to initialize the page. Call this function if |
12 | + * the "me too" information is displayed on a bug page and the user is |
13 | + * logged in. |
14 | + * |
15 | + * @method setup_me_too |
16 | + */ |
17 | +bugs.setup_me_too = function(user_is_affected) { |
18 | + var me_too_content = Y.get('#affectsmetoo'); |
19 | + var me_too_edit = new MeTooChoiceSource({ |
20 | + contentBox: me_too_content, |
21 | + value: user_is_affected, |
22 | + title: 'This bug:', |
23 | + items: [ |
24 | + {name: 'Affects me too', value: true, |
25 | + source_name: 'This bug affects me too', |
26 | + disabled: false}, |
27 | + {name: 'Does not affect me', value: false, |
28 | + source_name: "This bug doesn't affect me", |
29 | + disabled: false} |
30 | + ], |
31 | + elementToFlash: me_too_content, |
32 | + backgroundColor: '#FFFFFF' |
33 | + }); |
34 | + me_too_edit.render(); |
35 | +}; |
36 | + |
37 | +/** |
38 | + * This class is a derivative of ChoiceSource that handles the |
39 | + * specifics of editing "me too" option. |
40 | + * |
41 | + * @class MeTooChoiceSource |
42 | + * @extends ChoiceSource |
43 | + * @constructor |
44 | + */ |
45 | +function MeTooChoiceSource() { |
46 | + MeTooChoiceSource.superclass.constructor.apply(this, arguments); |
47 | +} |
48 | + |
49 | +Y.mix(MeTooChoiceSource, { |
50 | + NAME: 'metoocs', |
51 | + NS: 'metoocs' |
52 | +}); |
53 | + |
54 | +Y.extend(MeTooChoiceSource, Y.ChoiceSource, { |
55 | + initializer: function() { |
56 | + var widget = this; |
57 | + this.error_handler = new LP.client.ErrorHandler(); |
58 | + this.error_handler.clearProgressUI = function() { |
59 | + widget._uiClearWaiting(); |
60 | + }; |
61 | + this.error_handler.showError = function(error_msg) { |
62 | + widget.showError(error_msg); |
63 | + }; |
64 | + }, |
65 | + |
66 | + showError: function(err) { |
67 | + display_error(null, err); |
68 | + }, |
69 | + |
70 | + syncUI: function() { |
71 | + MeTooChoiceSource.superclass.syncUI.apply(this, arguments); |
72 | + // Show the flame icon if the user is affected by this bug. |
73 | + var content_box = this.get('contentBox'); |
74 | + var flame = content_box.query('.dynamic img[src=/@@/flame-icon]'); |
75 | + if (this.get('value')) { |
76 | + flame.removeClass('unseen'); |
77 | + } else { |
78 | + flame.addClass('unseen'); |
79 | + } |
80 | + }, |
81 | + |
82 | + render: function() { |
83 | + MeTooChoiceSource.superclass.render.apply(this, arguments); |
84 | + // Force the ChoiceSource to be rendered inline. |
85 | + this.get('boundingBox').setStyle('display', 'inline'); |
86 | + // Hide the static content and show the dynamic content. |
87 | + this.get('contentBox').query('.static').addClass('unseen'); |
88 | + this.get('contentBox').query('.dynamic').removeClass('unseen'); |
89 | + }, |
90 | + |
91 | + _saveData: function() { |
92 | + // Set the widget to the 'waiting' state. |
93 | + this._uiSetWaiting(); |
94 | + |
95 | + var value = this.getInput(); |
96 | + var client = new LP.client.Launchpad(); |
97 | + var widget = this; |
98 | + |
99 | + var config = { |
100 | + on: { |
101 | + success: function(entry) { |
102 | + widget._uiClearWaiting(); |
103 | + MeTooChoiceSource.superclass._saveData.call( |
104 | + widget, value); |
105 | + }, |
106 | + failure: this.error_handler.getFailureHandler() |
107 | + }, |
108 | + parameters: { |
109 | + affected: value |
110 | + } |
111 | + }; |
112 | + |
113 | + client.named_post( |
114 | + LP.client.cache.bug.self_link, 'markUserAffected', config); |
115 | + } |
116 | +}); |
117 | + |
118 | /* |
119 | * Check if the current user can unsubscribe the person |
120 | * being subscribed. |
121 | @@ -1402,6 +1512,6 @@ |
122 | } |
123 | } |
124 | }, '0.1', {requires: ['base', 'oop', 'node', 'event', 'io-base', 'substitute', |
125 | - 'widget-position-ext', 'lazr.formoverlay', 'lazr.anim', |
126 | + 'widget-position-ext', 'lazr.formoverlay', 'lazr.anim', |
127 | 'lazr.base', 'lazr.overlay', 'lazr.choiceedit', |
128 | 'lp.picker', 'lp.client.plugins', 'lp.subscriber']}); |
129 | |
130 | === modified file 'lib/lp/bugs/browser/bugtask.py' |
131 | --- lib/lp/bugs/browser/bugtask.py 2009-07-17 18:46:25 +0000 |
132 | +++ lib/lp/bugs/browser/bugtask.py 2009-07-23 10:34:08 +0000 |
133 | @@ -2915,13 +2915,15 @@ |
134 | return self.context.isUserAffected(self.user) |
135 | |
136 | @property |
137 | - def affects_form_value(self): |
138 | - """The value to use in the inline me too form.""" |
139 | - affected = self.context.isUserAffected(self.user) |
140 | - if affected is None or affected == False: |
141 | - return 'YES' |
142 | + def current_user_affected_js_status(self): |
143 | + """A javascript literal indicating if the user is affected.""" |
144 | + affected = self.current_user_affected_status |
145 | + if affected is None: |
146 | + return 'null' |
147 | + elif affected: |
148 | + return 'true' |
149 | else: |
150 | - return 'NO' |
151 | + return 'false' |
152 | |
153 | |
154 | class BugTaskTableRowView(LaunchpadView): |
155 | |
156 | === modified file 'lib/lp/bugs/browser/tests/test_bugtask.py' |
157 | --- lib/lp/bugs/browser/tests/test_bugtask.py 2009-06-25 00:40:31 +0000 |
158 | +++ lib/lp/bugs/browser/tests/test_bugtask.py 2009-07-23 10:34:08 +0000 |
159 | @@ -1,23 +1,63 @@ |
160 | # Copyright 2009 Canonical Ltd. This software is licensed under the |
161 | # GNU Affero General Public License version 3 (see the file LICENSE). |
162 | |
163 | +__metaclass__ = type |
164 | + |
165 | + |
166 | import unittest |
167 | |
168 | from zope.testing.doctest import DocTestSuite |
169 | |
170 | -from lp.bugs.browser import bugtask |
171 | +from canonical.launchpad.ftests import login |
172 | from canonical.launchpad.testing.systemdocs import ( |
173 | LayeredDocFileSuite, setUp, tearDown) |
174 | from canonical.testing import LaunchpadFunctionalLayer |
175 | |
176 | +from lp.bugs.browser import bugtask |
177 | +from lp.bugs.browser.bugtask import BugTasksAndNominationsView |
178 | +from lp.testing import TestCaseWithFactory |
179 | + |
180 | + |
181 | +class TestBugTasksAndNominationsView(TestCaseWithFactory): |
182 | + |
183 | + layer = LaunchpadFunctionalLayer |
184 | + |
185 | + def setUp(self): |
186 | + super(TestBugTasksAndNominationsView, self).setUp() |
187 | + login('foo.bar@canonical.com') |
188 | + self.bug = self.factory.makeBug() |
189 | + self.view = BugTasksAndNominationsView(self.bug, None) |
190 | + |
191 | + def test_current_user_affected_status(self): |
192 | + self.failUnlessEqual( |
193 | + None, self.view.current_user_affected_status) |
194 | + self.view.context.markUserAffected(self.view.user, True) |
195 | + self.failUnlessEqual( |
196 | + True, self.view.current_user_affected_status) |
197 | + self.view.context.markUserAffected(self.view.user, False) |
198 | + self.failUnlessEqual( |
199 | + False, self.view.current_user_affected_status) |
200 | + |
201 | + def test_current_user_affected_js_status(self): |
202 | + self.failUnlessEqual( |
203 | + 'null', self.view.current_user_affected_js_status) |
204 | + self.view.context.markUserAffected(self.view.user, True) |
205 | + self.failUnlessEqual( |
206 | + 'true', self.view.current_user_affected_js_status) |
207 | + self.view.context.markUserAffected(self.view.user, False) |
208 | + self.failUnlessEqual( |
209 | + 'false', self.view.current_user_affected_js_status) |
210 | + |
211 | |
212 | def test_suite(): |
213 | suite = unittest.TestSuite() |
214 | + suite.addTest(unittest.makeSuite(TestBugTasksAndNominationsView)) |
215 | suite.addTest(DocTestSuite(bugtask)) |
216 | suite.addTest(LayeredDocFileSuite( |
217 | 'bugtask-target-link-titles.txt', setUp=setUp, tearDown=tearDown, |
218 | layer=LaunchpadFunctionalLayer)) |
219 | return suite |
220 | |
221 | + |
222 | if __name__ == '__main__': |
223 | unittest.TextTestRunner().run(test_suite()) |
224 | |
225 | === modified file 'lib/lp/bugs/stories/bugs/xx-bug-affects-me-too.txt' |
226 | --- lib/lp/bugs/stories/bugs/xx-bug-affects-me-too.txt 2009-06-12 16:36:02 +0000 |
227 | +++ lib/lp/bugs/stories/bugs/xx-bug-affects-me-too.txt 2009-07-31 13:49:53 +0000 |
228 | @@ -1,7 +1,7 @@ |
229 | = Marking a bug as affecting the user = |
230 | |
231 | -Users can mark bugs as affecting them. Let's create a sample bug to try |
232 | -this on. |
233 | +Users can mark bugs as affecting them. Let's create a sample bug to |
234 | +try this out. |
235 | |
236 | >>> login(ANONYMOUS) |
237 | >>> from canonical.launchpad.webapp import canonical_url |
238 | @@ -9,15 +9,27 @@ |
239 | >>> test_bug_url = canonical_url(test_bug) |
240 | >>> logout() |
241 | |
242 | -The user goes to the bug's index page, and clicks the edit action link |
243 | -near 'This bug affects me too'. |
244 | +The user goes to the bug's index page, and finds a statement that the |
245 | +bug is not marked as affecting them. |
246 | |
247 | >>> user_browser.open(test_bug_url) |
248 | >>> print extract_text(find_tag_by_id( |
249 | - ... user_browser.contents, 'affectsmetooform')) |
250 | - This bug doesn't affect me... |
251 | - |
252 | - >>> user_browser.getLink('change').click() |
253 | + ... user_browser.contents, 'affectsmetoo').find( |
254 | + ... None, 'static')) |
255 | + This bug doesn't affect me |
256 | + |
257 | +Next to the statement is a link containing an edit icon. |
258 | + |
259 | + >>> edit_link = find_tag_by_id( |
260 | + ... user_browser.contents, 'affectsmetoo').a |
261 | + >>> print edit_link['href'] |
262 | + +affectsmetoo |
263 | + >>> print edit_link.img['src'] |
264 | + /@@/edit |
265 | + |
266 | +The user is affected by this bug, so clicks the link. |
267 | + |
268 | + >>> user_browser.getLink(url='+affectsmetoo').click() |
269 | >>> print user_browser.url |
270 | http://bugs.launchpad.dev/.../+bug/.../+affectsmetoo |
271 | >>> user_browser.getControl(name='field.affects').value |
272 | @@ -28,22 +40,26 @@ |
273 | >>> user_browser.getControl('Change').click() |
274 | |
275 | The bug page loads again, and now the text is changed, to make it |
276 | -clear to the user that they can change the selection. |
277 | +clear to the user that they have marked this bug as affecting them. |
278 | |
279 | - >>> print extract_text(find_tags_by_class( |
280 | - ... user_browser.contents, 'menu-link-affectsmetoo')[0]) |
281 | - change |
282 | + >>> print extract_text(find_tag_by_id( |
283 | + ... user_browser.contents, 'affectsmetoo').find( |
284 | + ... None, 'static')) |
285 | + This bug affects me too |
286 | |
287 | Next to it, we also see the 'hot bug' icon, to indicate that the user |
288 | has marked the bug as affecting them. |
289 | |
290 | >>> print find_tag_by_id( |
291 | - ... user_browser.contents, 'affectsmetooform').img['src'] |
292 | + ... user_browser.contents, 'affectsmetoo').img['src'] |
293 | /@@/flame-icon |
294 | |
295 | - >>> user_browser.getLink('change').click() |
296 | - |
297 | -The user is changing his selection to 'No' and submits the form. |
298 | +On second thoughts, the user realises that this bug does not affect |
299 | +them, so they click on the edit link once more. |
300 | + |
301 | + >>> user_browser.getLink(url='+affectsmetoo').click() |
302 | + |
303 | +The user changes his selection to 'No' and submits the form. |
304 | |
305 | >>> user_browser.getControl(name='field.affects').value = ['NO'] |
306 | >>> user_browser.getControl('Change').click() |
307 | @@ -51,18 +67,43 @@ |
308 | Back at the bug page, the text changes once again. |
309 | |
310 | >>> print extract_text(find_tag_by_id( |
311 | - ... user_browser.contents, 'affectsmetooform')) |
312 | - This bug doesn't affect me... |
313 | - |
314 | -== One-click interaction == |
315 | - |
316 | -If the user's browser provides javascript, they don't need to go to |
317 | -another page to change their selection. Instead, an in-page form is |
318 | -submitted when they click the action link. |
319 | - |
320 | - >>> me_too_form = user_browser.getForm(id='affectsmetooform') |
321 | - >>> user_browser.getControl(name='field.affects').value |
322 | - 'YES' |
323 | - >>> me_too_form.submit() |
324 | - >>> user_browser.getControl(name='field.affects').value |
325 | - 'NO' |
326 | + ... user_browser.contents, 'affectsmetoo').find( |
327 | + ... None, 'static')) |
328 | + This bug doesn't affect me |
329 | + |
330 | + |
331 | +== Static and dynamic support == |
332 | + |
333 | +A bug page contains markup to support both static (no Javascript) and |
334 | +dynamic (Javascript enabled) scenarios. |
335 | + |
336 | + >>> def class_filter(css_class): |
337 | + ... def test(node): |
338 | + ... return css_class in node.get('class', '').split() |
339 | + ... return test |
340 | + |
341 | + >>> static_content = find_tag_by_id( |
342 | + ... user_browser.contents, 'affectsmetoo').find( |
343 | + ... class_filter('static')) |
344 | + |
345 | + >>> static_content is not None |
346 | + True |
347 | + |
348 | + >>> dynamic_content = find_tag_by_id( |
349 | + ... user_browser.contents, 'affectsmetoo').find( |
350 | + ... class_filter('dynamic')) |
351 | + |
352 | + >>> dynamic_content is not None |
353 | + True |
354 | + |
355 | +The dynamic content is hidden by the presence of the "unseen" CSS |
356 | +class. |
357 | + |
358 | + >>> print static_content.get('class') |
359 | + static |
360 | + |
361 | + >>> print dynamic_content.get('class') |
362 | + dynamic unseen |
363 | + |
364 | +It is the responsibilty of Javascript running in the page to unhide |
365 | +the dynamic content and hide the static content. |
366 | |
367 | === modified file 'lib/lp/bugs/templates/bugtasks-and-nominations-table.pt' |
368 | --- lib/lp/bugs/templates/bugtasks-and-nominations-table.pt 2009-07-17 17:59:07 +0000 |
369 | +++ lib/lp/bugs/templates/bugtasks-and-nominations-table.pt 2009-07-31 14:47:35 +0000 |
370 | @@ -38,11 +38,9 @@ |
371 | |
372 | </table> |
373 | |
374 | -<div |
375 | - class="actions" |
376 | - tal:define="current_bugtask view/current_bugtask" |
377 | - |
378 | - tal:condition="view/displayAlsoAffectsLinks"> |
379 | +<div class="actions" |
380 | + tal:define="current_bugtask view/current_bugtask" |
381 | + tal:condition="view/displayAlsoAffectsLinks"> |
382 | <tal:also-affects-links define="context_menu context/menu:context"> |
383 | <tal:addupstream |
384 | define="link context_menu/addupstream" |
385 | @@ -56,56 +54,50 @@ |
386 | define="link context_menu/nominate" |
387 | condition="link/enabled" |
388 | replace="structure link/render" /> |
389 | - <form |
390 | - id="affectsmetooform" |
391 | - name="affectsmetooform" |
392 | - method="post" |
393 | - enctype="multipart/form-data" |
394 | - accept-charset="UTF-8" |
395 | - tal:define="link context_menu/affectsmetoo" |
396 | - tal:condition="link/enabled" |
397 | - tal:attributes="action link/url" |
398 | - style="display: inline"> |
399 | - <input |
400 | - name="field.affects" |
401 | - type="hidden" |
402 | - tal:attributes="value view/affects_form_value" /> |
403 | - <input |
404 | - type="hidden" |
405 | - name="field.actions.change" |
406 | - value="" /> |
407 | - <tal:affected condition="view/current_user_affected_status"> |
408 | - <img width="14" height="14" src="/@@/flame-icon" alt="" /> |
409 | - This bug affects me too |
410 | - </tal:affected> |
411 | - <tal:affected condition="not: view/current_user_affected_status"> |
412 | - This bug doesn't affect me |
413 | - </tal:affected> |
414 | - (<tal:affectsmetoo |
415 | - define="link context_menu/affectsmetoo" |
416 | - condition="link/enabled" |
417 | - replace="structure link/render" />) |
418 | - <tal:nothing condition="nothing"> |
419 | - The following is a trick to allow users to mark |
420 | - themselves as affected by a bug with only one click. |
421 | - If Javascript is available, we submit the form and |
422 | - immediately go back to the same page, saving them the |
423 | - need to go to a new page. |
424 | - </tal:nothing> |
425 | - <script type="text/javascript"> |
426 | - function sendMeTooForm(e) { |
427 | - $('affectsmetooform').submit(); |
428 | - e.preventDefault(); |
429 | - } |
430 | - function connectMeTooLink() { |
431 | - var me_too_link = getFirstElementByTagAndClassName( |
432 | - 'a', 'menu-link-affectsmetoo'); |
433 | - connect(me_too_link, 'onclick', sendMeTooForm); |
434 | - } |
435 | - registerLaunchpadFunction(connectMeTooLink); |
436 | + <span id="affectsmetoo" style="display: inline" |
437 | + tal:condition="link/enabled" |
438 | + tal:define="link context_menu/affectsmetoo; |
439 | + affected view/current_user_affected_status"> |
440 | + |
441 | + <tal:comment condition="nothing"> |
442 | + This .static section is shown in browsers with javascript |
443 | + enabled, and before setup_me_too is run. |
444 | + </tal:comment> |
445 | + <span class="static"> |
446 | + <tal:affected condition="affected"> |
447 | + <img width="14" height="14" src="/@@/flame-icon" alt="" /> |
448 | + This bug affects me too |
449 | + </tal:affected> |
450 | + <tal:not-affected condition="not:affected"> |
451 | + This bug doesn't affect me |
452 | + </tal:not-affected> |
453 | + <a href="+affectsmetoo"> |
454 | + <img class="editicon" src="/@@/edit" alt="Edit" /> |
455 | + </a> |
456 | + </span> |
457 | + |
458 | + <tal:comment condition="nothing"> |
459 | + This .dynamic section is used by setup_me_too to display |
460 | + controls and information in the correct places. |
461 | + </tal:comment> |
462 | + <span class="dynamic unseen"> |
463 | + <img class="editicon" src="/@@/edit" alt="Edit" /> |
464 | + <a href="+affectsmetoo" class="js-action" |
465 | + ><span class="value">Does this bug affect you?</span></a> |
466 | + <img src="/@@/flame-icon" alt=""/> |
467 | + </span> |
468 | + |
469 | + <script type="text/javascript" tal:content="string: |
470 | + YUI().use('event', 'bugs.bugtask_index', function(Y) { |
471 | + Y.on('load', function(e) { |
472 | + Y.bugs.setup_me_too(${view/current_user_affected_js_status}); |
473 | + }, window); |
474 | + }); |
475 | + "> |
476 | </script> |
477 | - </form> |
478 | + |
479 | + </span> |
480 | </tal:also-affects-links> |
481 | - |
482 | </div> |
483 | + |
484 | </tal:root> |
485 | |
486 | === added file 'lib/lp/bugs/windmill/tests/test_bugs/test_bug_me_too.py' |
487 | --- lib/lp/bugs/windmill/tests/test_bugs/test_bug_me_too.py 1970-01-01 00:00:00 +0000 |
488 | +++ lib/lp/bugs/windmill/tests/test_bugs/test_bug_me_too.py 2009-07-30 15:33:35 +0000 |
489 | @@ -0,0 +1,65 @@ |
490 | +# Copyright 2009 Canonical Ltd. This software is licensed under the |
491 | +# GNU Affero General Public License version 3 (see the file LICENSE). |
492 | + |
493 | +from canonical.launchpad.windmill.testing import lpuser |
494 | + |
495 | +from windmill.authoring import WindmillTestClient |
496 | + |
497 | + |
498 | +def test_me_too(): |
499 | + """Test the "this bug affects me too" options on bug pages. |
500 | + |
501 | + This test ensures that, with Javascript enabled, the "me too" |
502 | + status can be edited in-page. |
503 | + """ |
504 | + client = WindmillTestClient('Bug "me too" test') |
505 | + lpuser.SAMPLE_PERSON.ensure_login(client) |
506 | + |
507 | + # Open bug 11 and wait for it to finish loading. |
508 | + client.open(url=u'http://bugs.launchpad.dev:8085/jokosher/+bug/11/+index') |
509 | + client.waits.forPageLoad(timeout=u'20000') |
510 | + |
511 | + # Wait for setup_me_too to sort out the "me too" elements. |
512 | + client.waits.forElement( |
513 | + xpath=(u"//span[@id='affectsmetoo' and " |
514 | + u"@class='yui-metoocs-content']")) |
515 | + |
516 | + # Currently this bug does not affect the logged-in user. |
517 | + client.asserts.assertText( |
518 | + xpath=u"//span[@id='affectsmetoo']/span[@class='value']", |
519 | + validator=u"This bug doesn't affect me") |
520 | + |
521 | + # There is an edit icon next to the text which can be clicked to |
522 | + # edit the "me too" status. However, we won't click it with |
523 | + # Windmill because the widget actually responds to mouse-down, and |
524 | + # Windmill seems to do something funny instead. |
525 | + client.mouseDown( |
526 | + xpath=u"//span[@id='affectsmetoo']//img[@class='editicon']") |
527 | + client.mouseUp( |
528 | + xpath=u"//span[@id='affectsmetoo']//img[@class='editicon']") |
529 | + |
530 | + # Wait for the modal dialog to appear. |
531 | + client.waits.forElement(id=u'yui-pretty-overlay-modal') |
532 | + |
533 | + # There's a close button if we change our mind. |
534 | + client.click( |
535 | + xpath=(u"//div[@id='yui-pretty-overlay-modal']//" |
536 | + u"a[@class='close-button']")) |
537 | + |
538 | + # Wait for the modal dialog to disappear. Unfortunately the test |
539 | + # below doesn't work, nor does testing clientWidth, or anything I |
540 | + # could think of, so it's commented out for now because chasing |
541 | + # this is not a good use of time. |
542 | + |
543 | + # client.asserts.assertElemJS( |
544 | + # id=u'yui-pretty-overlay-modal', |
545 | + # js=(u'getComputedStyle(element, ' |
546 | + # u'"visibility").visibility == "hidden"')) |
547 | + |
548 | + # However, we want to mark this bug as affecting the logged-in |
549 | + # user. We can also click on the content box of the "me too" |
550 | + # widget; we are not forced to use the edit icon. |
551 | + client.click(xpath=u"//span[@id='affectsmetoo']") |
552 | + client.waits.forElement(id=u'yui-pretty-overlay-modal') |
553 | + |
554 | + # Do more here... |
This branch AJAXifies the "me-too" (aka "this bug affects me") part of
the bugs page.
File by file:
lib/canonical/ launchpad/ javascript/ bugs/bugtask- index.js
Added a function setup_me_too that uses a new widget, urce, to do its business.
MeTooChoiceSo
MeTooChoiceSource extends from ChoiceSource to add a few frills,
like showing and hiding a flame icon (displayed if the user is
affected by the bug), making the widget display inline (by default
it displays as a block), and of course, to do an API call to set the
affected status.
lib/lp/ bugs/browser/ bugtask. py bugs/browser/ tests/test_ bugtask. py
lib/lp/
New property current_ user_affected_ js_status returns one of "null", user_affected_ status.
"true" or "false", i.e. valid Javascript indicating the affected
status of the user. The tests also cover the existing
current_
lib/lp/ bugs/stories/ bugs/xx- bug-affects- me-too. txt
Quite extensive reworking of this pagetest.
lib/lp/ bugs/templates/ bugtasks- and-nominations -table. pt
Rip out the old static markup and Javascript code, replace it with
markup to support the static (no Javascript) and dynamic (Javascript
enabled) scenarios, and call out to setup_me_too.
There is a span with a "static" class that contains all the markup
when Javascript is disabled. There is a span with a "dynamic" class
that is hidden first of all. The visibility of these spans is
swapped by MeTooChoiceSource at run time. This means that the
framework markup can stay in the template.
lib/lp/ bugs/windmill/ tests/test_ bugs/test_ bug_me_ too.py
The beginnings of a Windmill test. This took so long and extracted
so much virtual blood from me that I'm going to complete it in a
later branch. Testing the overlays and stuff is extremely painful
and fiddly.
TODO:
There are no unit tests for MeTooChoiceSource. I will do these in a
later branch, and will land them at the same time as this branch.
Finish the Windmill test.