Merge lp:~danilo/launchpad/bug-772754-other-subscribers-activity into lp:launchpad
- bug-772754-other-subscribers-activity
- Merge into devel
Status: | Merged |
---|---|
Approved by: | Graham Binns |
Approved revision: | no longer in the source branch. |
Merged at revision: | 13243 |
Proposed branch: | lp:~danilo/launchpad/bug-772754-other-subscribers-activity |
Merge into: | lp:launchpad |
Prerequisite: | lp:~danilo/launchpad/bug-772754-other-subscribers-subscribers |
Diff against target: |
610 lines (+541/-3) 2 files modified
lib/lp/bugs/javascript/subscribers_list.js (+190/-2) lib/lp/bugs/javascript/tests/test_subscribers_list.js (+351/-1) |
To merge this branch: | bzr merge lp:~danilo/launchpad/bug-772754-other-subscribers-activity |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Graham Binns (community) | code | Approve | |
Review via email: mp+64180@code.launchpad.net |
Commit message
Description of the change
= Bug 772754: Other subscribers list, part 4 =
This is part of ongoing work for providing the "other subscribers" list as indicated in mockup https:/
This branch continues on the previous branches to provide methods to indicate either some global activity in the subscribers list (like "Loading subscribers") or some activity on a particular subscriber (eg. "Subscribing", "Unsubscribing", along with spinner).
It is comprehensively tested.
== Tests ==
lp/bugs/
== Demo and Q/A ==
N/A
= Launchpad lint =
Checking for conflicts and issues in changed files.
Linting changed files:
lib/lp/
lib/lp/
Graham Binns (gmb) : | # |
Preview Diff
1 | === modified file 'lib/lp/bugs/javascript/subscribers_list.js' | |||
2 | --- lib/lp/bugs/javascript/subscribers_list.js 2011-06-15 11:08:30 +0000 | |||
3 | +++ lib/lp/bugs/javascript/subscribers_list.js 2011-06-15 11:08:35 +0000 | |||
4 | @@ -82,6 +82,9 @@ | |||
5 | 82 | list: 'subscribers-list', | 82 | list: 'subscribers-list', |
6 | 83 | subscriber: 'subscriber', | 83 | subscriber: 'subscriber', |
7 | 84 | no_subscribers: 'no-subscribers-indicator', | 84 | no_subscribers: 'no-subscribers-indicator', |
8 | 85 | activity: 'global-activity-indicator', | ||
9 | 86 | activity_text: 'global-activity-text', | ||
10 | 87 | subscriber_activity: 'subscriber-activity-indicator', | ||
11 | 85 | actions: 'subscriber-actions', | 88 | actions: 'subscriber-actions', |
12 | 86 | unsubscribe: 'unsubscribe-action' | 89 | unsubscribe: 'unsubscribe-action' |
13 | 87 | }; | 90 | }; |
14 | @@ -152,12 +155,14 @@ | |||
15 | 152 | * of no subscribers. | 155 | * of no subscribers. |
16 | 153 | * | 156 | * |
17 | 154 | * @method resetNoSubscribers | 157 | * @method resetNoSubscribers |
18 | 158 | * @param force_hide {Boolean} Whether to force hiding of the "no subscribers" | ||
19 | 159 | * indication. | ||
20 | 155 | */ | 160 | */ |
22 | 156 | SubscribersList.prototype.resetNoSubscribers = function() { | 161 | SubscribersList.prototype.resetNoSubscribers = function(force_hide) { |
23 | 157 | var has_sections = ( | 162 | var has_sections = ( |
24 | 158 | this.container_node.one('.' + CSS_CLASSES.section) !== null); | 163 | this.container_node.one('.' + CSS_CLASSES.section) !== null); |
25 | 159 | var no_subs; | 164 | var no_subs; |
27 | 160 | if (has_sections) { | 165 | if (has_sections || force_hide === true) { |
28 | 161 | // Make sure the indicator for no subscribers is not there. | 166 | // Make sure the indicator for no subscribers is not there. |
29 | 162 | no_subs = this.container_node.one('.' + CSS_CLASSES.no_subscribers); | 167 | no_subs = this.container_node.one('.' + CSS_CLASSES.no_subscribers); |
30 | 163 | if (no_subs !== null) { | 168 | if (no_subs !== null) { |
31 | @@ -172,6 +177,106 @@ | |||
32 | 172 | }; | 177 | }; |
33 | 173 | 178 | ||
34 | 174 | /** | 179 | /** |
35 | 180 | * Returns or creates a node for progress indication for the subscribers list. | ||
36 | 181 | * | ||
37 | 182 | * If node is not present, it is created and added to the beginning of | ||
38 | 183 | * subscribers list container node. | ||
39 | 184 | * | ||
40 | 185 | * @method _ensureActivityNode | ||
41 | 186 | * @return {Y.Node} A node with the spinner img node and a span text node. | ||
42 | 187 | */ | ||
43 | 188 | SubscribersList.prototype._ensureActivityNode = function() { | ||
44 | 189 | var activity_node = this.container_node.one('.' + CSS_CLASSES.activity); | ||
45 | 190 | if (activity_node === null) { | ||
46 | 191 | activity_node = Y.Node.create('<div />') | ||
47 | 192 | .addClass(CSS_CLASSES.activity); | ||
48 | 193 | progress_icon = Y.Node.create('<img />') | ||
49 | 194 | .set('src', '/@@/spinner'); | ||
50 | 195 | activity_node.appendChild(progress_icon); | ||
51 | 196 | activity_node.appendChild( | ||
52 | 197 | Y.Node.create('<span />') | ||
53 | 198 | .addClass(CSS_CLASSES.activity_text)); | ||
54 | 199 | this.container_node.prepend(activity_node); | ||
55 | 200 | } | ||
56 | 201 | return activity_node; | ||
57 | 202 | }; | ||
58 | 203 | |||
59 | 204 | /** | ||
60 | 205 | * Sets icon in the activity node to either 'error' or 'spinner' icon. | ||
61 | 206 | * | ||
62 | 207 | * @method _setActivityErrorIcon | ||
63 | 208 | * @param node {Y.Node} An activity node as returned by _ensureActivityNode(). | ||
64 | 209 | * @param error {Boolean} Whether to show an error icon. | ||
65 | 210 | * Otherwise shows a spinner image. | ||
66 | 211 | */ | ||
67 | 212 | SubscribersList.prototype._setActivityErrorIcon = function(node, error) { | ||
68 | 213 | var progress_icon = node.one('img'); | ||
69 | 214 | if (error === true) { | ||
70 | 215 | progress_icon.set('src', '/@@/error'); | ||
71 | 216 | } else { | ||
72 | 217 | progress_icon.set('src', '/@@/spinner'); | ||
73 | 218 | } | ||
74 | 219 | }; | ||
75 | 220 | |||
76 | 221 | /** | ||
77 | 222 | * Sets the activity text inside the activity node. | ||
78 | 223 | * | ||
79 | 224 | * @method _setActivityText | ||
80 | 225 | * @param node {Y.Node} An activity node as returned by _ensureActivityNode(). | ||
81 | 226 | * @param text {String} Description of the activity currently in progress. | ||
82 | 227 | */ | ||
83 | 228 | SubscribersList.prototype._setActivityText = function(node, text) { | ||
84 | 229 | var text_node = node.one('.' + CSS_CLASSES.activity_text); | ||
85 | 230 | text_node.set('text', ' ' + text); | ||
86 | 231 | }; | ||
87 | 232 | |||
88 | 233 | /** | ||
89 | 234 | * Indicate some activity for the subscribers list with a progress spinner | ||
90 | 235 | * and optionally some text. | ||
91 | 236 | * | ||
92 | 237 | * @method startActivity | ||
93 | 238 | * @param text {String} Description of the action to indicate progress of. | ||
94 | 239 | */ | ||
95 | 240 | SubscribersList.prototype.startActivity = function(text) { | ||
96 | 241 | // We don't ever want "No subscribers" to be shown when loading is in | ||
97 | 242 | // progress. | ||
98 | 243 | this.resetNoSubscribers(true); | ||
99 | 244 | |||
100 | 245 | var activity_node = this._ensureActivityNode(); | ||
101 | 246 | // Ensure the icon is back to the spinner. | ||
102 | 247 | this._setActivityErrorIcon(activity_node, false); | ||
103 | 248 | this._setActivityText(activity_node, text); | ||
104 | 249 | }; | ||
105 | 250 | |||
106 | 251 | /** | ||
107 | 252 | * Stop any activity indication for the subscribers list and optionally | ||
108 | 253 | * display an error message. | ||
109 | 254 | * | ||
110 | 255 | * @method stopActivity | ||
111 | 256 | * @param error_text {String} Error message to display. If not a string, | ||
112 | 257 | * it is considered that the operation was successful and no error | ||
113 | 258 | * indication is added to the subscribers list. | ||
114 | 259 | */ | ||
115 | 260 | SubscribersList.prototype.stopActivity = function(error_text) { | ||
116 | 261 | var activity_node = this.container_node.one('.' + CSS_CLASSES.activity); | ||
117 | 262 | if (Y.Lang.isString(error_text)) { | ||
118 | 263 | // There is an error message, keep the node visible with | ||
119 | 264 | // the error message in. | ||
120 | 265 | activity_node = this._ensureActivityNode(true); | ||
121 | 266 | this._setActivityErrorIcon(activity_node, true); | ||
122 | 267 | this._setActivityText(activity_node, error_text); | ||
123 | 268 | this.resetNoSubscribers(true); | ||
124 | 269 | } else { | ||
125 | 270 | // No errors, remove the activity node if present. | ||
126 | 271 | if (activity_node !== null) { | ||
127 | 272 | activity_node.remove(); | ||
128 | 273 | } | ||
129 | 274 | // Restore "No subscribers" indication if needed. | ||
130 | 275 | this.resetNoSubscribers(); | ||
131 | 276 | } | ||
132 | 277 | }; | ||
133 | 278 | |||
134 | 279 | /** | ||
135 | 175 | * Get a CSS class to use for the section of the subscribers' list | 280 | * Get a CSS class to use for the section of the subscribers' list |
136 | 176 | * with subscriptions with the level `level`. | 281 | * with subscriptions with the level `level`. |
137 | 177 | * | 282 | * |
138 | @@ -551,5 +656,88 @@ | |||
139 | 551 | this._removeSectionNodeIfEmpty(existing_section); | 656 | this._removeSectionNodeIfEmpty(existing_section); |
140 | 552 | }; | 657 | }; |
141 | 553 | 658 | ||
142 | 659 | /** | ||
143 | 660 | * Indicates some activity for a subscriber in the subscribers list. | ||
144 | 661 | * Uses a regular Launchpad progress spinner UI. | ||
145 | 662 | * | ||
146 | 663 | * If subscriber is not in the list already, it fails with an exception. | ||
147 | 664 | * If there are any actions available for the subscriber (such as unsubscribe | ||
148 | 665 | * action), they are hidden. | ||
149 | 666 | * | ||
150 | 667 | * @method indicateSubscriberActivity | ||
151 | 668 | * @param subscriber {Object} Object containing at least `name` | ||
152 | 669 | * for the subscriber. | ||
153 | 670 | */ | ||
154 | 671 | SubscribersList.prototype.indicateSubscriberActivity = function(subscriber) { | ||
155 | 672 | var subscriber_node = this._getSubscriberNode(subscriber); | ||
156 | 673 | var progress_node = subscriber_node.one( | ||
157 | 674 | '.' + CSS_CLASSES.subscriber_activity); | ||
158 | 675 | |||
159 | 676 | // No-op if there is already indication of progress, | ||
160 | 677 | // and creates a new node with the spinner if there isn't. | ||
161 | 678 | if (progress_node === null) { | ||
162 | 679 | var actions_node = subscriber_node.one('.' + CSS_CLASSES.actions); | ||
163 | 680 | if (actions_node !== null) { | ||
164 | 681 | actions_node.setStyle('display', 'none'); | ||
165 | 682 | } | ||
166 | 683 | var progress_icon = Y.Node.create('<img />') | ||
167 | 684 | .set('src', '/@@/spinner'); | ||
168 | 685 | |||
169 | 686 | progress_node = Y.Node.create('<span />') | ||
170 | 687 | .addClass(CSS_CLASSES.subscriber_activity) | ||
171 | 688 | .setStyle('float', 'right'); | ||
172 | 689 | progress_node.appendChild(progress_icon); | ||
173 | 690 | subscriber_node.appendChild(progress_node); | ||
174 | 691 | } | ||
175 | 692 | }; | ||
176 | 693 | |||
177 | 694 | /** | ||
178 | 695 | * Stop any indication of activity for a subscriber in the subscribers list. | ||
179 | 696 | * | ||
180 | 697 | * If the spinner is present, it removes it. If `success` parameter is | ||
181 | 698 | * passed in, it determines if success or failure animation will be shown | ||
182 | 699 | * as well. | ||
183 | 700 | * | ||
184 | 701 | * If subscriber is not in the list already, it fails with an exception. | ||
185 | 702 | * If there are any actions available for the subscriber (such as unsubscribe | ||
186 | 703 | * action), they are re-displayed if hidden. | ||
187 | 704 | * | ||
188 | 705 | * @method stopSubscriberActivity | ||
189 | 706 | * @param subscriber {Object} Object containing at least `name` | ||
190 | 707 | * for the subscriber. | ||
191 | 708 | * @param success {Boolean} Whether to indicate success (`success` == true, | ||
192 | 709 | * flash green) or failure (false, red). Otherwise, perform no | ||
193 | 710 | * animation. | ||
194 | 711 | * @param callback {Function} Function to call if and when success/failure | ||
195 | 712 | * animation completes. | ||
196 | 713 | */ | ||
197 | 714 | SubscribersList.prototype.stopSubscriberActivity = function(subscriber, | ||
198 | 715 | success, | ||
199 | 716 | callback) { | ||
200 | 717 | var subscriber_node = this._getSubscriberNode(subscriber); | ||
201 | 718 | var progress_node = subscriber_node.one( | ||
202 | 719 | '.' + CSS_CLASSES.subscriber_activity); | ||
203 | 720 | if (progress_node !== null) { | ||
204 | 721 | // Remove and destroy the node if present. | ||
205 | 722 | progress_node.remove(true); | ||
206 | 723 | } | ||
207 | 724 | // If actions node is present and hidden, show it. | ||
208 | 725 | var actions_node = subscriber_node.one('.' + CSS_CLASSES.actions); | ||
209 | 726 | if (actions_node !== null) { | ||
210 | 727 | actions_node.setStyle('display', 'inline'); | ||
211 | 728 | } | ||
212 | 729 | |||
213 | 730 | if (success === true || success === false) { | ||
214 | 731 | var anim; | ||
215 | 732 | if (success === true) { | ||
216 | 733 | anim = Y.lazr.anim.green_flash({ node: subscriber_node }); | ||
217 | 734 | } else { | ||
218 | 735 | anim = Y.lazr.anim.red_flash({ node: subscriber_node }); | ||
219 | 736 | } | ||
220 | 737 | anim.on('end', callback); | ||
221 | 738 | anim.run(); | ||
222 | 739 | } | ||
223 | 740 | }; | ||
224 | 741 | |||
225 | 554 | 742 | ||
226 | 555 | }, "0.1", {"requires": ["node", "lazr.anim", "lp.client", "lp.names"]}); | 743 | }, "0.1", {"requires": ["node", "lazr.anim", "lp.client", "lp.names"]}); |
227 | 556 | 744 | ||
228 | === modified file 'lib/lp/bugs/javascript/tests/test_subscribers_list.js' | |||
229 | --- lib/lp/bugs/javascript/tests/test_subscribers_list.js 2011-06-15 11:08:30 +0000 | |||
230 | +++ lib/lp/bugs/javascript/tests/test_subscribers_list.js 2011-06-15 11:08:35 +0000 | |||
231 | @@ -430,6 +430,29 @@ | |||
232 | 430 | no_subs_nodes.item(0).get('text')); | 430 | no_subs_nodes.item(0).get('text')); |
233 | 431 | }, | 431 | }, |
234 | 432 | 432 | ||
235 | 433 | test_no_subscribers_force_hide: function() { | ||
236 | 434 | // When resetNoSubscribers() is called on an empty | ||
237 | 435 | // SubscribersList but with force_hide parameter set to true, | ||
238 | 436 | // indication of no subscribers is not added. | ||
239 | 437 | var subscribers_list = setUpSubscribersList(this.root); | ||
240 | 438 | subscribers_list.resetNoSubscribers(true); | ||
241 | 439 | var no_subs_nodes = this.root.all( | ||
242 | 440 | '.no-subscribers-indicator'); | ||
243 | 441 | Y.Assert.areEqual(0, no_subs_nodes.size()); | ||
244 | 442 | }, | ||
245 | 443 | |||
246 | 444 | test_no_subscribers_force_hide_removal: function() { | ||
247 | 445 | // When resetNoSubscribers() is called on an empty | ||
248 | 446 | // SubscribersList which already has a no-subscribers | ||
249 | 447 | // indication shown, it is removed. | ||
250 | 448 | var subscribers_list = setUpSubscribersList(this.root); | ||
251 | 449 | subscribers_list.resetNoSubscribers(); | ||
252 | 450 | subscribers_list.resetNoSubscribers(true); | ||
253 | 451 | var no_subs_nodes = this.root.all( | ||
254 | 452 | '.no-subscribers-indicator'); | ||
255 | 453 | Y.Assert.areEqual(0, no_subs_nodes.size()); | ||
256 | 454 | }, | ||
257 | 455 | |||
258 | 433 | test_subscribers_no_addition: function() { | 456 | test_subscribers_no_addition: function() { |
259 | 434 | // When resetNoSubscribers() is called on a SubscribersList | 457 | // When resetNoSubscribers() is called on a SubscribersList |
260 | 435 | // with some subscribers, no indication of no subscribers is added. | 458 | // with some subscribers, no indication of no subscribers is added. |
261 | @@ -474,6 +497,184 @@ | |||
262 | 474 | 497 | ||
263 | 475 | 498 | ||
264 | 476 | /** | 499 | /** |
265 | 500 | * Test activity/progress indication for the entire subscribers list. | ||
266 | 501 | */ | ||
267 | 502 | suite.add(new Y.Test.Case({ | ||
268 | 503 | name: 'SubscribersList.startActivity() and stopActivity() test', | ||
269 | 504 | |||
270 | 505 | _should: { | ||
271 | 506 | error: { | ||
272 | 507 | test_setActivityErrorIcon_error: true, | ||
273 | 508 | test_setActivityText_error: true | ||
274 | 509 | } | ||
275 | 510 | }, | ||
276 | 511 | |||
277 | 512 | setUp: function() { | ||
278 | 513 | this.root = Y.Node.create('<div />'); | ||
279 | 514 | Y.one('body').appendChild(this.root); | ||
280 | 515 | }, | ||
281 | 516 | |||
282 | 517 | tearDown: function() { | ||
283 | 518 | this.root.remove(); | ||
284 | 519 | }, | ||
285 | 520 | |||
286 | 521 | test_ensureActivityNode: function() { | ||
287 | 522 | // With no activity node present, one is created and put | ||
288 | 523 | // into the subscribers list container node. | ||
289 | 524 | var subscribers_list = setUpSubscribersList(this.root); | ||
290 | 525 | var node = subscribers_list._ensureActivityNode(); | ||
291 | 526 | Y.Assert.isNotNull(node); | ||
292 | 527 | Y.Assert.isTrue(node.hasClass('global-activity-indicator')); | ||
293 | 528 | Y.Assert.areSame( | ||
294 | 529 | subscribers_list.container_node, node.get('parentNode')); | ||
295 | 530 | }, | ||
296 | 531 | |||
297 | 532 | test_ensureActivityNode_contents: function() { | ||
298 | 533 | // Created node contains an img tag with the spinner icon | ||
299 | 534 | // and a span tag for the text. | ||
300 | 535 | var subscribers_list = setUpSubscribersList(this.root); | ||
301 | 536 | var node = subscribers_list._ensureActivityNode(); | ||
302 | 537 | var icon = node.one('img'); | ||
303 | 538 | Y.Assert.isNotNull(icon); | ||
304 | 539 | Y.Assert.areEqual('file:///@@/spinner', icon.get('src')); | ||
305 | 540 | var text = node.one('span'); | ||
306 | 541 | Y.Assert.isNotNull(text); | ||
307 | 542 | Y.Assert.isTrue(text.hasClass('global-activity-text')); | ||
308 | 543 | }, | ||
309 | 544 | |||
310 | 545 | test_ensureActivityNode_existing: function() { | ||
311 | 546 | // When activity node already exists, it is returned | ||
312 | 547 | // and no new one is created. | ||
313 | 548 | var subscribers_list = setUpSubscribersList(this.root); | ||
314 | 549 | var existing_node = subscribers_list._ensureActivityNode(); | ||
315 | 550 | var new_node = subscribers_list._ensureActivityNode(); | ||
316 | 551 | Y.Assert.areSame(existing_node, new_node); | ||
317 | 552 | Y.Assert.areEqual( | ||
318 | 553 | 1, | ||
319 | 554 | subscribers_list | ||
320 | 555 | .container_node | ||
321 | 556 | .all('.global-activity-indicator') | ||
322 | 557 | .size()); | ||
323 | 558 | }, | ||
324 | 559 | |||
325 | 560 | test_setActivityErrorIcon_error_icon: function() { | ||
326 | 561 | // With the activity node passed in, error icon is set | ||
327 | 562 | // when desired. | ||
328 | 563 | var subscribers_list = setUpSubscribersList(this.root); | ||
329 | 564 | var node = subscribers_list._ensureActivityNode(); | ||
330 | 565 | var icon_node = node.one('img'); | ||
331 | 566 | subscribers_list._setActivityErrorIcon(node, true); | ||
332 | 567 | Y.Assert.areEqual('file:///@@/error', icon_node.get('src')); | ||
333 | 568 | }, | ||
334 | 569 | |||
335 | 570 | test_setActivityErrorIcon_spinner_icon: function() { | ||
336 | 571 | // With the activity node passed in, spinner icon is restored | ||
337 | 572 | // when requested (error parameter !== true). | ||
338 | 573 | var subscribers_list = setUpSubscribersList(this.root); | ||
339 | 574 | var node = subscribers_list._ensureActivityNode(); | ||
340 | 575 | var icon_node = node.one('img'); | ||
341 | 576 | subscribers_list._setActivityErrorIcon(node, false); | ||
342 | 577 | Y.Assert.areEqual('file:///@@/spinner', icon_node.get('src')); | ||
343 | 578 | }, | ||
344 | 579 | |||
345 | 580 | test_setActivityErrorIcon_error: function() { | ||
346 | 581 | // With non-activity node passed in, it fails. | ||
347 | 582 | var subscribers_list = setUpSubscribersList(this.root); | ||
348 | 583 | var node = Y.Node.create('<div />'); | ||
349 | 584 | subscribers_list._setActivityErrorIcon(node, true); | ||
350 | 585 | }, | ||
351 | 586 | |||
352 | 587 | test_setActivityText: function() { | ||
353 | 588 | // With activity node and text passed in, proper | ||
354 | 589 | // text is set in the activity text node. | ||
355 | 590 | var subscribers_list = setUpSubscribersList(this.root); | ||
356 | 591 | var node = subscribers_list._ensureActivityNode(); | ||
357 | 592 | subscribers_list._setActivityText(node, "Blah"); | ||
358 | 593 | // Single whitespace is prepended to better separate | ||
359 | 594 | // icon from the text. | ||
360 | 595 | Y.Assert.areEqual(" Blah", node.one('span').get('text')); | ||
361 | 596 | }, | ||
362 | 597 | |||
363 | 598 | test_setActivityText_error: function() { | ||
364 | 599 | // With non-activity node passed in, it fails. | ||
365 | 600 | var subscribers_list = setUpSubscribersList(this.root); | ||
366 | 601 | var node = Y.Node.create('<div />'); | ||
367 | 602 | subscribers_list._setActivityText(node, "Blah"); | ||
368 | 603 | }, | ||
369 | 604 | |||
370 | 605 | test_startActivity: function() { | ||
371 | 606 | // startActivity adds the spinner icon and sets the appropriate text. | ||
372 | 607 | var subscribers_list = setUpSubscribersList(this.root); | ||
373 | 608 | subscribers_list.startActivity("Blah"); | ||
374 | 609 | |||
375 | 610 | var node = subscribers_list._ensureActivityNode(); | ||
376 | 611 | |||
377 | 612 | Y.Assert.areEqual('file:///@@/spinner', node.one('img').get('src')); | ||
378 | 613 | Y.Assert.areEqual(" Blah", node.one('span').get('text')); | ||
379 | 614 | }, | ||
380 | 615 | |||
381 | 616 | test_startActivity_restores_state: function() { | ||
382 | 617 | // startActivity removes the no-subscribers indicator if present | ||
383 | 618 | // and restores the activity node icon. | ||
384 | 619 | var subscribers_list = setUpSubscribersList(this.root); | ||
385 | 620 | // Add a no-subscribers indication. | ||
386 | 621 | subscribers_list.resetNoSubscribers(); | ||
387 | 622 | // Create an activity node and set the error icon. | ||
388 | 623 | var node = subscribers_list._ensureActivityNode(); | ||
389 | 624 | subscribers_list._setActivityErrorIcon(node, true); | ||
390 | 625 | |||
391 | 626 | // Call startActivity() and see how it restores everything. | ||
392 | 627 | subscribers_list.startActivity(); | ||
393 | 628 | Y.Assert.areEqual('file:///@@/spinner', node.one('img').get('src')); | ||
394 | 629 | Y.Assert.isNull( | ||
395 | 630 | subscribers_list.container_node.one('.no-subscribers-indicator')); | ||
396 | 631 | }, | ||
397 | 632 | |||
398 | 633 | test_stopActivity: function() { | ||
399 | 634 | // stopActivity without parameters assumes a successful completion | ||
400 | 635 | // of the activity, so it removes the activity node and restores | ||
401 | 636 | // no-subscribers indication if needed. | ||
402 | 637 | var subscribers_list = setUpSubscribersList(this.root); | ||
403 | 638 | subscribers_list.startActivity("Blah"); | ||
404 | 639 | subscribers_list.stopActivity(); | ||
405 | 640 | |||
406 | 641 | var node = subscribers_list.container_node.one( | ||
407 | 642 | '.global-activity-indicator'); | ||
408 | 643 | Y.Assert.isNull(node); | ||
409 | 644 | // Indication of no subscribers is restored. | ||
410 | 645 | Y.Assert.isNotNull( | ||
411 | 646 | subscribers_list.container_node.one('.no-subscribers-indicator')); | ||
412 | 647 | }, | ||
413 | 648 | |||
414 | 649 | test_stopActivity_noop: function() { | ||
415 | 650 | // stopActivity without parameters assumes a successful completion | ||
416 | 651 | // of the activity. If no activity was in progress, nothing happens. | ||
417 | 652 | var subscribers_list = setUpSubscribersList(this.root); | ||
418 | 653 | subscribers_list.stopActivity(); | ||
419 | 654 | |||
420 | 655 | var node = subscribers_list.container_node.one( | ||
421 | 656 | '.global-activity-indicator'); | ||
422 | 657 | Y.Assert.isNull(node); | ||
423 | 658 | }, | ||
424 | 659 | |||
425 | 660 | test_stopActivity_with_error_message: function() { | ||
426 | 661 | // stopActivity with error message passed in creates an activity | ||
427 | 662 | // node even if activity was not in progress and sets the error | ||
428 | 663 | // icon and error text to the passed in message.. | ||
429 | 664 | var subscribers_list = setUpSubscribersList(this.root); | ||
430 | 665 | subscribers_list.stopActivity("Problem!"); | ||
431 | 666 | var node = subscribers_list._ensureActivityNode(); | ||
432 | 667 | Y.Assert.areEqual('file:///@@/error', node.one('img').get('src')); | ||
433 | 668 | Y.Assert.areEqual(" Problem!", node.one('span').get('text')); | ||
434 | 669 | |||
435 | 670 | // Indication of no subscribers is not added. | ||
436 | 671 | Y.Assert.isNull( | ||
437 | 672 | subscribers_list.container_node.one('.no-subscribers-indicator')); | ||
438 | 673 | } | ||
439 | 674 | })); | ||
440 | 675 | |||
441 | 676 | |||
442 | 677 | /** | ||
443 | 477 | * Function to get a list of all the sections present in the | 678 | * Function to get a list of all the sections present in the |
444 | 478 | * subscribers_list (a SubscribersList object). | 679 | * subscribers_list (a SubscribersList object). |
445 | 479 | */ | 680 | */ |
446 | @@ -815,7 +1016,6 @@ | |||
447 | 815 | })); | 1016 | })); |
448 | 816 | 1017 | ||
449 | 817 | 1018 | ||
450 | 818 | |||
451 | 819 | /** | 1019 | /** |
452 | 820 | * Test adding of subscribers and relevant helper methods. | 1020 | * Test adding of subscribers and relevant helper methods. |
453 | 821 | */ | 1021 | */ |
454 | @@ -1235,6 +1435,156 @@ | |||
455 | 1235 | })); | 1435 | })); |
456 | 1236 | 1436 | ||
457 | 1237 | 1437 | ||
458 | 1438 | /** | ||
459 | 1439 | * Test showing/stopping indication of activity for a subscriber. | ||
460 | 1440 | */ | ||
461 | 1441 | suite.add(new Y.Test.Case({ | ||
462 | 1442 | name: 'SubscribersList.indicateSubscriberActivity() and ' + | ||
463 | 1443 | 'SubscribersList.stopSubscriberActivity() test', | ||
464 | 1444 | |||
465 | 1445 | setUp: function() { | ||
466 | 1446 | this.root = Y.Node.create('<div />'); | ||
467 | 1447 | Y.one('body').appendChild(this.root); | ||
468 | 1448 | }, | ||
469 | 1449 | |||
470 | 1450 | tearDown: function() { | ||
471 | 1451 | this.root.remove(); | ||
472 | 1452 | }, | ||
473 | 1453 | |||
474 | 1454 | _should: { | ||
475 | 1455 | error: { | ||
476 | 1456 | test_indicateSubscriberActivity_error: | ||
477 | 1457 | new Error('Subscriber is not present in the subscribers list. ' + | ||
478 | 1458 | 'Please call addSubscriber(subscriber) first.'), | ||
479 | 1459 | test_stopSubscriberActivity_error: | ||
480 | 1460 | new Error('Subscriber is not present in the subscribers list. ' + | ||
481 | 1461 | 'Please call addSubscriber(subscriber) first.') | ||
482 | 1462 | } | ||
483 | 1463 | }, | ||
484 | 1464 | |||
485 | 1465 | test_indicateSubscriberActivity_error: function() { | ||
486 | 1466 | // When subscriber is not in the list, fails with an exception. | ||
487 | 1467 | var subscribers_list = setUpSubscribersList(this.root); | ||
488 | 1468 | var subscriber = { name: 'user' }; | ||
489 | 1469 | subscribers_list.indicateSubscriberActivity(subscriber); | ||
490 | 1470 | }, | ||
491 | 1471 | |||
492 | 1472 | test_indicateSubscriberActivity_node: function() { | ||
493 | 1473 | // Creates a node with spinner image in it. | ||
494 | 1474 | var subscribers_list = setUpSubscribersList(this.root); | ||
495 | 1475 | var subscriber = { name: 'user' }; | ||
496 | 1476 | var node = subscribers_list.addSubscriber(subscriber, 'Details'); | ||
497 | 1477 | subscribers_list.indicateSubscriberActivity(subscriber); | ||
498 | 1478 | |||
499 | 1479 | // This is the created node. | ||
500 | 1480 | var progress_node = node.one('.subscriber-activity-indicator'); | ||
501 | 1481 | Y.Assert.isNotNull(progress_node); | ||
502 | 1482 | var progress_icon = progress_node.one('img'); | ||
503 | 1483 | // We get an absolute URI, instead of the relative one which | ||
504 | 1484 | // the code sets. Since the test runs from the local file system, | ||
505 | 1485 | // that means "file://". | ||
506 | 1486 | Y.Assert.areEqual('file:///@@/spinner', progress_icon.get('src')); | ||
507 | 1487 | }, | ||
508 | 1488 | |||
509 | 1489 | test_indicateSubscriberActivity_actions_hidden: function() { | ||
510 | 1490 | // If there are any actions (in an actions node), they are | ||
511 | 1491 | // all hidden. | ||
512 | 1492 | var subscribers_list = setUpSubscribersList(this.root); | ||
513 | 1493 | var subscriber = { name: 'user' }; | ||
514 | 1494 | var node = subscribers_list.addSubscriber(subscriber, 'Details'); | ||
515 | 1495 | var actions_node = subscribers_list._getOrCreateActionsNode(node); | ||
516 | 1496 | |||
517 | 1497 | subscribers_list.indicateSubscriberActivity(subscriber); | ||
518 | 1498 | Y.Assert.areEqual('none', actions_node.getStyle('display')); | ||
519 | 1499 | }, | ||
520 | 1500 | |||
521 | 1501 | test_stopSubscriberActivity_error: function() { | ||
522 | 1502 | // When subscriber is not in the list, fails with an exception. | ||
523 | 1503 | var subscribers_list = setUpSubscribersList(this.root); | ||
524 | 1504 | var subscriber = { name: 'user' }; | ||
525 | 1505 | subscribers_list.stopSubscriberActivity(subscriber); | ||
526 | 1506 | }, | ||
527 | 1507 | |||
528 | 1508 | test_stopSubscriberActivity_noop: function() { | ||
529 | 1509 | // When there's no activity in progress, nothing happens. | ||
530 | 1510 | var subscribers_list = setUpSubscribersList(this.root); | ||
531 | 1511 | var subscriber = { name: 'user' }; | ||
532 | 1512 | var node = subscribers_list.addSubscriber(subscriber, 'Details'); | ||
533 | 1513 | subscribers_list.stopSubscriberActivity(subscriber); | ||
534 | 1514 | }, | ||
535 | 1515 | |||
536 | 1516 | test_stopSubscriberActivity_spinner_removed: function() { | ||
537 | 1517 | // When there is some activity in progress, spinner is removed. | ||
538 | 1518 | var subscribers_list = setUpSubscribersList(this.root); | ||
539 | 1519 | var subscriber = { name: 'user' }; | ||
540 | 1520 | var node = subscribers_list.addSubscriber(subscriber, 'Details'); | ||
541 | 1521 | // Create the spinner. | ||
542 | 1522 | subscribers_list.indicateSubscriberActivity(subscriber); | ||
543 | 1523 | // And remove it. | ||
544 | 1524 | subscribers_list.stopSubscriberActivity(subscriber); | ||
545 | 1525 | Y.Assert.isNull(node.one('.subscriber-activity-indicator')); | ||
546 | 1526 | }, | ||
547 | 1527 | |||
548 | 1528 | test_stopSubscriberActivity_actions_restored: function() { | ||
549 | 1529 | // When there is some activity in progress, spinner is removed. | ||
550 | 1530 | var subscribers_list = setUpSubscribersList(this.root); | ||
551 | 1531 | var subscriber = { name: 'user' }; | ||
552 | 1532 | var node = subscribers_list.addSubscriber(subscriber, 'Details'); | ||
553 | 1533 | var actions_node = subscribers_list._getOrCreateActionsNode(node); | ||
554 | 1534 | // Hide actions. | ||
555 | 1535 | actions_node.setStyle('display', 'none'); | ||
556 | 1536 | // And restore actions. | ||
557 | 1537 | subscribers_list.stopSubscriberActivity(subscriber); | ||
558 | 1538 | Y.Assert.areEqual('inline', actions_node.getStyle('display')); | ||
559 | 1539 | }, | ||
560 | 1540 | |||
561 | 1541 | test_stopSubscriberActivity_success_callback: function() { | ||
562 | 1542 | // When we are indicating successful/failed operation, | ||
563 | 1543 | // green_flash/red_flash animation is executed and callback | ||
564 | 1544 | // function is called when it ends. | ||
565 | 1545 | var subscribers_list = setUpSubscribersList(this.root); | ||
566 | 1546 | var subscriber = { name: 'user' }; | ||
567 | 1547 | subscribers_list.addSubscriber(subscriber, 'Details'); | ||
568 | 1548 | var callback_called = false; | ||
569 | 1549 | var callback = function() { | ||
570 | 1550 | callback_called = true; | ||
571 | 1551 | }; | ||
572 | 1552 | |||
573 | 1553 | subscribers_list.stopSubscriberActivity( | ||
574 | 1554 | subscriber, true, callback); | ||
575 | 1555 | // Callback is not called immediatelly. | ||
576 | 1556 | Y.Assert.isFalse(callback_called); | ||
577 | 1557 | this.wait(function() { | ||
578 | 1558 | // But after waiting for animation to complete, | ||
579 | 1559 | // callback is called. | ||
580 | 1560 | Y.Assert.isTrue(callback_called); | ||
581 | 1561 | }, 1100); | ||
582 | 1562 | }, | ||
583 | 1563 | |||
584 | 1564 | test_stopSubscriberActivity_no_callback: function() { | ||
585 | 1565 | // When we pass the callback in, but success is neither | ||
586 | 1566 | // 'true' nor 'false', callback is not called. | ||
587 | 1567 | var subscribers_list = setUpSubscribersList(this.root); | ||
588 | 1568 | var subscriber = { name: 'user' }; | ||
589 | 1569 | subscribers_list.addSubscriber(subscriber, 'Details'); | ||
590 | 1570 | var callback_called = false; | ||
591 | 1571 | var callback = function() { | ||
592 | 1572 | callback_called = true; | ||
593 | 1573 | }; | ||
594 | 1574 | |||
595 | 1575 | subscribers_list.stopSubscriberActivity( | ||
596 | 1576 | subscriber, "no-callback", callback); | ||
597 | 1577 | // Callback is not called. | ||
598 | 1578 | Y.Assert.isFalse(callback_called); | ||
599 | 1579 | this.wait(function() { | ||
600 | 1580 | // Nor is it called after any potential animations complete. | ||
601 | 1581 | Y.Assert.isFalse(callback_called); | ||
602 | 1582 | }, 1100); | ||
603 | 1583 | } | ||
604 | 1584 | |||
605 | 1585 | })); | ||
606 | 1586 | |||
607 | 1587 | |||
608 | 1238 | var handle_complete = function(data) { | 1588 | var handle_complete = function(data) { |
609 | 1239 | window.status = '::::' + JSON.stringify(data); | 1589 | window.status = '::::' + JSON.stringify(data); |
610 | 1240 | }; | 1590 | }; |