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
=== modified file 'lib/canonical/launchpad/javascript/bugs/bugtask-index.js'
--- lib/canonical/launchpad/javascript/bugs/bugtask-index.js 2009-07-29 11:13:40 +0000
+++ lib/canonical/launchpad/javascript/bugs/bugtask-index.js 2009-07-31 13:10:26 +0000
@@ -1308,6 +1308,116 @@
1308 }1308 }
1309};1309};
13101310
1311/**
1312 * Set up the "me too" selection.
1313 *
1314 * Called once, on load, to initialize the page. Call this function if
1315 * the "me too" information is displayed on a bug page and the user is
1316 * logged in.
1317 *
1318 * @method setup_me_too
1319 */
1320bugs.setup_me_too = function(user_is_affected) {
1321 var me_too_content = Y.get('#affectsmetoo');
1322 var me_too_edit = new MeTooChoiceSource({
1323 contentBox: me_too_content,
1324 value: user_is_affected,
1325 title: 'This bug:',
1326 items: [
1327 {name: 'Affects me too', value: true,
1328 source_name: 'This bug affects me too',
1329 disabled: false},
1330 {name: 'Does not affect me', value: false,
1331 source_name: "This bug doesn't affect me",
1332 disabled: false}
1333 ],
1334 elementToFlash: me_too_content,
1335 backgroundColor: '#FFFFFF'
1336 });
1337 me_too_edit.render();
1338};
1339
1340/**
1341 * This class is a derivative of ChoiceSource that handles the
1342 * specifics of editing "me too" option.
1343 *
1344 * @class MeTooChoiceSource
1345 * @extends ChoiceSource
1346 * @constructor
1347 */
1348function MeTooChoiceSource() {
1349 MeTooChoiceSource.superclass.constructor.apply(this, arguments);
1350}
1351
1352Y.mix(MeTooChoiceSource, {
1353 NAME: 'metoocs',
1354 NS: 'metoocs'
1355});
1356
1357Y.extend(MeTooChoiceSource, Y.ChoiceSource, {
1358 initializer: function() {
1359 var widget = this;
1360 this.error_handler = new LP.client.ErrorHandler();
1361 this.error_handler.clearProgressUI = function() {
1362 widget._uiClearWaiting();
1363 };
1364 this.error_handler.showError = function(error_msg) {
1365 widget.showError(error_msg);
1366 };
1367 },
1368
1369 showError: function(err) {
1370 display_error(null, err);
1371 },
1372
1373 syncUI: function() {
1374 MeTooChoiceSource.superclass.syncUI.apply(this, arguments);
1375 // Show the flame icon if the user is affected by this bug.
1376 var content_box = this.get('contentBox');
1377 var flame = content_box.query('.dynamic img[src=/@@/flame-icon]');
1378 if (this.get('value')) {
1379 flame.removeClass('unseen');
1380 } else {
1381 flame.addClass('unseen');
1382 }
1383 },
1384
1385 render: function() {
1386 MeTooChoiceSource.superclass.render.apply(this, arguments);
1387 // Force the ChoiceSource to be rendered inline.
1388 this.get('boundingBox').setStyle('display', 'inline');
1389 // Hide the static content and show the dynamic content.
1390 this.get('contentBox').query('.static').addClass('unseen');
1391 this.get('contentBox').query('.dynamic').removeClass('unseen');
1392 },
1393
1394 _saveData: function() {
1395 // Set the widget to the 'waiting' state.
1396 this._uiSetWaiting();
1397
1398 var value = this.getInput();
1399 var client = new LP.client.Launchpad();
1400 var widget = this;
1401
1402 var config = {
1403 on: {
1404 success: function(entry) {
1405 widget._uiClearWaiting();
1406 MeTooChoiceSource.superclass._saveData.call(
1407 widget, value);
1408 },
1409 failure: this.error_handler.getFailureHandler()
1410 },
1411 parameters: {
1412 affected: value
1413 }
1414 };
1415
1416 client.named_post(
1417 LP.client.cache.bug.self_link, 'markUserAffected', config);
1418 }
1419});
1420
1311/*1421/*
1312 * Check if the current user can unsubscribe the person1422 * Check if the current user can unsubscribe the person
1313 * being subscribed.1423 * being subscribed.
@@ -1402,6 +1512,6 @@
1402 }1512 }
1403}1513}
1404}, '0.1', {requires: ['base', 'oop', 'node', 'event', 'io-base', 'substitute',1514}, '0.1', {requires: ['base', 'oop', 'node', 'event', 'io-base', 'substitute',
1405 'widget-position-ext', 'lazr.formoverlay', 'lazr.anim', 1515 'widget-position-ext', 'lazr.formoverlay', 'lazr.anim',
1406 'lazr.base', 'lazr.overlay', 'lazr.choiceedit',1516 'lazr.base', 'lazr.overlay', 'lazr.choiceedit',
1407 'lp.picker', 'lp.client.plugins', 'lp.subscriber']});1517 'lp.picker', 'lp.client.plugins', 'lp.subscriber']});
14081518
=== modified file 'lib/lp/bugs/browser/bugtask.py'
--- lib/lp/bugs/browser/bugtask.py 2009-07-17 18:46:25 +0000
+++ lib/lp/bugs/browser/bugtask.py 2009-07-23 10:34:08 +0000
@@ -2915,13 +2915,15 @@
2915 return self.context.isUserAffected(self.user)2915 return self.context.isUserAffected(self.user)
29162916
2917 @property2917 @property
2918 def affects_form_value(self):2918 def current_user_affected_js_status(self):
2919 """The value to use in the inline me too form."""2919 """A javascript literal indicating if the user is affected."""
2920 affected = self.context.isUserAffected(self.user)2920 affected = self.current_user_affected_status
2921 if affected is None or affected == False:2921 if affected is None:
2922 return 'YES'2922 return 'null'
2923 elif affected:
2924 return 'true'
2923 else:2925 else:
2924 return 'NO'2926 return 'false'
29252927
29262928
2927class BugTaskTableRowView(LaunchpadView):2929class BugTaskTableRowView(LaunchpadView):
29282930
=== modified file 'lib/lp/bugs/browser/tests/test_bugtask.py'
--- lib/lp/bugs/browser/tests/test_bugtask.py 2009-06-25 00:40:31 +0000
+++ lib/lp/bugs/browser/tests/test_bugtask.py 2009-07-23 10:34:08 +0000
@@ -1,23 +1,63 @@
1# Copyright 2009 Canonical Ltd. This software is licensed under the1# Copyright 2009 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4__metaclass__ = type
5
6
4import unittest7import unittest
58
6from zope.testing.doctest import DocTestSuite9from zope.testing.doctest import DocTestSuite
710
8from lp.bugs.browser import bugtask11from canonical.launchpad.ftests import login
9from canonical.launchpad.testing.systemdocs import (12from canonical.launchpad.testing.systemdocs import (
10 LayeredDocFileSuite, setUp, tearDown)13 LayeredDocFileSuite, setUp, tearDown)
11from canonical.testing import LaunchpadFunctionalLayer14from canonical.testing import LaunchpadFunctionalLayer
1215
16from lp.bugs.browser import bugtask
17from lp.bugs.browser.bugtask import BugTasksAndNominationsView
18from lp.testing import TestCaseWithFactory
19
20
21class TestBugTasksAndNominationsView(TestCaseWithFactory):
22
23 layer = LaunchpadFunctionalLayer
24
25 def setUp(self):
26 super(TestBugTasksAndNominationsView, self).setUp()
27 login('foo.bar@canonical.com')
28 self.bug = self.factory.makeBug()
29 self.view = BugTasksAndNominationsView(self.bug, None)
30
31 def test_current_user_affected_status(self):
32 self.failUnlessEqual(
33 None, self.view.current_user_affected_status)
34 self.view.context.markUserAffected(self.view.user, True)
35 self.failUnlessEqual(
36 True, self.view.current_user_affected_status)
37 self.view.context.markUserAffected(self.view.user, False)
38 self.failUnlessEqual(
39 False, self.view.current_user_affected_status)
40
41 def test_current_user_affected_js_status(self):
42 self.failUnlessEqual(
43 'null', self.view.current_user_affected_js_status)
44 self.view.context.markUserAffected(self.view.user, True)
45 self.failUnlessEqual(
46 'true', self.view.current_user_affected_js_status)
47 self.view.context.markUserAffected(self.view.user, False)
48 self.failUnlessEqual(
49 'false', self.view.current_user_affected_js_status)
50
1351
14def test_suite():52def test_suite():
15 suite = unittest.TestSuite()53 suite = unittest.TestSuite()
54 suite.addTest(unittest.makeSuite(TestBugTasksAndNominationsView))
16 suite.addTest(DocTestSuite(bugtask))55 suite.addTest(DocTestSuite(bugtask))
17 suite.addTest(LayeredDocFileSuite(56 suite.addTest(LayeredDocFileSuite(
18 'bugtask-target-link-titles.txt', setUp=setUp, tearDown=tearDown,57 'bugtask-target-link-titles.txt', setUp=setUp, tearDown=tearDown,
19 layer=LaunchpadFunctionalLayer))58 layer=LaunchpadFunctionalLayer))
20 return suite59 return suite
2160
61
22if __name__ == '__main__':62if __name__ == '__main__':
23 unittest.TextTestRunner().run(test_suite())63 unittest.TextTestRunner().run(test_suite())
2464
=== modified file 'lib/lp/bugs/stories/bugs/xx-bug-affects-me-too.txt'
--- lib/lp/bugs/stories/bugs/xx-bug-affects-me-too.txt 2009-06-12 16:36:02 +0000
+++ lib/lp/bugs/stories/bugs/xx-bug-affects-me-too.txt 2009-07-31 13:49:53 +0000
@@ -1,7 +1,7 @@
1= Marking a bug as affecting the user =1= Marking a bug as affecting the user =
22
3Users can mark bugs as affecting them. Let's create a sample bug to try3Users can mark bugs as affecting them. Let's create a sample bug to
4this on.4try this out.
55
6 >>> login(ANONYMOUS)6 >>> login(ANONYMOUS)
7 >>> from canonical.launchpad.webapp import canonical_url7 >>> from canonical.launchpad.webapp import canonical_url
@@ -9,15 +9,27 @@
9 >>> test_bug_url = canonical_url(test_bug)9 >>> test_bug_url = canonical_url(test_bug)
10 >>> logout()10 >>> logout()
1111
12The user goes to the bug's index page, and clicks the edit action link12The user goes to the bug's index page, and finds a statement that the
13near 'This bug affects me too'.13bug is not marked as affecting them.
1414
15 >>> user_browser.open(test_bug_url)15 >>> user_browser.open(test_bug_url)
16 >>> print extract_text(find_tag_by_id(16 >>> print extract_text(find_tag_by_id(
17 ... user_browser.contents, 'affectsmetooform'))17 ... user_browser.contents, 'affectsmetoo').find(
18 This bug doesn't affect me...18 ... None, 'static'))
1919 This bug doesn't affect me
20 >>> user_browser.getLink('change').click()20
21Next to the statement is a link containing an edit icon.
22
23 >>> edit_link = find_tag_by_id(
24 ... user_browser.contents, 'affectsmetoo').a
25 >>> print edit_link['href']
26 +affectsmetoo
27 >>> print edit_link.img['src']
28 /@@/edit
29
30The user is affected by this bug, so clicks the link.
31
32 >>> user_browser.getLink(url='+affectsmetoo').click()
21 >>> print user_browser.url33 >>> print user_browser.url
22 http://bugs.launchpad.dev/.../+bug/.../+affectsmetoo34 http://bugs.launchpad.dev/.../+bug/.../+affectsmetoo
23 >>> user_browser.getControl(name='field.affects').value35 >>> user_browser.getControl(name='field.affects').value
@@ -28,22 +40,26 @@
28 >>> user_browser.getControl('Change').click()40 >>> user_browser.getControl('Change').click()
2941
30The bug page loads again, and now the text is changed, to make it42The bug page loads again, and now the text is changed, to make it
31clear to the user that they can change the selection.43clear to the user that they have marked this bug as affecting them.
3244
33 >>> print extract_text(find_tags_by_class(45 >>> print extract_text(find_tag_by_id(
34 ... user_browser.contents, 'menu-link-affectsmetoo')[0])46 ... user_browser.contents, 'affectsmetoo').find(
35 change47 ... None, 'static'))
48 This bug affects me too
3649
37Next to it, we also see the 'hot bug' icon, to indicate that the user50Next to it, we also see the 'hot bug' icon, to indicate that the user
38has marked the bug as affecting them.51has marked the bug as affecting them.
3952
40 >>> print find_tag_by_id(53 >>> print find_tag_by_id(
41 ... user_browser.contents, 'affectsmetooform').img['src']54 ... user_browser.contents, 'affectsmetoo').img['src']
42 /@@/flame-icon55 /@@/flame-icon
4356
44 >>> user_browser.getLink('change').click()57On second thoughts, the user realises that this bug does not affect
4558them, so they click on the edit link once more.
46The user is changing his selection to 'No' and submits the form.59
60 >>> user_browser.getLink(url='+affectsmetoo').click()
61
62The user changes his selection to 'No' and submits the form.
4763
48 >>> user_browser.getControl(name='field.affects').value = ['NO']64 >>> user_browser.getControl(name='field.affects').value = ['NO']
49 >>> user_browser.getControl('Change').click()65 >>> user_browser.getControl('Change').click()
@@ -51,18 +67,43 @@
51Back at the bug page, the text changes once again.67Back at the bug page, the text changes once again.
5268
53 >>> print extract_text(find_tag_by_id(69 >>> print extract_text(find_tag_by_id(
54 ... user_browser.contents, 'affectsmetooform'))70 ... user_browser.contents, 'affectsmetoo').find(
55 This bug doesn't affect me...71 ... None, 'static'))
5672 This bug doesn't affect me
57== One-click interaction ==73
5874
59If the user's browser provides javascript, they don't need to go to75== Static and dynamic support ==
60another page to change their selection. Instead, an in-page form is76
61submitted when they click the action link.77A bug page contains markup to support both static (no Javascript) and
6278dynamic (Javascript enabled) scenarios.
63 >>> me_too_form = user_browser.getForm(id='affectsmetooform')79
64 >>> user_browser.getControl(name='field.affects').value80 >>> def class_filter(css_class):
65 'YES'81 ... def test(node):
66 >>> me_too_form.submit()82 ... return css_class in node.get('class', '').split()
67 >>> user_browser.getControl(name='field.affects').value83 ... return test
68 'NO'84
85 >>> static_content = find_tag_by_id(
86 ... user_browser.contents, 'affectsmetoo').find(
87 ... class_filter('static'))
88
89 >>> static_content is not None
90 True
91
92 >>> dynamic_content = find_tag_by_id(
93 ... user_browser.contents, 'affectsmetoo').find(
94 ... class_filter('dynamic'))
95
96 >>> dynamic_content is not None
97 True
98
99The dynamic content is hidden by the presence of the "unseen" CSS
100class.
101
102 >>> print static_content.get('class')
103 static
104
105 >>> print dynamic_content.get('class')
106 dynamic unseen
107
108It is the responsibilty of Javascript running in the page to unhide
109the dynamic content and hide the static content.
69110
=== modified file 'lib/lp/bugs/templates/bugtasks-and-nominations-table.pt'
--- lib/lp/bugs/templates/bugtasks-and-nominations-table.pt 2009-07-17 17:59:07 +0000
+++ lib/lp/bugs/templates/bugtasks-and-nominations-table.pt 2009-07-31 14:47:35 +0000
@@ -38,11 +38,9 @@
3838
39</table>39</table>
4040
41<div41<div class="actions"
42 class="actions"42 tal:define="current_bugtask view/current_bugtask"
43 tal:define="current_bugtask view/current_bugtask"43 tal:condition="view/displayAlsoAffectsLinks">
44
45 tal:condition="view/displayAlsoAffectsLinks">
46 <tal:also-affects-links define="context_menu context/menu:context">44 <tal:also-affects-links define="context_menu context/menu:context">
47 <tal:addupstream45 <tal:addupstream
48 define="link context_menu/addupstream"46 define="link context_menu/addupstream"
@@ -56,56 +54,50 @@
56 define="link context_menu/nominate"54 define="link context_menu/nominate"
57 condition="link/enabled"55 condition="link/enabled"
58 replace="structure link/render" />56 replace="structure link/render" />
59 <form57 <span id="affectsmetoo" style="display: inline"
60 id="affectsmetooform"58 tal:condition="link/enabled"
61 name="affectsmetooform"59 tal:define="link context_menu/affectsmetoo;
62 method="post"60 affected view/current_user_affected_status">
63 enctype="multipart/form-data"61
64 accept-charset="UTF-8"62 <tal:comment condition="nothing">
65 tal:define="link context_menu/affectsmetoo"63 This .static section is shown in browsers with javascript
66 tal:condition="link/enabled"64 enabled, and before setup_me_too is run.
67 tal:attributes="action link/url"65 </tal:comment>
68 style="display: inline">66 <span class="static">
69 <input67 <tal:affected condition="affected">
70 name="field.affects"68 <img width="14" height="14" src="/@@/flame-icon" alt="" />
71 type="hidden"69 This bug affects me too
72 tal:attributes="value view/affects_form_value" />70 </tal:affected>
73 <input71 <tal:not-affected condition="not:affected">
74 type="hidden"72 This bug doesn't affect me
75 name="field.actions.change"73 </tal:not-affected>
76 value="" />74 <a href="+affectsmetoo">
77 <tal:affected condition="view/current_user_affected_status">75 <img class="editicon" src="/@@/edit" alt="Edit" />
78 <img width="14" height="14" src="/@@/flame-icon" alt="" />76 </a>
79 This bug affects me too77 </span>
80 </tal:affected>78
81 <tal:affected condition="not: view/current_user_affected_status">79 <tal:comment condition="nothing">
82 This bug doesn't affect me80 This .dynamic section is used by setup_me_too to display
83 </tal:affected>81 controls and information in the correct places.
84 (<tal:affectsmetoo82 </tal:comment>
85 define="link context_menu/affectsmetoo"83 <span class="dynamic unseen">
86 condition="link/enabled"84 <img class="editicon" src="/@@/edit" alt="Edit" />
87 replace="structure link/render" />)85 <a href="+affectsmetoo" class="js-action"
88 <tal:nothing condition="nothing">86 ><span class="value">Does this bug affect you?</span></a>
89 The following is a trick to allow users to mark87 <img src="/@@/flame-icon" alt=""/>
90 themselves as affected by a bug with only one click.88 </span>
91 If Javascript is available, we submit the form and89
92 immediately go back to the same page, saving them the90 <script type="text/javascript" tal:content="string:
93 need to go to a new page.91 YUI().use('event', 'bugs.bugtask_index', function(Y) {
94 </tal:nothing>92 Y.on('load', function(e) {
95 <script type="text/javascript">93 Y.bugs.setup_me_too(${view/current_user_affected_js_status});
96 function sendMeTooForm(e) {94 }, window);
97 $('affectsmetooform').submit();95 });
98 e.preventDefault();96 ">
99 }
100 function connectMeTooLink() {
101 var me_too_link = getFirstElementByTagAndClassName(
102 'a', 'menu-link-affectsmetoo');
103 connect(me_too_link, 'onclick', sendMeTooForm);
104 }
105 registerLaunchpadFunction(connectMeTooLink);
106 </script>97 </script>
107 </form>98
99 </span>
108 </tal:also-affects-links>100 </tal:also-affects-links>
109
110</div>101</div>
102
111</tal:root>103</tal:root>
112104
=== added file 'lib/lp/bugs/windmill/tests/test_bugs/test_bug_me_too.py'
--- lib/lp/bugs/windmill/tests/test_bugs/test_bug_me_too.py 1970-01-01 00:00:00 +0000
+++ lib/lp/bugs/windmill/tests/test_bugs/test_bug_me_too.py 2009-07-30 15:33:35 +0000
@@ -0,0 +1,65 @@
1# Copyright 2009 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4from canonical.launchpad.windmill.testing import lpuser
5
6from windmill.authoring import WindmillTestClient
7
8
9def test_me_too():
10 """Test the "this bug affects me too" options on bug pages.
11
12 This test ensures that, with Javascript enabled, the "me too"
13 status can be edited in-page.
14 """
15 client = WindmillTestClient('Bug "me too" test')
16 lpuser.SAMPLE_PERSON.ensure_login(client)
17
18 # Open bug 11 and wait for it to finish loading.
19 client.open(url=u'http://bugs.launchpad.dev:8085/jokosher/+bug/11/+index')
20 client.waits.forPageLoad(timeout=u'20000')
21
22 # Wait for setup_me_too to sort out the "me too" elements.
23 client.waits.forElement(
24 xpath=(u"//span[@id='affectsmetoo' and "
25 u"@class='yui-metoocs-content']"))
26
27 # Currently this bug does not affect the logged-in user.
28 client.asserts.assertText(
29 xpath=u"//span[@id='affectsmetoo']/span[@class='value']",
30 validator=u"This bug doesn't affect me")
31
32 # There is an edit icon next to the text which can be clicked to
33 # edit the "me too" status. However, we won't click it with
34 # Windmill because the widget actually responds to mouse-down, and
35 # Windmill seems to do something funny instead.
36 client.mouseDown(
37 xpath=u"//span[@id='affectsmetoo']//img[@class='editicon']")
38 client.mouseUp(
39 xpath=u"//span[@id='affectsmetoo']//img[@class='editicon']")
40
41 # Wait for the modal dialog to appear.
42 client.waits.forElement(id=u'yui-pretty-overlay-modal')
43
44 # There's a close button if we change our mind.
45 client.click(
46 xpath=(u"//div[@id='yui-pretty-overlay-modal']//"
47 u"a[@class='close-button']"))
48
49 # Wait for the modal dialog to disappear. Unfortunately the test
50 # below doesn't work, nor does testing clientWidth, or anything I
51 # could think of, so it's commented out for now because chasing
52 # this is not a good use of time.
53
54 # client.asserts.assertElemJS(
55 # id=u'yui-pretty-overlay-modal',
56 # js=(u'getComputedStyle(element, '
57 # u'"visibility").visibility == "hidden"'))
58
59 # However, we want to mark this bug as affecting the logged-in
60 # user. We can also click on the content box of the "me too"
61 # widget; we are not forced to use the edit icon.
62 client.click(xpath=u"//span[@id='affectsmetoo']")
63 client.waits.forElement(id=u'yui-pretty-overlay-modal')
64
65 # Do more here...