Merge lp:~allenap/launchpad/me-too-ajax-bug-361097 into lp:launchpad

Proposed by Gavin Panella
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
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
To post a comment you must log in.
Revision history for this message
Gavin Panella (allenap) wrote :

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,
  MeTooChoiceSource, to do its business.

  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
lib/lp/bugs/browser/tests/test_bugtask.py

  New property current_user_affected_js_status returns one of "null",
  "true" or "false", i.e. valid Javascript indicating the affected
  status of the user. The tests also cover the existing
  current_user_affected_status.

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.

Revision history for this message
Michael Nelson (michael.nelson) wrote :
Download full text (4.7 KiB)

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://devpad.canonical.com/~beuno/ui_movies/.
<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://devpad.canonical.com/~beuno/ui_movies/me_too.swf?
<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/ChoiceList text. Abel's already reviewed it and it has landed, so if you pull your lazr-js dep and my branch again (bzr+ssh://bazaar.launchpad.net/~allenap/launchpad/me-too-ajax-bug-361097) you can see the final thing. Not urgent though, whenever you have mo.
<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...

Read more...

review: Approve (ui)
Revision history for this message
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.

review: Approve (ui)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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...