launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #03474
[Merge] lp:~danilo/launchpad/subscribers-list-js into lp:launchpad
Данило Шеган has proposed merging lp:~danilo/launchpad/subscribers-list-js into lp:launchpad with lp:~danilo/launchpad/bug-770241 as a prerequisite.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~danilo/launchpad/subscribers-list-js/+merge/59387
= Bug subscribers list: user link removal =
Move user link removal to the newly started subscribers_list module and add tests for it.
== Proposed fix ==
remove_user_name_link() in bugtask_index_portlets.js was:
- used every time as anim.on('end') handler together with reset()
- simply doing node.remove() which requires no separate method
So, I've joined it all together, added tests and moved it to bugs.subscribers_list JS module as remove_user_link() method.
== Implementation details ==
- bugtask_index_portlets.js: other than the change to replace repeated code with remove_user_link(), the only change is unindenting the big block that was unnecessarily in anim.on('end') in one occurrence
- tests are relatively comprehensive, and because of the green_flash animation taking a full second, a bit slow :/
- I am still working on more clean-ups in bugtask_index_portlets.js because it's basically untestable because of the hacky nature
== Tests ==
lib/lp/bugs/javascript/tests/test_subscribers_list.html
== Demo and Q/A ==
1. Mark https://bugs.launchpad.dev/bugs/2 as duplicate of https://bugs.launchpad.dev/bugs/1
2. "Subscribe someone else" on bug 2 (eg. ubuntu-team)
3. "Subscribe (the same) someone else" on bug 1.
4. Subscribe yourself
4. Reload bug 1 page.
5. Play with unsubscribe icons
= 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
--
https://code.launchpad.net/~danilo/launchpad/subscribers-list-js/+merge/59387
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~danilo/launchpad/subscribers-list-js into lp:launchpad.
=== modified file 'lib/lp/bugs/javascript/bugtask_index_portlets.js'
--- lib/lp/bugs/javascript/bugtask_index_portlets.js 2011-04-28 13:46:24 +0000
+++ lib/lp/bugs/javascript/bugtask_index_portlets.js 2011-04-28 15:09:35 +0000
@@ -608,38 +608,26 @@
var config = {
on: {
success: function(client) {
- icon_parent.removeChild(icon);
- var anim = Y.lazr.anim.green_flash({ node: icon_parent_div });
- anim.on('end', function(e) {
- remove_user_name_link(icon_parent_div);
- Y.lp.bugs.subscribers_list.reset();
- var person_link = Y.one('.' + person.get('css_name'));
- if (Y.Lang.isNull(person_link) &&
- subscription.is_current_user_subscribing()) {
- // Current user has been completely unsubscribed.
- subscription.disable_spinner(
- subscription_labels.SUBSCRIBE);
- set_subscription_link_parent_class(
- subscription_link, false, false);
- subscription.set('is_direct', false);
- subscription.set('has_dupes', false);
- } else {
- if (is_dupe) {
- // A direct subscription remains.
- set_subscription_link_parent_class(
- subscription_link, true, false);
- subscription.set('is_direct', true);
- subscription.set('has_dupes', false);
- } else {
- // A dupe subscription remains.
- set_subscription_link_parent_class(
- subscription_link, false, true);
- subscription.set('is_direct', false);
- subscription.set('has_dupes', true);
- }
- }
- });
- anim.run();
+ Y.lp.bugs.subscribers_list.remove_user_link(person, is_dupe);
+ var person_link = Y.one('.' + person.get('css_name'));
+ var has_direct, has_dupes;
+ if (Y.Lang.isNull(person_link) &&
+ subscription.is_current_user_subscribing()) {
+ // Current user has been completely unsubscribed.
+ subscription.disable_spinner(
+ subscription_labels.SUBSCRIBE);
+ has_direct = false;
+ has_dupes = false;
+ } else {
+ // If we removed the duplicate subscription,
+ // we are left with the direct one, and vice versa.
+ has_direct = is_dupe;
+ has_dupes = !is_dupe;
+ }
+ subscription.set('is_direct', has_direct);
+ subscription.set('has_dupes', has_dupes);
+ set_subscription_link_parent_class(
+ subscription_link, is_direct, has_dupes);
},
failure: error_handler.getFailureHandler()
@@ -832,13 +820,7 @@
subscription.set('has_dupes', false);
}
- var flash_node = Y.one('.' + subscriber.get('css_name'));
- var anim = Y.lazr.anim.green_flash({ node: flash_node });
- anim.on('end', function(e) {
- remove_user_name_link(flash_node);
- Y.lp.bugs.subscribers_list.reset();
- });
- anim.run();
+ Y.lp.bugs.subscribers_list.remove_user_link(subscriber);
},
failure: error_handler.getFailureHandler()
@@ -904,17 +886,7 @@
// Remove the subscriber's name from the subscriber
// list, if it's there.
- var subscriber_node = Y.one('.' + subscriber.get('css_name'));
- if (Y.Lang.isValue(subscriber_node)) {
- var subscriber_anim = Y.lazr.anim.green_flash({
- node: subscriber_node
- });
- subscriber_anim.on('end', function(e) {
- remove_user_name_link(subscriber_node);
- Y.lp.bugs.subscribers_list.reset();
- });
- subscriber_anim.run();
- }
+ Y.lp.bugs.subscribers_list.remove_user_link(subscriber);
subscription.set('is_muted', true);
update_mute_after_subscription_change(subscription);
update_subscription_after_mute_or_unmute(subscription);
@@ -1179,17 +1151,6 @@
}
/*
- * Used to remove the user's name from the subscriber's list.
- *
- * @method remove_user_name_link
- * @param user_node {Node} Node representing the user name link.
- */
-function remove_user_name_link(user_node) {
- var parent = user_node.get('parentNode');
- parent.removeChild(user_node);
-}
-
-/*
* Set the class on subscription link's parentNode.
*
* This is used to reset the class used by the
=== modified file 'lib/lp/bugs/javascript/subscribers_list.js'
--- lib/lp/bugs/javascript/subscribers_list.js 2011-04-28 09:26:07 +0000
+++ lib/lp/bugs/javascript/subscribers_list.js 2011-04-28 15:09:35 +0000
@@ -38,6 +38,41 @@
dup_list.remove();
}
}
-namespace.reset = reset;
-
-}, "0.1", {"requires": ["node"]});
+namespace._reset = reset;
+
+/**
+ * Remove the user's name from the subscribers list.
+ * It uses the green-flash animation to indicate successful removal.
+ *
+ * @method remove_user_link
+ * @param subscriber {Subscriber} Subscriber that you want to remove.
+ * @param is_dupe {Boolean} Uses subscription link from the duplicates
+ * instead.
+ */
+function remove_user_link(subscriber, is_dupe) {
+ var user_node_id;
+ var user_name = subscriber.get('css_name');
+ if (is_dupe === true) {
+ user_node_id = '#dupe-' + user_name;
+ } else {
+ user_node_id = '#direct-' + user_name;
+ }
+ var user_node = Y.one(user_node_id);
+ if (Y.Lang.isValue(user_node)) {
+ // If there's an icon, we remove it prior to animation
+ // so animation looks better.
+ var unsub_icon = user_node.one('#unsubscribe-icon-' + user_name);
+ if (Y.Lang.isValue(unsub_icon)) {
+ unsub_icon.remove();
+ }
+ var anim = Y.lazr.anim.green_flash({ node: user_node });
+ anim.on('end', function() {
+ user_node.remove();
+ reset();
+ });
+ anim.run();
+ }
+}
+namespace.remove_user_link = remove_user_link;
+
+}, "0.1", {"requires": ["node", "lazr.anim"]});
=== modified file 'lib/lp/bugs/javascript/tests/test_subscribers_list.html'
--- lib/lp/bugs/javascript/tests/test_subscribers_list.html 2011-04-28 09:26:07 +0000
+++ lib/lp/bugs/javascript/tests/test_subscribers_list.html 2011-04-28 15:09:35 +0000
@@ -21,6 +21,10 @@
<script type="text/javascript"
src="../../../app/javascript/errors.js"></script>
+ <!-- Pre-requisite -->
+ <script type="text/javascript"
+ src="../subscriber.js"></script>
+
<!-- The module under test -->
<script type="text/javascript"
src="../subscribers_list.js"></script>
=== modified file 'lib/lp/bugs/javascript/tests/test_subscribers_list.js'
--- lib/lp/bugs/javascript/tests/test_subscribers_list.js 2011-04-28 09:26:07 +0000
+++ lib/lp/bugs/javascript/tests/test_subscribers_list.js 2011-04-28 15:09:35 +0000
@@ -1,13 +1,37 @@
YUI({
base: '../../../../canonical/launchpad/icing/yui/',
filter: 'raw', combine: false, fetchCSS: false
- }).use('test', 'console', 'lp.bugs.subscribers_list',
- 'node-event-simulate',
+ }).use('test', 'console', 'lp.bugs.subscriber',
+ 'lp.bugs.subscribers_list', 'node-event-simulate',
function(Y) {
var suite = new Y.Test.Suite("lp.bugs.subscribers_list Tests");
var module = Y.lp.bugs.subscribers_list;
+
+/**
+ * Set-up all the nodes required for subscribers list testing.
+ */
+function setUpSubscribersList(root_node, with_dupes) {
+ // Set-up subscribers list.
+ var direct_links = Y.Node.create('<div></div>')
+ .set('id', 'subscribers-links');
+ var direct_list = Y.Node.create('<div></div>')
+ .set('id', 'subscribers-direct');
+ direct_list.appendChild(direct_links);
+ root_node.appendChild(direct_list);
+
+ if (with_dupes === true) {
+ var dupe_links = Y.Node.create('<div></div>')
+ .set('id', 'subscribers-from-duplicates');
+ var dupe_list = Y.Node.create('<div></div>')
+ .set('id', 'subscribers-from-duplicates-container');
+ dupe_list.appendChild(dupe_links);
+ root_node.appendChild(dupe_list);
+ }
+ return direct_list;
+}
+
/**
* Test resetting of the subscribers list.
*/
@@ -26,15 +50,11 @@
test_no_subscribers: function() {
// There are no subscribers left in the subscribers_list
// (iow, subscribers_links is empty).
- var subscribers_links = Y.Node.create('<div></div>')
- .set('id', 'subscribers-links');
- var subscribers_list = Y.Node.create('<div></div>');
- subscribers_list.appendChild(subscribers_links);
- this.root.appendChild(subscribers_list);
+ var subscribers_list = setUpSubscribersList(this.root);
// Resetting the list adds a 'None' div to the
// subscribers_list (and not to the subscriber_links).
- module.reset();
+ module._reset();
var none_node = subscribers_list.one('#none-subscribers');
Y.Assert.isNotNull(none_node);
Y.Assert.areEqual('None', none_node.get('innerHTML'));
@@ -46,16 +66,13 @@
test_subscribers: function() {
// When there is at least one subscriber, nothing
// happens when reset() is called.
- var subscribers_links = Y.Node.create('<div></div>')
- .set('id', 'subscribers-links');
+ var subscribers_list = setUpSubscribersList(this.root);
+ var subscribers_links = subscribers_list.one('#subscribers-links');
subscribers_links.appendChild(
Y.Node.create('<div>Subscriber</div>'));
- var subscribers_list = Y.Node.create('<div></div>');
- subscribers_list.appendChild(subscribers_links);
- this.root.appendChild(subscribers_list);
// Resetting the list is a no-op.
- module.reset();
+ module._reset();
var none_node = subscribers_list.one('#none-subscribers');
Y.Assert.isNull(none_node);
},
@@ -63,38 +80,24 @@
test_empty_duplicates: function() {
// There are no subscribers among the duplicate subscribers.
- var dupe_subscribers = Y.Node.create('<div></div>')
- .set('id', 'subscribers-from-duplicates');
- this.root.appendChild(dupe_subscribers);
-
- // There must always be subscribers-links node for reset() to work.
- var subscribers_links = Y.Node.create('<div></div>')
- .set('id', 'subscribers-links');
- this.root.appendChild(subscribers_links);
+ var subscribers_list = setUpSubscribersList(this.root, true);
+ var dupe_subscribers = this.root.one('#subscribers-from-duplicates');
// Resetting the list removes the entire duplicate subscribers node.
- module.reset();
- var dupes_node = Y.one('#subscribers-from-duplicates');
- Y.Assert.isNull(dupes_node);
+ module._reset();
+ Y.Assert.isNull(Y.one('#subscribers-from-duplicates'));
},
test_duplicates: function() {
// There are subscribers among the duplicate subscribers,
// and nothing changes.
- var dupe_subscribers = Y.Node.create('<div></div>')
- .set('id', 'subscribers-from-duplicates');
- dupe_subscribers.appendChild(
- Y.Node.create('<div>Subscriber</div>'));
- this.root.appendChild(dupe_subscribers);
-
- // There must always be subscribers-links node for reset() to work.
- var subscribers_links = Y.Node.create('<div></div>')
- .set('id', 'subscribers-links');
- this.root.appendChild(subscribers_links);
+ var subscribers_list = setUpSubscribersList(this.root, true);
+ var dupe_subscribers = this.root.one('#subscribers-from-duplicates');
+ dupe_subscribers.appendChild(Y.Node.create('<div>Subscriber</div>'));
// Resetting the list does nothing.
- module.reset();
+ module._reset();
// The list is still there.
var dupes_node = this.root.one('#subscribers-from-duplicates');
@@ -104,6 +107,227 @@
}));
+/**
+ * Test removal of a single person link from the subscribers list.
+ */
+suite.add(new Y.Test.Case({
+ name: 'Removal of a subscriber link',
+
+ addSubscriber: function(root_node, subscriber, through_dupe) {
+ var css_class = subscriber.get('css_name');
+ var id;
+ if (through_dupe === true) {
+ links = root_node.one('#subscribers-from-duplicates');
+ id = 'dupe-' + css_class;
+ } else {
+ links = root_node.one('#subscribers-links');
+ id = 'direct-' + css_class;
+ }
+ return links.appendChild(
+ Y.Node.create('<div></div>')
+ .addClass(css_class)
+ .set('id', id)
+ .set('text', subscriber.get('uri')));
+ },
+
+ setUp: function() {
+ this.root = Y.Node.create('<div></div>');
+ Y.one('body').appendChild(this.root);
+ this.subscriber_ids = {};
+ },
+
+ tearDown: function() {
+ this.root.remove();
+ delete this.subscriber_ids;
+ },
+
+ test_no_matching_subscriber: function() {
+ // If there is no matching subscriber, removal silently passes.
+
+ // Set-up subscribers list.
+ setUpSubscribersList(this.root);
+
+ var person = new Y.lp.bugs.subscriber.Subscriber({
+ uri: 'myself',
+ subscriber_ids: this.subscriber_ids
+ });
+ var other_person = new Y.lp.bugs.subscriber.Subscriber({
+ uri: 'someone',
+ subscriber_ids: this.subscriber_ids
+ });
+ this.addSubscriber(this.root, other_person);
+
+ module.remove_user_link(person);
+
+ // `other_person` is not removed.
+ Y.Assert.isNotNull(
+ this.root.one('.' + other_person.get('css_name')));
+ },
+
+ test_unsubscribe_icon_removal: function() {
+ // If there is an unsubscribe icon, it gets removed
+ // before animation starts.
+
+ // Set-up subscribers list.
+ setUpSubscribersList(this.root);
+
+ var person = new Y.lp.bugs.subscriber.Subscriber({
+ uri: 'myself',
+ subscriber_ids: this.subscriber_ids
+ });
+ this.addSubscriber(this.root, person);
+ var css_name = person.get('css_name');
+ this.root.one('.' + css_name)
+ .appendChild('<div></div>')
+ .appendChild('<img></img>')
+ .set('id', 'unsubscribe-icon-' + css_name);
+
+ module.remove_user_link(person);
+
+ // Unsubscribe icon is removed immediatelly.
+ Y.Assert.isNull(this.root.one('#unsubscribe-icon-' + css_name));
+ },
+
+ test_direct_subscriber: function() {
+ // If there is a direct subscriber, removal works fine.
+
+ // Set-up subscribers list.
+ setUpSubscribersList(this.root);
+
+ var person = new Y.lp.bugs.subscriber.Subscriber({
+ uri: 'myself',
+ subscriber_ids: this.subscriber_ids
+ });
+ this.addSubscriber(this.root, person);
+
+ module.remove_user_link(person);
+
+ this.wait(function() {
+ // There is no subscriber link anymore.
+ Y.Assert.isNull(this.root.one('.' + person.get('css_name')));
+ // And the reset() call adds the "None" node.
+ Y.Assert.isNotNull(this.root.one('#none-subscribers'));
+ }, 1100);
+ },
+
+ test_direct_subscriber_remove_dupe: function() {
+ // If there is only a direct subscriber, attempting removal of
+ // a duplicate subscription link does nothing.
+
+ // Set-up subscribers list.
+ setUpSubscribersList(this.root);
+
+ var person = new Y.lp.bugs.subscriber.Subscriber({
+ uri: 'myself',
+ subscriber_ids: this.subscriber_ids
+ });
+ this.addSubscriber(this.root, person);
+
+ module.remove_user_link(person, true);
+
+ this.wait(function() {
+ // There is no subscriber link anymore.
+ Y.Assert.isNotNull(this.root.one('.' + person.get('css_name')));
+ }, 1100);
+ },
+
+ test_dupe_subscriber: function() {
+ // If there is a duplicate subscriber, removal works fine.
+
+ // Set-up subscribers list.
+ setUpSubscribersList(this.root, true);
+
+ var person = new Y.lp.bugs.subscriber.Subscriber({
+ uri: 'myself',
+ subscriber_ids: this.subscriber_ids
+ });
+ this.addSubscriber(this.root, person, true);
+
+ module.remove_user_link(person, true);
+
+ this.wait(function() {
+ // There is no subscriber link anymore.
+ Y.Assert.isNull(this.root.one('.' + person.get('css_name')));
+ // And the reset() call cleans up the entire duplicate section.
+ Y.Assert.isNull(this.root.one('#subscribers-from-duplicates'));
+ }, 1100);
+ },
+
+ test_dupe_subscriber_remove_direct: function() {
+ // If there is a duplicate subscriber, trying to remove the
+ // direct subscription user link doesn't do anything.
+
+ // Set-up subscribers list.
+ setUpSubscribersList(this.root, true);
+
+ var person = new Y.lp.bugs.subscriber.Subscriber({
+ uri: 'myself',
+ subscriber_ids: this.subscriber_ids
+ });
+ this.addSubscriber(this.root, person, true);
+
+ module.remove_user_link(person);
+
+ this.wait(function() {
+ // There is no subscriber link anymore.
+ Y.Assert.isNotNull(this.root.one('.' + person.get('css_name')));
+ }, 1100);
+ },
+
+ test_direct_and_dupe_subscriber_remove_dupe: function() {
+ // If there a subscriber is both directly subscribed and
+ // subscribed through duplicate, removal removes only one link.
+
+ // Set-up subscribers list.
+ setUpSubscribersList(this.root, true);
+
+ var person = new Y.lp.bugs.subscriber.Subscriber({
+ uri: 'myself',
+ subscriber_ids: this.subscriber_ids
+ });
+ this.addSubscriber(this.root, person);
+ this.addSubscriber(this.root, person, true);
+
+ // Remove the duplicate subscription link.
+ module.remove_user_link(person, true);
+
+ this.wait(function() {
+ // Remaining entry is the direct subscription one.
+ var nodes = this.root.all('.' + person.get('css_name'));
+ Y.Assert.areEqual(1, nodes.size());
+ Y.Assert.areEqual('direct-' + person.get('css_name'),
+ nodes.pop().get('id'));
+ }, 1100);
+ },
+
+ test_direct_and_dupe_subscriber_remove_direct: function() {
+ // If there a subscriber is both directly subscribed and
+ // subscribed through duplicate, removal removes only one link.
+
+ // Set-up subscribers list.
+ setUpSubscribersList(this.root, true);
+
+ var person = new Y.lp.bugs.subscriber.Subscriber({
+ uri: 'myself',
+ subscriber_ids: this.subscriber_ids
+ });
+ this.addSubscriber(this.root, person);
+ this.addSubscriber(this.root, person, true);
+
+ // Remove the direct subscription link.
+ module.remove_user_link(person);
+
+ this.wait(function() {
+ // Remaining entry is the duplicate subscription one.
+ var nodes = this.root.all('.' + person.get('css_name'));
+ Y.Assert.areEqual(1, nodes.size());
+ Y.Assert.areEqual('dupe-' + person.get('css_name'),
+ nodes.pop().get('id'));
+ }, 1100);
+ }
+}));
+
+
var handle_complete = function(data) {
status_node = Y.Node.create(
'<p id="complete">Test status: complete</p>');