Merge lp:~rharding/launchpad/bug_yui35_three into lp:launchpad

Proposed by Richard Harding on 2012-06-28
Status: Merged
Approved by: j.c.sackett on 2012-06-28
Approved revision: no longer in the source branch.
Merged at revision: 15521
Proposed branch: lp:~rharding/launchpad/bug_yui35_three
Merge into: lp:launchpad
Diff against target: 7140 lines (+3466/-3571)
8 files modified
lib/lp/app/javascript/testing/helpers.js (+34/-0)
lib/lp/bugs/javascript/tests/test_bug_subscription_portlet.html (+3/-1)
lib/lp/bugs/javascript/tests/test_bug_subscription_portlet.js (+628/-680)
lib/lp/bugs/javascript/tests/test_subscription.html (+3/-2)
lib/lp/bugs/javascript/tests/test_subscription.js (+2791/-2832)
lib/lp/registry/javascript/tests/test_structural_subscription.html (+2/-0)
lib/lp/registry/javascript/tests/test_structural_subscription.js (+4/-55)
standard_test_template.js (+1/-1)
To merge this branch: bzr merge lp:~rharding/launchpad/bug_yui35_three
Reviewer Review Type Date Requested Status
j.c.sackett (community) 2012-06-28 Approve on 2012-06-28
Review via email: mp+112590@code.launchpad.net

Commit Message

Update more bug JS tests for yui 3.5. Pull LPClient into the testing.helpers.

Description of the Change

= Summary =

This branch helps update several of the test suites in the bug JS to work in YUI3.5 and to use our common test runner.

== Implementation Notes ==

This fixes an outstand XXX bug by moving LPClient into the newish testing.helpers module. Three tests are updated that were using it.

Updating the tests is mostly just reorganizing them to add an actual module. There are some smaller bugs fixed such as the quotes around selectors using attributes.

== Tests ==

./bin/test -x -cvv --layer=YUITestLayer

== Lint ==

Updated for lint since we indented a lot of code.

== LoC Qualification ==

It's a negative LoC change.

To post a comment you must log in.
j.c.sackett (jcsackett) wrote :

This looks fine to me.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/app/javascript/testing/helpers.js'
2--- lib/lp/app/javascript/testing/helpers.js 2012-06-27 17:29:08 +0000
3+++ lib/lp/app/javascript/testing/helpers.js 2012-06-28 15:32:36 +0000
4@@ -19,6 +19,40 @@
5 };
6
7
8+ ns.LPClient = function () {
9+ if (!(this instanceof ns.LPClient)) {
10+ throw new Error("Constructor called as a function");
11+ }
12+ this.received = [];
13+ // We create new functions every time because we allow them to be
14+ // configured.
15+ this.named_post = function(url, func, config) {
16+ this._call('named_post', config, arguments);
17+ };
18+ this.patch = function(bug_filter, data, config) {
19+ this._call('patch', config, arguments);
20+ };
21+ }
22+ ns.LPClient.prototype._call = function(name, config, args) {
23+ this.received.push(
24+ [name, Array.prototype.slice.call(args)]);
25+ if (!Y.Lang.isValue(args.callee.args)) {
26+ throw new Error("Set call_args on "+name);
27+ }
28+ var do_action = function () {
29+ if (Y.Lang.isValue(args.callee.fail) && args.callee.fail) {
30+ config.on.failure.apply(undefined, args.callee.args);
31+ } else {
32+ config.on.success.apply(undefined, args.callee.args);
33+ }
34+ };
35+ if (Y.Lang.isValue(args.callee.halt) && args.callee.halt) {
36+ args.callee.resume = do_action;
37+ } else {
38+ do_action();
39+ }
40+ };
41+
42 }, '0.1', {
43 'requires': [ 'history']
44 });
45
46=== modified file 'lib/lp/bugs/javascript/tests/test_bug_subscription_portlet.html'
47--- lib/lp/bugs/javascript/tests/test_bug_subscription_portlet.html 2012-03-14 04:41:36 +0000
48+++ lib/lp/bugs/javascript/tests/test_bug_subscription_portlet.html 2012-06-28 15:32:36 +0000
49@@ -21,6 +21,8 @@
50
51 <script type="text/javascript"
52 src="../../../../../build/js/lp/app/testing/testrunner.js"></script>
53+ <script type="text/javascript"
54+ src="../../../../../build/js/lp/app/testing/helpers.js"></script>
55
56 <link rel="stylesheet" href="../../../app/javascript/testing/test.css" />
57
58@@ -58,7 +60,7 @@
59 <body class="yui3-skin-sam">
60 <ul id="suites">
61 <!-- <li>lp.large_indicator.test</li> -->
62- <li>lp.bug_subscription_portlet.test</li>
63+ <li>lp.bugs.subscription_portlet.test</li>
64 </ul>
65 </body>
66 </html>
67
68=== modified file 'lib/lp/bugs/javascript/tests/test_bug_subscription_portlet.js'
69--- lib/lp/bugs/javascript/tests/test_bug_subscription_portlet.js 2011-09-27 16:14:09 +0000
70+++ lib/lp/bugs/javascript/tests/test_bug_subscription_portlet.js 2012-06-28 15:32:36 +0000
71@@ -1,682 +1,630 @@
72-YUI({
73- base: '../../../../canonical/launchpad/icing/yui/',
74- filter: 'raw', combine: false, fetchCSS: false
75- }).use('test', 'console', 'lp.bugs.bugtask_index.portlets.subscription',
76- 'node-event-simulate', function(Y) {
77-
78-var suite = new Y.Test.Suite(
79- "lp.bugs.bugtask_index.portlets.subscription Tests");
80-var module = Y.lp.bugs.bugtask_index.portlets.subscription;
81-
82-/**
83- * XXX gary 2011-05-26 bug 793579
84- * LPClient is copied three times (see also test_structural_subscription.js
85- * and test_subscription.js). It should be pushed to a shared test module.
86- */
87-function LPClient(){
88- if (!(this instanceof LPClient)) {
89- throw new Error("Constructor called as a function");
90- }
91- this.received = [];
92- // We create new functions every time because we allow them to be
93- // configured.
94- this.named_post = function(url, func, config) {
95- this._call('named_post', config, arguments);
96- };
97- this.patch = function(bug_filter, data, config) {
98- this._call('patch', config, arguments);
99- };
100-}
101-LPClient.prototype._call = function(name, config, args) {
102- this.received.push(
103- [name, Array.prototype.slice.call(args)]);
104- if (!Y.Lang.isValue(args.callee.args)) {
105- throw new Error("Set call_args on "+name);
106- }
107- var do_action = function () {
108- if (Y.Lang.isValue(args.callee.fail) && args.callee.fail) {
109- config.on.failure.apply(undefined, args.callee.args);
110- } else {
111- config.on.success.apply(undefined, args.callee.args);
112- }
113- };
114- if (Y.Lang.isValue(args.callee.halt) && args.callee.halt) {
115- args.callee.resume = do_action;
116- } else {
117- do_action();
118- }
119-};
120-
121-function make_status_node() {
122- var status = Y.Node.create('<div/>')
123- .set('id', 'current_user_subscription')
124- .append(Y.Node.create('<span/>'));
125- Y.one('body').appendChild(status);
126- return status;
127-}
128-
129-function add_link_to_status_node() {
130- var status = Y.one('#current_user_subscription');
131- status.append(
132- Y.Node.create('<a/>')
133- .addClass('menu-link-subscription')
134- .addClass('sprite')
135- .addClass('modify')
136- .addClass('edit')
137- .set('href', 'http://example.com')
138- .set('text', 'Example text')
139- );
140-}
141-
142-function make_mute_node() {
143- var parent = Y.Node.create('<div/>')
144- .set('id', 'mute-link-container')
145- .append(Y.Node.create('<a/>')
146- .addClass('menu-link-mute_subscription')
147- .addClass(module.UNMUTED_CLASS)
148- .set('text', 'This is a mute link')
149- .set('href', 'http://www.example.com/+mute')
150- );
151- Y.one('body').appendChild(parent);
152- return parent;
153-}
154-
155-function setup_LP(bug_link) {
156- window.LP = {
157- cache: {
158- notifications_text: {
159- not_only_other_subscription: 'You are',
160- only_other_subscription:
161- 'You have subscriptions that may cause you to receive ' +
162- 'notifications, but you are',
163- direct_all: 'subscribed to all notifications for this bug.',
164- direct_metadata:
165- 'subscribed to all notifications except comments for ' +
166- 'this bug.',
167- direct_lifecycle:
168- 'subscribed to notifications when this bug is closed ' +
169- 'or reopened.',
170- not_direct:
171- "not directly subscribed to this bug's notifications.",
172- muted:
173- 'Your personal email notifications from this bug ' +
174- 'are muted.'
175+/* Copyright (c) 2012 Canonical Ltd. All rights reserved. */
176+
177+YUI.add('lp.bugs.subscription_portlet.test', function (Y) {
178+ var module = Y.lp.bugs.bugtask_index.portlets.subscription;
179+
180+ // Notification levels.
181+ var DISCUSSION = 'Discussion';
182+ var DETAILS = 'Details';
183+ var LIFECYCLE = 'Lifecycle';
184+
185+ function make_status_node() {
186+ var status = Y.Node.create('<div/>')
187+ .set('id', 'current_user_subscription')
188+ .append(Y.Node.create('<span/>'));
189+ Y.one('body').appendChild(status);
190+ return status;
191+ }
192+
193+ function add_link_to_status_node() {
194+ var status = Y.one('#current_user_subscription');
195+ status.append(
196+ Y.Node.create('<a/>')
197+ .addClass('menu-link-subscription')
198+ .addClass('sprite')
199+ .addClass('modify')
200+ .addClass('edit')
201+ .set('href', 'http://example.com')
202+ .set('text', 'Example text')
203+ );
204+ }
205+
206+ function make_mute_node() {
207+ var parent = Y.Node.create('<div/>')
208+ .set('id', 'mute-link-container')
209+ .append(Y.Node.create('<a/>')
210+ .addClass('menu-link-mute_subscription')
211+ .addClass(module.UNMUTED_CLASS)
212+ .set('text', 'This is a mute link')
213+ .set('href', 'http://www.example.com/+mute')
214+ );
215+ Y.one('body').appendChild(parent);
216+ return parent;
217+ }
218+
219+ function setup_LP(bug_link) {
220+ window.LP = {
221+ cache: {
222+ notifications_text: {
223+ not_only_other_subscription: 'You are',
224+ only_other_subscription:
225+ 'You have subscriptions that may cause you to receive ' +
226+ 'notifications, but you are',
227+ direct_all: 'subscribed to all notifications for this bug.',
228+ direct_metadata:
229+ 'subscribed to all notifications except comments for ' +
230+ 'this bug.',
231+ direct_lifecycle:
232+ 'subscribed to notifications when this bug is closed ' +
233+ 'or reopened.',
234+ not_direct:
235+ "not directly subscribed to this bug's notifications.",
236+ muted:
237+ 'Your personal email notifications from this bug ' +
238+ 'are muted.'
239+ },
240+ context: {web_link: 'http://example.com', bug_link: bug_link},
241+ other_subscription_notifications: false
242 },
243- context: {web_link: 'http://example.com', bug_link: bug_link},
244- other_subscription_notifications: false
245- },
246- links: {me: '~tweedledee'}
247- };
248-}
249-
250-// Notification levels.
251-var DISCUSSION = 'Discussion';
252-var DETAILS = 'Details';
253-var LIFECYCLE = 'Lifecycle';
254-
255-function make_subscription(level) {
256- if (Y.Lang.isUndefined(level)) {
257- level = DISCUSSION;
258- }
259- window.LP.cache.subscription = {'bug_notification_level': level};
260-}
261-
262-/**
263- * Test update_subscription_status.
264- */
265-suite.add(new Y.Test.Case({
266- name: 'Test update_subscription_status',
267-
268- setUp: function() {
269- this.status_node = make_status_node();
270- this.mute_node = make_mute_node();
271- add_link_to_status_node();
272- setup_LP();
273- },
274-
275- tearDown: function() {
276- this.status_node.remove();
277- this.mute_node.remove();
278- delete window.LP;
279- },
280-
281- test_can_create_link: function() {
282- this.status_node.one('a').remove();
283- make_subscription();
284- Y.Assert.isTrue(Y.Lang.isNull(this.status_node.one('a')));
285- module.update_subscription_status();
286- var link = this.status_node.one('a');
287- Y.Assert.isTrue(Y.Lang.isValue(link));
288- Y.Assert.isTrue(link.hasClass('menu-link-subscription'));
289- Y.Assert.isTrue(link.hasClass('sprite'));
290- Y.Assert.isTrue(link.hasClass('modify'));
291- Y.Assert.isTrue(link.hasClass('edit'));
292- Y.Assert.isTrue(link.hasClass('js-action'));
293- Y.Assert.areEqual(
294- // window.LP.context.web_link + '/+subscribe',
295- 'http://example.com/+subscribe',
296- link.get('href'));
297- },
298-
299- test_no_subscription: function() {
300- module.update_subscription_status();
301- Y.Assert.areEqual(
302- 'You are',
303- this.status_node.one('span').get('text'));
304- Y.Assert.areEqual(
305- "not directly subscribed to this bug's notifications.",
306- this.status_node.one('a').get('text'));
307- },
308-
309- test_other_subscription: function() {
310- window.LP.cache.other_subscription_notifications = true;
311- module.update_subscription_status();
312- Y.Assert.areEqual(
313- 'You have subscriptions that may cause you to receive ' +
314- 'notifications, but you are',
315- this.status_node.one('span').get('text'));
316- Y.Assert.areEqual(
317- "not directly subscribed to this bug's notifications.",
318- this.status_node.one('a').get('text'));
319- },
320-
321- test_full_subscription: function() {
322- make_subscription(DISCUSSION);
323- module.update_subscription_status();
324- Y.Assert.areEqual(
325- 'You are',
326- this.status_node.one('span').get('text'));
327- Y.Assert.areEqual(
328- "subscribed to all notifications for this bug.",
329- this.status_node.one('a').get('text'));
330- },
331-
332- test_metadata_subscription: function() {
333- make_subscription(DETAILS);
334- module.update_subscription_status();
335- Y.Assert.areEqual(
336- 'You are',
337- this.status_node.one('span').get('text'));
338- Y.Assert.areEqual(
339- 'subscribed to all notifications except comments for this bug.',
340- this.status_node.one('a').get('text'));
341- },
342-
343- test_lifecycle_subscription: function() {
344- make_subscription(LIFECYCLE);
345- module.update_subscription_status();
346- Y.Assert.areEqual(
347- 'You are',
348- this.status_node.one('span').get('text'));
349- Y.Assert.areEqual(
350- 'subscribed to notifications when this bug is closed or ' +
351- 'reopened.',
352- this.status_node.one('a').get('text'));
353- },
354-
355- test_direct_subscription_has_precedence: function() {
356- window.LP.cache.other_subscription_notifications = true;
357- make_subscription(LIFECYCLE);
358- module.update_subscription_status();
359- Y.Assert.areEqual(
360- 'You are',
361- this.status_node.one('span').get('text'));
362- Y.Assert.areEqual(
363- 'subscribed to notifications when this bug is closed or ' +
364- 'reopened.',
365- this.status_node.one('a').get('text'));
366- },
367-
368- test_muted_subscription: function() {
369- make_subscription(LIFECYCLE);
370- this.mute_node.one('a').replaceClass(
371- module.UNMUTED_CLASS, module.MUTED_CLASS);
372- Y.Assert.isTrue(Y.Lang.isValue(this.status_node.one('a')));
373- module.update_subscription_status();
374- Y.Assert.areEqual(
375- 'Your personal email notifications from this bug are muted.',
376- this.status_node.one('span').get('text'));
377- Y.Assert.isFalse(Y.Lang.isValue(this.status_node.one('a')));
378- }
379-
380-}));
381-
382-/**
383- * Test setup_mute_link_handlers.
384- */
385-suite.add(new Y.Test.Case({
386- name: 'Test setup_mute_link_handlers',
387-
388- setUp: function() {
389- this.status_node = make_status_node();
390- this.mute_node = make_mute_node();
391- this.link = this.mute_node.one('a');
392- add_link_to_status_node();
393- this.bug_link = 'http://example.net/firefox/bug/1';
394- setup_LP(this.bug_link);
395- make_subscription(DISCUSSION);
396- module.update_subscription_status();
397- module.setup_mute_link_handlers();
398- module._lp_client = new LPClient();
399- module._lp_client.named_post.args = [];
400- },
401-
402- tearDown: function() {
403- this.status_node.remove();
404- this.mute_node.remove();
405- delete window.LP;
406- delete module._lp_client;
407- var error_overlay = Y.one('.yui3-lazr-formoverlay');
408- if (Y.Lang.isValue(error_overlay)) {
409- error_overlay.remove();
410- }
411- },
412-
413- test_mute_success: function() {
414- this.link.simulate('click');
415- Y.Assert.areEqual(1, module._lp_client.received.length);
416- Y.Assert.areEqual('named_post', module._lp_client.received[0][0]);
417- var args = module._lp_client.received[0][1];
418- Y.Assert.areEqual(this.bug_link, args[0]);
419- Y.Assert.areEqual('mute', args[1]);
420- Y.ObjectAssert.areEqual({}, args[2].parameters);
421- Y.Assert.isTrue(this.link.hasClass(module.MUTED_CLASS));
422- Y.Assert.isFalse(this.link.hasClass('spinner'));
423- Y.Assert.isFalse(this.link.hasClass(module.UNMUTED_CLASS));
424- Y.Assert.areEqual(
425- 'Your personal email notifications from this bug are muted.',
426- this.status_node.one('span').get('text'));
427- },
428-
429- test_unmute_success: function() {
430- this.link.replaceClass(module.UNMUTED_CLASS, module.MUTED_CLASS);
431- this.link.simulate('click');
432- Y.Assert.areEqual(1, module._lp_client.received.length);
433- Y.Assert.areEqual('named_post', module._lp_client.received[0][0]);
434- var args = module._lp_client.received[0][1];
435- Y.Assert.areEqual(this.bug_link, args[0]);
436- Y.Assert.areEqual('unmute', args[1]);
437- Y.ObjectAssert.areEqual({}, args[2].parameters);
438- Y.Assert.isTrue(this.link.hasClass(module.UNMUTED_CLASS));
439- Y.Assert.isFalse(this.link.hasClass('spinner'));
440- Y.Assert.isFalse(this.link.hasClass(module.MUTED_CLASS));
441- Y.Assert.areEqual(
442- 'You are',
443- this.status_node.one('span').get('text'));
444- Y.Assert.areEqual(
445- "subscribed to all notifications for this bug.",
446- this.status_node.one('a').get('text'));
447- },
448-
449- test_mute_spinner_and_failure: function() {
450- module._lp_client.named_post.fail = true;
451- module._lp_client.named_post.args = [
452- true,
453- {status: 400, responseText: 'Rutebegas!'}];
454- module._lp_client.named_post.halt = true;
455- this.link.simulate('click');
456- // Right now, this is as if we are waiting for the server to
457- // reply. The link is spinning.
458- Y.Assert.isTrue(this.link.hasClass('spinner'));
459- Y.Assert.isFalse(this.link.hasClass(module.UNMUTED_CLASS));
460- // Now the server replies with an error.
461- module._lp_client.named_post.resume();
462- // We have no spinner.
463- Y.Assert.isTrue(this.link.hasClass(module.UNMUTED_CLASS));
464- Y.Assert.isFalse(this.link.hasClass('spinner'));
465- // The page has rendered the error overlay.
466- var error_box = Y.one('.yui3-lazr-formoverlay-errors');
467- Y.Assert.isTrue(Y.Lang.isValue(error_box));
468- }
469-
470-}));
471-
472-/**
473- * Test setup_subscription_link_handlers.
474- */
475-suite.add(new Y.Test.Case({
476- name: 'Test setup_subscription_link_handlers',
477-
478- setUp: function() {
479- this.status_node = make_status_node();
480- this.mute_node = make_mute_node();
481- this.mute_link = this.mute_node.one('a');
482- this.bug_link = 'http://example.net/firefox/bug/1';
483- setup_LP(this.bug_link);
484- module._lp_client = new LPClient();
485- },
486-
487- tearDown: function() {
488- this.status_node.remove();
489- this.mute_node.remove();
490- delete window.LP;
491- delete module._lp_client;
492- var error_overlay = Y.one('.yui3-lazr-formoverlay');
493- if (Y.Lang.isValue(error_overlay)) {
494- error_overlay.remove();
495- }
496- var pretty_overlay = Y.one('.pretty-overlay-window');
497- if (Y.Lang.isValue(pretty_overlay)) {
498- pretty_overlay.remove();
499- }
500- },
501-
502- init: function(sub_level, has_other_subs, response) {
503- if (Y.Lang.isValue(sub_level)) {
504- make_subscription(sub_level);
505- } else if (!has_other_subs) {
506- this.mute_node.addClass('hidden');
507- }
508- window.LP.cache.other_subscription_notifications = has_other_subs;
509- var args = [];
510- if (Y.Lang.isValue(response)) {
511- args = [
512- {getAttrs: function () {
513- return {bug_notification_level: response};
514- }}];
515- }
516- module._lp_client.named_post.args = args;
517- module.update_subscription_status();
518- module.setup_mute_link_handlers();
519- },
520-
521- link: function() {
522- return this.status_node.one('a');
523- },
524-
525- test_overlay_add_subscription: function() {
526- this.init(null, false);
527- this.link().simulate('click');
528- var overlay = Y.one('.pretty-overlay-window');
529- // We have the "Add" title, not the "Change" title.
530- Y.Assert.areEqual('Add a mail subscription for this bug',
531- overlay.one('h2').get('text'));
532- // There is no status.
533- Y.Assert.isFalse(Y.Lang.isValue(overlay.one('.subscription-status')));
534- // The action links are visible.
535- var action_links = overlay.one('.subscription-actions');
536- Y.Assert.isFalse(action_links.one(
537- '.Discussion a').hasClass('hidden'));
538- Y.Assert.isFalse(action_links.one('.Details a').hasClass('hidden'));
539- Y.Assert.isFalse(action_links.one('.Lifecycle a').hasClass('hidden'));
540- // The "Remove" link is not present.
541- Y.Assert.isFalse(Y.Lang.isValue(overlay.one('a.remove')));
542- // The link is spinning.
543- Y.Assert.isTrue(this.link().hasClass('spinner'));
544- Y.Assert.isFalse(this.link().hasClass('edit'));
545- // When we click on the "x," the overlay goes away, and the spinner
546- // is gone.
547- overlay.one('a.close-button').simulate('click');
548- Y.Assert.isFalse(Y.Lang.isValue(Y.one('.pretty-overlay-window')));
549- Y.Assert.isFalse(this.link().hasClass('spinner'));
550- Y.Assert.isTrue(this.link().hasClass('edit'));
551- },
552-
553- test_overlay_with_discussion_subscription: function() {
554- // Starting with a DISCUSSION subscription, we get a "change" overlay.
555- this.init(DISCUSSION, false);
556- this.link().simulate('click');
557- var overlay = Y.one('.pretty-overlay-window');
558- // We have the "Change" title, not the "Add" title.
559- Y.Assert.areEqual('Change your mail subscription for this bug',
560- overlay.one('h2').get('text'));
561- // We show the Discussion status.
562- var status = overlay.one('.subscription-status');
563- Y.Assert.isFalse(overlay.one('span.Discussion').hasClass('hidden'));
564- Y.Assert.isTrue(overlay.one('span.Details').hasClass('hidden'));
565- Y.Assert.isTrue(overlay.one('span.Lifecycle').hasClass('hidden'));
566- // The action links are visible except for Discussion.
567- var action_links = overlay.one('.subscription-actions');
568- Y.Assert.isTrue(action_links.one('.Discussion a').hasClass('hidden'));
569- Y.Assert.isFalse(action_links.one('.Details a').hasClass('hidden'));
570- Y.Assert.isFalse(action_links.one('.Lifecycle a').hasClass('hidden'));
571- // We have a remove link.
572- Y.Assert.isTrue(Y.Lang.isValue(overlay.one('a.remove')));
573- Y.Assert.areEqual(
574- 'Remove your direct subscription',
575- overlay.one('div.subscription-actions+div').get('text'));
576- },
577-
578- test_overlay_with_details_subscription: function() {
579- // Test overlay with existing DETAILS subscription.
580- this.init(DETAILS, true);
581- this.link().simulate('click');
582- var overlay = Y.one('.pretty-overlay-window');
583- // We show the Lifecycle status.
584- var status = overlay.one('.subscription-status');
585- Y.Assert.isTrue(overlay.one('span.Discussion').hasClass('hidden'));
586- Y.Assert.isFalse(overlay.one('span.Details').hasClass('hidden'));
587- Y.Assert.isTrue(overlay.one('span.Lifecycle').hasClass('hidden'));
588- // The action links are visible except for Lifecycle.
589- var action_links = overlay.one('.subscription-actions');
590- Y.Assert.isFalse(action_links.one(
591- '.Discussion a').hasClass('hidden'));
592- Y.Assert.isTrue(action_links.one('.Details a').hasClass('hidden'));
593- Y.Assert.isFalse(action_links.one('.Lifecycle a').hasClass('hidden'));
594- // We have a remove link.
595- Y.Assert.isTrue(Y.Lang.isValue(overlay.one('a.remove')));
596- Y.Assert.areEqual(
597- 'Remove your direct subscription',
598- overlay.one('div.subscription-actions+div').get('text'));
599- },
600-
601- test_overlay_with_lifecycle_subscription: function() {
602- // Test overlay with existing LIFECYCLE subscription.
603- this.init(LIFECYCLE, true);
604- this.link().simulate('click');
605- var overlay = Y.one('.pretty-overlay-window');
606- // We show the Lifecycle status.
607- var status = overlay.one('.subscription-status');
608- Y.Assert.isTrue(overlay.one('span.Discussion').hasClass('hidden'));
609- Y.Assert.isTrue(overlay.one('span.Details').hasClass('hidden'));
610- Y.Assert.isFalse(overlay.one('span.Lifecycle').hasClass('hidden'));
611- // The action links are visible except for Lifecycle.
612- var action_links = overlay.one('.subscription-actions');
613- Y.Assert.isFalse(action_links.one(
614- '.Discussion a').hasClass('hidden'));
615- Y.Assert.isFalse(action_links.one('.Details a').hasClass('hidden'));
616- Y.Assert.isTrue(action_links.one('.Lifecycle a').hasClass('hidden'));
617- // We have a remove link.
618- Y.Assert.isTrue(Y.Lang.isValue(overlay.one('a.remove')));
619- Y.Assert.areEqual(
620- 'Remove your direct subscription',
621- overlay.one('div.subscription-actions+div').get('text'));
622- },
623-
624- test_subscribe_discussion: function() {
625- this.init(null, false, DISCUSSION);
626- // The mute node is hidden initially.
627- Y.Assert.isTrue(this.mute_node.hasClass('hidden'));
628- this.link().simulate('click');
629- var overlay = Y.one('.pretty-overlay-window');
630- overlay.one('.subscription-actions .Discussion a').simulate('click');
631- // The overlay has been destroyed, and the spinner is gone.
632- Y.Assert.isFalse(Y.Lang.isValue(Y.one('.pretty-overlay-window')));
633- Y.Assert.isFalse(this.link().hasClass('spinner'));
634- // We got an appropriate call to the webservice.
635- Y.Assert.areEqual('named_post', module._lp_client.received[0][0]);
636- var args = module._lp_client.received[0][1];
637- Y.Assert.areEqual(this.bug_link, args[0]);
638- Y.Assert.areEqual('subscribe', args[1]);
639- Y.ObjectAssert.areEqual({person: '~tweedledee', level: DISCUSSION},
640- args[2].parameters);
641- // There is now a subscription object in the cache.
642- Y.Assert.isTrue(Y.Lang.isValue(window.LP.cache.subscription));
643- // The mute node is not hidden.
644- Y.Assert.isFalse(this.mute_node.hasClass('hidden'));
645- // The link has updated its text.
646- Y.Assert.areEqual(
647- "You are subscribed to all notifications for this bug.",
648- this.status_node.get('text'));
649- },
650-
651- test_subscribe_details: function() {
652- this.init(null, false, DETAILS);
653- this.link().simulate('click');
654- var overlay = Y.one('.pretty-overlay-window');
655- overlay.one('.subscription-actions .Details a').simulate('click');
656- // We got an appropriate call to the webservice.
657- Y.Assert.areEqual('named_post', module._lp_client.received[0][0]);
658- var args = module._lp_client.received[0][1];
659- Y.Assert.areEqual(this.bug_link, args[0]);
660- Y.Assert.areEqual('subscribe', args[1]);
661- Y.ObjectAssert.areEqual({person: '~tweedledee', level: DETAILS},
662- args[2].parameters);
663- // The link has updated its text.
664- Y.Assert.areEqual(
665- "You are subscribed to all notifications except comments for "+
666- "this bug.",
667- this.status_node.get('text'));
668- },
669-
670- test_subscribe_lifecycle: function() {
671- this.init(null, false, LIFECYCLE);
672- this.link().simulate('click');
673- var overlay = Y.one('.pretty-overlay-window');
674- overlay.one('.subscription-actions .Lifecycle a').simulate('click');
675- // We got an appropriate call to the webservice.
676- Y.Assert.areEqual('named_post', module._lp_client.received[0][0]);
677- var args = module._lp_client.received[0][1];
678- Y.Assert.areEqual(this.bug_link, args[0]);
679- Y.Assert.areEqual('subscribe', args[1]);
680- Y.ObjectAssert.areEqual({person: '~tweedledee', level: LIFECYCLE},
681- args[2].parameters);
682- // The link has updated its text.
683- Y.Assert.areEqual(
684- "You are subscribed to notifications when this bug is closed "+
685- "or reopened.",
686- this.status_node.get('text'));
687- },
688-
689- test_unsubscribe: function() {
690- this.init(LIFECYCLE, false);
691- this.link().simulate('click');
692- var overlay = Y.one('.pretty-overlay-window');
693- overlay.one('a.remove').simulate('click');
694- // There is no warning.
695- var warning = overlay.one('.private-bug-warning');
696- Y.Assert.isNull(warning);
697- // We got an appropriate call to the webservice.
698- Y.Assert.areEqual('named_post', module._lp_client.received[0][0]);
699- var args = module._lp_client.received[0][1];
700- Y.Assert.areEqual(this.bug_link, args[0]);
701- Y.Assert.areEqual('unsubscribe', args[1]);
702- Y.ObjectAssert.areEqual({}, args[2].parameters);
703- // The overlay has been destroyed, and the spinner is gone.
704- Y.Assert.isFalse(Y.Lang.isValue(Y.one('.pretty-overlay-window')));
705- Y.Assert.isFalse(this.link().hasClass('spinner'));
706- // The mute node is hidden.
707- Y.Assert.isTrue(this.mute_node.hasClass('hidden'));
708- // The link has updated its text.
709- Y.Assert.areEqual(
710- "You are not directly subscribed to this bug's notifications.",
711- this.status_node.get('text'));
712- },
713-
714- test_unsubscribe_from_private_bug: function() {
715- this.init(LIFECYCLE, false);
716- window.LP.cache.bug_is_private = true;
717- this.link().simulate('click');
718- var overlay = Y.one('.pretty-overlay-window');
719- overlay.one('a.remove').simulate('click');
720- // The overlay now has a warning.
721- var warning = overlay.one('.private-bug-warning');
722- Y.Assert.isTrue(Y.Lang.isValue(warning));
723- // Find and click the OK button.
724- var ok_btn = warning.one('.ok-btn');
725- ok_btn.simulate('click');
726- // We got an appropriate call to the webservice.
727- Y.Assert.areEqual('named_post', module._lp_client.received[0][0]);
728- var args = module._lp_client.received[0][1];
729- Y.Assert.areEqual(this.bug_link, args[0]);
730- Y.Assert.areEqual('unsubscribe', args[1]);
731- Y.ObjectAssert.areEqual({}, args[2].parameters);
732- // The overlay has been destroyed, and the spinner is gone.
733- Y.Assert.isFalse(Y.Lang.isValue(Y.one('.pretty-overlay-window')));
734- Y.Assert.isFalse(this.link().hasClass('spinner'));
735- },
736-
737- test_unsubscribe_from_private_bug_cancel: function() {
738- this.init(LIFECYCLE, false);
739- window.LP.cache.bug_is_private = true;
740- this.link().simulate('click');
741- var overlay = Y.one('.pretty-overlay-window');
742- overlay.one('a.remove').simulate('click');
743- // The overlay now has a warning.
744- var warning = overlay.one('.private-bug-warning');
745- Y.Assert.isTrue(Y.Lang.isValue(warning));
746- // Find and click the Cancel button.
747- var cancel_btn = warning.one('.cancel-btn');
748- cancel_btn.simulate('click');
749- // The warning is gone.
750- warning = overlay.one('.private-bug-warning');
751- Y.Assert.isNull(warning);
752- },
753-
754- test_unsubscribe_with_other: function() {
755- this.init(LIFECYCLE, true);
756- this.link().simulate('click');
757- var overlay = Y.one('.pretty-overlay-window');
758- overlay.one('a.remove').simulate('click');
759- // We got an appropriate call to the webservice.
760- Y.Assert.areEqual('named_post', module._lp_client.received[0][0]);
761- var args = module._lp_client.received[0][1];
762- Y.Assert.areEqual(this.bug_link, args[0]);
763- Y.Assert.areEqual('unsubscribe', args[1]);
764- Y.ObjectAssert.areEqual({}, args[2].parameters);
765- // The overlay has been destroyed, and the spinner is gone.
766- Y.Assert.isFalse(Y.Lang.isValue(Y.one('.pretty-overlay-window')));
767- Y.Assert.isFalse(this.link().hasClass('spinner'));
768- // The mute node is not hidden.
769- Y.Assert.isFalse(this.mute_node.hasClass('hidden'));
770- // The link has updated its text.
771- Y.Assert.areEqual(
772- "You have subscriptions that may cause you to receive "+
773- "notifications, but you are not directly subscribed to this "+
774- "bug's notifications.",
775- this.status_node.get('text'));
776- },
777-
778- test_io_spinner_and_error: function() {
779- this.init(LIFECYCLE, true);
780- module._lp_client.named_post.fail = true;
781- module._lp_client.named_post.args = [
782- true,
783- {status: 400, responseText: 'Rutebegas!'}];
784- module._lp_client.named_post.halt = true;
785- this.link().simulate('click');
786- var overlay = Y.one('.pretty-overlay-window');
787- var unsub = overlay.one('a.remove');
788- unsub.simulate('click');
789- // Right now, this is as if we are waiting for the server to
790- // reply. The link is spinning.
791- Y.Assert.isTrue(unsub.hasClass('spinner'));
792- Y.Assert.isFalse(unsub.hasClass('remove'));
793- // Now the server replies with an error.
794- module._lp_client.named_post.resume();
795- // We have no spinner.
796- Y.Assert.isTrue(unsub.hasClass('remove'));
797- Y.Assert.isFalse(unsub.hasClass('spinner'));
798- // The page has rendered the error overlay.
799- var error_box = Y.one('.yui3-lazr-formoverlay-errors');
800- Y.Assert.isTrue(Y.Lang.isValue(error_box));
801- // The overlay is still hanging around too, as expected, but for
802- // better or worse (popup-on-popup is not the best UI).
803- Y.Assert.isTrue(Y.Lang.isValue(Y.one('.pretty-overlay-window')));
804- }
805-
806-}));
807-
808-
809-Y.Test.Runner.on('complete', function(data) {
810- window.status = '::::' + JSON.stringify(data);
811- });
812-Y.Test.Runner.add(suite);
813-
814-var console = new Y.Console({newestOnTop: false});
815-console.render('#log');
816-
817-Y.on('domready', function() {
818- Y.Test.Runner.run();
819-});
820+ links: {me: '~tweedledee'}
821+ };
822+ }
823+
824+ function make_subscription(level) {
825+ if (Y.Lang.isUndefined(level)) {
826+ level = DISCUSSION;
827+ }
828+ window.LP.cache.subscription = {'bug_notification_level': level};
829+ }
830+
831+ var tests = Y.namespace('lp.bugs.subscription_portlet.test');
832+ tests.suite = new Y.Test.Suite('bugs.subscription_portlet Tests');
833+
834+ /**
835+ * Test update_subscription_status.
836+ */
837+ tests.suite.add(new Y.Test.Case({
838+ name: 'Test update_subscription_status',
839+
840+ setUp: function() {
841+ this.status_node = make_status_node();
842+ this.mute_node = make_mute_node();
843+ add_link_to_status_node();
844+ setup_LP();
845+ },
846+
847+ tearDown: function() {
848+ this.status_node.remove();
849+ this.mute_node.remove();
850+ delete window.LP;
851+ },
852+
853+ test_can_create_link: function() {
854+ this.status_node.one('a').remove();
855+ make_subscription();
856+ Y.Assert.isTrue(Y.Lang.isNull(this.status_node.one('a')));
857+ module.update_subscription_status();
858+ var link = this.status_node.one('a');
859+ Y.Assert.isTrue(Y.Lang.isValue(link));
860+ Y.Assert.isTrue(link.hasClass('menu-link-subscription'));
861+ Y.Assert.isTrue(link.hasClass('sprite'));
862+ Y.Assert.isTrue(link.hasClass('modify'));
863+ Y.Assert.isTrue(link.hasClass('edit'));
864+ Y.Assert.isTrue(link.hasClass('js-action'));
865+ Y.Assert.areEqual(
866+ // window.LP.context.web_link + '/+subscribe',
867+ 'http://example.com/+subscribe',
868+ link.get('href'));
869+ },
870+
871+ test_no_subscription: function() {
872+ module.update_subscription_status();
873+ Y.Assert.areEqual(
874+ 'You are',
875+ this.status_node.one('span').get('text'));
876+ Y.Assert.areEqual(
877+ "not directly subscribed to this bug's notifications.",
878+ this.status_node.one('a').get('text'));
879+ },
880+
881+ test_other_subscription: function() {
882+ window.LP.cache.other_subscription_notifications = true;
883+ module.update_subscription_status();
884+ Y.Assert.areEqual(
885+ 'You have subscriptions that may cause you to receive ' +
886+ 'notifications, but you are',
887+ this.status_node.one('span').get('text'));
888+ Y.Assert.areEqual(
889+ "not directly subscribed to this bug's notifications.",
890+ this.status_node.one('a').get('text'));
891+ },
892+
893+ test_full_subscription: function() {
894+ make_subscription(DISCUSSION);
895+ module.update_subscription_status();
896+ Y.Assert.areEqual(
897+ 'You are',
898+ this.status_node.one('span').get('text'));
899+ Y.Assert.areEqual(
900+ "subscribed to all notifications for this bug.",
901+ this.status_node.one('a').get('text'));
902+ },
903+
904+ test_metadata_subscription: function() {
905+ make_subscription(DETAILS);
906+ module.update_subscription_status();
907+ Y.Assert.areEqual(
908+ 'You are',
909+ this.status_node.one('span').get('text'));
910+ Y.Assert.areEqual(
911+ 'subscribed to all notifications except comments for this bug.',
912+ this.status_node.one('a').get('text'));
913+ },
914+
915+ test_lifecycle_subscription: function() {
916+ make_subscription(LIFECYCLE);
917+ module.update_subscription_status();
918+ Y.Assert.areEqual(
919+ 'You are',
920+ this.status_node.one('span').get('text'));
921+ Y.Assert.areEqual(
922+ 'subscribed to notifications when this bug is closed or ' +
923+ 'reopened.',
924+ this.status_node.one('a').get('text'));
925+ },
926+
927+ test_direct_subscription_has_precedence: function() {
928+ window.LP.cache.other_subscription_notifications = true;
929+ make_subscription(LIFECYCLE);
930+ module.update_subscription_status();
931+ Y.Assert.areEqual(
932+ 'You are',
933+ this.status_node.one('span').get('text'));
934+ Y.Assert.areEqual(
935+ 'subscribed to notifications when this bug is closed or ' +
936+ 'reopened.',
937+ this.status_node.one('a').get('text'));
938+ },
939+
940+ test_muted_subscription: function() {
941+ make_subscription(LIFECYCLE);
942+ this.mute_node.one('a').replaceClass(
943+ module.UNMUTED_CLASS, module.MUTED_CLASS);
944+ Y.Assert.isTrue(Y.Lang.isValue(this.status_node.one('a')));
945+ module.update_subscription_status();
946+ Y.Assert.areEqual(
947+ 'Your personal email notifications from this bug are muted.',
948+ this.status_node.one('span').get('text'));
949+ Y.Assert.isFalse(Y.Lang.isValue(this.status_node.one('a')));
950+ }
951+
952+ }));
953+
954+ /**
955+ * Test setup_mute_link_handlers.
956+ */
957+ tests.suite.add(new Y.Test.Case({
958+ name: 'Test setup_mute_link_handlers',
959+
960+ setUp: function() {
961+ this.status_node = make_status_node();
962+ this.mute_node = make_mute_node();
963+ this.link = this.mute_node.one('a');
964+ add_link_to_status_node();
965+ this.bug_link = 'http://example.net/firefox/bug/1';
966+ setup_LP(this.bug_link);
967+ make_subscription(DISCUSSION);
968+ module.update_subscription_status();
969+ module.setup_mute_link_handlers();
970+ module._lp_client = new Y.lp.testing.helpers.LPClient();
971+ module._lp_client.named_post.args = [];
972+ },
973+
974+ tearDown: function() {
975+ this.status_node.remove();
976+ this.mute_node.remove();
977+ delete window.LP;
978+ delete module._lp_client;
979+ var error_overlay = Y.one('.yui3-lazr-formoverlay');
980+ if (Y.Lang.isValue(error_overlay)) {
981+ error_overlay.remove();
982+ }
983+ },
984+
985+ test_mute_success: function() {
986+ this.link.simulate('click');
987+ Y.Assert.areEqual(1, module._lp_client.received.length);
988+ Y.Assert.areEqual('named_post', module._lp_client.received[0][0]);
989+ var args = module._lp_client.received[0][1];
990+ Y.Assert.areEqual(this.bug_link, args[0]);
991+ Y.Assert.areEqual('mute', args[1]);
992+ Y.ObjectAssert.areEqual({}, args[2].parameters);
993+ Y.Assert.isTrue(this.link.hasClass(module.MUTED_CLASS));
994+ Y.Assert.isFalse(this.link.hasClass('spinner'));
995+ Y.Assert.isFalse(this.link.hasClass(module.UNMUTED_CLASS));
996+ Y.Assert.areEqual(
997+ 'Your personal email notifications from this bug are muted.',
998+ this.status_node.one('span').get('text'));
999+ },
1000+
1001+ test_unmute_success: function() {
1002+ this.link.replaceClass(module.UNMUTED_CLASS, module.MUTED_CLASS);
1003+ this.link.simulate('click');
1004+ Y.Assert.areEqual(1, module._lp_client.received.length);
1005+ Y.Assert.areEqual('named_post', module._lp_client.received[0][0]);
1006+ var args = module._lp_client.received[0][1];
1007+ Y.Assert.areEqual(this.bug_link, args[0]);
1008+ Y.Assert.areEqual('unmute', args[1]);
1009+ Y.ObjectAssert.areEqual({}, args[2].parameters);
1010+ Y.Assert.isTrue(this.link.hasClass(module.UNMUTED_CLASS));
1011+ Y.Assert.isFalse(this.link.hasClass('spinner'));
1012+ Y.Assert.isFalse(this.link.hasClass(module.MUTED_CLASS));
1013+ Y.Assert.areEqual(
1014+ 'You are',
1015+ this.status_node.one('span').get('text'));
1016+ Y.Assert.areEqual(
1017+ "subscribed to all notifications for this bug.",
1018+ this.status_node.one('a').get('text'));
1019+ },
1020+
1021+ test_mute_spinner_and_failure: function() {
1022+ module._lp_client.named_post.fail = true;
1023+ module._lp_client.named_post.args = [
1024+ true,
1025+ {status: 400, responseText: 'Rutebegas!'}];
1026+ module._lp_client.named_post.halt = true;
1027+ this.link.simulate('click');
1028+ // Right now, this is as if we are waiting for the server to
1029+ // reply. The link is spinning.
1030+ Y.Assert.isTrue(this.link.hasClass('spinner'));
1031+ Y.Assert.isFalse(this.link.hasClass(module.UNMUTED_CLASS));
1032+ // Now the server replies with an error.
1033+ module._lp_client.named_post.resume();
1034+ // We have no spinner.
1035+ Y.Assert.isTrue(this.link.hasClass(module.UNMUTED_CLASS));
1036+ Y.Assert.isFalse(this.link.hasClass('spinner'));
1037+ // The page has rendered the error overlay.
1038+ var error_box = Y.one('.yui3-lazr-formoverlay-errors');
1039+ Y.Assert.isTrue(Y.Lang.isValue(error_box));
1040+ }
1041+ }));
1042+
1043+ /**
1044+ * Test setup_subscription_link_handlers.
1045+ */
1046+ tests.suite.add(new Y.Test.Case({
1047+ name: 'Test setup_subscription_link_handlers',
1048+
1049+ setUp: function() {
1050+ this.status_node = make_status_node();
1051+ this.mute_node = make_mute_node();
1052+ this.mute_link = this.mute_node.one('a');
1053+ this.bug_link = 'http://example.net/firefox/bug/1';
1054+ setup_LP(this.bug_link);
1055+ module._lp_client = new Y.lp.testing.helpers.LPClient();
1056+ },
1057+
1058+ tearDown: function() {
1059+ this.status_node.remove();
1060+ this.mute_node.remove();
1061+ delete window.LP;
1062+ delete module._lp_client;
1063+ var error_overlay = Y.one('.yui3-lazr-formoverlay');
1064+ if (Y.Lang.isValue(error_overlay)) {
1065+ error_overlay.remove();
1066+ }
1067+ var pretty_overlay = Y.one('.pretty-overlay-window');
1068+ if (Y.Lang.isValue(pretty_overlay)) {
1069+ pretty_overlay.remove();
1070+ }
1071+ },
1072+
1073+ init: function(sub_level, has_other_subs, response) {
1074+ if (Y.Lang.isValue(sub_level)) {
1075+ make_subscription(sub_level);
1076+ } else if (!has_other_subs) {
1077+ this.mute_node.addClass('hidden');
1078+ }
1079+ window.LP.cache.other_subscription_notifications = has_other_subs;
1080+ var args = [];
1081+ if (Y.Lang.isValue(response)) {
1082+ args = [
1083+ {getAttrs: function () {
1084+ return {bug_notification_level: response};
1085+ }}];
1086+ }
1087+ module._lp_client.named_post.args = args;
1088+ module.update_subscription_status();
1089+ module.setup_mute_link_handlers();
1090+ },
1091+
1092+ link: function() {
1093+ return this.status_node.one('a');
1094+ },
1095+
1096+ test_overlay_add_subscription: function() {
1097+ this.init(null, false);
1098+ this.link().simulate('click');
1099+ var overlay = Y.one('.pretty-overlay-window');
1100+ // We have the "Add" title, not the "Change" title.
1101+ Y.Assert.areEqual('Add a mail subscription for this bug',
1102+ overlay.one('h2').get('text'));
1103+ // There is no status.
1104+ Y.Assert.isFalse(Y.Lang.isValue(overlay.one('.subscription-status')));
1105+ // The action links are visible.
1106+ var action_links = overlay.one('.subscription-actions');
1107+ Y.Assert.isFalse(action_links.one(
1108+ '.Discussion a').hasClass('hidden'));
1109+ Y.Assert.isFalse(action_links.one('.Details a').hasClass('hidden'));
1110+ Y.Assert.isFalse(action_links.one('.Lifecycle a').hasClass('hidden'));
1111+ // The "Remove" link is not present.
1112+ Y.Assert.isFalse(Y.Lang.isValue(overlay.one('a.remove')));
1113+ // The link is spinning.
1114+ Y.Assert.isTrue(this.link().hasClass('spinner'));
1115+ Y.Assert.isFalse(this.link().hasClass('edit'));
1116+ // When we click on the "x," the overlay goes away, and the spinner
1117+ // is gone.
1118+ overlay.one('a.close-button').simulate('click');
1119+ Y.Assert.isFalse(Y.Lang.isValue(Y.one('.pretty-overlay-window')));
1120+ Y.Assert.isFalse(this.link().hasClass('spinner'));
1121+ Y.Assert.isTrue(this.link().hasClass('edit'));
1122+ },
1123+
1124+ test_overlay_with_discussion_subscription: function() {
1125+ // Starting with a DISCUSSION subscription, we get a "change" overlay.
1126+ this.init(DISCUSSION, false);
1127+ this.link().simulate('click');
1128+ var overlay = Y.one('.pretty-overlay-window');
1129+ // We have the "Change" title, not the "Add" title.
1130+ Y.Assert.areEqual('Change your mail subscription for this bug',
1131+ overlay.one('h2').get('text'));
1132+ // We show the Discussion status.
1133+ var status = overlay.one('.subscription-status');
1134+ Y.Assert.isFalse(overlay.one('span.Discussion').hasClass('hidden'));
1135+ Y.Assert.isTrue(overlay.one('span.Details').hasClass('hidden'));
1136+ Y.Assert.isTrue(overlay.one('span.Lifecycle').hasClass('hidden'));
1137+ // The action links are visible except for Discussion.
1138+ var action_links = overlay.one('.subscription-actions');
1139+ Y.Assert.isTrue(action_links.one('.Discussion a').hasClass('hidden'));
1140+ Y.Assert.isFalse(action_links.one('.Details a').hasClass('hidden'));
1141+ Y.Assert.isFalse(action_links.one('.Lifecycle a').hasClass('hidden'));
1142+ // We have a remove link.
1143+ Y.Assert.isTrue(Y.Lang.isValue(overlay.one('a.remove')));
1144+ Y.Assert.areEqual(
1145+ 'Remove your direct subscription',
1146+ overlay.one('div.subscription-actions+div').get('text'));
1147+ },
1148+
1149+ test_overlay_with_details_subscription: function() {
1150+ // Test overlay with existing DETAILS subscription.
1151+ this.init(DETAILS, true);
1152+ this.link().simulate('click');
1153+ var overlay = Y.one('.pretty-overlay-window');
1154+ // We show the Lifecycle status.
1155+ var status = overlay.one('.subscription-status');
1156+ Y.Assert.isTrue(overlay.one('span.Discussion').hasClass('hidden'));
1157+ Y.Assert.isFalse(overlay.one('span.Details').hasClass('hidden'));
1158+ Y.Assert.isTrue(overlay.one('span.Lifecycle').hasClass('hidden'));
1159+ // The action links are visible except for Lifecycle.
1160+ var action_links = overlay.one('.subscription-actions');
1161+ Y.Assert.isFalse(action_links.one(
1162+ '.Discussion a').hasClass('hidden'));
1163+ Y.Assert.isTrue(action_links.one('.Details a').hasClass('hidden'));
1164+ Y.Assert.isFalse(action_links.one('.Lifecycle a').hasClass('hidden'));
1165+ // We have a remove link.
1166+ Y.Assert.isTrue(Y.Lang.isValue(overlay.one('a.remove')));
1167+ Y.Assert.areEqual(
1168+ 'Remove your direct subscription',
1169+ overlay.one('div.subscription-actions+div').get('text'));
1170+ },
1171+
1172+ test_overlay_with_lifecycle_subscription: function() {
1173+ // Test overlay with existing LIFECYCLE subscription.
1174+ this.init(LIFECYCLE, true);
1175+ this.link().simulate('click');
1176+ var overlay = Y.one('.pretty-overlay-window');
1177+ // We show the Lifecycle status.
1178+ var status = overlay.one('.subscription-status');
1179+ Y.Assert.isTrue(overlay.one('span.Discussion').hasClass('hidden'));
1180+ Y.Assert.isTrue(overlay.one('span.Details').hasClass('hidden'));
1181+ Y.Assert.isFalse(overlay.one('span.Lifecycle').hasClass('hidden'));
1182+ // The action links are visible except for Lifecycle.
1183+ var action_links = overlay.one('.subscription-actions');
1184+ Y.Assert.isFalse(action_links.one(
1185+ '.Discussion a').hasClass('hidden'));
1186+ Y.Assert.isFalse(action_links.one('.Details a').hasClass('hidden'));
1187+ Y.Assert.isTrue(action_links.one('.Lifecycle a').hasClass('hidden'));
1188+ // We have a remove link.
1189+ Y.Assert.isTrue(Y.Lang.isValue(overlay.one('a.remove')));
1190+ Y.Assert.areEqual(
1191+ 'Remove your direct subscription',
1192+ overlay.one('div.subscription-actions+div').get('text'));
1193+ },
1194+
1195+ test_subscribe_discussion: function() {
1196+ this.init(null, false, DISCUSSION);
1197+ // The mute node is hidden initially.
1198+ Y.Assert.isTrue(this.mute_node.hasClass('hidden'));
1199+ this.link().simulate('click');
1200+ var overlay = Y.one('.pretty-overlay-window');
1201+ overlay.one('.subscription-actions .Discussion a').simulate('click');
1202+ // The overlay has been destroyed, and the spinner is gone.
1203+ Y.Assert.isFalse(Y.Lang.isValue(Y.one('.pretty-overlay-window')));
1204+ Y.Assert.isFalse(this.link().hasClass('spinner'));
1205+ // We got an appropriate call to the webservice.
1206+ Y.Assert.areEqual('named_post', module._lp_client.received[0][0]);
1207+ var args = module._lp_client.received[0][1];
1208+ Y.Assert.areEqual(this.bug_link, args[0]);
1209+ Y.Assert.areEqual('subscribe', args[1]);
1210+ Y.ObjectAssert.areEqual({person: '~tweedledee', level: DISCUSSION},
1211+ args[2].parameters);
1212+ // There is now a subscription object in the cache.
1213+ Y.Assert.isTrue(Y.Lang.isValue(window.LP.cache.subscription));
1214+ // The mute node is not hidden.
1215+ Y.Assert.isFalse(this.mute_node.hasClass('hidden'));
1216+ // The link has updated its text.
1217+ Y.Assert.areEqual(
1218+ "You are subscribed to all notifications for this bug.",
1219+ this.status_node.get('text'));
1220+ },
1221+
1222+ test_subscribe_details: function() {
1223+ this.init(null, false, DETAILS);
1224+ this.link().simulate('click');
1225+ var overlay = Y.one('.pretty-overlay-window');
1226+ overlay.one('.subscription-actions .Details a').simulate('click');
1227+ // We got an appropriate call to the webservice.
1228+ Y.Assert.areEqual('named_post', module._lp_client.received[0][0]);
1229+ var args = module._lp_client.received[0][1];
1230+ Y.Assert.areEqual(this.bug_link, args[0]);
1231+ Y.Assert.areEqual('subscribe', args[1]);
1232+ Y.ObjectAssert.areEqual({person: '~tweedledee', level: DETAILS},
1233+ args[2].parameters);
1234+ // The link has updated its text.
1235+ Y.Assert.areEqual(
1236+ "You are subscribed to all notifications except comments for "+
1237+ "this bug.",
1238+ this.status_node.get('text'));
1239+ },
1240+
1241+ test_subscribe_lifecycle: function() {
1242+ this.init(null, false, LIFECYCLE);
1243+ this.link().simulate('click');
1244+ var overlay = Y.one('.pretty-overlay-window');
1245+ overlay.one('.subscription-actions .Lifecycle a').simulate('click');
1246+ // We got an appropriate call to the webservice.
1247+ Y.Assert.areEqual('named_post', module._lp_client.received[0][0]);
1248+ var args = module._lp_client.received[0][1];
1249+ Y.Assert.areEqual(this.bug_link, args[0]);
1250+ Y.Assert.areEqual('subscribe', args[1]);
1251+ Y.ObjectAssert.areEqual({person: '~tweedledee', level: LIFECYCLE},
1252+ args[2].parameters);
1253+ // The link has updated its text.
1254+ Y.Assert.areEqual(
1255+ "You are subscribed to notifications when this bug is closed "+
1256+ "or reopened.",
1257+ this.status_node.get('text'));
1258+ },
1259+
1260+ test_unsubscribe: function() {
1261+ this.init(LIFECYCLE, false);
1262+ this.link().simulate('click');
1263+ var overlay = Y.one('.pretty-overlay-window');
1264+ overlay.one('a.remove').simulate('click');
1265+ // There is no warning.
1266+ var warning = overlay.one('.private-bug-warning');
1267+ Y.Assert.isNull(warning);
1268+ // We got an appropriate call to the webservice.
1269+ Y.Assert.areEqual('named_post', module._lp_client.received[0][0]);
1270+ var args = module._lp_client.received[0][1];
1271+ Y.Assert.areEqual(this.bug_link, args[0]);
1272+ Y.Assert.areEqual('unsubscribe', args[1]);
1273+ Y.ObjectAssert.areEqual({}, args[2].parameters);
1274+ // The overlay has been destroyed, and the spinner is gone.
1275+ Y.Assert.isFalse(Y.Lang.isValue(Y.one('.pretty-overlay-window')));
1276+ Y.Assert.isFalse(this.link().hasClass('spinner'));
1277+ // The mute node is hidden.
1278+ Y.Assert.isTrue(this.mute_node.hasClass('hidden'));
1279+ // The link has updated its text.
1280+ Y.Assert.areEqual(
1281+ "You are not directly subscribed to this bug's notifications.",
1282+ this.status_node.get('text'));
1283+ },
1284+
1285+ test_unsubscribe_from_private_bug: function() {
1286+ this.init(LIFECYCLE, false);
1287+ window.LP.cache.bug_is_private = true;
1288+ this.link().simulate('click');
1289+ var overlay = Y.one('.pretty-overlay-window');
1290+ overlay.one('a.remove').simulate('click');
1291+ // The overlay now has a warning.
1292+ var warning = overlay.one('.private-bug-warning');
1293+ Y.Assert.isTrue(Y.Lang.isValue(warning));
1294+ // Find and click the OK button.
1295+ var ok_btn = warning.one('.ok-btn');
1296+ ok_btn.simulate('click');
1297+ // We got an appropriate call to the webservice.
1298+ Y.Assert.areEqual('named_post', module._lp_client.received[0][0]);
1299+ var args = module._lp_client.received[0][1];
1300+ Y.Assert.areEqual(this.bug_link, args[0]);
1301+ Y.Assert.areEqual('unsubscribe', args[1]);
1302+ Y.ObjectAssert.areEqual({}, args[2].parameters);
1303+ // The overlay has been destroyed, and the spinner is gone.
1304+ Y.Assert.isFalse(Y.Lang.isValue(Y.one('.pretty-overlay-window')));
1305+ Y.Assert.isFalse(this.link().hasClass('spinner'));
1306+ },
1307+
1308+ test_unsubscribe_from_private_bug_cancel: function() {
1309+ this.init(LIFECYCLE, false);
1310+ window.LP.cache.bug_is_private = true;
1311+ this.link().simulate('click');
1312+ var overlay = Y.one('.pretty-overlay-window');
1313+ overlay.one('a.remove').simulate('click');
1314+ // The overlay now has a warning.
1315+ var warning = overlay.one('.private-bug-warning');
1316+ Y.Assert.isTrue(Y.Lang.isValue(warning));
1317+ // Find and click the Cancel button.
1318+ var cancel_btn = warning.one('.cancel-btn');
1319+ cancel_btn.simulate('click');
1320+ // The warning is gone.
1321+ warning = overlay.one('.private-bug-warning');
1322+ Y.Assert.isNull(warning);
1323+ },
1324+
1325+ test_unsubscribe_with_other: function() {
1326+ this.init(LIFECYCLE, true);
1327+ this.link().simulate('click');
1328+ var overlay = Y.one('.pretty-overlay-window');
1329+ overlay.one('a.remove').simulate('click');
1330+ // We got an appropriate call to the webservice.
1331+ Y.Assert.areEqual('named_post', module._lp_client.received[0][0]);
1332+ var args = module._lp_client.received[0][1];
1333+ Y.Assert.areEqual(this.bug_link, args[0]);
1334+ Y.Assert.areEqual('unsubscribe', args[1]);
1335+ Y.ObjectAssert.areEqual({}, args[2].parameters);
1336+ // The overlay has been destroyed, and the spinner is gone.
1337+ Y.Assert.isFalse(Y.Lang.isValue(Y.one('.pretty-overlay-window')));
1338+ Y.Assert.isFalse(this.link().hasClass('spinner'));
1339+ // The mute node is not hidden.
1340+ Y.Assert.isFalse(this.mute_node.hasClass('hidden'));
1341+ // The link has updated its text.
1342+ Y.Assert.areEqual(
1343+ "You have subscriptions that may cause you to receive "+
1344+ "notifications, but you are not directly subscribed to this "+
1345+ "bug's notifications.",
1346+ this.status_node.get('text'));
1347+ },
1348+
1349+ test_io_spinner_and_error: function() {
1350+ this.init(LIFECYCLE, true);
1351+ module._lp_client.named_post.fail = true;
1352+ module._lp_client.named_post.args = [
1353+ true,
1354+ {status: 400, responseText: 'Rutebegas!'}];
1355+ module._lp_client.named_post.halt = true;
1356+ this.link().simulate('click');
1357+ var overlay = Y.one('.pretty-overlay-window');
1358+ var unsub = overlay.one('a.remove');
1359+ unsub.simulate('click');
1360+ // Right now, this is as if we are waiting for the server to
1361+ // reply. The link is spinning.
1362+ Y.Assert.isTrue(unsub.hasClass('spinner'));
1363+ Y.Assert.isFalse(unsub.hasClass('remove'));
1364+ // Now the server replies with an error.
1365+ module._lp_client.named_post.resume();
1366+ // We have no spinner.
1367+ Y.Assert.isTrue(unsub.hasClass('remove'));
1368+ Y.Assert.isFalse(unsub.hasClass('spinner'));
1369+ // The page has rendered the error overlay.
1370+ var error_box = Y.one('.yui3-lazr-formoverlay-errors');
1371+ Y.Assert.isTrue(Y.Lang.isValue(error_box));
1372+ // The overlay is still hanging around too, as expected, but for
1373+ // better or worse (popup-on-popup is not the best UI).
1374+ Y.Assert.isTrue(Y.Lang.isValue(Y.one('.pretty-overlay-window')));
1375+ }
1376+ }));
1377+
1378+}, '0.1', {
1379+ 'requires': ['test', 'lp.testing.helpers', 'console',
1380+ 'lp.bugs.bugtask_index.portlets.subscription', 'node-event-simulate']
1381 });
1382
1383=== modified file 'lib/lp/bugs/javascript/tests/test_subscription.html'
1384--- lib/lp/bugs/javascript/tests/test_subscription.html 2012-03-14 04:41:36 +0000
1385+++ lib/lp/bugs/javascript/tests/test_subscription.html 2012-06-28 15:32:36 +0000
1386@@ -21,6 +21,8 @@
1387
1388 <script type="text/javascript"
1389 src="../../../../../build/js/lp/app/testing/testrunner.js"></script>
1390+ <script type="text/javascript"
1391+ src="../../../../../build/js/lp/app/testing/helpers.js"></script>
1392
1393 <link rel="stylesheet" href="../../../app/javascript/testing/test.css" />
1394
1395@@ -61,8 +63,7 @@
1396 </head>
1397 <body class="yui3-skin-sam">
1398 <ul id="suites">
1399- <!-- <li>lp.large_indicator.test</li> -->
1400- <li>lp.subscription.test</li>
1401+ <li>lp.bugs.subscription.test</li>
1402 </ul>
1403 <!-- Example markup required by test suite -->
1404 <div id="test-root"></div>
1405
1406=== modified file 'lib/lp/bugs/javascript/tests/test_subscription.js'
1407--- lib/lp/bugs/javascript/tests/test_subscription.js 2011-09-21 06:38:51 +0000
1408+++ lib/lp/bugs/javascript/tests/test_subscription.js 2012-06-28 15:32:36 +0000
1409@@ -1,2836 +1,2795 @@
1410-YUI({
1411- base: '../../../../canonical/launchpad/icing/yui/',
1412- filter: 'raw', combine: false, fetchCSS: false
1413- }).use('test', 'console', 'lp.bugs.subscription', 'node-event-simulate',
1414- 'lazr.effects',
1415- function(Y) {
1416-
1417-var suite = new Y.Test.Suite("lp.bugs.subscription Tests");
1418-var module = Y.lp.bugs.subscription;
1419-
1420-/**
1421- * XXX gary 2011-05-26 bug 793579
1422- * LPClient is copied three times (see also test_structural_subscription.js
1423- * and test_bug_subscription_portlet.js). It should be pushed to a
1424- * shared test module.
1425- */
1426-function LPClient(){
1427- if (!(this instanceof LPClient)) {
1428- throw new Error("Constructor called as a function");
1429- }
1430- this.received = [];
1431- // We create new functions every time because we allow them to be
1432- // configured.
1433- this.named_post = function(url, func, config) {
1434- this._call('named_post', config, arguments);
1435- };
1436- this.patch = function(bug_filter, data, config) {
1437- this._call('patch', config, arguments);
1438- };
1439-}
1440-LPClient.prototype._call = function(name, config, args) {
1441- this.received.push(
1442- [name, Array.prototype.slice.call(args)]);
1443- if (!Y.Lang.isValue(args.callee.args)) {
1444- throw new Error("Set call_args on "+name);
1445- }
1446- var do_action = function () {
1447- if (Y.Lang.isValue(args.callee.fail) && args.callee.fail) {
1448- config.on.failure.apply(undefined, args.callee.args);
1449- } else {
1450- config.on.success.apply(undefined, args.callee.args);
1451- }
1452- };
1453- if (Y.Lang.isValue(args.callee.halt) && args.callee.halt) {
1454- args.callee.resume = do_action;
1455- } else {
1456- do_action();
1457- }
1458-};
1459-
1460-/**
1461- * Test selection of the string by the number.
1462- * We expect to receive a plural string for all numbers
1463- * not equal to 1, and a singular string otherwise.
1464- */
1465-suite.add(new Y.Test.Case({
1466- name: 'Choose object by number',
1467-
1468- test_singular: function() {
1469- Y.Assert.areEqual(
1470- 'SINGULAR',
1471- module._choose_by_number(1, 'SINGULAR', 'PLURAL'));
1472- },
1473-
1474- test_plural: function() {
1475- Y.Assert.areEqual(
1476- 'PLURAL',
1477- module._choose_by_number(5, 'SINGULAR', 'PLURAL'));
1478- },
1479-
1480- test_zero: function() {
1481- Y.Assert.areEqual(
1482- 'PLURAL',
1483- module._choose_by_number(0, 'SINGULAR', 'PLURAL'));
1484- }
1485-}));
1486-
1487-/**
1488- * Replacing references to cache objects with actual objects.
1489- */
1490-suite.add(new Y.Test.Case({
1491- name: 'Replacing references with real objects',
1492-
1493- test_nothing: function() {
1494- // When there are no references, nothing gets replaced.
1495- var object = {
1496- something: 'nothing'
1497- };
1498- var cache = {};
1499- module._replace_textual_references(object, cache);
1500- Y.Assert.areEqual('nothing', object.something);
1501- },
1502-
1503- test_simple: function() {
1504- // With a simple reference, it gets substituted.
1505- var object = {
1506- something: 'subscription-cache-reference-1'
1507- };
1508- var cache = {
1509- 'subscription-cache-reference-1': 'OK'
1510- };
1511- module._replace_textual_references(object, cache);
1512- Y.Assert.areEqual('OK', object.something);
1513- },
1514-
1515- test_multiple: function() {
1516- // With multiple references, they all get substituted.0
1517- var object = {
1518- something: 'subscription-cache-reference-1',
1519- other: 'subscription-cache-reference-2'
1520- };
1521- var cache = {
1522- 'subscription-cache-reference-1': 'OK 1',
1523- 'subscription-cache-reference-2': 'OK 2'
1524- };
1525- module._replace_textual_references(object, cache);
1526- Y.Assert.areEqual('OK 1', object.something);
1527- Y.Assert.areEqual('OK 2', object.other);
1528- },
1529-
1530- test_recursive: function() {
1531- // Even references in nested objects get replaced.
1532- var object = {
1533- nested: {
1534+/* Copyright (c) 2012 Canonical Ltd. All rights reserved. */
1535+YUI.add('lp.bugs.subscription.test', function (Y) {
1536+ var module = Y.lp.bugs.subscription;
1537+
1538+ var reduction_ids = [module._action_ids.mute,
1539+ module._action_ids.subscribe_only_metadata,
1540+ module._action_ids.subscribe_only_closed,
1541+ module._action_ids.unsubscribe];
1542+
1543+ var increasing_ids = [module._action_ids.unmute,
1544+ module._action_ids.subscribe_all,
1545+ module._action_ids.subscribe_metadata,
1546+ module._action_ids.subscribe_closed];
1547+
1548+ /**
1549+ * Helper to construct a single 'category' of subscriptions,
1550+ * grouped by type (personally, as team member and as team admin).
1551+ */
1552+ function _constructCategory(personal, as_member, as_admin) {
1553+ if (personal === undefined) {
1554+ personal = [];
1555+ }
1556+ if (as_member === undefined) {
1557+ as_member = [];
1558+ }
1559+ if (as_admin === undefined) {
1560+ as_admin = [];
1561+ }
1562+ return {
1563+ count: personal.length + as_admin.length + as_member.length,
1564+ personal: personal,
1565+ as_team_member: as_member,
1566+ as_team_admin: as_admin
1567+ };
1568+ }
1569+
1570+ var tests = Y.namespace('lp.bugs.subscription.test');
1571+ tests.suite = new Y.Test.Suite('bugs.subscription Tests');
1572+
1573+ tests.suite.add(new Y.Test.Case({
1574+ name: 'bugs.subscription_tests',
1575+
1576+ setUp: function () {},
1577+ tearDown: function () {},
1578+
1579+ test_library_exists: function () {
1580+ Y.Assert.isObject(Y.lp.bugs.subscription,
1581+ "Could not locate the lp.bugs.subscription module");
1582+ }
1583+
1584+ }));
1585+
1586+ /**
1587+ * Test selection of the string by the number.
1588+ * We expect to receive a plural string for all numbers
1589+ * not equal to 1, and a singular string otherwise.
1590+ */
1591+ tests.suite.add(new Y.Test.Case({
1592+ name: 'Choose object by number',
1593+
1594+ test_singular: function() {
1595+ Y.Assert.areEqual(
1596+ 'SINGULAR',
1597+ module._choose_by_number(1, 'SINGULAR', 'PLURAL'));
1598+ },
1599+
1600+ test_plural: function() {
1601+ Y.Assert.areEqual(
1602+ 'PLURAL',
1603+ module._choose_by_number(5, 'SINGULAR', 'PLURAL'));
1604+ },
1605+
1606+ test_zero: function() {
1607+ Y.Assert.areEqual(
1608+ 'PLURAL',
1609+ module._choose_by_number(0, 'SINGULAR', 'PLURAL'));
1610+ }
1611+ }));
1612+
1613+ /**
1614+ * Replacing references to cache objects with actual objects.
1615+ */
1616+ tests.suite.add(new Y.Test.Case({
1617+ name: 'Replacing references with real objects',
1618+
1619+ test_nothing: function() {
1620+ // When there are no references, nothing gets replaced.
1621+ var object = {
1622+ something: 'nothing'
1623+ };
1624+ var cache = {};
1625+ module._replace_textual_references(object, cache);
1626+ Y.Assert.areEqual('nothing', object.something);
1627+ },
1628+
1629+ test_simple: function() {
1630+ // With a simple reference, it gets substituted.
1631+ var object = {
1632 something: 'subscription-cache-reference-1'
1633- }
1634- };
1635- var cache = {
1636- 'subscription-cache-reference-1': 'OK'
1637- };
1638- module._replace_textual_references(object, cache);
1639- Y.Assert.areEqual('OK', object.nested.something);
1640- }
1641-}));
1642-
1643-
1644-/**
1645- * Gather subscription records for all assignments.
1646- */
1647-suite.add(new Y.Test.Case({
1648- name: 'Gather assignment subscription information',
1649-
1650- test_nothing: function() {
1651- // When there are no subscriptions as assignee, returns empty list.
1652- var mock_category = {
1653- count: 0,
1654- personal: [],
1655- as_team_member: [],
1656- as_team_admin: []
1657- };
1658- Y.ArrayAssert.itemsAreEqual(
1659- [],
1660- module._gather_subscriptions_as_assignee(mock_category));
1661- },
1662-
1663- test_personal: function() {
1664- // When a person is directly the bug assignee, we get that
1665- // subscription details returned.
1666- var mock_category = {
1667- count: 1,
1668- personal: [{}],
1669- as_team_member: [],
1670- as_team_admin: []
1671- };
1672- var subs = module._gather_subscriptions_as_assignee(mock_category);
1673- Y.Assert.areEqual(1, subs.length);
1674- Y.Assert.areEqual(module._reasons.YOU_ASSIGNED, subs[0].reason);
1675- Y.Assert.areEqual(module._actions.CHANGE_ASSIGNEES, subs[0].action);
1676- },
1677-
1678- test_team_member: function() {
1679- // When a person is the bug assignee through team membership,
1680- // we get that subscription details returned.
1681- var mock_category = {
1682- count: 1,
1683- personal: [],
1684- as_team_member: [{ principal: 'my team'}],
1685- as_team_admin: []
1686- };
1687- var subs = module._gather_subscriptions_as_assignee(mock_category);
1688- Y.Assert.areEqual(1, subs.length);
1689- Y.Assert.areEqual(module._reasons.TEAM_ASSIGNED, subs[0].reason);
1690- // And there is a 'team' variable containing the team object.
1691- Y.Assert.areEqual('my team', subs[0].vars.team);
1692- Y.Assert.areEqual(module._actions.CONTACT_TEAMS, subs[0].action);
1693- },
1694-
1695- test_team_member_multiple: function() {
1696- // If a person is a member of multiple teams are assigned to work
1697- // on a single bug (eg. on different bug tasks) they get only one
1698- // subscription returned.
1699- var mock_category = {
1700- count: 2,
1701- personal: [],
1702- as_team_member: [{ principal: 'team1'},
1703- { principal: 'team2'}],
1704- as_team_admin: []
1705- };
1706- var subs = module._gather_subscriptions_as_assignee(mock_category);
1707- Y.Assert.areEqual(1, subs.length);
1708- Y.Assert.areEqual(module._reasons.TEAMS_ASSIGNED, subs[0].reason);
1709- // And there is a 'teams' variable containing all the team objects.
1710- Y.ArrayAssert.itemsAreEqual(['team1', 'team2'],
1711- subs[0].vars.teams);
1712- Y.Assert.areEqual(module._actions.CONTACT_TEAMS, subs[0].action);
1713- },
1714-
1715- test_team_member_multiple_duplicate: function() {
1716- // As with the previous test, but we need to show that each team is
1717- // only represented once even if they are responsible for multiple
1718- // bug tasks.
1719- // We test with full-fledged objects to make sure they work with the
1720- // mechanism used to find dupes.
1721- var team1 = {display_name: 'team 1',
1722- web_link: 'http://launchpad.net/~team1'},
1723- team2 = {display_name: 'team 2',
1724- web_link: 'http://launchpad.net/~team2'},
1725- mock_category = {
1726- count: 2,
1727- personal: [],
1728- as_team_member: [{ principal: team1 },
1729- { principal: team2 },
1730- { principal: team2 }],
1731- as_team_admin: []
1732- },
1733- subs = module._gather_subscriptions_as_assignee(mock_category);
1734- Y.Assert.areEqual(1, subs.length);
1735- Y.Assert.areEqual(module._reasons.TEAMS_ASSIGNED, subs[0].reason);
1736- // And there is a 'teams' variable containing all the team objects.
1737- var teams_found = [];
1738- var index;
1739- for (index = 0; index < subs[0].vars.teams.length; index++) {
1740- teams_found.push(subs[0].vars.teams[index].title);
1741- }
1742- Y.ArrayAssert.itemsAreEqual(['team 1', 'team 2'], teams_found);
1743- },
1744-
1745- test_team_admin: function() {
1746- // When a person is the bug assignee through team membership,
1747- // and a team admin at the same time, that subscription is returned.
1748- var mock_category = {
1749- count: 1,
1750- personal: [],
1751- as_team_member: [],
1752- as_team_admin: [{ principal: 'my team' }]
1753- };
1754- var subs = module._gather_subscriptions_as_assignee(mock_category);
1755- Y.Assert.areEqual(1, subs.length);
1756- Y.Assert.areEqual(
1757- module._reasons.ADMIN_TEAM_ASSIGNED, subs[0].reason);
1758- // And there is a 'team' variable containing the team object.
1759- Y.Assert.areEqual('my team', subs[0].vars.team);
1760- Y.Assert.areEqual(module._actions.CHANGE_ASSIGNEES, subs[0].action);
1761- },
1762-
1763- test_team_admin_multiple: function() {
1764- // If a person is a member of multiple teams are assigned to work
1765- // on a single bug (eg. on different bug tasks) they get only one
1766- // subscription returned.
1767- var mock_category = {
1768- count: 2,
1769- personal: [],
1770- as_team_member: [],
1771- as_team_admin: [{ principal: 'team1'},
1772- { principal: 'team2'}]
1773- };
1774- var subs = module._gather_subscriptions_as_assignee(mock_category);
1775- Y.Assert.areEqual(1, subs.length);
1776- Y.Assert.areEqual(
1777- module._reasons.ADMIN_TEAMS_ASSIGNED, subs[0].reason);
1778- // And there is a 'teams' variable containing all the team objects.
1779- Y.ArrayAssert.itemsAreEqual(['team1', 'team2'],
1780- subs[0].vars.teams);
1781- Y.Assert.areEqual(module._actions.CHANGE_ASSIGNEES, subs[0].action);
1782- },
1783-
1784- test_team_admin_multiple_duplicate: function() {
1785- // As with the previous test, but we need to show that each team is
1786- // only represented once even if they are responsible for multiple
1787- // bug tasks.
1788- // We test with full-fledged objects to make sure they work with the
1789- // mechanism used to find dupes.
1790- var team1 = {display_name: 'team 1',
1791- web_link: 'http://launchpad.net/~team1'},
1792- team2 = {display_name: 'team 2',
1793- web_link: 'http://launchpad.net/~team2'},
1794- mock_category = {
1795- count: 2,
1796- personal: [],
1797- as_team_admin: [{ principal: team1 },
1798- { principal: team2 },
1799- { principal: team2 }],
1800- as_team_member: []
1801- },
1802- subs = module._gather_subscriptions_as_assignee(mock_category);
1803- Y.Assert.areEqual(1, subs.length);
1804- // And there is a 'teams' variable containing all the team objects.
1805- var teams_found = [];
1806- for (index = 0; index < subs[0].vars.teams.length; index++) {
1807- teams_found.push(subs[0].vars.teams[index].title);
1808- }
1809- Y.ArrayAssert.itemsAreEqual(['team 1', 'team 2'], teams_found);
1810- },
1811-
1812- test_combined: function() {
1813- // Test that multiple assignments, even if they are in different
1814- // categories, work properly.
1815- var mock_category = {
1816- count: 3,
1817- personal: [{}],
1818- as_team_member: [{ principal: 'users' }],
1819- as_team_admin: [{ principal: 'admins' }]
1820- };
1821- var subs = module._gather_subscriptions_as_assignee(mock_category);
1822- Y.Assert.areEqual(3, subs.length);
1823- },
1824-
1825- test_object_links: function() {
1826- // Test that team assignments actually provide decent link data.
1827- var mock_category = {
1828- count: 1,
1829- personal: [],
1830- as_team_member: [
1831- { principal: { display_name: 'My team',
1832- web_link: 'http://link' } }],
1833- as_team_admin: []
1834- };
1835- var subs = module._gather_subscriptions_as_assignee(mock_category);
1836- Y.Assert.areEqual('My team', subs[0].vars.team.title);
1837- Y.Assert.areEqual('http://link', subs[0].vars.team.url);
1838- }
1839-}));
1840-
1841-/**
1842- * Gather subscription records for bug supervisor.
1843- */
1844-suite.add(new Y.Test.Case({
1845- name: 'Gather bug supervisor subscription information',
1846-
1847- test_nothing: function() {
1848- // When there are no subscriptions as bug supervisor,
1849- // returns empty list.
1850- var mock_category = {
1851- count: 0,
1852- personal: [],
1853- as_team_member: [],
1854- as_team_admin: []
1855- };
1856- Y.ArrayAssert.itemsAreEqual(
1857- [],
1858- module._gather_subscriptions_as_supervisor(mock_category));
1859- },
1860-
1861- test_personal: function() {
1862- // Person is the implicit bug supervisor by being the owner
1863- // of the project with no bug supervisor.
1864- var mock_category = {
1865- count: 1,
1866- personal: [{pillar: 'project'}],
1867- as_team_member: [],
1868- as_team_admin: []
1869- };
1870- var subs = module._gather_subscriptions_as_supervisor(mock_category);
1871- Y.Assert.areEqual(1, subs.length);
1872- Y.Assert.areEqual(module._reasons.YOU_OWNER, subs[0].reason);
1873- Y.Assert.areEqual('project', subs[0].vars.pillar);
1874- Y.Assert.areEqual(module._actions.SET_BUG_SUPERVISOR, subs[0].action);
1875- },
1876-
1877- test_personal_multiple: function() {
1878- // Person is the implicit bug supervisor by being the owner
1879- // of several projects (eg. multiple bug tasks) with no bug
1880- // supervisor.
1881- var mock_category = {
1882- count: 2,
1883- personal: [ {pillar: {title: 'project'} },
1884- {pillar: {title:'distro'} }],
1885- as_team_member: [],
1886- as_team_admin: []
1887- };
1888- var subs = module._gather_subscriptions_as_supervisor(mock_category);
1889- Y.Assert.areEqual(2, subs.length);
1890- },
1891-
1892- test_team_member: function() {
1893- // Person is a member of the team which is the implicit
1894- // bug supervisor.
1895- var mock_category = {
1896- count: 1,
1897- personal: [],
1898- as_team_member: [{ principal: 'my team',
1899- pillar: 'project' }],
1900- as_team_admin: []
1901- };
1902- var subs = module._gather_subscriptions_as_supervisor(mock_category);
1903- Y.Assert.areEqual(1, subs.length);
1904- Y.Assert.areEqual(module._reasons.TEAM_OWNER, subs[0].reason);
1905- // And there is a 'team' variable containing the team object.
1906- Y.Assert.areEqual('my team', subs[0].vars.team);
1907- Y.Assert.areEqual('project', subs[0].vars.pillar);
1908- Y.Assert.areEqual(module._actions.CONTACT_TEAMS, subs[0].action);
1909- },
1910-
1911- test_team_member_multiple: function() {
1912- // Person is a member of several teams which are implicit bug
1913- // supervisors on multiple bugtasks, we get subscription
1914- // records separately.
1915- var mock_category = {
1916- count: 2,
1917- personal: [],
1918- as_team_member: [{ principal: 'team1',
1919- pillar: {display_name: 'project'} },
1920- { principal: 'team2',
1921- pillar: {display_name: 'distro'} }],
1922- as_team_admin: []
1923- };
1924- var subs = module._gather_subscriptions_as_supervisor(mock_category);
1925- Y.Assert.areEqual(2, subs.length);
1926- },
1927-
1928- test_team_admin: function() {
1929- // Person is an admin of the team which is the implicit
1930- // bug supervisor.
1931- var mock_category = {
1932- count: 1,
1933- personal: [],
1934- as_team_member: [],
1935- as_team_admin: [{ principal: 'my team',
1936- pillar: 'project' }]
1937- };
1938- var subs = module._gather_subscriptions_as_supervisor(mock_category);
1939- Y.Assert.areEqual(1, subs.length);
1940- Y.Assert.areEqual(
1941- module._reasons.ADMIN_TEAM_OWNER, subs[0].reason);
1942- // And there is a 'team' variable containing the team object.
1943- Y.Assert.areEqual('my team', subs[0].vars.team);
1944- Y.Assert.areEqual('project', subs[0].vars.pillar);
1945- Y.Assert.areEqual(module._actions.SET_BUG_SUPERVISOR, subs[0].action);
1946- },
1947-
1948- test_team_admin_multiple: function() {
1949- // Person is an admin of several teams which are implicit bug
1950- // supervisors on multiple bugtasks, we get subscription
1951- // records separately.
1952- var mock_category = {
1953- count: 2,
1954- personal: [],
1955- as_team_member: [],
1956- as_team_admin: [{ principal: 'team1',
1957- pillar: {display_name: 'project'} },
1958- { principal: 'team2',
1959- pillar: {display_name: 'distro'} }]
1960- };
1961- var subs = module._gather_subscriptions_as_supervisor(mock_category);
1962- Y.Assert.areEqual(2, subs.length);
1963- },
1964-
1965- test_repeated_pillars: function() {
1966- // Different bug tasks might still be on the same pillar,
1967- // and we should only get one action.
1968- var mock_pillar = { display_name: 'project',
1969- web_link: 'http://project/' };
1970- var mock_category = {
1971- count: 1,
1972- personal: [{pillar: mock_pillar},
1973- {pillar: mock_pillar}],
1974- as_team_member: [],
1975- as_team_admin: []
1976- };
1977- var subs = module._gather_subscriptions_as_supervisor(mock_category);
1978- Y.Assert.areEqual(1, subs.length);
1979- Y.Assert.areEqual(module._reasons.YOU_OWNER, subs[0].reason);
1980- Y.Assert.areEqual(mock_pillar, subs[0].vars.pillar.self);
1981- Y.Assert.areEqual(module._actions.SET_BUG_SUPERVISOR, subs[0].action);
1982- },
1983-
1984- test_combined: function() {
1985- // Test that multiple implicit bug supervisor roles
1986- // are all returned.
1987- var mock_category = {
1988- count: 3,
1989- personal: [{pillar: 'project1'}],
1990- as_team_member: [{ principal: 'users', pillar: 'project2' }],
1991- as_team_admin: [{ principal: 'admins', pillar: 'distro' }]
1992- };
1993- var subs = module._gather_subscriptions_as_assignee(mock_category);
1994- Y.Assert.areEqual(3, subs.length);
1995- },
1996-
1997- test_object_links: function() {
1998- // Test that team-as-supervisor actually provide decent link data,
1999- // along with pillars as well.
2000- var mock_category = {
2001- count: 1,
2002- personal: [],
2003- as_team_member: [{
2004- principal: { display_name: 'My team',
2005- web_link: 'http://link' },
2006- pillar: { display_name: 'My project',
2007- web_link: 'http://project/' }
2008- }],
2009- as_team_admin: []
2010- };
2011- var subs = module._gather_subscriptions_as_supervisor(mock_category);
2012- Y.Assert.areEqual('My team', subs[0].vars.team.title);
2013- Y.Assert.areEqual('http://link', subs[0].vars.team.url);
2014-
2015- Y.Assert.areEqual('My project', subs[0].vars.pillar.title);
2016- Y.Assert.areEqual('http://project/', subs[0].vars.pillar.url);
2017- }
2018-}));
2019-
2020-/**
2021- * Gather subscription records for dupe bug subscriptions.
2022- */
2023-suite.add(new Y.Test.Case({
2024- name: 'Gather subscription information for duplicates',
2025-
2026- test_nothing: function() {
2027- // When there are no duplicate subscriptions, returns empty list.
2028- var mock_category = {
2029- count: 0,
2030- personal: [],
2031- as_team_member: [],
2032- as_team_admin: []
2033- };
2034- Y.ArrayAssert.itemsAreEqual(
2035- [],
2036- module._gather_subscriptions_from_duplicates(mock_category));
2037- },
2038-
2039- test_personal: function() {
2040- // A person is subscribed to a duplicate bug.
2041- var mock_category = {
2042- count: 1,
2043- personal: [{bug: 'dupe bug'}],
2044- as_team_member: [],
2045- as_team_admin: []
2046- };
2047- var subs = module._gather_subscriptions_from_duplicates(
2048- mock_category);
2049- Y.Assert.areEqual(1, subs.length);
2050- Y.Assert.areEqual(
2051- module._reasons.YOU_SUBSCRIBED_TO_DUPLICATE, subs[0].reason);
2052- Y.Assert.areEqual('dupe bug', subs[0].vars.duplicate_bug);
2053- Y.Assert.areEqual(module._actions.UNSUBSCRIBE_DUPLICATES,
2054- subs[0].action);
2055- },
2056-
2057- test_personal_multiple: function() {
2058- // A person is subscribed to multiple duplicate bugs.
2059- // They are returned together as one subscription record.
2060- var mock_category = {
2061- count: 2,
2062- personal: [{bug: 'dupe1'}, {bug: 'dupe2'}],
2063- as_team_member: [],
2064- as_team_admin: []
2065- };
2066- var subs = module._gather_subscriptions_from_duplicates(
2067- mock_category);
2068- Y.Assert.areEqual(1, subs.length);
2069- Y.Assert.areEqual(
2070- module._reasons.YOU_SUBSCRIBED_TO_DUPLICATES, subs[0].reason);
2071- Y.ArrayAssert.itemsAreEqual(
2072- ['dupe1', 'dupe2'], subs[0].vars.duplicate_bugs);
2073- Y.Assert.areEqual(module._actions.UNSUBSCRIBE_DUPLICATES,
2074- subs[0].action);
2075- },
2076-
2077- test_team_member: function() {
2078- // A person is a member of the team subscribed to a duplicate bug.
2079- var mock_category = {
2080- count: 1,
2081- personal: [],
2082- as_team_member: [{ principal: 'my team',
2083- bug: 'dupe' }],
2084- as_team_admin: []
2085- };
2086- var subs = module._gather_subscriptions_from_duplicates(
2087- mock_category);
2088- Y.Assert.areEqual(1, subs.length);
2089- Y.Assert.areEqual(
2090- module._reasons.TEAM_SUBSCRIBED_TO_DUPLICATE, subs[0].reason);
2091- // And there is a 'team' variable containing the team object.
2092- Y.Assert.areEqual('my team', subs[0].vars.team);
2093- // And a 'duplicate_bug' variable pointing to the dupe.
2094- Y.Assert.areEqual('dupe', subs[0].vars.duplicate_bug);
2095- Y.Assert.areEqual(module._actions.CONTACT_TEAMS, subs[0].action);
2096- },
2097-
2098- test_team_member_multiple_bugs: function() {
2099- // A person is a member of the team subscribed to multiple
2100- // duplicate bugs.
2101- var mock_category = {
2102- count: 1,
2103- personal: [],
2104- as_team_member: [{
2105- principal: 'my team',
2106- bug: 'dupe1'
2107- }, {
2108- principal: 'my team',
2109- bug: 'dupe2'
2110- }],
2111- as_team_admin: []
2112- };
2113- var subs = module._gather_subscriptions_from_duplicates(
2114- mock_category);
2115- Y.Assert.areEqual(1, subs.length);
2116- Y.Assert.areEqual(
2117- module._reasons.TEAM_SUBSCRIBED_TO_DUPLICATES, subs[0].reason);
2118- // And there is a 'team' variable containing the team object.
2119- Y.Assert.areEqual('my team', subs[0].vars.team);
2120- // And a 'duplicate_bugs' variable with the list of dupes.
2121- Y.ArrayAssert.itemsAreEqual(
2122- ['dupe1', 'dupe2'], subs[0].vars.duplicate_bugs);
2123- Y.Assert.areEqual(module._actions.CONTACT_TEAMS, subs[0].action);
2124- },
2125-
2126- test_team_member_multiple: function() {
2127- // A person is a member of several teams subscribed to
2128- // duplicate bugs.
2129- var mock_category = {
2130- count: 2,
2131- personal: [],
2132- as_team_member: [{ principal: 'team1',
2133- bug: 'dupe1' },
2134- { principal: 'team2',
2135- bug: 'dupe1' }],
2136- as_team_admin: []
2137- };
2138-
2139- // Result is two separate subscription records.
2140- var subs = module._gather_subscriptions_from_duplicates(
2141- mock_category);
2142- Y.Assert.areEqual(2, subs.length);
2143- },
2144-
2145- test_team_admin: function() {
2146- // A person is an admin of the team subscribed to a duplicate bug.
2147- var mock_category = {
2148- count: 1,
2149- personal: [],
2150- as_team_member: [],
2151- as_team_admin: [{ principal: 'my team',
2152- bug: 'dupe' }]
2153- };
2154- var subs = module._gather_subscriptions_from_duplicates(
2155- mock_category);
2156- Y.Assert.areEqual(1, subs.length);
2157- Y.Assert.areEqual(
2158- module._reasons.ADMIN_TEAM_SUBSCRIBED_TO_DUPLICATE,
2159- subs[0].reason);
2160- // And there is a 'team' variable containing the team object.
2161- Y.Assert.areEqual('my team', subs[0].vars.team);
2162- // And a 'duplicate_bug' variable pointing to the dupe.
2163- Y.Assert.areEqual('dupe', subs[0].vars.duplicate_bug);
2164- Y.Assert.areEqual(module._actions.UNSUBSCRIBE_DUPLICATES,
2165- subs[0].action);
2166- },
2167-
2168- test_team_admin_multiple_bugs: function() {
2169- // A person is an admin of the team subscribed to multiple
2170- // duplicate bugs.
2171- var mock_category = {
2172- count: 1,
2173- personal: [],
2174- as_team_member: [],
2175- as_team_admin: [{
2176- principal: 'my team',
2177- bug: 'dupe1'
2178- }, {
2179- principal: 'my team',
2180- bug: 'dupe2'
2181- }]
2182- };
2183- var subs = module._gather_subscriptions_from_duplicates(
2184- mock_category);
2185- Y.Assert.areEqual(1, subs.length);
2186- Y.Assert.areEqual(
2187- module._reasons.ADMIN_TEAM_SUBSCRIBED_TO_DUPLICATES,
2188- subs[0].reason);
2189- // And there is a 'team' variable containing the team object.
2190- Y.Assert.areEqual('my team', subs[0].vars.team);
2191- // And a 'duplicate_bugs' variable with the list of dupes.
2192- Y.ArrayAssert.itemsAreEqual(
2193- ['dupe1', 'dupe2'], subs[0].vars.duplicate_bugs);
2194- Y.Assert.areEqual(module._actions.UNSUBSCRIBE_DUPLICATES,
2195- subs[0].action);
2196- },
2197-
2198- test_team_admin_multiple: function() {
2199- // A person is an admin of several teams subscribed to
2200- // duplicate bugs.
2201- var mock_category = {
2202- count: 2,
2203- personal: [],
2204- as_team_member: [],
2205- as_team_admin: [{ principal: 'team1',
2206- bug: 'dupe1' },
2207- { principal: 'team2',
2208- bug: 'dupe1' }]
2209- };
2210-
2211- // Result is two separate subscription records.
2212- var subs = module._gather_subscriptions_from_duplicates(
2213- mock_category);
2214- Y.Assert.areEqual(2, subs.length);
2215- },
2216-
2217- test_object_links: function() {
2218- // Test that team dupe subscriptions actually provide decent
2219- // link data, including duplicate bugs link data.
2220- var mock_category = {
2221- count: 1,
2222- personal: [],
2223- as_team_member: [{
2224- principal: { display_name: 'My team',
2225- web_link: 'http://link' },
2226- bug: { id: 1,
2227- web_link: 'http://launchpad/bug/1' }
2228- }],
2229- as_team_admin: []
2230- };
2231- var subs = module._gather_subscriptions_from_duplicates(
2232- mock_category);
2233- Y.Assert.areEqual('My team', subs[0].vars.team.title);
2234- Y.Assert.areEqual('http://link', subs[0].vars.team.url);
2235-
2236- Y.Assert.areEqual('#1', subs[0].vars.duplicate_bug.title);
2237- Y.Assert.areEqual(
2238- 'http://launchpad/bug/1', subs[0].vars.duplicate_bug.url);
2239- }
2240-}));
2241-
2242-/**
2243- * Gather subscription records for direct team subscriptions.
2244- */
2245-suite.add(new Y.Test.Case({
2246- name: 'Gather team subscription information',
2247-
2248- test_nothing: function() {
2249- // When there are no subscriptions through team, returns empty list.
2250- var mock_category = {
2251- count: 0,
2252- personal: [],
2253- as_team_member: [],
2254- as_team_admin: []
2255- };
2256- Y.ArrayAssert.itemsAreEqual(
2257- [],
2258- module._gather_subscriptions_through_team(mock_category));
2259- },
2260-
2261- test_personal: function() {
2262- // A personal subscription is not considered a team subscription.
2263- var mock_category = {
2264- count: 1,
2265- personal: [{}],
2266- as_team_member: [],
2267- as_team_admin: []
2268- };
2269- Y.ArrayAssert.itemsAreEqual(
2270- [],
2271- module._gather_subscriptions_through_team(mock_category));
2272- },
2273-
2274- test_team_member: function() {
2275- // Person is a member of the team subscribed to the bug.
2276- var mock_category = {
2277- count: 1,
2278- personal: [],
2279- as_team_member: [{ principal: 'my team'}],
2280- as_team_admin: []
2281- };
2282- var subs = module._gather_subscriptions_through_team(mock_category);
2283- Y.Assert.areEqual(1, subs.length);
2284- Y.Assert.areEqual(module._reasons.TEAM_SUBSCRIBED, subs[0].reason);
2285- // And there is a 'team' variable containing the team object.
2286- Y.Assert.areEqual('my team', subs[0].vars.team);
2287- Y.Assert.areEqual(module._actions.CONTACT_TEAMS, subs[0].action);
2288- },
2289-
2290- test_team_member_multiple: function() {
2291- // Person is a member of several teams subscribed to the bug.
2292- var mock_category = {
2293- count: 2,
2294- personal: [],
2295- as_team_member: [{ principal: 'team1'},
2296- { principal: 'team2'}],
2297- as_team_admin: []
2298- };
2299- var subs = module._gather_subscriptions_through_team(mock_category);
2300- Y.Assert.areEqual(1, subs.length);
2301- Y.Assert.areEqual(module._reasons.TEAMS_SUBSCRIBED, subs[0].reason);
2302- // And there is a 'teams' variable containing all the team objects.
2303- Y.ArrayAssert.itemsAreEqual(['team1', 'team2'],
2304- subs[0].vars.teams);
2305- Y.Assert.areEqual(module._actions.CONTACT_TEAMS, subs[0].action);
2306- },
2307-
2308- test_team_member_multiple_duplicate: function() {
2309- // As with the previous test, but we need to show that each team is
2310- // only represented once even if they are responsible for multiple
2311- // bug tasks.
2312- // We test with full-fledged objects to make sure they work with the
2313- // mechanism used to find dupes.
2314- var team1 = {display_name: 'team 1',
2315- web_link: 'http://launchpad.net/~team1'},
2316- team2 = {display_name: 'team 2',
2317- web_link: 'http://launchpad.net/~team2'},
2318- mock_category = {
2319- count: 2,
2320- personal: [],
2321- as_team_member: [{ principal: team1 },
2322- { principal: team2 },
2323- { principal: team2 }],
2324- as_team_admin: []
2325- },
2326- subs = module._gather_subscriptions_through_team(mock_category);
2327- Y.Assert.areEqual(1, subs.length);
2328- // And there is a 'teams' variable containing all the team objects.
2329- var teams_found = [];
2330- for (index = 0; index < subs[0].vars.teams.length; index++) {
2331- teams_found.push(subs[0].vars.teams[index].title);
2332- }
2333- Y.ArrayAssert.itemsAreEqual(['team 1', 'team 2'], teams_found);
2334- },
2335-
2336- test_team_admin: function() {
2337- // Person is an admin of the team subscribed to the bug.
2338- var mock_category = {
2339- count: 1,
2340- personal: [],
2341- as_team_member: [],
2342- as_team_admin: [{ principal: 'my team' }]
2343- };
2344- var subs = module._gather_subscriptions_through_team(mock_category);
2345- Y.Assert.areEqual(1, subs.length);
2346- Y.Assert.areEqual(
2347- module._reasons.ADMIN_TEAM_SUBSCRIBED, subs[0].reason);
2348- // And there is a 'team' variable containing the team object.
2349- Y.Assert.areEqual('my team', subs[0].vars.team);
2350- Y.Assert.areEqual(module._actions.CHANGE_TEAM_SUBSCRIPTIONS,
2351- subs[0].action);
2352- },
2353-
2354- test_team_admin_multiple: function() {
2355- // Person is an admin of the several teams subscribed to the bug.
2356- var mock_category = {
2357- count: 2,
2358- personal: [],
2359- as_team_member: [],
2360- as_team_admin: [{ principal: 'team1'},
2361- { principal: 'team2'}]
2362- };
2363- var subs = module._gather_subscriptions_through_team(mock_category);
2364- Y.Assert.areEqual(1, subs.length);
2365- Y.Assert.areEqual(
2366- module._reasons.ADMIN_TEAMS_SUBSCRIBED, subs[0].reason);
2367- // And there is a 'teams' variable containing all the team objects.
2368- Y.ArrayAssert.itemsAreEqual(['team1', 'team2'],
2369- subs[0].vars.teams);
2370- Y.Assert.areEqual(module._actions.CHANGE_TEAM_SUBSCRIPTIONS,
2371- subs[0].action);
2372- },
2373-
2374- test_team_admin_multiple_duplicate: function() {
2375- // As with the previous test, but we need to show that each team is
2376- // only represented once even if they are responsible for multiple
2377- // bug tasks.
2378- // We test with full-fledged objects to make sure they work with the
2379- // mechanism used to find dupes.
2380- var team1 = {display_name: 'team 1',
2381- web_link: 'http://launchpad.net/~team1'},
2382- team2 = {display_name: 'team 2',
2383- web_link: 'http://launchpad.net/~team2'},
2384- mock_category = {
2385- count: 2,
2386- personal: [],
2387- as_team_admin: [{ principal: team1 },
2388- { principal: team2 },
2389- { principal: team2 }],
2390- as_team_member: []
2391- },
2392- subs = module._gather_subscriptions_through_team(mock_category);
2393- Y.Assert.areEqual(1, subs.length);
2394- // And there is a 'teams' variable containing all the team objects.
2395- var teams_found = [];
2396- for (index = 0; index < subs[0].vars.teams.length; index++) {
2397- teams_found.push(subs[0].vars.teams[index].title);
2398- }
2399- Y.ArrayAssert.itemsAreEqual(['team 1', 'team 2'], teams_found);
2400- },
2401-
2402- test_combined: function() {
2403- // Test that multiple subscriptions, even if they are in different
2404- // categories, work properly, and that personal subscriptions are
2405- // still ignored.
2406- var mock_category = {
2407- count: 3,
2408- personal: [{}],
2409- as_team_member: [{ principal: 'users' }],
2410- as_team_admin: [{ principal: 'admins' }]
2411- };
2412- var subs = module._gather_subscriptions_through_team(mock_category);
2413- Y.Assert.areEqual(2, subs.length);
2414- },
2415-
2416- test_object_links: function() {
2417- // Test that team subscriptions actually provide decent link data.
2418- var mock_category = {
2419- count: 1,
2420- personal: [],
2421- as_team_member: [
2422- { principal: { display_name: 'My team',
2423- web_link: 'http://link' } }],
2424- as_team_admin: []
2425- };
2426- var subs = module._gather_subscriptions_through_team(mock_category);
2427- Y.Assert.areEqual('My team', subs[0].vars.team.title);
2428- Y.Assert.areEqual('http://link', subs[0].vars.team.url);
2429- }
2430-}));
2431-
2432-
2433-/**
2434- * Helper to construct a single 'category' of subscriptions,
2435- * grouped by type (personally, as team member and as team admin).
2436- */
2437-function _constructCategory(personal, as_member, as_admin) {
2438- if (personal === undefined) {
2439- personal = [];
2440- }
2441- if (as_member === undefined) {
2442- as_member = [];
2443- }
2444- if (as_admin === undefined) {
2445- as_admin = [];
2446- }
2447- return {
2448- count: personal.length + as_admin.length + as_member.length,
2449- personal: personal,
2450- as_team_member: as_member,
2451- as_team_admin: as_admin
2452- };
2453-}
2454-
2455-/**
2456- * Get the reason for a direct subscription.
2457- * Tests for method get_direct_subscription_information().
2458- */
2459-suite.add(new Y.Test.Case({
2460- name: 'Get reason and actions for a direct subscription',
2461-
2462- _should: {
2463- error: {
2464- test_multiple_direct_subscriptions:
2465- new Error('Programmer error: a person should not have more than '+
2466- 'one direct personal subscription.'),
2467- test_direct_subscription_at_unknown_level:
2468- new Error('Programmer error: unknown bug notification level: '+
2469- 'The Larch')
2470- }
2471- },
2472-
2473- setUp: function() {
2474- window.LP = {cache: {subscription_info: []}};
2475- },
2476-
2477- tearDown: function() {
2478- delete window.LP;
2479- },
2480-
2481- test_multiple_direct_subscriptions: function() {
2482- // It should not be possible to have multiple direct,
2483- // personal subscriptions.
2484- // This errors out (see _should.error above).
2485- var info = {
2486- direct: _constructCategory(['1', '2']),
2487- count: 2
2488- };
2489- module._get_direct_subscription_information(info);
2490- },
2491-
2492- test_no_subscriptions_at_all: function() {
2493- // There are no subscriptions at all.
2494- var info = {
2495- direct: _constructCategory(),
2496- from_duplicates: _constructCategory()
2497- };
2498- info.count = info.direct.count + info.from_duplicates.count;
2499-
2500- direct_info = module._get_direct_subscription_information(info);
2501- Y.Assert.areEqual(
2502- module._reasons.NOT_SUBSCRIBED,
2503- direct_info.reason);
2504- Y.ArrayAssert.itemsAreEqual(
2505- [],
2506- direct_info.reductions);
2507- Y.ArrayAssert.itemsAreEqual(
2508- ['select-direct-subscription-discussion',
2509- 'select-direct-subscription-metadata',
2510- 'select-direct-subscription-lifecycle'],
2511- direct_info.increases);
2512- },
2513-
2514- test_only_structural_subscriptions: function() {
2515- // There are only structural subscriptions.
2516- var info = {
2517- direct: _constructCategory(),
2518- from_duplicates: _constructCategory()
2519- };
2520- info.count = info.direct.count + info.from_duplicates.count;
2521- window.LP.cache.subscription_info.push(true);
2522-
2523- direct_info = module._get_direct_subscription_information(info);
2524- Y.Assert.areSame(
2525- module._reasons.NOT_PERSONALLY_SUBSCRIBED,
2526- direct_info.reason);
2527- Y.ArrayAssert.itemsAreEqual(
2528- ['mute-direct-subscription',
2529- 'select-only-direct-subscription-metadata',
2530- 'select-only-direct-subscription-lifecycle'],
2531- direct_info.reductions);
2532- Y.ArrayAssert.itemsAreEqual(
2533- ['select-direct-subscription-discussion'],
2534- direct_info.increases);
2535- },
2536-
2537- test_no_direct_subscriptions: function() {
2538- // There is no direct subscription, but there are
2539- // other subscriptions.
2540- var info = {
2541- direct: _constructCategory(),
2542- from_duplicates: _constructCategory(['dupe'])
2543- };
2544- info.count = info.direct.count + info.from_duplicates.count;
2545- direct_info = module._get_direct_subscription_information(info);
2546- Y.Assert.areSame(
2547- module._reasons.NOT_PERSONALLY_SUBSCRIBED,
2548- direct_info.reason);
2549- Y.ArrayAssert.itemsAreEqual(
2550- ['mute-direct-subscription',
2551- 'select-only-direct-subscription-metadata',
2552- 'select-only-direct-subscription-lifecycle'],
2553- direct_info.reductions);
2554- Y.ArrayAssert.itemsAreEqual(
2555- ['select-direct-subscription-discussion'],
2556- direct_info.increases);
2557- },
2558-
2559- test_muted_subscription: function() {
2560- // The direct subscription is muted.
2561- var info = {
2562- direct: _constructCategory(['direct']),
2563- muted: true
2564- };
2565- info.count = info.direct.count;
2566- direct_info = module._get_direct_subscription_information(info);
2567- Y.Assert.areSame(
2568- module._reasons.MUTED_SUBSCRIPTION,
2569- direct_info.reason);
2570- Y.ArrayAssert.itemsAreEqual(
2571- [],
2572- direct_info.reductions);
2573- Y.ArrayAssert.itemsAreEqual(
2574- ['unmute-direct-subscription'],
2575- direct_info.increases);
2576- },
2577-
2578- test_direct_subscription_at_discussion_level: function() {
2579- // The larch^D^D^D^D^D^D simple direct subscription.
2580- var sub = {
2581- bug: {
2582- 'private': false,
2583- security_related: false
2584- },
2585- principal_is_reporter: false,
2586- subscription: {bug_notification_level: 'Discussion'}
2587- };
2588- var info = {
2589- direct: _constructCategory([sub]),
2590- count: 1
2591- };
2592-
2593- var direct_info = module._get_direct_subscription_information(info);
2594- Y.Assert.areSame(
2595- module._reasons.YOU_SUBSCRIBED,
2596- direct_info.reason);
2597- Y.ArrayAssert.itemsAreEqual(
2598- ['mute-direct-subscription',
2599- 'select-only-direct-subscription-metadata',
2600- 'select-only-direct-subscription-lifecycle',
2601- 'remove-direct-subscription'],
2602- direct_info.reductions);
2603- Y.ArrayAssert.itemsAreEqual(
2604- [],
2605- direct_info.increases);
2606- },
2607-
2608- test_direct_subscription_at_metadata_level: function() {
2609- // The simple direct subscription at metadata level.
2610- var sub = {
2611- bug: {
2612- 'private': false,
2613- security_related: false
2614- },
2615- principal_is_reporter: false,
2616- subscription: {bug_notification_level: 'Details'}
2617- };
2618- var info = {
2619- direct: _constructCategory([sub]),
2620- count: 1
2621- };
2622-
2623- var direct_info = module._get_direct_subscription_information(info);
2624- Y.Assert.areSame(
2625- module._reasons.YOU_SUBSCRIBED,
2626- direct_info.reason);
2627- Y.ArrayAssert.itemsAreEqual(
2628- ['mute-direct-subscription',
2629- 'select-only-direct-subscription-lifecycle',
2630- 'remove-direct-subscription'],
2631- direct_info.reductions);
2632- Y.ArrayAssert.itemsAreEqual(
2633- ['select-direct-subscription-discussion'],
2634- direct_info.increases);
2635- },
2636-
2637- test_direct_subscription_at_lifecycle_level: function() {
2638- // The simple direct subscription at lifecycle level.
2639- var sub = {
2640- bug: {
2641- 'private': false,
2642- security_related: false
2643- },
2644- principal_is_reporter: false,
2645- subscription: {bug_notification_level: 'Lifecycle'}
2646- };
2647- var info = {
2648- direct: _constructCategory([sub]),
2649- count: 1
2650- };
2651-
2652- var direct_info = module._get_direct_subscription_information(info);
2653- Y.Assert.areSame(
2654- module._reasons.YOU_SUBSCRIBED,
2655- direct_info.reason);
2656- Y.ArrayAssert.itemsAreEqual(
2657- ['mute-direct-subscription',
2658- 'remove-direct-subscription'],
2659- direct_info.reductions);
2660- Y.ArrayAssert.itemsAreEqual(
2661- ['select-direct-subscription-discussion',
2662- 'select-direct-subscription-metadata'],
2663- direct_info.increases);
2664- },
2665-
2666- test_direct_subscription_at_unknown_level: function() {
2667- // The simple direct subscription at unknown level.
2668- var sub = {
2669- bug: {
2670- 'private': false,
2671- security_related: false
2672- },
2673- principal_is_reporter: false,
2674- subscription: {bug_notification_level: 'The Larch'}
2675- };
2676- var info = {
2677- direct: _constructCategory([sub]),
2678- count: 1
2679- };
2680- // This should raise an error.
2681- module._get_direct_subscription_information(info);
2682- },
2683-
2684- test_direct_subscription_as_reporter: function() {
2685- // The direct subscription created for bug reporter.
2686- var sub = {
2687- bug: {},
2688- principal_is_reporter: true,
2689- subscription: {bug_notification_level: 'Discussion'}
2690- };
2691- var info = {
2692- direct: _constructCategory([sub]),
2693- count: 1
2694- };
2695-
2696- var direct_info = module._get_direct_subscription_information(info);
2697- Y.Assert.areSame(
2698- module._reasons.YOU_REPORTED,
2699- direct_info.reason);
2700- Y.ArrayAssert.itemsAreEqual(
2701- ['mute-direct-subscription',
2702- 'select-only-direct-subscription-metadata',
2703- 'select-only-direct-subscription-lifecycle',
2704- 'remove-direct-subscription'],
2705- direct_info.reductions);
2706- Y.ArrayAssert.itemsAreEqual(
2707- [],
2708- direct_info.increases);
2709- },
2710-
2711- test_direct_subscription_for_supervisor: function() {
2712- // The direct subscription created on private bugs for
2713- // the bug supervisor.
2714- var sub = {
2715- bug: {
2716- 'private': true
2717- },
2718- subscription: {bug_notification_level: 'Discussion'}
2719- };
2720- var info = {
2721- direct: _constructCategory([sub]),
2722- count: 1
2723- };
2724- var direct_info = module._get_direct_subscription_information(info);
2725- Y.Assert.areSame(
2726- module._reasons.YOU_SUBSCRIBED_BUG_SUPERVISOR,
2727- direct_info.reason);
2728- Y.ArrayAssert.itemsAreEqual(
2729- ['mute-direct-subscription',
2730- 'select-only-direct-subscription-metadata',
2731- 'select-only-direct-subscription-lifecycle',
2732- 'remove-direct-subscription'],
2733- direct_info.reductions);
2734- Y.ArrayAssert.itemsAreEqual(
2735- [],
2736- direct_info.increases);
2737- },
2738-
2739- test_direct_subscription_for_security_contact: function() {
2740- // The simple direct subscription.
2741- var sub = {
2742- bug: {
2743- security_related: true
2744- },
2745- subscription: {bug_notification_level: 'Discussion'}
2746- };
2747- var info = {
2748- direct: _constructCategory([sub]),
2749- count: 1
2750- };
2751- var direct_info = module._get_direct_subscription_information(info);
2752- Y.Assert.areSame(
2753- module._reasons.YOU_SUBSCRIBED_SECURITY_CONTACT,
2754- direct_info.reason);
2755- Y.ArrayAssert.itemsAreEqual(
2756- ['mute-direct-subscription',
2757- 'select-only-direct-subscription-metadata',
2758- 'select-only-direct-subscription-lifecycle',
2759- 'remove-direct-subscription'],
2760- direct_info.reductions);
2761- Y.ArrayAssert.itemsAreEqual(
2762- [],
2763- direct_info.increases);
2764- },
2765-
2766- test_direct_subscription_and_other_subscriptions: function() {
2767- // Other subscriptions are present along with the simple direct
2768- // subscription.
2769- var sub = {
2770- bug: {
2771- 'private': false,
2772- security_related: false
2773- },
2774- principal_is_reporter: false,
2775- subscription: {bug_notification_level: 'Discussion'}
2776- };
2777- var info = {
2778- direct: _constructCategory([sub]),
2779- from_duplicates: _constructCategory(['dupe']),
2780- count: 2
2781- };
2782-
2783- var direct_info = module._get_direct_subscription_information(info);
2784- Y.Assert.areSame(
2785- module._reasons.YOU_SUBSCRIBED,
2786- direct_info.reason);
2787- Y.ArrayAssert.itemsAreEqual(
2788- ['mute-direct-subscription',
2789- 'select-only-direct-subscription-metadata',
2790- 'select-only-direct-subscription-lifecycle',
2791- 'remove-direct-subscription-with-warning'],
2792- direct_info.reductions);
2793- Y.ArrayAssert.itemsAreEqual(
2794- [],
2795- direct_info.increases);
2796- }
2797-
2798-}));
2799-
2800-/**
2801- * Test for get_objectlink_html() method.
2802- */
2803-suite.add(new Y.Test.Case({
2804- name: 'Test conversion of ObjectLink to HTML.',
2805-
2806- _should: {
2807- error: {
2808- test_non_link: new Error('Not a proper ObjectLink.')
2809- }
2810- },
2811-
2812- test_string: function() {
2813- // When a string is passed in, it is returned unmodified.
2814- var link = 'test';
2815- Y.Assert.areEqual(
2816- link,
2817- module._get_objectlink_html(link));
2818- },
2819-
2820- test_non_link: function() {
2821- // When an object that doesn't have both 'title' and 'url'
2822- // passed in, it fails. (see _should.error above)
2823- var link = {};
2824- module._get_objectlink_html(link);
2825- },
2826-
2827- test_simple: function() {
2828- // When a string is passed in, it is returned unmodified.
2829- var link = {
2830- title: 'Title',
2831- url: 'http://url/'
2832- };
2833- Y.Assert.areEqual(
2834- '<a href="http://url/">Title</a>',
2835- module._get_objectlink_html(link));
2836- },
2837-
2838- test_escaping_title: function() {
2839- // Even with title containing HTML characters, they are properly
2840- // escaped.
2841- var link = {
2842- title: 'Title<script>',
2843- url: 'http://url/'
2844- };
2845- Y.Assert.areEqual(
2846- '<a href="http://url/">Title&lt;script&gt;</a>',
2847- module._get_objectlink_html(link));
2848- },
2849-
2850- test_escaping_url: function() {
2851- // Even with title containing HTML characters, they are properly
2852- // escaped.
2853- var url = 'http://url/" onclick="javascript:alert(\'test\');" a="';
2854- var link = {
2855- title: 'Title',
2856- url: url
2857- };
2858- // Firefox returns:
2859- // '<a href="http://url/%22%20onclick=%22' +
2860- // 'javascript:alert%28%27test%27%29;%22%20a=%22">Title</a>'
2861- // WebKit returns:
2862- // '<a href="http://url/&quot; onclick=&quot;'+
2863- // 'javascript:alert(\'test\');&quot; a=&quot;">Title</a>'
2864- Y.Assert.areNotEqual(
2865- '<a href="' + url + '">Title</a>',
2866- module._get_objectlink_html(link));
2867- }
2868-
2869-}));
2870-
2871-/**
2872- * Test for safely_render_description() method.
2873- */
2874-suite.add(new Y.Test.Case({
2875- name: 'Test variable substitution in subscription descriptions.',
2876-
2877- _should: {
2878- error: {
2879- test_non_link: new Error('Not a proper ObjectLink.')
2880- }
2881- },
2882-
2883- test_no_variables: function() {
2884- // For a string with no variables, no substitution is performed.
2885- var sub = {
2886- reason: 'test string with no vars',
2887- vars: { no: 'vars' }
2888- };
2889-
2890- Y.Assert.areEqual(
2891- sub.reason,
2892- module._safely_render_description(sub));
2893- },
2894-
2895- test_missing_variable: function() {
2896- // If a variable is missing, it is not substituted.
2897- var sub = {
2898- reason: 'test string with {foo}',
2899- vars: {}
2900- };
2901-
2902- Y.Assert.areEqual(
2903- 'test string with {foo}',
2904- module._safely_render_description(sub));
2905- },
2906-
2907- test_string_variable: function() {
2908- // Plain string variables are directly substituted.
2909- var sub = {
2910- reason: 'test string with {foo}',
2911- vars: { foo: 'nothing' }
2912- };
2913-
2914- Y.Assert.areEqual(
2915- 'test string with nothing',
2916- module._safely_render_description(sub));
2917- },
2918-
2919- _constructObjectLink: function(title, url) {
2920- // Constructs a mock ObjectLink.
2921- return { title: title, url: url };
2922- },
2923-
2924- test_objectlink_variable: function() {
2925- // ObjectLink variables get turned into actual HTML links.
2926- var sub = {
2927- reason: 'test string with {foo}',
2928- vars: { foo: this._constructObjectLink('Title', 'http://link/') }
2929- };
2930-
2931- Y.Assert.areEqual(
2932- 'test string with <a href="http://link/">Title</a>',
2933- module._safely_render_description(sub));
2934- },
2935-
2936- test_multiple_variables: function() {
2937- // For multiple variables, they all get replaced.
2938- var sub = {
2939- reason: '{simple} string with {foo} {simple}',
2940- vars: {
2941- foo: this._constructObjectLink('Link', 'http://link/'),
2942- simple: "test"
2943- }
2944- };
2945-
2946- Y.Assert.areEqual(
2947- 'test string with <a href="http://link/">Link</a> test',
2948- module._safely_render_description(sub));
2949- },
2950-
2951- test_extra_variable: function() {
2952- // Passing in extra variables causes them to be replaced as well.
2953- var sub = {
2954- reason: 'test string with {extra}',
2955- vars: {}
2956- };
2957- var extra_vars = {
2958- extra: 'something extra'
2959- };
2960-
2961- Y.Assert.areEqual(
2962- 'test string with something extra',
2963- module._safely_render_description(sub, extra_vars));
2964- },
2965-
2966- test_extra_objectlink_variable: function() {
2967- // Passing in extra ObjectLink variable gets properly substituted.
2968- var sub = {
2969- reason: 'test string with {extra}',
2970- vars: {}
2971- };
2972- var extra_vars = {
2973- extra: this._constructObjectLink('extras', 'http://link/')
2974- };
2975-
2976- Y.Assert.areEqual(
2977- 'test string with <a href="http://link/">extras</a>',
2978- module._safely_render_description(sub, extra_vars));
2979- }
2980-
2981-}));
2982-
2983-reduction_ids = [module._action_ids.mute,
2984- module._action_ids.subscribe_only_metadata,
2985- module._action_ids.subscribe_only_closed,
2986- module._action_ids.unsubscribe];
2987-
2988-increasing_ids = [module._action_ids.unmute,
2989- module._action_ids.subscribe_all,
2990- module._action_ids.subscribe_metadata,
2991- module._action_ids.subscribe_closed];
2992-
2993-/**
2994- * Test for get_direct_description_node() method.
2995- */
2996-suite.add(new Y.Test.Case({
2997- name: 'Test direct node construction.',
2998-
2999- setUp: function () {
3000- window.LP = { links: {},
3001- cache: {},
3002- subscription_info: {
3003- direct: _constructCategory(),
3004- bug_id: 1,
3005- count: 0
3006- }
3007- };
3008- },
3009-
3010- tearDown: function () {
3011- delete window.LP;
3012- },
3013-
3014- test_basic_structure: function() {
3015- // The node has the three main components we expect.
3016- var node = module._get_direct_description_node();
3017- Y.Assert.areEqual('direct-subscription', node.get('id'));
3018- Y.Assert.isTrue(Y.Lang.isValue(node.one('.reason')));
3019- Y.Assert.isTrue(Y.Lang.isValue(node.one('.reductions')));
3020- Y.Assert.isTrue(Y.Lang.isValue(node.one('.increases')));
3021- Y.Assert.isTrue(
3022- Y.Lang.isValue(
3023- node.one('#'+module._action_ids.unsubscribe_with_warning)));
3024- },
3025-
3026- test_reductions_structure: function() {
3027- var node = module._get_direct_description_node().one('.reductions');
3028- var i;
3029- for (i = 0; i < reduction_ids; i++) {
3030- Y.Assert.isTrue(
3031- Y.Lang.isValue(node.one('#'+reduction_ids[i])));
3032- }
3033- },
3034-
3035- test_increases_structure: function() {
3036- var node = module._get_direct_description_node().one('.increases');
3037- var i;
3038- for (i = 0; i < increasing_ids; i++) {
3039- Y.Assert.isTrue(
3040- Y.Lang.isValue(node.one('#'+increasing_ids[i])));
3041- }
3042- }
3043-
3044-}));
3045-
3046-/**
3047- * Test for reveal_direct_description_actions() method.
3048- */
3049-suite.add(new Y.Test.Case({
3050- name: 'Test direct node modification with appropriate description.',
3051-
3052- setUp: function () {
3053- window.LP = { links: {},
3054- cache: {},
3055- subscription_info: {
3056- direct: _constructCategory(),
3057- bug_id: 1,
3058- count: 0
3059- }
3060- };
3061- },
3062-
3063- tearDown: function () {
3064- delete window.LP;
3065- },
3066-
3067- test_reason_displayed: function() {
3068- // A description is added in.
3069- var node = module._get_direct_description_node();
3070- var expected_text = 'Kumquat rutebega papaya';
3071- var info = {
3072- reason: expected_text,
3073- increases: [],
3074- reductions: []
3075- };
3076- module._reveal_direct_description_actions(node, info);
3077- Y.Assert.isTrue(node.get('text').indexOf(expected_text) !== -1);
3078- },
3079-
3080- test_reductions_displayed: function() {
3081- // Reductions are revealed, increases are not.
3082- var node = module._get_direct_description_node();
3083- var expected_text = 'Kumquat rutebega papaya';
3084- var info = {
3085- reason: expected_text,
3086- increases: [],
3087- reductions: reduction_ids
3088- };
3089- module._reveal_direct_description_actions(node, info);
3090- var i;
3091- for (i = 0; i < reduction_ids.length; i++) {
3092- Y.Assert.isFalse(
3093- node.one('#'+reduction_ids[i]).hasClass('hidden'));
3094- }
3095- for (i = 0; i < increasing_ids.length; i++) {
3096- Y.Assert.isTrue(
3097- node.one('#'+increasing_ids[i]).hasClass('hidden'));
3098- }
3099- },
3100-
3101- test_increases_displayed: function() {
3102- // Increases are revealed, reductions are not.
3103- var node = module._get_direct_description_node();
3104- var expected_text = 'Kumquat rutebega papaya';
3105- var info = {
3106- reason: expected_text,
3107- increases: increasing_ids,
3108- reductions: []
3109- };
3110- module._reveal_direct_description_actions(node, info);
3111- var i;
3112- for (i = 0; i < reduction_ids.length; i++) {
3113- Y.Assert.isTrue(
3114- node.one('#'+reduction_ids[i]).hasClass('hidden'));
3115- }
3116- for (i = 0; i < increasing_ids.length; i++) {
3117- Y.Assert.isFalse(
3118- node.one('#'+increasing_ids[i]).hasClass('hidden'));
3119- }
3120- },
3121-
3122- test_unsubscribe_with_warning_displayed: function() {
3123- // Unsubscribe with warning is special because it is not
3124- // one of the reductions that gets displayed the reduction box.
3125- var node = module._get_direct_description_node();
3126- var expected_text = 'Kumquat rutebega papaya';
3127- // Get a copy of the reduction ids.
3128- var reductions = reduction_ids.slice(0);
3129- // Remove unsubscribe.
3130- reductions.splice(
3131- reductions.indexOf(module._action_ids.unsubscribe), 1);
3132- // Add unsubscribe_with_warning.
3133- reductions.push(module._action_ids.unsubscribe_with_warning);
3134- var info = {
3135- reason: expected_text,
3136- increases: [],
3137- reductions: reductions
3138- };
3139- module._reveal_direct_description_actions(node, info);
3140- var i;
3141- for (i = 0; i < reductions.length; i++) {
3142- Y.Assert.isFalse(
3143- node.one('#'+reductions[i]).hasClass('hidden'));
3144- }
3145- Y.Assert.isTrue(
3146- node.one('#'+module._action_ids.unsubscribe).hasClass('hidden'));
3147- for (i = 0; i < increasing_ids.length; i++) {
3148- Y.Assert.isTrue(
3149- node.one('#'+increasing_ids[i]).hasClass('hidden'));
3150- }
3151- },
3152-
3153- test_redisplay: function() {
3154- // If the function is called twice with different values, the
3155- // redisplay is correct.
3156- var node = module._get_direct_description_node();
3157- var expected_text = 'Kumquat rutebega papaya';
3158- var info = {
3159- reason: expected_text,
3160- increases: increasing_ids,
3161- reductions: []
3162- };
3163- module._reveal_direct_description_actions(node, info);
3164- info = {
3165- reason: expected_text,
3166- increases: [],
3167- reductions: reduction_ids
3168- };
3169- module._reveal_direct_description_actions(node, info);
3170- var i;
3171- for (i = 0; i < reduction_ids.length; i++) {
3172- Y.Assert.isFalse(
3173- node.one('#'+reduction_ids[i]).hasClass('hidden'));
3174- }
3175- for (i = 0; i < increasing_ids.length; i++) {
3176- Y.Assert.isTrue(
3177- node.one('#'+increasing_ids[i]).hasClass('hidden'));
3178- }
3179- }
3180-
3181-}));
3182-
3183-/**
3184- * Tests for the *_action functions used for direct personal subscriptions.
3185- */
3186-suite.add(new Y.Test.Case({
3187- name: 'Test the direct personal subscription action node functions.',
3188-
3189- assert_action_matches_expectations: function (
3190- node_function, expected_id, expected_text, expected_class,
3191- expected_method, expected_args, begin_with_subscription,
3192- send_subscription, private_bug, click_ok) {
3193- // We begin with set up.
3194- module._lp_client = new LPClient();
3195- var sub = {
3196- bug: {
3197- 'private': private_bug,
3198- security_related: false
3199- },
3200- principal_is_reporter: false,
3201- subscription: {bug_notification_level: 'Details'}
3202- };
3203- if (send_subscription) {
3204- module._lp_client.named_post.args = [
3205- {getAttrs: function () {
3206- return sub.subscription;
3207- }}];
3208- } else {
3209- module._lp_client.named_post.args = [];
3210- }
3211- var initial_subscriptions = [];
3212- if (begin_with_subscription) {
3213- initial_subscriptions.push(sub);
3214- }
3215- var bug_link = 'http://example.net/firefox/bug/1';
3216- window.LP = { links: {me: '~tweedledee'},
3217- cache: {context: {bug_link: bug_link},
3218- bug_is_private: private_bug,
3219- bug_subscription_info: {
3220- direct: _constructCategory(
3221- initial_subscriptions),
3222- bug_id: 1,
3223- count: initial_subscriptions.length
3224- }
3225- }
3226- };
3227- var display = module._get_direct_description_node();
3228- Y.one('body').appendChild(display);
3229- // Now we are actually ready to begin the tests. First we verify
3230- // the id, text, and link class are all as we expect.
3231- var node = node_function();
3232- Y.Assert.areEqual(expected_id, node.get('id'));
3233- Y.Assert.areEqual(expected_text, node.get('text'));
3234- Y.Assert.isTrue(node.one('a').hasClass(expected_class));
3235- // Now we verify that the link has been set up with the expected
3236- // method name and arguments. For this, we use the version of the
3237- // node that was actually inserted into the display. It shares the
3238- // same id.
3239- node = display.one('#'+expected_id);
3240- node.one('a').simulate('click');
3241- var co = Y.one('.yui3-overlay.yui3-lp-app-confirmationoverlay');
3242- if (!private_bug) {
3243- // If this a public bug, check the confirmation overlay is
3244- // nowhere to be found
3245- Y.Assert.isNull(co);
3246- } else {
3247- // Otherwise (private bug), click true
3248- var div = co.one('.yui3-lazr-formoverlay-actions');
3249- if (click_ok) {
3250- var ok = div.one('.ok-btn');
3251- ok.simulate('click');
3252- } else {
3253- var cancel = div.one('.cancel-btn');
3254- cancel.simulate('click');
3255- Y.Assert.areEqual(
3256- initial_subscriptions.length,
3257- window.LP.cache.bug_subscription_info.count);
3258- return;
3259- }
3260- }
3261- // We should have had a named_post to the bug_link, calling the
3262- // expected_method with the expected_args.
3263- Y.Assert.areEqual(1, module._lp_client.received.length);
3264- Y.Assert.areEqual('named_post', module._lp_client.received[0][0]);
3265- var args = module._lp_client.received[0][1];
3266- Y.Assert.areEqual(bug_link, args[0]);
3267- Y.Assert.areEqual(expected_method, args[1]);
3268- Y.ObjectAssert.areEqual(expected_args, args[2].parameters);
3269- },
3270-
3271- tearDown: function() {
3272- var display = Y.one('#direct-subscription');
3273- if (Y.Lang.isValue(display)) {
3274- display.remove();
3275- }
3276- delete window.LP;
3277- delete module._lp_client;
3278- },
3279-
3280- test_mute_action: function() {
3281- this.assert_action_matches_expectations(
3282- module._mute_action, module._action_ids.mute,
3283- 'mute all emails from this bug',
3284- 'mute', 'mute', {}, true, true, false, true);
3285- },
3286-
3287- test_unmute_action: function() {
3288- this.assert_action_matches_expectations(
3289- module._unmute_action, module._action_ids.unmute,
3290- 'unmute emails from this bug',
3291- 'unmute', 'unmute', {}, true, false, false, true);
3292- },
3293-
3294- test_subscribe_all_action: function() {
3295- this.assert_action_matches_expectations(
3296- module._subscribe_all_action, module._action_ids.subscribe_all,
3297- 'receive all emails about this bug',
3298- 'edit', 'subscribe',
3299- {person: '~tweedledee', level: 'Discussion'},
3300- false, true, false, true);
3301- },
3302-
3303- test_subscribe_metadata_action: function() {
3304- this.assert_action_matches_expectations(
3305- module._subscribe_metadata_action,
3306- module._action_ids.subscribe_metadata,
3307- 'receive all emails about this bug except comments',
3308- 'edit', 'subscribe',
3309- {person: '~tweedledee', level: 'Details'},
3310- false, true, false, true);
3311- },
3312-
3313- test_subscribe_closed_action: function() {
3314- this.assert_action_matches_expectations(
3315- module._subscribe_closed_action,
3316- module._action_ids.subscribe_closed,
3317- 'only receive email when this bug is closed',
3318- 'edit', 'subscribe',
3319- {person: '~tweedledee', level: 'Lifecycle'},
3320- false, true, false, true);
3321- },
3322-
3323- test_subscribe_only_metadata_action: function() {
3324- this.assert_action_matches_expectations(
3325- module._subscribe_only_metadata_action,
3326- module._action_ids.subscribe_only_metadata,
3327- 'stop receiving comments from this bug',
3328- 'edit', 'subscribe',
3329- {person: '~tweedledee', level: 'Details'},
3330- false, true, false, true);
3331- },
3332-
3333- test_subscribe_only_closed_action: function() {
3334- this.assert_action_matches_expectations(
3335- module._subscribe_only_closed_action,
3336- module._action_ids.subscribe_only_closed,
3337- 'only receive email when this bug is closed',
3338- 'edit', 'subscribe',
3339- {person: '~tweedledee', level: 'Lifecycle'},
3340- false, true, false, true);
3341- },
3342-
3343- test_unsubscribe_action: function() {
3344- this.assert_action_matches_expectations(
3345- module._unsubscribe_action, module._action_ids.unsubscribe,
3346- 'unsubscribe from this bug',
3347- 'remove', 'unsubscribe', {}, true, false, false, true);
3348- },
3349-
3350- test_unsubscribe_with_warning_action: function() {
3351- this.assert_action_matches_expectations(
3352- module._unsubscribe_with_warning_action,
3353- module._action_ids.unsubscribe_with_warning,
3354- 'You can also unsubscribe from this bug. However, you also '+
3355- 'have other subscriptions to this bug that may send you email '+
3356- 'once you have unsubscribed.',
3357- 'remove', 'unsubscribe', {}, true, false, false, true);
3358- },
3359-
3360- test_unsubscribe_action_private_bug_cancel: function() {
3361- this.assert_action_matches_expectations(
3362- module._unsubscribe_action, module._action_ids.unsubscribe,
3363- 'unsubscribe from this bug',
3364- 'remove', null, {}, true, false, true, false);
3365- },
3366-
3367- test_unsubscribe_action_private_bug: function() {
3368- this.assert_action_matches_expectations(
3369- module._unsubscribe_action, module._action_ids.unsubscribe,
3370- 'unsubscribe from this bug',
3371- 'remove', 'unsubscribe', {}, true, false, true, true);
3372- }
3373-
3374-}));
3375-
3376-/**
3377- * Test for get_single_description_node() method.
3378- */
3379-suite.add(new Y.Test.Case({
3380- name: 'Test single subscription description node construction.',
3381-
3382- test_simple_text: function() {
3383- // A simple subscription with 'Text' as the reason and no variables.
3384- var sub = { reason: 'Text', vars: {}, action: function() {} };
3385- var node = module._get_single_description_node(sub);
3386-
3387- // The node has appropriate CSS class set.
3388- Y.Assert.isTrue(node.hasClass('subscription-description'));
3389-
3390- // There is also a sub-node containing the actual description.
3391- var subnode = node.one('.description-text');
3392- Y.Assert.areEqual('Text', subnode.get('text'));
3393- },
3394-
3395- test_variable_substitution: function() {
3396- // A subscription with variables and extra variables
3397- // has them replaced.
3398- var sub = { reason: 'Test {var1} {var2}',
3399- vars: { var1: 'my text'},
3400- action: function() {} };
3401- var extra_data = { var2: 'globally' };
3402- var node = module._get_single_description_node(sub, extra_data);
3403-
3404- // The node has appropriate CSS class set.
3405- Y.Assert.isTrue(node.hasClass('subscription-description'));
3406-
3407- // There is also a sub-node containing the actual description.
3408- var subnode = node.one('.description-text');
3409- Y.Assert.areEqual('Test my text globally', subnode.get('text'));
3410- }
3411-
3412-}));
3413-
3414-/**
3415- * Test for get_other_descriptions_node() method.
3416- */
3417-suite.add(new Y.Test.Case({
3418- name: 'Test creation of node describing all non-direct subscriptions.',
3419-
3420- setUp: function() {
3421- // Monkey patch effects duration to make effects instant.
3422- // This keeps wait times to a minimum.
3423- this.original_defaults = Y.lazr.effects.slide_effect_defaults;
3424- Y.lazr.effects.slide_effect_defaults.duration = 0;
3425- },
3426-
3427- tearDown: function() {
3428- Y.lazr.effects.slide_effect_defaults = this.original_defaults;
3429- },
3430-
3431-
3432- test_no_subscriptions: function() {
3433- // With just a personal subscription, undefined is returned.
3434- var info = {
3435- direct: _constructCategory([{ bug: {} }]),
3436- from_duplicate: _constructCategory(),
3437- as_assignee: _constructCategory(),
3438- as_owner: _constructCategory(),
3439- count: 1
3440- };
3441- window.LP = { cache: {} };
3442- Y.Assert.areSame(
3443- undefined,
3444- module._get_other_descriptions_node(info));
3445- delete window.LP;
3446- },
3447-
3448- test_one_subscription: function() {
3449- // There is a subscription on the duplicate bug.
3450- var info = {
3451- direct: _constructCategory(),
3452- from_duplicate: _constructCategory([{ bug: {id: 1} }]),
3453- as_assignee: _constructCategory(),
3454- as_owner: _constructCategory(),
3455- count: 1
3456- };
3457- window.LP = { links: { me: '~' } };
3458-
3459- // A node is returned with ID of 'other-subscriptions'.
3460- var node = module._get_other_descriptions_node(info);
3461- Y.Assert.areEqual(
3462- 'other-subscriptions', node.get('id'));
3463- // And it contains single '.subscription-description' node.
3464- Y.Assert.areEqual(
3465- 1, node.all('.subscription-description').size());
3466- delete window.LP;
3467- },
3468-
3469- test_multiple_subscription: function() {
3470- // There is a subscription on the duplicate bug 1,
3471- // and another as assignee on bug 2.
3472- var info = {
3473- direct: _constructCategory(),
3474- from_duplicate: _constructCategory([{ bug: {id: 1} }]),
3475- as_assignee: _constructCategory([{ bug: {id: 2} }]),
3476- as_owner: _constructCategory(),
3477- count: 1
3478- };
3479- window.LP = { cache: { context: { web_link: '/' } },
3480- links: { me: '~' } };
3481-
3482- // A node is returned containing two
3483- // '.subscription-description' nodes.
3484- var node = module._get_other_descriptions_node(info);
3485- Y.Assert.areEqual(
3486- 2, node.all('.subscription-description').size());
3487- delete window.LP;
3488- },
3489-
3490- test_no_direct_has_structural_subscriptions: function() {
3491- // With no non-personal subscriptions, and a structural
3492- // subscription, the node is still constructed because
3493- // structural subscriptions go there as well.
3494- var info = {
3495- direct: _constructCategory([{ bug: {} }]),
3496- from_duplicate: _constructCategory(),
3497- as_assignee: _constructCategory(),
3498- as_owner: _constructCategory(),
3499- count: 1
3500- };
3501- window.LP = { cache: { subscription_info: ['1'] } };
3502- Y.Assert.isNotUndefined(
3503- module._get_other_descriptions_node(info));
3504- delete window.LP;
3505- },
3506-
3507- test_header: function() {
3508- // There is a subscription on the duplicate bug.
3509- var info = {
3510- direct: _constructCategory(),
3511- from_duplicate: _constructCategory([{ bug: {id: 1} }]),
3512- as_assignee: _constructCategory(),
3513- as_owner: _constructCategory(),
3514- count: 1
3515- };
3516-
3517- window.LP = { links: { me: '~' } };
3518-
3519- // A returned node contains the 'other-subscriptions-header'
3520- // div with the link.
3521- var node = module._get_other_descriptions_node(info);
3522- var header = node.one('#other-subscriptions-header');
3523- Y.Assert.isNotUndefined(header);
3524- var link = header.one('a');
3525- Y.Assert.areEqual('Other subscriptions', link.get('text'));
3526-
3527- delete window.LP;
3528- },
3529-
3530- test_header_slideout: function() {
3531- // Clicking on the header slides-out the box, and
3532- // clicking it again slides it back in.
3533- var info = {
3534- direct: _constructCategory(),
3535- from_duplicate: _constructCategory([{ bug: {id: 1} }]),
3536- as_assignee: _constructCategory(),
3537- as_owner: _constructCategory(),
3538- count: 1
3539- };
3540-
3541- window.LP = { links: { me: '~' } };
3542-
3543- // A returned node contains the 'other-subscriptions-header'
3544- // div with the link.
3545- var node = module._get_other_descriptions_node(info);
3546- var link = node.one('#other-subscriptions-header a');
3547- var list = node.one('#other-subscriptions-list');
3548-
3549- // Initially, the list is hidden.
3550- Y.Assert.isTrue(link.hasClass('treeCollapsed'));
3551- Y.Assert.isTrue(list.hasClass('lazr-closed'));
3552- Y.Assert.areEqual('none', list.getStyle('display'));
3553-
3554- // Clicking the link slides out the list of other subscriptions.
3555- link.simulate('click');
3556- this.wait(function() {
3557- Y.Assert.isFalse(link.hasClass('treeCollapsed'));
3558- Y.Assert.isTrue(link.hasClass('treeExpanded'));
3559- Y.Assert.isFalse(list.hasClass('lazr-closed'));
3560- Y.Assert.areNotEqual('none', list.getStyle('display'));
3561-
3562- // Clicking it again, slides it back in.
3563- // It has to be nested inside 'wait' because we need
3564- // to wait for the first click to "finish".
3565+ };
3566+ var cache = {
3567+ 'subscription-cache-reference-1': 'OK'
3568+ };
3569+ module._replace_textual_references(object, cache);
3570+ Y.Assert.areEqual('OK', object.something);
3571+ },
3572+
3573+ test_multiple: function() {
3574+ // With multiple references, they all get substituted.0
3575+ var object = {
3576+ something: 'subscription-cache-reference-1',
3577+ other: 'subscription-cache-reference-2'
3578+ };
3579+ var cache = {
3580+ 'subscription-cache-reference-1': 'OK 1',
3581+ 'subscription-cache-reference-2': 'OK 2'
3582+ };
3583+ module._replace_textual_references(object, cache);
3584+ Y.Assert.areEqual('OK 1', object.something);
3585+ Y.Assert.areEqual('OK 2', object.other);
3586+ },
3587+
3588+ test_recursive: function() {
3589+ // Even references in nested objects get replaced.
3590+ var object = {
3591+ nested: {
3592+ something: 'subscription-cache-reference-1'
3593+ }
3594+ };
3595+ var cache = {
3596+ 'subscription-cache-reference-1': 'OK'
3597+ };
3598+ module._replace_textual_references(object, cache);
3599+ Y.Assert.areEqual('OK', object.nested.something);
3600+ }
3601+ }));
3602+
3603+
3604+ /**
3605+ * Gather subscription records for all assignments.
3606+ */
3607+ tests.suite.add(new Y.Test.Case({
3608+ name: 'Gather assignment subscription information',
3609+
3610+ test_nothing: function() {
3611+ // When there are no subscriptions as assignee, returns empty list.
3612+ var mock_category = {
3613+ count: 0,
3614+ personal: [],
3615+ as_team_member: [],
3616+ as_team_admin: []
3617+ };
3618+ Y.ArrayAssert.itemsAreEqual(
3619+ [],
3620+ module._gather_subscriptions_as_assignee(mock_category));
3621+ },
3622+
3623+ test_personal: function() {
3624+ // When a person is directly the bug assignee, we get that
3625+ // subscription details returned.
3626+ var mock_category = {
3627+ count: 1,
3628+ personal: [{}],
3629+ as_team_member: [],
3630+ as_team_admin: []
3631+ };
3632+ var subs = module._gather_subscriptions_as_assignee(mock_category);
3633+ Y.Assert.areEqual(1, subs.length);
3634+ Y.Assert.areEqual(module._reasons.YOU_ASSIGNED, subs[0].reason);
3635+ Y.Assert.areEqual(module._actions.CHANGE_ASSIGNEES, subs[0].action);
3636+ },
3637+
3638+ test_team_member: function() {
3639+ // When a person is the bug assignee through team membership,
3640+ // we get that subscription details returned.
3641+ var mock_category = {
3642+ count: 1,
3643+ personal: [],
3644+ as_team_member: [{ principal: 'my team'}],
3645+ as_team_admin: []
3646+ };
3647+ var subs = module._gather_subscriptions_as_assignee(mock_category);
3648+ Y.Assert.areEqual(1, subs.length);
3649+ Y.Assert.areEqual(module._reasons.TEAM_ASSIGNED, subs[0].reason);
3650+ // And there is a 'team' variable containing the team object.
3651+ Y.Assert.areEqual('my team', subs[0].vars.team);
3652+ Y.Assert.areEqual(module._actions.CONTACT_TEAMS, subs[0].action);
3653+ },
3654+
3655+ test_team_member_multiple: function() {
3656+ // If a person is a member of multiple teams are assigned to work
3657+ // on a single bug (eg. on different bug tasks) they get only one
3658+ // subscription returned.
3659+ var mock_category = {
3660+ count: 2,
3661+ personal: [],
3662+ as_team_member: [{ principal: 'team1'},
3663+ { principal: 'team2'}],
3664+ as_team_admin: []
3665+ };
3666+ var subs = module._gather_subscriptions_as_assignee(mock_category);
3667+ Y.Assert.areEqual(1, subs.length);
3668+ Y.Assert.areEqual(module._reasons.TEAMS_ASSIGNED, subs[0].reason);
3669+ // And there is a 'teams' variable containing all the team objects.
3670+ Y.ArrayAssert.itemsAreEqual(['team1', 'team2'],
3671+ subs[0].vars.teams);
3672+ Y.Assert.areEqual(module._actions.CONTACT_TEAMS, subs[0].action);
3673+ },
3674+
3675+ test_team_member_multiple_duplicate: function() {
3676+ // As with the previous test, but we need to show that each team is
3677+ // only represented once even if they are responsible for multiple
3678+ // bug tasks.
3679+ // We test with full-fledged objects to make sure they work with the
3680+ // mechanism used to find dupes.
3681+ var team1 = {display_name: 'team 1',
3682+ web_link: 'http://launchpad.net/~team1'},
3683+ team2 = {display_name: 'team 2',
3684+ web_link: 'http://launchpad.net/~team2'},
3685+ mock_category = {
3686+ count: 2,
3687+ personal: [],
3688+ as_team_member: [{ principal: team1 },
3689+ { principal: team2 },
3690+ { principal: team2 }],
3691+ as_team_admin: []
3692+ },
3693+ subs = module._gather_subscriptions_as_assignee(mock_category);
3694+ Y.Assert.areEqual(1, subs.length);
3695+ Y.Assert.areEqual(module._reasons.TEAMS_ASSIGNED, subs[0].reason);
3696+ // And there is a 'teams' variable containing all the team objects.
3697+ var teams_found = [];
3698+ var index;
3699+ for (index = 0; index < subs[0].vars.teams.length; index++) {
3700+ teams_found.push(subs[0].vars.teams[index].title);
3701+ }
3702+ Y.ArrayAssert.itemsAreEqual(['team 1', 'team 2'], teams_found);
3703+ },
3704+
3705+ test_team_admin: function() {
3706+ // When a person is the bug assignee through team membership,
3707+ // and a team admin at the same time, that subscription is returned.
3708+ var mock_category = {
3709+ count: 1,
3710+ personal: [],
3711+ as_team_member: [],
3712+ as_team_admin: [{ principal: 'my team' }]
3713+ };
3714+ var subs = module._gather_subscriptions_as_assignee(mock_category);
3715+ Y.Assert.areEqual(1, subs.length);
3716+ Y.Assert.areEqual(
3717+ module._reasons.ADMIN_TEAM_ASSIGNED, subs[0].reason);
3718+ // And there is a 'team' variable containing the team object.
3719+ Y.Assert.areEqual('my team', subs[0].vars.team);
3720+ Y.Assert.areEqual(module._actions.CHANGE_ASSIGNEES, subs[0].action);
3721+ },
3722+
3723+ test_team_admin_multiple: function() {
3724+ // If a person is a member of multiple teams are assigned to work
3725+ // on a single bug (eg. on different bug tasks) they get only one
3726+ // subscription returned.
3727+ var mock_category = {
3728+ count: 2,
3729+ personal: [],
3730+ as_team_member: [],
3731+ as_team_admin: [{ principal: 'team1'},
3732+ { principal: 'team2'}]
3733+ };
3734+ var subs = module._gather_subscriptions_as_assignee(mock_category);
3735+ Y.Assert.areEqual(1, subs.length);
3736+ Y.Assert.areEqual(
3737+ module._reasons.ADMIN_TEAMS_ASSIGNED, subs[0].reason);
3738+ // And there is a 'teams' variable containing all the team objects.
3739+ Y.ArrayAssert.itemsAreEqual(['team1', 'team2'],
3740+ subs[0].vars.teams);
3741+ Y.Assert.areEqual(module._actions.CHANGE_ASSIGNEES, subs[0].action);
3742+ },
3743+
3744+ test_team_admin_multiple_duplicate: function() {
3745+ // As with the previous test, but we need to show that each team is
3746+ // only represented once even if they are responsible for multiple
3747+ // bug tasks.
3748+ // We test with full-fledged objects to make sure they work with the
3749+ // mechanism used to find dupes.
3750+ var team1 = {display_name: 'team 1',
3751+ web_link: 'http://launchpad.net/~team1'},
3752+ team2 = {display_name: 'team 2',
3753+ web_link: 'http://launchpad.net/~team2'},
3754+ mock_category = {
3755+ count: 2,
3756+ personal: [],
3757+ as_team_admin: [{ principal: team1 },
3758+ { principal: team2 },
3759+ { principal: team2 }],
3760+ as_team_member: []
3761+ },
3762+ subs = module._gather_subscriptions_as_assignee(mock_category);
3763+ Y.Assert.areEqual(1, subs.length);
3764+ // And there is a 'teams' variable containing all the team objects.
3765+ var teams_found = [];
3766+ for (index = 0; index < subs[0].vars.teams.length; index++) {
3767+ teams_found.push(subs[0].vars.teams[index].title);
3768+ }
3769+ Y.ArrayAssert.itemsAreEqual(['team 1', 'team 2'], teams_found);
3770+ },
3771+
3772+ test_combined: function() {
3773+ // Test that multiple assignments, even if they are in different
3774+ // categories, work properly.
3775+ var mock_category = {
3776+ count: 3,
3777+ personal: [{}],
3778+ as_team_member: [{ principal: 'users' }],
3779+ as_team_admin: [{ principal: 'admins' }]
3780+ };
3781+ var subs = module._gather_subscriptions_as_assignee(mock_category);
3782+ Y.Assert.areEqual(3, subs.length);
3783+ },
3784+
3785+ test_object_links: function() {
3786+ // Test that team assignments actually provide decent link data.
3787+ var mock_category = {
3788+ count: 1,
3789+ personal: [],
3790+ as_team_member: [
3791+ { principal: { display_name: 'My team',
3792+ web_link: 'http://link' } }],
3793+ as_team_admin: []
3794+ };
3795+ var subs = module._gather_subscriptions_as_assignee(mock_category);
3796+ Y.Assert.areEqual('My team', subs[0].vars.team.title);
3797+ Y.Assert.areEqual('http://link', subs[0].vars.team.url);
3798+ }
3799+ }));
3800+
3801+ /**
3802+ * Gather subscription records for bug supervisor.
3803+ */
3804+ tests.suite.add(new Y.Test.Case({
3805+ name: 'Gather bug supervisor subscription information',
3806+
3807+ test_nothing: function() {
3808+ // When there are no subscriptions as bug supervisor,
3809+ // returns empty list.
3810+ var mock_category = {
3811+ count: 0,
3812+ personal: [],
3813+ as_team_member: [],
3814+ as_team_admin: []
3815+ };
3816+ Y.ArrayAssert.itemsAreEqual(
3817+ [],
3818+ module._gather_subscriptions_as_supervisor(mock_category));
3819+ },
3820+
3821+ test_personal: function() {
3822+ // Person is the implicit bug supervisor by being the owner
3823+ // of the project with no bug supervisor.
3824+ var mock_category = {
3825+ count: 1,
3826+ personal: [{pillar: 'project'}],
3827+ as_team_member: [],
3828+ as_team_admin: []
3829+ };
3830+ var subs = module._gather_subscriptions_as_supervisor(mock_category);
3831+ Y.Assert.areEqual(1, subs.length);
3832+ Y.Assert.areEqual(module._reasons.YOU_OWNER, subs[0].reason);
3833+ Y.Assert.areEqual('project', subs[0].vars.pillar);
3834+ Y.Assert.areEqual(module._actions.SET_BUG_SUPERVISOR, subs[0].action);
3835+ },
3836+
3837+ test_personal_multiple: function() {
3838+ // Person is the implicit bug supervisor by being the owner
3839+ // of several projects (eg. multiple bug tasks) with no bug
3840+ // supervisor.
3841+ var mock_category = {
3842+ count: 2,
3843+ personal: [ {pillar: {title: 'project'} },
3844+ {pillar: {title:'distro'} }],
3845+ as_team_member: [],
3846+ as_team_admin: []
3847+ };
3848+ var subs = module._gather_subscriptions_as_supervisor(mock_category);
3849+ Y.Assert.areEqual(2, subs.length);
3850+ },
3851+
3852+ test_team_member: function() {
3853+ // Person is a member of the team which is the implicit
3854+ // bug supervisor.
3855+ var mock_category = {
3856+ count: 1,
3857+ personal: [],
3858+ as_team_member: [{ principal: 'my team',
3859+ pillar: 'project' }],
3860+ as_team_admin: []
3861+ };
3862+ var subs = module._gather_subscriptions_as_supervisor(mock_category);
3863+ Y.Assert.areEqual(1, subs.length);
3864+ Y.Assert.areEqual(module._reasons.TEAM_OWNER, subs[0].reason);
3865+ // And there is a 'team' variable containing the team object.
3866+ Y.Assert.areEqual('my team', subs[0].vars.team);
3867+ Y.Assert.areEqual('project', subs[0].vars.pillar);
3868+ Y.Assert.areEqual(module._actions.CONTACT_TEAMS, subs[0].action);
3869+ },
3870+
3871+ test_team_member_multiple: function() {
3872+ // Person is a member of several teams which are implicit bug
3873+ // supervisors on multiple bugtasks, we get subscription
3874+ // records separately.
3875+ var mock_category = {
3876+ count: 2,
3877+ personal: [],
3878+ as_team_member: [{ principal: 'team1',
3879+ pillar: {display_name: 'project'} },
3880+ { principal: 'team2',
3881+ pillar: {display_name: 'distro'} }],
3882+ as_team_admin: []
3883+ };
3884+ var subs = module._gather_subscriptions_as_supervisor(mock_category);
3885+ Y.Assert.areEqual(2, subs.length);
3886+ },
3887+
3888+ test_team_admin: function() {
3889+ // Person is an admin of the team which is the implicit
3890+ // bug supervisor.
3891+ var mock_category = {
3892+ count: 1,
3893+ personal: [],
3894+ as_team_member: [],
3895+ as_team_admin: [{ principal: 'my team',
3896+ pillar: 'project' }]
3897+ };
3898+ var subs = module._gather_subscriptions_as_supervisor(mock_category);
3899+ Y.Assert.areEqual(1, subs.length);
3900+ Y.Assert.areEqual(
3901+ module._reasons.ADMIN_TEAM_OWNER, subs[0].reason);
3902+ // And there is a 'team' variable containing the team object.
3903+ Y.Assert.areEqual('my team', subs[0].vars.team);
3904+ Y.Assert.areEqual('project', subs[0].vars.pillar);
3905+ Y.Assert.areEqual(module._actions.SET_BUG_SUPERVISOR, subs[0].action);
3906+ },
3907+
3908+ test_team_admin_multiple: function() {
3909+ // Person is an admin of several teams which are implicit bug
3910+ // supervisors on multiple bugtasks, we get subscription
3911+ // records separately.
3912+ var mock_category = {
3913+ count: 2,
3914+ personal: [],
3915+ as_team_member: [],
3916+ as_team_admin: [{ principal: 'team1',
3917+ pillar: {display_name: 'project'} },
3918+ { principal: 'team2',
3919+ pillar: {display_name: 'distro'} }]
3920+ };
3921+ var subs = module._gather_subscriptions_as_supervisor(mock_category);
3922+ Y.Assert.areEqual(2, subs.length);
3923+ },
3924+
3925+ test_repeated_pillars: function() {
3926+ // Different bug tasks might still be on the same pillar,
3927+ // and we should only get one action.
3928+ var mock_pillar = { display_name: 'project',
3929+ web_link: 'http://project/' };
3930+ var mock_category = {
3931+ count: 1,
3932+ personal: [{pillar: mock_pillar},
3933+ {pillar: mock_pillar}],
3934+ as_team_member: [],
3935+ as_team_admin: []
3936+ };
3937+ var subs = module._gather_subscriptions_as_supervisor(mock_category);
3938+ Y.Assert.areEqual(1, subs.length);
3939+ Y.Assert.areEqual(module._reasons.YOU_OWNER, subs[0].reason);
3940+ Y.Assert.areEqual(mock_pillar, subs[0].vars.pillar.self);
3941+ Y.Assert.areEqual(module._actions.SET_BUG_SUPERVISOR, subs[0].action);
3942+ },
3943+
3944+ test_combined: function() {
3945+ // Test that multiple implicit bug supervisor roles
3946+ // are all returned.
3947+ var mock_category = {
3948+ count: 3,
3949+ personal: [{pillar: 'project1'}],
3950+ as_team_member: [{ principal: 'users', pillar: 'project2' }],
3951+ as_team_admin: [{ principal: 'admins', pillar: 'distro' }]
3952+ };
3953+ var subs = module._gather_subscriptions_as_assignee(mock_category);
3954+ Y.Assert.areEqual(3, subs.length);
3955+ },
3956+
3957+ test_object_links: function() {
3958+ // Test that team-as-supervisor actually provide decent link data,
3959+ // along with pillars as well.
3960+ var mock_category = {
3961+ count: 1,
3962+ personal: [],
3963+ as_team_member: [{
3964+ principal: { display_name: 'My team',
3965+ web_link: 'http://link' },
3966+ pillar: { display_name: 'My project',
3967+ web_link: 'http://project/' }
3968+ }],
3969+ as_team_admin: []
3970+ };
3971+ var subs = module._gather_subscriptions_as_supervisor(mock_category);
3972+ Y.Assert.areEqual('My team', subs[0].vars.team.title);
3973+ Y.Assert.areEqual('http://link', subs[0].vars.team.url);
3974+
3975+ Y.Assert.areEqual('My project', subs[0].vars.pillar.title);
3976+ Y.Assert.areEqual('http://project/', subs[0].vars.pillar.url);
3977+ }
3978+ }));
3979+
3980+ /**
3981+ * Gather subscription records for dupe bug subscriptions.
3982+ */
3983+ tests.suite.add(new Y.Test.Case({
3984+ name: 'Gather subscription information for duplicates',
3985+
3986+ test_nothing: function() {
3987+ // When there are no duplicate subscriptions, returns empty list.
3988+ var mock_category = {
3989+ count: 0,
3990+ personal: [],
3991+ as_team_member: [],
3992+ as_team_admin: []
3993+ };
3994+ Y.ArrayAssert.itemsAreEqual(
3995+ [],
3996+ module._gather_subscriptions_from_duplicates(mock_category));
3997+ },
3998+
3999+ test_personal: function() {
4000+ // A person is subscribed to a duplicate bug.
4001+ var mock_category = {
4002+ count: 1,
4003+ personal: [{bug: 'dupe bug'}],
4004+ as_team_member: [],
4005+ as_team_admin: []
4006+ };
4007+ var subs = module._gather_subscriptions_from_duplicates(
4008+ mock_category);
4009+ Y.Assert.areEqual(1, subs.length);
4010+ Y.Assert.areEqual(
4011+ module._reasons.YOU_SUBSCRIBED_TO_DUPLICATE, subs[0].reason);
4012+ Y.Assert.areEqual('dupe bug', subs[0].vars.duplicate_bug);
4013+ Y.Assert.areEqual(module._actions.UNSUBSCRIBE_DUPLICATES,
4014+ subs[0].action);
4015+ },
4016+
4017+ test_personal_multiple: function() {
4018+ // A person is subscribed to multiple duplicate bugs.
4019+ // They are returned together as one subscription record.
4020+ var mock_category = {
4021+ count: 2,
4022+ personal: [{bug: 'dupe1'}, {bug: 'dupe2'}],
4023+ as_team_member: [],
4024+ as_team_admin: []
4025+ };
4026+ var subs = module._gather_subscriptions_from_duplicates(
4027+ mock_category);
4028+ Y.Assert.areEqual(1, subs.length);
4029+ Y.Assert.areEqual(
4030+ module._reasons.YOU_SUBSCRIBED_TO_DUPLICATES, subs[0].reason);
4031+ Y.ArrayAssert.itemsAreEqual(
4032+ ['dupe1', 'dupe2'], subs[0].vars.duplicate_bugs);
4033+ Y.Assert.areEqual(module._actions.UNSUBSCRIBE_DUPLICATES,
4034+ subs[0].action);
4035+ },
4036+
4037+ test_team_member: function() {
4038+ // A person is a member of the team subscribed to a duplicate bug.
4039+ var mock_category = {
4040+ count: 1,
4041+ personal: [],
4042+ as_team_member: [{ principal: 'my team',
4043+ bug: 'dupe' }],
4044+ as_team_admin: []
4045+ };
4046+ var subs = module._gather_subscriptions_from_duplicates(
4047+ mock_category);
4048+ Y.Assert.areEqual(1, subs.length);
4049+ Y.Assert.areEqual(
4050+ module._reasons.TEAM_SUBSCRIBED_TO_DUPLICATE, subs[0].reason);
4051+ // And there is a 'team' variable containing the team object.
4052+ Y.Assert.areEqual('my team', subs[0].vars.team);
4053+ // And a 'duplicate_bug' variable pointing to the dupe.
4054+ Y.Assert.areEqual('dupe', subs[0].vars.duplicate_bug);
4055+ Y.Assert.areEqual(module._actions.CONTACT_TEAMS, subs[0].action);
4056+ },
4057+
4058+ test_team_member_multiple_bugs: function() {
4059+ // A person is a member of the team subscribed to multiple
4060+ // duplicate bugs.
4061+ var mock_category = {
4062+ count: 1,
4063+ personal: [],
4064+ as_team_member: [{
4065+ principal: 'my team',
4066+ bug: 'dupe1'
4067+ }, {
4068+ principal: 'my team',
4069+ bug: 'dupe2'
4070+ }],
4071+ as_team_admin: []
4072+ };
4073+ var subs = module._gather_subscriptions_from_duplicates(
4074+ mock_category);
4075+ Y.Assert.areEqual(1, subs.length);
4076+ Y.Assert.areEqual(
4077+ module._reasons.TEAM_SUBSCRIBED_TO_DUPLICATES, subs[0].reason);
4078+ // And there is a 'team' variable containing the team object.
4079+ Y.Assert.areEqual('my team', subs[0].vars.team);
4080+ // And a 'duplicate_bugs' variable with the list of dupes.
4081+ Y.ArrayAssert.itemsAreEqual(
4082+ ['dupe1', 'dupe2'], subs[0].vars.duplicate_bugs);
4083+ Y.Assert.areEqual(module._actions.CONTACT_TEAMS, subs[0].action);
4084+ },
4085+
4086+ test_team_member_multiple: function() {
4087+ // A person is a member of several teams subscribed to
4088+ // duplicate bugs.
4089+ var mock_category = {
4090+ count: 2,
4091+ personal: [],
4092+ as_team_member: [{ principal: 'team1',
4093+ bug: 'dupe1' },
4094+ { principal: 'team2',
4095+ bug: 'dupe1' }],
4096+ as_team_admin: []
4097+ };
4098+
4099+ // Result is two separate subscription records.
4100+ var subs = module._gather_subscriptions_from_duplicates(
4101+ mock_category);
4102+ Y.Assert.areEqual(2, subs.length);
4103+ },
4104+
4105+ test_team_admin: function() {
4106+ // A person is an admin of the team subscribed to a duplicate bug.
4107+ var mock_category = {
4108+ count: 1,
4109+ personal: [],
4110+ as_team_member: [],
4111+ as_team_admin: [{ principal: 'my team',
4112+ bug: 'dupe' }]
4113+ };
4114+ var subs = module._gather_subscriptions_from_duplicates(
4115+ mock_category);
4116+ Y.Assert.areEqual(1, subs.length);
4117+ Y.Assert.areEqual(
4118+ module._reasons.ADMIN_TEAM_SUBSCRIBED_TO_DUPLICATE,
4119+ subs[0].reason);
4120+ // And there is a 'team' variable containing the team object.
4121+ Y.Assert.areEqual('my team', subs[0].vars.team);
4122+ // And a 'duplicate_bug' variable pointing to the dupe.
4123+ Y.Assert.areEqual('dupe', subs[0].vars.duplicate_bug);
4124+ Y.Assert.areEqual(module._actions.UNSUBSCRIBE_DUPLICATES,
4125+ subs[0].action);
4126+ },
4127+
4128+ test_team_admin_multiple_bugs: function() {
4129+ // A person is an admin of the team subscribed to multiple
4130+ // duplicate bugs.
4131+ var mock_category = {
4132+ count: 1,
4133+ personal: [],
4134+ as_team_member: [],
4135+ as_team_admin: [{
4136+ principal: 'my team',
4137+ bug: 'dupe1'
4138+ }, {
4139+ principal: 'my team',
4140+ bug: 'dupe2'
4141+ }]
4142+ };
4143+ var subs = module._gather_subscriptions_from_duplicates(
4144+ mock_category);
4145+ Y.Assert.areEqual(1, subs.length);
4146+ Y.Assert.areEqual(
4147+ module._reasons.ADMIN_TEAM_SUBSCRIBED_TO_DUPLICATES,
4148+ subs[0].reason);
4149+ // And there is a 'team' variable containing the team object.
4150+ Y.Assert.areEqual('my team', subs[0].vars.team);
4151+ // And a 'duplicate_bugs' variable with the list of dupes.
4152+ Y.ArrayAssert.itemsAreEqual(
4153+ ['dupe1', 'dupe2'], subs[0].vars.duplicate_bugs);
4154+ Y.Assert.areEqual(module._actions.UNSUBSCRIBE_DUPLICATES,
4155+ subs[0].action);
4156+ },
4157+
4158+ test_team_admin_multiple: function() {
4159+ // A person is an admin of several teams subscribed to
4160+ // duplicate bugs.
4161+ var mock_category = {
4162+ count: 2,
4163+ personal: [],
4164+ as_team_member: [],
4165+ as_team_admin: [{ principal: 'team1',
4166+ bug: 'dupe1' },
4167+ { principal: 'team2',
4168+ bug: 'dupe1' }]
4169+ };
4170+
4171+ // Result is two separate subscription records.
4172+ var subs = module._gather_subscriptions_from_duplicates(
4173+ mock_category);
4174+ Y.Assert.areEqual(2, subs.length);
4175+ },
4176+
4177+ test_object_links: function() {
4178+ // Test that team dupe subscriptions actually provide decent
4179+ // link data, including duplicate bugs link data.
4180+ var mock_category = {
4181+ count: 1,
4182+ personal: [],
4183+ as_team_member: [{
4184+ principal: { display_name: 'My team',
4185+ web_link: 'http://link' },
4186+ bug: { id: 1,
4187+ web_link: 'http://launchpad/bug/1' }
4188+ }],
4189+ as_team_admin: []
4190+ };
4191+ var subs = module._gather_subscriptions_from_duplicates(
4192+ mock_category);
4193+ Y.Assert.areEqual('My team', subs[0].vars.team.title);
4194+ Y.Assert.areEqual('http://link', subs[0].vars.team.url);
4195+
4196+ Y.Assert.areEqual('#1', subs[0].vars.duplicate_bug.title);
4197+ Y.Assert.areEqual(
4198+ 'http://launchpad/bug/1', subs[0].vars.duplicate_bug.url);
4199+ }
4200+ }));
4201+
4202+ /**
4203+ * Gather subscription records for direct team subscriptions.
4204+ */
4205+ tests.suite.add(new Y.Test.Case({
4206+ name: 'Gather team subscription information',
4207+
4208+ test_nothing: function() {
4209+ // When there are no subscriptions through team, returns empty list.
4210+ var mock_category = {
4211+ count: 0,
4212+ personal: [],
4213+ as_team_member: [],
4214+ as_team_admin: []
4215+ };
4216+ Y.ArrayAssert.itemsAreEqual(
4217+ [],
4218+ module._gather_subscriptions_through_team(mock_category));
4219+ },
4220+
4221+ test_personal: function() {
4222+ // A personal subscription is not considered a team subscription.
4223+ var mock_category = {
4224+ count: 1,
4225+ personal: [{}],
4226+ as_team_member: [],
4227+ as_team_admin: []
4228+ };
4229+ Y.ArrayAssert.itemsAreEqual(
4230+ [],
4231+ module._gather_subscriptions_through_team(mock_category));
4232+ },
4233+
4234+ test_team_member: function() {
4235+ // Person is a member of the team subscribed to the bug.
4236+ var mock_category = {
4237+ count: 1,
4238+ personal: [],
4239+ as_team_member: [{ principal: 'my team'}],
4240+ as_team_admin: []
4241+ };
4242+ var subs = module._gather_subscriptions_through_team(mock_category);
4243+ Y.Assert.areEqual(1, subs.length);
4244+ Y.Assert.areEqual(module._reasons.TEAM_SUBSCRIBED, subs[0].reason);
4245+ // And there is a 'team' variable containing the team object.
4246+ Y.Assert.areEqual('my team', subs[0].vars.team);
4247+ Y.Assert.areEqual(module._actions.CONTACT_TEAMS, subs[0].action);
4248+ },
4249+
4250+ test_team_member_multiple: function() {
4251+ // Person is a member of several teams subscribed to the bug.
4252+ var mock_category = {
4253+ count: 2,
4254+ personal: [],
4255+ as_team_member: [{ principal: 'team1'},
4256+ { principal: 'team2'}],
4257+ as_team_admin: []
4258+ };
4259+ var subs = module._gather_subscriptions_through_team(mock_category);
4260+ Y.Assert.areEqual(1, subs.length);
4261+ Y.Assert.areEqual(module._reasons.TEAMS_SUBSCRIBED, subs[0].reason);
4262+ // And there is a 'teams' variable containing all the team objects.
4263+ Y.ArrayAssert.itemsAreEqual(['team1', 'team2'],
4264+ subs[0].vars.teams);
4265+ Y.Assert.areEqual(module._actions.CONTACT_TEAMS, subs[0].action);
4266+ },
4267+
4268+ test_team_member_multiple_duplicate: function() {
4269+ // As with the previous test, but we need to show that each team is
4270+ // only represented once even if they are responsible for multiple
4271+ // bug tasks.
4272+ // We test with full-fledged objects to make sure they work with the
4273+ // mechanism used to find dupes.
4274+ var team1 = {display_name: 'team 1',
4275+ web_link: 'http://launchpad.net/~team1'},
4276+ team2 = {display_name: 'team 2',
4277+ web_link: 'http://launchpad.net/~team2'},
4278+ mock_category = {
4279+ count: 2,
4280+ personal: [],
4281+ as_team_member: [{ principal: team1 },
4282+ { principal: team2 },
4283+ { principal: team2 }],
4284+ as_team_admin: []
4285+ },
4286+ subs = module._gather_subscriptions_through_team(mock_category);
4287+ Y.Assert.areEqual(1, subs.length);
4288+ // And there is a 'teams' variable containing all the team objects.
4289+ var teams_found = [];
4290+ for (index = 0; index < subs[0].vars.teams.length; index++) {
4291+ teams_found.push(subs[0].vars.teams[index].title);
4292+ }
4293+ Y.ArrayAssert.itemsAreEqual(['team 1', 'team 2'], teams_found);
4294+ },
4295+
4296+ test_team_admin: function() {
4297+ // Person is an admin of the team subscribed to the bug.
4298+ var mock_category = {
4299+ count: 1,
4300+ personal: [],
4301+ as_team_member: [],
4302+ as_team_admin: [{ principal: 'my team' }]
4303+ };
4304+ var subs = module._gather_subscriptions_through_team(mock_category);
4305+ Y.Assert.areEqual(1, subs.length);
4306+ Y.Assert.areEqual(
4307+ module._reasons.ADMIN_TEAM_SUBSCRIBED, subs[0].reason);
4308+ // And there is a 'team' variable containing the team object.
4309+ Y.Assert.areEqual('my team', subs[0].vars.team);
4310+ Y.Assert.areEqual(module._actions.CHANGE_TEAM_SUBSCRIPTIONS,
4311+ subs[0].action);
4312+ },
4313+
4314+ test_team_admin_multiple: function() {
4315+ // Person is an admin of the several teams subscribed to the bug.
4316+ var mock_category = {
4317+ count: 2,
4318+ personal: [],
4319+ as_team_member: [],
4320+ as_team_admin: [{ principal: 'team1'},
4321+ { principal: 'team2'}]
4322+ };
4323+ var subs = module._gather_subscriptions_through_team(mock_category);
4324+ Y.Assert.areEqual(1, subs.length);
4325+ Y.Assert.areEqual(
4326+ module._reasons.ADMIN_TEAMS_SUBSCRIBED, subs[0].reason);
4327+ // And there is a 'teams' variable containing all the team objects.
4328+ Y.ArrayAssert.itemsAreEqual(['team1', 'team2'],
4329+ subs[0].vars.teams);
4330+ Y.Assert.areEqual(module._actions.CHANGE_TEAM_SUBSCRIPTIONS,
4331+ subs[0].action);
4332+ },
4333+
4334+ test_team_admin_multiple_duplicate: function() {
4335+ // As with the previous test, but we need to show that each team is
4336+ // only represented once even if they are responsible for multiple
4337+ // bug tasks.
4338+ // We test with full-fledged objects to make sure they work with the
4339+ // mechanism used to find dupes.
4340+ var team1 = {display_name: 'team 1',
4341+ web_link: 'http://launchpad.net/~team1'},
4342+ team2 = {display_name: 'team 2',
4343+ web_link: 'http://launchpad.net/~team2'},
4344+ mock_category = {
4345+ count: 2,
4346+ personal: [],
4347+ as_team_admin: [{ principal: team1 },
4348+ { principal: team2 },
4349+ { principal: team2 }],
4350+ as_team_member: []
4351+ },
4352+ subs = module._gather_subscriptions_through_team(mock_category);
4353+ Y.Assert.areEqual(1, subs.length);
4354+ // And there is a 'teams' variable containing all the team objects.
4355+ var teams_found = [];
4356+ for (index = 0; index < subs[0].vars.teams.length; index++) {
4357+ teams_found.push(subs[0].vars.teams[index].title);
4358+ }
4359+ Y.ArrayAssert.itemsAreEqual(['team 1', 'team 2'], teams_found);
4360+ },
4361+
4362+ test_combined: function() {
4363+ // Test that multiple subscriptions, even if they are in different
4364+ // categories, work properly, and that personal subscriptions are
4365+ // still ignored.
4366+ var mock_category = {
4367+ count: 3,
4368+ personal: [{}],
4369+ as_team_member: [{ principal: 'users' }],
4370+ as_team_admin: [{ principal: 'admins' }]
4371+ };
4372+ var subs = module._gather_subscriptions_through_team(mock_category);
4373+ Y.Assert.areEqual(2, subs.length);
4374+ },
4375+
4376+ test_object_links: function() {
4377+ // Test that team subscriptions actually provide decent link data.
4378+ var mock_category = {
4379+ count: 1,
4380+ personal: [],
4381+ as_team_member: [
4382+ { principal: { display_name: 'My team',
4383+ web_link: 'http://link' } }],
4384+ as_team_admin: []
4385+ };
4386+ var subs = module._gather_subscriptions_through_team(mock_category);
4387+ Y.Assert.areEqual('My team', subs[0].vars.team.title);
4388+ Y.Assert.areEqual('http://link', subs[0].vars.team.url);
4389+ }
4390+ }));
4391+
4392+ /**
4393+ * Get the reason for a direct subscription.
4394+ * Tests for method get_direct_subscription_information().
4395+ */
4396+ tests.suite.add(new Y.Test.Case({
4397+ name: 'Get reason and actions for a direct subscription',
4398+
4399+ _should: {
4400+ error: {
4401+ test_multiple_direct_subscriptions:
4402+ new Error('Programmer error: a person should not have more than '+
4403+ 'one direct personal subscription.'),
4404+ test_direct_subscription_at_unknown_level:
4405+ new Error('Programmer error: unknown bug notification level: '+
4406+ 'The Larch')
4407+ }
4408+ },
4409+
4410+ setUp: function() {
4411+ window.LP = {cache: {subscription_info: []}};
4412+ },
4413+
4414+ tearDown: function() {
4415+ delete window.LP;
4416+ },
4417+
4418+ test_multiple_direct_subscriptions: function() {
4419+ // It should not be possible to have multiple direct,
4420+ // personal subscriptions.
4421+ // This errors out (see _should.error above).
4422+ var info = {
4423+ direct: _constructCategory(['1', '2']),
4424+ count: 2
4425+ };
4426+ module._get_direct_subscription_information(info);
4427+ },
4428+
4429+ test_no_subscriptions_at_all: function() {
4430+ // There are no subscriptions at all.
4431+ var info = {
4432+ direct: _constructCategory(),
4433+ from_duplicates: _constructCategory()
4434+ };
4435+ info.count = info.direct.count + info.from_duplicates.count;
4436+
4437+ direct_info = module._get_direct_subscription_information(info);
4438+ Y.Assert.areEqual(
4439+ module._reasons.NOT_SUBSCRIBED,
4440+ direct_info.reason);
4441+ Y.ArrayAssert.itemsAreEqual(
4442+ [],
4443+ direct_info.reductions);
4444+ Y.ArrayAssert.itemsAreEqual(
4445+ ['select-direct-subscription-discussion',
4446+ 'select-direct-subscription-metadata',
4447+ 'select-direct-subscription-lifecycle'],
4448+ direct_info.increases);
4449+ },
4450+
4451+ test_only_structural_subscriptions: function() {
4452+ // There are only structural subscriptions.
4453+ var info = {
4454+ direct: _constructCategory(),
4455+ from_duplicates: _constructCategory()
4456+ };
4457+ info.count = info.direct.count + info.from_duplicates.count;
4458+ window.LP.cache.subscription_info.push(true);
4459+
4460+ direct_info = module._get_direct_subscription_information(info);
4461+ Y.Assert.areSame(
4462+ module._reasons.NOT_PERSONALLY_SUBSCRIBED,
4463+ direct_info.reason);
4464+ Y.ArrayAssert.itemsAreEqual(
4465+ ['mute-direct-subscription',
4466+ 'select-only-direct-subscription-metadata',
4467+ 'select-only-direct-subscription-lifecycle'],
4468+ direct_info.reductions);
4469+ Y.ArrayAssert.itemsAreEqual(
4470+ ['select-direct-subscription-discussion'],
4471+ direct_info.increases);
4472+ },
4473+
4474+ test_no_direct_subscriptions: function() {
4475+ // There is no direct subscription, but there are
4476+ // other subscriptions.
4477+ var info = {
4478+ direct: _constructCategory(),
4479+ from_duplicates: _constructCategory(['dupe'])
4480+ };
4481+ info.count = info.direct.count + info.from_duplicates.count;
4482+ direct_info = module._get_direct_subscription_information(info);
4483+ Y.Assert.areSame(
4484+ module._reasons.NOT_PERSONALLY_SUBSCRIBED,
4485+ direct_info.reason);
4486+ Y.ArrayAssert.itemsAreEqual(
4487+ ['mute-direct-subscription',
4488+ 'select-only-direct-subscription-metadata',
4489+ 'select-only-direct-subscription-lifecycle'],
4490+ direct_info.reductions);
4491+ Y.ArrayAssert.itemsAreEqual(
4492+ ['select-direct-subscription-discussion'],
4493+ direct_info.increases);
4494+ },
4495+
4496+ test_muted_subscription: function() {
4497+ // The direct subscription is muted.
4498+ var info = {
4499+ direct: _constructCategory(['direct']),
4500+ muted: true
4501+ };
4502+ info.count = info.direct.count;
4503+ direct_info = module._get_direct_subscription_information(info);
4504+ Y.Assert.areSame(
4505+ module._reasons.MUTED_SUBSCRIPTION,
4506+ direct_info.reason);
4507+ Y.ArrayAssert.itemsAreEqual(
4508+ [],
4509+ direct_info.reductions);
4510+ Y.ArrayAssert.itemsAreEqual(
4511+ ['unmute-direct-subscription'],
4512+ direct_info.increases);
4513+ },
4514+
4515+ test_direct_subscription_at_discussion_level: function() {
4516+ // The larch^D^D^D^D^D^D simple direct subscription.
4517+ var sub = {
4518+ bug: {
4519+ 'private': false,
4520+ security_related: false
4521+ },
4522+ principal_is_reporter: false,
4523+ subscription: {bug_notification_level: 'Discussion'}
4524+ };
4525+ var info = {
4526+ direct: _constructCategory([sub]),
4527+ count: 1
4528+ };
4529+
4530+ var direct_info = module._get_direct_subscription_information(info);
4531+ Y.Assert.areSame(
4532+ module._reasons.YOU_SUBSCRIBED,
4533+ direct_info.reason);
4534+ Y.ArrayAssert.itemsAreEqual(
4535+ ['mute-direct-subscription',
4536+ 'select-only-direct-subscription-metadata',
4537+ 'select-only-direct-subscription-lifecycle',
4538+ 'remove-direct-subscription'],
4539+ direct_info.reductions);
4540+ Y.ArrayAssert.itemsAreEqual(
4541+ [],
4542+ direct_info.increases);
4543+ },
4544+
4545+ test_direct_subscription_at_metadata_level: function() {
4546+ // The simple direct subscription at metadata level.
4547+ var sub = {
4548+ bug: {
4549+ 'private': false,
4550+ security_related: false
4551+ },
4552+ principal_is_reporter: false,
4553+ subscription: {bug_notification_level: 'Details'}
4554+ };
4555+ var info = {
4556+ direct: _constructCategory([sub]),
4557+ count: 1
4558+ };
4559+
4560+ var direct_info = module._get_direct_subscription_information(info);
4561+ Y.Assert.areSame(
4562+ module._reasons.YOU_SUBSCRIBED,
4563+ direct_info.reason);
4564+ Y.ArrayAssert.itemsAreEqual(
4565+ ['mute-direct-subscription',
4566+ 'select-only-direct-subscription-lifecycle',
4567+ 'remove-direct-subscription'],
4568+ direct_info.reductions);
4569+ Y.ArrayAssert.itemsAreEqual(
4570+ ['select-direct-subscription-discussion'],
4571+ direct_info.increases);
4572+ },
4573+
4574+ test_direct_subscription_at_lifecycle_level: function() {
4575+ // The simple direct subscription at lifecycle level.
4576+ var sub = {
4577+ bug: {
4578+ 'private': false,
4579+ security_related: false
4580+ },
4581+ principal_is_reporter: false,
4582+ subscription: {bug_notification_level: 'Lifecycle'}
4583+ };
4584+ var info = {
4585+ direct: _constructCategory([sub]),
4586+ count: 1
4587+ };
4588+
4589+ var direct_info = module._get_direct_subscription_information(info);
4590+ Y.Assert.areSame(
4591+ module._reasons.YOU_SUBSCRIBED,
4592+ direct_info.reason);
4593+ Y.ArrayAssert.itemsAreEqual(
4594+ ['mute-direct-subscription',
4595+ 'remove-direct-subscription'],
4596+ direct_info.reductions);
4597+ Y.ArrayAssert.itemsAreEqual(
4598+ ['select-direct-subscription-discussion',
4599+ 'select-direct-subscription-metadata'],
4600+ direct_info.increases);
4601+ },
4602+
4603+ test_direct_subscription_at_unknown_level: function() {
4604+ // The simple direct subscription at unknown level.
4605+ var sub = {
4606+ bug: {
4607+ 'private': false,
4608+ security_related: false
4609+ },
4610+ principal_is_reporter: false,
4611+ subscription: {bug_notification_level: 'The Larch'}
4612+ };
4613+ var info = {
4614+ direct: _constructCategory([sub]),
4615+ count: 1
4616+ };
4617+ // This should raise an error.
4618+ module._get_direct_subscription_information(info);
4619+ },
4620+
4621+ test_direct_subscription_as_reporter: function() {
4622+ // The direct subscription created for bug reporter.
4623+ var sub = {
4624+ bug: {},
4625+ principal_is_reporter: true,
4626+ subscription: {bug_notification_level: 'Discussion'}
4627+ };
4628+ var info = {
4629+ direct: _constructCategory([sub]),
4630+ count: 1
4631+ };
4632+
4633+ var direct_info = module._get_direct_subscription_information(info);
4634+ Y.Assert.areSame(
4635+ module._reasons.YOU_REPORTED,
4636+ direct_info.reason);
4637+ Y.ArrayAssert.itemsAreEqual(
4638+ ['mute-direct-subscription',
4639+ 'select-only-direct-subscription-metadata',
4640+ 'select-only-direct-subscription-lifecycle',
4641+ 'remove-direct-subscription'],
4642+ direct_info.reductions);
4643+ Y.ArrayAssert.itemsAreEqual(
4644+ [],
4645+ direct_info.increases);
4646+ },
4647+
4648+ test_direct_subscription_for_supervisor: function() {
4649+ // The direct subscription created on private bugs for
4650+ // the bug supervisor.
4651+ var sub = {
4652+ bug: {
4653+ 'private': true
4654+ },
4655+ subscription: {bug_notification_level: 'Discussion'}
4656+ };
4657+ var info = {
4658+ direct: _constructCategory([sub]),
4659+ count: 1
4660+ };
4661+ var direct_info = module._get_direct_subscription_information(info);
4662+ Y.Assert.areSame(
4663+ module._reasons.YOU_SUBSCRIBED_BUG_SUPERVISOR,
4664+ direct_info.reason);
4665+ Y.ArrayAssert.itemsAreEqual(
4666+ ['mute-direct-subscription',
4667+ 'select-only-direct-subscription-metadata',
4668+ 'select-only-direct-subscription-lifecycle',
4669+ 'remove-direct-subscription'],
4670+ direct_info.reductions);
4671+ Y.ArrayAssert.itemsAreEqual(
4672+ [],
4673+ direct_info.increases);
4674+ },
4675+
4676+ test_direct_subscription_for_security_contact: function() {
4677+ // The simple direct subscription.
4678+ var sub = {
4679+ bug: {
4680+ security_related: true
4681+ },
4682+ subscription: {bug_notification_level: 'Discussion'}
4683+ };
4684+ var info = {
4685+ direct: _constructCategory([sub]),
4686+ count: 1
4687+ };
4688+ var direct_info = module._get_direct_subscription_information(info);
4689+ Y.Assert.areSame(
4690+ module._reasons.YOU_SUBSCRIBED_SECURITY_CONTACT,
4691+ direct_info.reason);
4692+ Y.ArrayAssert.itemsAreEqual(
4693+ ['mute-direct-subscription',
4694+ 'select-only-direct-subscription-metadata',
4695+ 'select-only-direct-subscription-lifecycle',
4696+ 'remove-direct-subscription'],
4697+ direct_info.reductions);
4698+ Y.ArrayAssert.itemsAreEqual(
4699+ [],
4700+ direct_info.increases);
4701+ },
4702+
4703+ test_direct_subscription_and_other_subscriptions: function() {
4704+ // Other subscriptions are present along with the simple direct
4705+ // subscription.
4706+ var sub = {
4707+ bug: {
4708+ 'private': false,
4709+ security_related: false
4710+ },
4711+ principal_is_reporter: false,
4712+ subscription: {bug_notification_level: 'Discussion'}
4713+ };
4714+ var info = {
4715+ direct: _constructCategory([sub]),
4716+ from_duplicates: _constructCategory(['dupe']),
4717+ count: 2
4718+ };
4719+
4720+ var direct_info = module._get_direct_subscription_information(info);
4721+ Y.Assert.areSame(
4722+ module._reasons.YOU_SUBSCRIBED,
4723+ direct_info.reason);
4724+ Y.ArrayAssert.itemsAreEqual(
4725+ ['mute-direct-subscription',
4726+ 'select-only-direct-subscription-metadata',
4727+ 'select-only-direct-subscription-lifecycle',
4728+ 'remove-direct-subscription-with-warning'],
4729+ direct_info.reductions);
4730+ Y.ArrayAssert.itemsAreEqual(
4731+ [],
4732+ direct_info.increases);
4733+ }
4734+
4735+ }));
4736+
4737+ /**
4738+ * Test for get_objectlink_html() method.
4739+ */
4740+ tests.suite.add(new Y.Test.Case({
4741+ name: 'Test conversion of ObjectLink to HTML.',
4742+
4743+ _should: {
4744+ error: {
4745+ test_non_link: new Error('Not a proper ObjectLink.')
4746+ }
4747+ },
4748+
4749+ test_string: function() {
4750+ // When a string is passed in, it is returned unmodified.
4751+ var link = 'test';
4752+ Y.Assert.areEqual(
4753+ link,
4754+ module._get_objectlink_html(link));
4755+ },
4756+
4757+ test_non_link: function() {
4758+ // When an object that doesn't have both 'title' and 'url'
4759+ // passed in, it fails. (see _should.error above)
4760+ var link = {};
4761+ module._get_objectlink_html(link);
4762+ },
4763+
4764+ test_simple: function() {
4765+ // When a string is passed in, it is returned unmodified.
4766+ var link = {
4767+ title: 'Title',
4768+ url: 'http://url/'
4769+ };
4770+ Y.Assert.areEqual(
4771+ '<a href="http://url/">Title</a>',
4772+ module._get_objectlink_html(link));
4773+ },
4774+
4775+ test_escaping_title: function() {
4776+ // Even with title containing HTML characters, they are properly
4777+ // escaped.
4778+ var link = {
4779+ title: 'Title<script>',
4780+ url: 'http://url/'
4781+ };
4782+ Y.Assert.areEqual(
4783+ '<a href="http://url/">Title&lt;script&gt;</a>',
4784+ module._get_objectlink_html(link));
4785+ },
4786+
4787+ test_escaping_url: function() {
4788+ // Even with title containing HTML characters, they are properly
4789+ // escaped.
4790+ var url = 'http://url/" onclick="javascript:alert(\'test\');" a="';
4791+ var link = {
4792+ title: 'Title',
4793+ url: url
4794+ };
4795+ // Firefox returns:
4796+ // '<a href="http://url/%22%20onclick=%22' +
4797+ // 'javascript:alert%28%27test%27%29;%22%20a=%22">Title</a>'
4798+ // WebKit returns:
4799+ // '<a href="http://url/&quot; onclick=&quot;'+
4800+ // 'javascript:alert(\'test\');&quot; a=&quot;">Title</a>'
4801+ Y.Assert.areNotEqual(
4802+ '<a href="' + url + '">Title</a>',
4803+ module._get_objectlink_html(link));
4804+ }
4805+
4806+ }));
4807+
4808+ /**
4809+ * Test for safely_render_description() method.
4810+ */
4811+ tests.suite.add(new Y.Test.Case({
4812+ name: 'Test variable substitution in subscription descriptions.',
4813+
4814+ _should: {
4815+ error: {
4816+ test_non_link: new Error('Not a proper ObjectLink.')
4817+ }
4818+ },
4819+
4820+ test_no_variables: function() {
4821+ // For a string with no variables, no substitution is performed.
4822+ var sub = {
4823+ reason: 'test string with no vars',
4824+ vars: { no: 'vars' }
4825+ };
4826+
4827+ Y.Assert.areEqual(
4828+ sub.reason,
4829+ module._safely_render_description(sub));
4830+ },
4831+
4832+ test_missing_variable: function() {
4833+ // If a variable is missing, it is not substituted.
4834+ var sub = {
4835+ reason: 'test string with {foo}',
4836+ vars: {}
4837+ };
4838+
4839+ Y.Assert.areEqual(
4840+ 'test string with {foo}',
4841+ module._safely_render_description(sub));
4842+ },
4843+
4844+ test_string_variable: function() {
4845+ // Plain string variables are directly substituted.
4846+ var sub = {
4847+ reason: 'test string with {foo}',
4848+ vars: { foo: 'nothing' }
4849+ };
4850+
4851+ Y.Assert.areEqual(
4852+ 'test string with nothing',
4853+ module._safely_render_description(sub));
4854+ },
4855+
4856+ _constructObjectLink: function(title, url) {
4857+ // Constructs a mock ObjectLink.
4858+ return { title: title, url: url };
4859+ },
4860+
4861+ test_objectlink_variable: function() {
4862+ // ObjectLink variables get turned into actual HTML links.
4863+ var sub = {
4864+ reason: 'test string with {foo}',
4865+ vars: { foo: this._constructObjectLink('Title', 'http://link/') }
4866+ };
4867+
4868+ Y.Assert.areEqual(
4869+ 'test string with <a href="http://link/">Title</a>',
4870+ module._safely_render_description(sub));
4871+ },
4872+
4873+ test_multiple_variables: function() {
4874+ // For multiple variables, they all get replaced.
4875+ var sub = {
4876+ reason: '{simple} string with {foo} {simple}',
4877+ vars: {
4878+ foo: this._constructObjectLink('Link', 'http://link/'),
4879+ simple: "test"
4880+ }
4881+ };
4882+
4883+ Y.Assert.areEqual(
4884+ 'test string with <a href="http://link/">Link</a> test',
4885+ module._safely_render_description(sub));
4886+ },
4887+
4888+ test_extra_variable: function() {
4889+ // Passing in extra variables causes them to be replaced as well.
4890+ var sub = {
4891+ reason: 'test string with {extra}',
4892+ vars: {}
4893+ };
4894+ var extra_vars = {
4895+ extra: 'something extra'
4896+ };
4897+
4898+ Y.Assert.areEqual(
4899+ 'test string with something extra',
4900+ module._safely_render_description(sub, extra_vars));
4901+ },
4902+
4903+ test_extra_objectlink_variable: function() {
4904+ // Passing in extra ObjectLink variable gets properly substituted.
4905+ var sub = {
4906+ reason: 'test string with {extra}',
4907+ vars: {}
4908+ };
4909+ var extra_vars = {
4910+ extra: this._constructObjectLink('extras', 'http://link/')
4911+ };
4912+
4913+ Y.Assert.areEqual(
4914+ 'test string with <a href="http://link/">extras</a>',
4915+ module._safely_render_description(sub, extra_vars));
4916+ }
4917+
4918+ }));
4919+
4920+
4921+ /**
4922+ * Test for get_direct_description_node() method.
4923+ */
4924+ tests.suite.add(new Y.Test.Case({
4925+ name: 'Test direct node construction.',
4926+
4927+ setUp: function () {
4928+ window.LP = { links: {},
4929+ cache: {},
4930+ subscription_info: {
4931+ direct: _constructCategory(),
4932+ bug_id: 1,
4933+ count: 0
4934+ }
4935+ };
4936+ },
4937+
4938+ tearDown: function () {
4939+ delete window.LP;
4940+ },
4941+
4942+ test_basic_structure: function() {
4943+ // The node has the three main components we expect.
4944+ var node = module._get_direct_description_node();
4945+ Y.Assert.areEqual('direct-subscription', node.get('id'));
4946+ Y.Assert.isTrue(Y.Lang.isValue(node.one('.reason')));
4947+ Y.Assert.isTrue(Y.Lang.isValue(node.one('.reductions')));
4948+ Y.Assert.isTrue(Y.Lang.isValue(node.one('.increases')));
4949+ Y.Assert.isTrue(
4950+ Y.Lang.isValue(
4951+ node.one('#'+module._action_ids.unsubscribe_with_warning)));
4952+ },
4953+
4954+ test_reductions_structure: function() {
4955+ var node = module._get_direct_description_node().one('.reductions');
4956+ var i;
4957+ for (i = 0; i < reduction_ids; i++) {
4958+ Y.Assert.isTrue(
4959+ Y.Lang.isValue(node.one('#'+reduction_ids[i])));
4960+ }
4961+ },
4962+
4963+ test_increases_structure: function() {
4964+ var node = module._get_direct_description_node().one('.increases');
4965+ var i;
4966+ for (i = 0; i < increasing_ids; i++) {
4967+ Y.Assert.isTrue(
4968+ Y.Lang.isValue(node.one('#'+increasing_ids[i])));
4969+ }
4970+ }
4971+
4972+ }));
4973+
4974+ /**
4975+ * Test for reveal_direct_description_actions() method.
4976+ */
4977+ tests.suite.add(new Y.Test.Case({
4978+ name: 'Test direct node modification with appropriate description.',
4979+
4980+ setUp: function () {
4981+ window.LP = { links: {},
4982+ cache: {},
4983+ subscription_info: {
4984+ direct: _constructCategory(),
4985+ bug_id: 1,
4986+ count: 0
4987+ }
4988+ };
4989+ },
4990+
4991+ tearDown: function () {
4992+ delete window.LP;
4993+ },
4994+
4995+ test_reason_displayed: function() {
4996+ // A description is added in.
4997+ var node = module._get_direct_description_node();
4998+ var expected_text = 'Kumquat rutebega papaya';
4999+ var info = {
5000+ reason: expected_text,
The diff has been truncated for viewing.