Merge lp:~danilo/launchpad/bug-770241 into lp:launchpad

Proposed by Данило Шеган
Status: Merged
Approved by: Данило Шеган
Approved revision: no longer in the source branch.
Merged at revision: 12942
Proposed branch: lp:~danilo/launchpad/bug-770241
Merge into: lp:launchpad
Diff against target: 291 lines (+212/-28)
4 files modified
lib/lp/bugs/javascript/bugtask_index_portlets.js (+4/-28)
lib/lp/bugs/javascript/subscribers_list.js (+43/-0)
lib/lp/bugs/javascript/tests/test_subscribers_list.html (+44/-0)
lib/lp/bugs/javascript/tests/test_subscribers_list.js (+121/-0)
To merge this branch: bzr merge lp:~danilo/launchpad/bug-770241
Reviewer Review Type Date Requested Status
Graham Binns (community) code Approve
Review via email: mp+59338@code.launchpad.net

Commit message

[r=gmb][bug=770241] When the last subscriber is unsubscribed, put the "none" in appropriate div as expected by subscribe code.

Description of the change

= Bug 770241 =

Using JS to empty the list of subscribers on a bug page (iow, unsubscribe everybody) and then try to "subscribe someone else" fails because "None" div is added to the subscribers-links instead of the parent node for it (as is done on reload of the page with no subscribers).

== Proposed fix ==

 * Move set_none_for_empty_subscribers to a separate JS module for managing the subscribers list and rename it to reset()
 * Instead of adding directly to #subscribers-links, add the "None" div to the parent node
 * Provide tests for the behaviour of reset() in all these cases

== Implementation details ==

 * test_subscribers_list.html is boiler-plate for test set-up
 * reset() is mostly old code
 * the plan is to move more of the subscribers list management methods
   to the new module, to split it up from the other code.

== Tests ==

lib/lp/bugs/javascript/tests/test_subscribers_list.html

== Demo and Q/A ==

https://bugs.launchpad.dev/debian/+source/mozilla-firefox/+bug/3
(unsubscribe everyone, and then try to "subscribe someone else")

= Launchpad lint =

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/lp/bugs/javascript/tests/test_subscribers_list.html
  lib/lp/bugs/javascript/bugtask_index_portlets.js
  lib/lp/bugs/javascript/subscribers_list.js
  lib/lp/bugs/javascript/tests/test_subscribers_list.js

To post a comment you must log in.
Revision history for this message
Graham Binns (gmb) :
review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/bugs/javascript/bugtask_index_portlets.js'
2--- lib/lp/bugs/javascript/bugtask_index_portlets.js 2011-04-28 07:55:44 +0000
3+++ lib/lp/bugs/javascript/bugtask_index_portlets.js 2011-04-28 13:50:50 +0000
4@@ -612,7 +612,7 @@
5 var anim = Y.lazr.anim.green_flash({ node: icon_parent_div });
6 anim.on('end', function(e) {
7 remove_user_name_link(icon_parent_div);
8- set_none_for_empty_subscribers();
9+ Y.lp.bugs.subscribers_list.reset();
10 var person_link = Y.one('.' + person.get('css_name'));
11 if (Y.Lang.isNull(person_link) &&
12 subscription.is_current_user_subscribing()) {
13@@ -836,7 +836,7 @@
14 var anim = Y.lazr.anim.green_flash({ node: flash_node });
15 anim.on('end', function(e) {
16 remove_user_name_link(flash_node);
17- set_none_for_empty_subscribers();
18+ Y.lp.bugs.subscribers_list.reset();
19 });
20 anim.run();
21 },
22@@ -911,7 +911,7 @@
23 });
24 subscriber_anim.on('end', function(e) {
25 remove_user_name_link(subscriber_node);
26- set_none_for_empty_subscribers();
27+ Y.lp.bugs.subscribers_list.reset();
28 });
29 subscriber_anim.run();
30 }
31@@ -1190,31 +1190,6 @@
32 }
33
34 /*
35- * Add the "None" div to the subscribers list if
36- * there aren't any subscribers left.
37- *
38- * @method set_none_for_empty_subscribers
39- */
40-function set_none_for_empty_subscribers() {
41- var subscriber_list = Y.one('#subscribers-links');
42- // Assume if subscriber_list has no child divs
43- // then the list of subscribers is empty.
44- if (!Y.Lang.isValue(subscriber_list.one('div')) &&
45- !Y.Lang.isValue(Y.one('#none-subscribers'))) {
46- var none_div = Y.Node.create('<div id="none-subscribers">None</div>');
47- subscriber_list.appendChild(none_div);
48- }
49-
50- // Clear the empty duplicate subscribers list if it exists.
51- var dup_list = Y.one('#subscribers-from-duplicates');
52- if (Y.Lang.isValue(dup_list) &&
53- !Y.Lang.isValue(dup_list.one('div'))) {
54- var parent = dup_list.get('parentNode');
55- parent.removeChild(dup_list);
56- }
57-}
58-
59-/*
60 * Set the class on subscription link's parentNode.
61 *
62 * This is used to reset the class used by the
63@@ -1522,4 +1497,5 @@
64 "lazr.overlay", "lazr.choiceedit", "lp.app.picker",
65 "lp.client",
66 "lp.client.plugins", "lp.bugs.subscriber",
67+ "lp.bugs.subscribers_list",
68 "lp.bugs.bug_notification_level", "lp.app.errors"]});
69
70=== added file 'lib/lp/bugs/javascript/subscribers_list.js'
71--- lib/lp/bugs/javascript/subscribers_list.js 1970-01-01 00:00:00 +0000
72+++ lib/lp/bugs/javascript/subscribers_list.js 2011-04-28 13:50:50 +0000
73@@ -0,0 +1,43 @@
74+/* Copyright 2011 Canonical Ltd. This software is licensed under the
75+ * GNU Affero General Public License version 3 (see the file LICENSE).
76+ *
77+ * Functions for managing the subscribers list.
78+ *
79+ * @module bugs
80+ * @submodule subscribers_list
81+ */
82+
83+YUI.add('lp.bugs.subscribers_list', function(Y) {
84+
85+var namespace = Y.namespace('lp.bugs.subscribers_list');
86+
87+/**
88+ * Reset the subscribers list if needed.
89+ *
90+ * Adds the "None" div to the subscribers list if
91+ * there aren't any subscribers left, and clears up
92+ * the duplicate subscribers list if empty.
93+ *
94+ * @method reset
95+ */
96+function reset() {
97+ var subscriber_list = Y.one('#subscribers-links');
98+ // Assume if subscriber_list has no child divs
99+ // then the list of subscribers is empty.
100+ if (!Y.Lang.isValue(subscriber_list.one('div')) &&
101+ !Y.Lang.isValue(Y.one('#none-subscribers'))) {
102+ var none_div = Y.Node.create('<div id="none-subscribers">None</div>');
103+ var subscribers = subscriber_list.get('parentNode');
104+ subscribers.appendChild(none_div);
105+ }
106+
107+ // Clear the empty duplicate subscribers list if it exists.
108+ var dup_list = Y.one('#subscribers-from-duplicates');
109+ if (Y.Lang.isValue(dup_list) &&
110+ !Y.Lang.isValue(dup_list.one('div'))) {
111+ dup_list.remove();
112+ }
113+}
114+namespace.reset = reset;
115+
116+}, "0.1", {"requires": ["node"]});
117
118=== added file 'lib/lp/bugs/javascript/tests/test_subscribers_list.html'
119--- lib/lp/bugs/javascript/tests/test_subscribers_list.html 1970-01-01 00:00:00 +0000
120+++ lib/lp/bugs/javascript/tests/test_subscribers_list.html 2011-04-28 13:50:50 +0000
121@@ -0,0 +1,44 @@
122+<html>
123+ <head>
124+ <title>Bug subscriptions: subscribers list</title>
125+
126+ <!-- YUI 3.0 Setup -->
127+ <script type="text/javascript"
128+ src="../../../../canonical/launchpad/icing/yui/yui/yui.js"></script>
129+ <script type="text/javascript"
130+ src="../../../../canonical/launchpad/icing/lazr/build/lazr.js"></script>
131+ <link rel="stylesheet"
132+ href="../../../../canonical/launchpad/icing/yui/cssreset/reset.css"/>
133+ <link rel="stylesheet"
134+ href="../../../../canonical/launchpad/icing/yui/cssfonts/fonts.css"/>
135+ <link rel="stylesheet"
136+ href="../../../../canonical/launchpad/icing/yui/cssbase/base.css"/>
137+ <link rel="stylesheet"
138+ href="../../../../canonical/launchpad/javascript/test.css" />
139+
140+ <script type="text/javascript"
141+ src="../../../app/javascript/client.js"></script>
142+ <script type="text/javascript"
143+ src="../../../app/javascript/errors.js"></script>
144+
145+ <!-- The module under test -->
146+ <script type="text/javascript"
147+ src="../subscribers_list.js"></script>
148+
149+ <!-- The test suite -->
150+ <script type="text/javascript"
151+ src="test_subscribers_list.js"></script>
152+
153+ <!-- Pretty up the sample html -->
154+ <style type="text/css">
155+ div#sample {margin:15px; width:200px; border:1px solid #999; padding:10px;}
156+ </style>
157+ </head>
158+ <body class="yui3-skin-sam">
159+ <!-- Example markup required by test suite -->
160+ <div id="test-root"></div>
161+
162+ <!-- The test output -->
163+ <div id="log"></div>
164+ </body>
165+</html>
166
167=== added file 'lib/lp/bugs/javascript/tests/test_subscribers_list.js'
168--- lib/lp/bugs/javascript/tests/test_subscribers_list.js 1970-01-01 00:00:00 +0000
169+++ lib/lp/bugs/javascript/tests/test_subscribers_list.js 2011-04-28 13:50:50 +0000
170@@ -0,0 +1,121 @@
171+YUI({
172+ base: '../../../../canonical/launchpad/icing/yui/',
173+ filter: 'raw', combine: false, fetchCSS: false
174+ }).use('test', 'console', 'lp.bugs.subscribers_list',
175+ 'node-event-simulate',
176+ function(Y) {
177+
178+var suite = new Y.Test.Suite("lp.bugs.subscribers_list Tests");
179+var module = Y.lp.bugs.subscribers_list;
180+
181+/**
182+ * Test resetting of the subscribers list.
183+ */
184+suite.add(new Y.Test.Case({
185+ name: 'Resetting subscribers list',
186+
187+ setUp: function() {
188+ this.root = Y.Node.create('<div></div>');
189+ Y.one('body').appendChild(this.root);
190+ },
191+
192+ tearDown: function() {
193+ this.root.remove();
194+ },
195+
196+ test_no_subscribers: function() {
197+ // There are no subscribers left in the subscribers_list
198+ // (iow, subscribers_links is empty).
199+ var subscribers_links = Y.Node.create('<div></div>')
200+ .set('id', 'subscribers-links');
201+ var subscribers_list = Y.Node.create('<div></div>');
202+ subscribers_list.appendChild(subscribers_links);
203+ this.root.appendChild(subscribers_list);
204+
205+ // Resetting the list adds a 'None' div to the
206+ // subscribers_list (and not to the subscriber_links).
207+ module.reset();
208+ var none_node = subscribers_list.one('#none-subscribers');
209+ Y.Assert.isNotNull(none_node);
210+ Y.Assert.areEqual('None', none_node.get('innerHTML'));
211+ Y.Assert.areEqual(subscribers_list,
212+ none_node.get('parentNode'));
213+
214+ },
215+
216+ test_subscribers: function() {
217+ // When there is at least one subscriber, nothing
218+ // happens when reset() is called.
219+ var subscribers_links = Y.Node.create('<div></div>')
220+ .set('id', 'subscribers-links');
221+ subscribers_links.appendChild(
222+ Y.Node.create('<div>Subscriber</div>'));
223+ var subscribers_list = Y.Node.create('<div></div>');
224+ subscribers_list.appendChild(subscribers_links);
225+ this.root.appendChild(subscribers_list);
226+
227+ // Resetting the list is a no-op.
228+ module.reset();
229+ var none_node = subscribers_list.one('#none-subscribers');
230+ Y.Assert.isNull(none_node);
231+ },
232+
233+
234+ test_empty_duplicates: function() {
235+ // There are no subscribers among the duplicate subscribers.
236+ var dupe_subscribers = Y.Node.create('<div></div>')
237+ .set('id', 'subscribers-from-duplicates');
238+ this.root.appendChild(dupe_subscribers);
239+
240+ // There must always be subscribers-links node for reset() to work.
241+ var subscribers_links = Y.Node.create('<div></div>')
242+ .set('id', 'subscribers-links');
243+ this.root.appendChild(subscribers_links);
244+
245+ // Resetting the list removes the entire duplicate subscribers node.
246+ module.reset();
247+ var dupes_node = Y.one('#subscribers-from-duplicates');
248+ Y.Assert.isNull(dupes_node);
249+
250+ },
251+
252+ test_duplicates: function() {
253+ // There are subscribers among the duplicate subscribers,
254+ // and nothing changes.
255+ var dupe_subscribers = Y.Node.create('<div></div>')
256+ .set('id', 'subscribers-from-duplicates');
257+ dupe_subscribers.appendChild(
258+ Y.Node.create('<div>Subscriber</div>'));
259+ this.root.appendChild(dupe_subscribers);
260+
261+ // There must always be subscribers-links node for reset() to work.
262+ var subscribers_links = Y.Node.create('<div></div>')
263+ .set('id', 'subscribers-links');
264+ this.root.appendChild(subscribers_links);
265+
266+ // Resetting the list does nothing.
267+ module.reset();
268+
269+ // The list is still there.
270+ var dupes_node = this.root.one('#subscribers-from-duplicates');
271+ Y.Assert.isNotNull(dupes_node);
272+ Y.Assert.areEqual(1, dupes_node.all('div').size());
273+ }
274+}));
275+
276+
277+var handle_complete = function(data) {
278+ status_node = Y.Node.create(
279+ '<p id="complete">Test status: complete</p>');
280+ Y.one('body').appendChild(status_node);
281+ };
282+Y.Test.Runner.on('complete', handle_complete);
283+Y.Test.Runner.add(suite);
284+
285+var console = new Y.Console({newestOnTop: false});
286+console.render('#log');
287+
288+Y.on('domready', function() {
289+ Y.Test.Runner.run();
290+});
291+});