Merge lp:~wallyworld/launchpad/subscription-portlet-update-1017818 into lp:launchpad

Proposed by Ian Booth
Status: Merged
Approved by: j.c.sackett
Approved revision: no longer in the source branch.
Merged at revision: 15567
Proposed branch: lp:~wallyworld/launchpad/subscription-portlet-update-1017818
Merge into: lp:launchpad
Diff against target: 730 lines (+250/-195)
8 files modified
lib/lp/app/browser/informationtype.py (+4/-4)
lib/lp/bugs/browser/bug.py (+39/-2)
lib/lp/bugs/browser/tests/test_bug_views.py (+42/-2)
lib/lp/bugs/browser/tests/test_bugview.py (+5/-3)
lib/lp/bugs/javascript/bugtask_index.js (+0/-141)
lib/lp/bugs/javascript/information_type_choice.js (+78/-26)
lib/lp/bugs/javascript/tests/test_information_type_choice.html (+5/-0)
lib/lp/bugs/javascript/tests/test_information_type_choice.js (+77/-17)
To merge this branch: bzr merge lp:~wallyworld/launchpad/subscription-portlet-update-1017818
Reviewer Review Type Date Requested Status
j.c.sackett (community) Approve
Review via email: mp+113162@code.launchpad.net

Commit message

Ensure subscribers portlet is updated after changing bug information type.

Description of the change

== Implementation ==

This branch reinstalls the behaviour whereby changing a bug's privacy settings (now information type) using the ui results in the subscribers list also being updated since subscribers may have changed as a result.

The core change was to have the ui invoke a POST operation against the bug secrecy view as opposed to making a direct API call to transitionToInformationType on the bug object. The view action does the transition and then gathers the data to return to the XHR caller.

A change was required to the data added to the json request cache. The info type vocab objects were being inserted with title eg 'User Data' as the enum value, instead of the token eg USERDATA. This is because lazr-restful uses the title for marshalling over the API, but everything else uses token. So some extra javascript was added to handle the required data transformation into/outof the request cache.

Essentially, for the view itself, some code deleted when info type was implemented was reinserted. On the javascript side, some older code was reinstated and moved to the new module and some old code that should have been deleted with info type was introduced was finally removed.

== Tests ==

Add test for BugSecrecyView
Add extra yui tests for the information_type_choice module.

I also enhanced an existing information_type_choice yui test to make it more complete.

== Lint ==

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/lp/app/browser/informationtype.py
  lib/lp/bugs/browser/bug.py
  lib/lp/bugs/browser/tests/test_bug_views.py
  lib/lp/bugs/browser/tests/test_bugview.py
  lib/lp/bugs/javascript/bugtask_index.js
  lib/lp/bugs/javascript/information_type_choice.js
  lib/lp/bugs/javascript/tests/test_information_type_choice.html
  lib/lp/bugs/javascript/tests/test_information_type_choice.js

To post a comment you must log in.
Revision history for this message
j.c.sackett (jcsackett) wrote :

Looks good, Ian. Thanks.

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/browser/informationtype.py'
2--- lib/lp/app/browser/informationtype.py 2012-06-20 05:25:44 +0000
3+++ lib/lp/app/browser/informationtype.py 2012-07-04 01:25:24 +0000
4@@ -20,13 +20,13 @@
5
6 def initialize(self):
7 cache = IJSONRequestCache(self.request)
8- cache.objects['information_types'] = [
9- {'value': term.value, 'description': term.description,
10+ cache.objects['information_type_data'] = [
11+ {'value': term.name, 'description': term.description,
12 'name': term.title,
13 'description_css_class': 'choice-description'}
14- for term in InformationTypeVocabulary()]
15+ for term in InformationTypeVocabulary(self.context)]
16 cache.objects['private_types'] = [
17- type.title for type in PRIVATE_INFORMATION_TYPES]
18+ type.name for type in PRIVATE_INFORMATION_TYPES]
19 cache.objects['show_userdata_as_private'] = (
20 self.show_userdata_as_private)
21
22
23=== modified file 'lib/lp/bugs/browser/bug.py'
24--- lib/lp/bugs/browser/bug.py 2012-06-26 05:59:47 +0000
25+++ lib/lp/bugs/browser/bug.py 2012-07-04 01:25:24 +0000
26@@ -35,8 +35,13 @@
27 )
28 from lazr.lifecycle.event import ObjectModifiedEvent
29 from lazr.lifecycle.snapshot import Snapshot
30+from lazr.restful import (
31+ EntryResource,
32+ ResourceJSONEncoder,
33+ )
34 from lazr.restful.interface import copy_field
35 from lazr.restful.interfaces import IJSONRequestCache
36+from simplejson import dumps
37 from zope import formlib
38 from zope.app.form.browser import TextWidget
39 from zope.component import getUtility
40@@ -60,6 +65,7 @@
41 from lp.app.errors import NotFoundError
42 from lp.app.widgets.itemswidgets import LaunchpadRadioWidgetWithDescription
43 from lp.app.widgets.project import ProjectScopeWidget
44+from lp.bugs.browser.bugsubscription import BugPortletSubscribersWithDetails
45 from lp.bugs.browser.widgets.bug import BugTagsWidget
46 from lp.bugs.enums import BugNotificationLevel
47 from lp.bugs.interfaces.bug import (
48@@ -74,6 +80,7 @@
49 from lp.bugs.interfaces.bugtask import (
50 BugTaskSearchParams,
51 BugTaskStatus,
52+ IBugTask,
53 IFrontPageBugTaskSearch,
54 )
55 from lp.bugs.interfaces.bugwatch import IBugWatchSet
56@@ -825,7 +832,9 @@
57 @property
58 def next_url(self):
59 """Return the next URL to call when this call completes."""
60- return canonical_url(self.context)
61+ if not self.request.is_ajax:
62+ return canonical_url(self.context)
63+ return None
64
65 cancel_url = next_url
66
67@@ -834,7 +843,8 @@
68 """See `LaunchpadFormView.`"""
69 return {'information_type': self.context.bug.information_type}
70
71- @action('Change', name='change')
72+ @action('Change', name='change',
73+ failure=LaunchpadFormView.ajax_failure_handler)
74 def change_action(self, action, data):
75 """Update the bug."""
76 data = dict(data)
77@@ -853,6 +863,33 @@
78 ObjectModifiedEvent(
79 bug, bug_before_modification, changed_fields,
80 user=self.user))
81+ if self.request.is_ajax:
82+ if changed:
83+ return self._getSubscriptionDetails()
84+ else:
85+ return ''
86+
87+ def _getSubscriptionDetails(self):
88+ cache = dict()
89+ # The subscription details for the current user.
90+ self.extractBugSubscriptionDetails(self.user, self.context.bug, cache)
91+
92+ # The subscription details for other users to populate the subscribers
93+ # list in the portlet.
94+ if IBugTask.providedBy(self.context):
95+ bug = self.context.bug
96+ else:
97+ bug = self.context
98+ subscribers_portlet = BugPortletSubscribersWithDetails(
99+ bug, self.request)
100+ subscription_data = subscribers_portlet.subscriber_data
101+ result_data = dict(
102+ cache_data=cache,
103+ subscription_data=subscription_data)
104+ self.request.response.setHeader('content-type', 'application/json')
105+ return dumps(
106+ result_data, cls=ResourceJSONEncoder,
107+ media_type=EntryResource.JSON_TYPE)
108
109 def _handlePrivacyChanged(self, user_will_be_subscribed):
110 """Handle the case where the privacy of the bug has been changed.
111
112=== modified file 'lib/lp/bugs/browser/tests/test_bug_views.py'
113--- lib/lp/bugs/browser/tests/test_bug_views.py 2012-06-22 05:52:17 +0000
114+++ lib/lp/bugs/browser/tests/test_bug_views.py 2012-07-04 01:25:24 +0000
115@@ -6,6 +6,7 @@
116 __metaclass__ = type
117
118 from BeautifulSoup import BeautifulSoup
119+import simplejson
120 from soupmatchers import (
121 HTMLContains,
122 Tag,
123@@ -296,7 +297,8 @@
124
125 layer = DatabaseFunctionalLayer
126
127- def createInitializedSecrecyView(self, person=None, bug=None):
128+ def createInitializedSecrecyView(self, person=None, bug=None,
129+ request=None):
130 """Create and return an initialized BugSecrecyView."""
131 if person is None:
132 person = self.factory.makePerson()
133@@ -307,7 +309,7 @@
134 bug.default_bugtask, name='+secrecy', form={
135 'field.information_type': 'USERDATA',
136 'field.actions.change': 'Change',
137- })
138+ }, request=request)
139 return view
140
141 def test_notification_shown_if_marking_private_and_not_subscribed(self):
142@@ -346,6 +348,44 @@
143 view = self.createInitializedSecrecyView(person, bug)
144 self.assertContentEqual([], view.request.response.notifications)
145
146+ def test_secrecy_view_ajax_render(self):
147+ # When the bug secrecy view is called from an ajax request, it should
148+ # provide a json encoded dict when rendered. The dict contains bug
149+ # subscription information resulting from the update to the bug
150+ # privacy as well as information used to populate the updated
151+ # subscribers list.
152+ person = self.factory.makePerson()
153+ bug = self.factory.makeBug(owner=person)
154+ with person_logged_in(person):
155+ bug.subscribe(person, person)
156+
157+ extra = {'HTTP_X_REQUESTED_WITH': 'XMLHttpRequest'}
158+ request = LaunchpadTestRequest(
159+ method='POST', form={
160+ 'field.actions.change': 'Change',
161+ 'field.information_type': 'USERDATA'},
162+ **extra)
163+ view = self.createInitializedSecrecyView(person, bug, request)
164+ result_data = simplejson.loads(view.render())
165+
166+ cache_data = result_data['cache_data']
167+ self.assertFalse(cache_data['other_subscription_notifications'])
168+ subscription_data = cache_data['subscription']
169+ self.assertEqual(
170+ 'http://launchpad.dev/api/devel/bugs/%s' % bug.id,
171+ subscription_data['bug_link'])
172+ self.assertEqual(
173+ 'http://launchpad.dev/api/devel/~%s' % person.name,
174+ subscription_data['person_link'])
175+ self.assertEqual(
176+ 'Discussion', subscription_data['bug_notification_level'])
177+
178+ [subscriber_data] = result_data['subscription_data']
179+ subscriber = removeSecurityProxy(bug).default_bugtask.pillar.owner
180+ self.assertEqual(
181+ subscriber.name, subscriber_data['subscriber']['name'])
182+ self.assertEqual('Discussion', subscriber_data['subscription_level'])
183+
184 def test_set_information_type(self):
185 # Test that the bug's information_type can be updated using the
186 # view with the feature flag on.
187
188=== modified file 'lib/lp/bugs/browser/tests/test_bugview.py'
189--- lib/lp/bugs/browser/tests/test_bugview.py 2012-06-07 15:22:52 +0000
190+++ lib/lp/bugs/browser/tests/test_bugview.py 2012-07-04 01:25:24 +0000
191@@ -100,8 +100,10 @@
192 view.initialize()
193 cache = IJSONRequestCache(view.request)
194 expected = [
195- InformationType.PUBLIC, InformationType.UNEMBARGOEDSECURITY,
196- InformationType.EMBARGOEDSECURITY, InformationType.USERDATA]
197+ InformationType.PUBLIC.name,
198+ InformationType.UNEMBARGOEDSECURITY.name,
199+ InformationType.EMBARGOEDSECURITY.name,
200+ InformationType.USERDATA.name]
201 self.assertContentEqual(expected, [
202 type['value']
203- for type in cache.objects['information_types']])
204+ for type in cache.objects['information_type_data']])
205
206=== modified file 'lib/lp/bugs/javascript/bugtask_index.js'
207--- lib/lp/bugs/javascript/bugtask_index.js 2012-06-20 05:25:44 +0000
208+++ lib/lp/bugs/javascript/bugtask_index.js 2012-07-04 01:25:24 +0000
209@@ -278,147 +278,6 @@
210 }
211 };
212
213-
214-var update_privacy_settings = function(data) {
215- // XXX noodles 2009-03-17 bug=336866 It seems the etag
216- // returned by lp_save() is incorrect. Remove it for now
217- // so that the second save does not result in a '412
218- // precondition failed' error.
219- //
220- // XXX deryck 2009-04-29 bug=369293 Also, this has to
221- // happen before *any* call to lp_save now that bug
222- // subscribing can be done inline. Named operations
223- // don't return new objects, making the cached bug's
224- // etag invalid as well.
225- lp_bug_entry.removeAttr('http_etag');
226-
227- privacy_form_overlay.hide();
228-
229- var privacy_text = Y.one('#privacy-text');
230- var privacy_div = Y.one('#privacy');
231- privacy_link.setStyle('display', 'none');
232- privacy_spinner.setStyle('display', 'inline');
233-
234- if (lp_client === undefined) {
235- lp_client = new Y.lp.client.Launchpad();
236- }
237-
238- if (lp_bug_entry === undefined) {
239- var bug_repr = LP.cache.bug;
240- lp_bug_entry = new Y.lp.client.Entry(
241- lp_client, bug_repr, bug_repr.self_link);
242- }
243-
244- var private_flag = data['field.private'] !== undefined;
245- var security_related =
246- data['field.security_related'] !== undefined;
247-
248- lp_bug_entry.set('private', private_flag);
249- lp_bug_entry.set('security_related', security_related);
250- var error_handler = new Y.lp.client.ErrorHandler();
251- error_handler.clearProgressUI = function () {
252- privacy_spinner.setStyle('display', 'none');
253- privacy_link.setStyle('display', 'inline');
254- };
255- error_handler.showError = function (error_msg) {
256- Y.lp.anim.red_flash({
257- node: privacy_div,
258- duration: namespace.ANIM_DURATION
259- }).run();
260- privacy_form_overlay.showError(error_msg);
261- privacy_form_overlay.show();
262- };
263-
264- var base_url = LP.cache.context.web_link;
265- var submit_url = base_url+"/+secrecy";
266- var qs = Y.lp.client.append_qs('', 'field.actions.change', 'Change');
267- qs = Y.lp.client.append_qs(qs, 'field.private', private_flag?'on':'off');
268- qs = Y.lp.client.append_qs(
269- qs, 'field.security_related', security_related?'on':'off');
270- var sub_list_node = Y.one('#other-bug-subscribers');
271- var subscribers_list = sub_list_node.getData('subscribers_loader');
272- var config = {
273- method: "POST",
274- headers: {'Accept': 'application/xhtml'},
275- data: qs,
276- on: {
277- start: function () {
278- subscribers_list.subscribers_list.startActivity(
279- 'Updating subscribers...');
280- },
281- end: function () {
282- subscribers_list.subscribers_list.stopActivity();
283- },
284- success: function (id, response) {
285- if (response.responseText !== '') {
286- var result_data = Y.JSON.parse(response.responseText);
287- var subscribers = result_data.subscription_data;
288- subscribers_list._loadSubscribersFromList(subscribers);
289- var cache_data = result_data.cache_data;
290- var item;
291- for (item in cache_data) {
292- if (cache_data.hasOwnProperty(item)) {
293- LP.cache[item] = cache_data[item];
294- }
295- }
296- var ns = Y.lp.bugs.bugtask_index.portlets.subscription;
297- ns.update_subscription_status();
298- }
299- privacy_spinner.setStyle('display', 'none');
300- privacy_link.setStyle('display', 'inline');
301-
302- var banner = Y.lp.app.banner.privacy.getPrivacyBanner();
303- if (private_flag) {
304- Y.one('body').replaceClass('public', 'private');
305- privacy_div.replaceClass('public', 'private');
306- privacy_text.set(
307- 'innerHTML',
308- 'This report is <strong>private</strong> ');
309- banner.show();
310- } else {
311- Y.one('body').replaceClass('private', 'public');
312- privacy_div.replaceClass('private', 'public');
313- privacy_text.set(
314- 'innerHTML', 'This report is public ');
315- banner.hide();
316- }
317- privacy_text.appendChild(privacy_link);
318- privacy_text.appendChild(privacy_spinner);
319-
320- var security_message = Y.one('#security-message');
321- if (security_related) {
322- if (security_message === null) {
323- var security_message_html = [
324- '<div style="',
325- ' margin-top: 0.5em;',
326- ' padding-right: 18px;',
327- ' center right no-repeat;"',
328- ' class="sprite security"',
329- ' id="security-message"',
330- '>Security vulnerability</div>'
331- ].join('');
332- security_message = Y.Node.create(
333- security_message_html);
334- privacy_div.appendChild(security_message);
335- }
336- } else {
337- if (security_message !== null) {
338- privacy_div.removeChild(security_message);
339- }
340- }
341- Y.lp.client.display_notifications(
342- response.getResponseHeader('X-Lazr-Notifications'));
343- Y.lp.anim.green_flash({
344- node: privacy_div,
345- duration: namespace.ANIM_DURATION
346- }).run();
347- },
348- failure: error_handler.getFailureHandler()
349- }
350- };
351- lp_client.io_provider.io(submit_url, config);
352-};
353-
354 /**
355 * Do a preemptive search for branches that contain the current bug's ID.
356 */
357
358=== modified file 'lib/lp/bugs/javascript/information_type_choice.js'
359--- lib/lp/bugs/javascript/information_type_choice.js 2012-06-20 05:25:44 +0000
360+++ lib/lp/bugs/javascript/information_type_choice.js 2012-07-04 01:25:24 +0000
361@@ -7,43 +7,82 @@
362 YUI.add('lp.bugs.information_type_choice', function(Y) {
363
364 var namespace = Y.namespace('lp.bugs.information_type_choice');
365-var information_type_descriptions = {};
366
367 // For testing.
368 var skip_animation = false;
369
370+/**
371+ * Lookup the information_type property, keyed on the named value.
372+ * @param key the key to lookup
373+ * @param key_name the key property name
374+ * @param value_name the value property_name
375+ * @return {*}
376+ */
377+var information_type_value_from_key = function(key, key_name,
378+ value_name) {
379+ var data = null;
380+ Y.Array.some(LP.cache.information_type_data, function(info_type) {
381+ if (info_type[key_name] === key) {
382+ data = info_type[value_name];
383+ return true;
384+ }
385+ });
386+ return data;
387+};
388+
389 namespace.save_information_type = function(widget, value, lp_client) {
390- var error_handler = new Y.lp.client.ErrorHandler();
391+ var error_handler = new Y.lp.client.FormErrorHandler();
392 error_handler.showError = function(error_msg) {
393 Y.lp.app.errors.display_error(
394 Y.one('#information-type'), error_msg);
395 };
396 error_handler.handleError = function(ioId, response) {
397- var orig_value = LP.cache.bug.information_type;
398+ var orig_value = information_type_value_from_key(
399+ LP.cache.bug.information_type, 'name', 'value');
400 widget.set('value', orig_value);
401 widget._showFailed();
402 update_privacy_portlet(orig_value);
403 return false;
404 };
405+ var base_url = LP.cache.context.web_link;
406+ var submit_url = base_url+"/+secrecy";
407+ var qs = Y.lp.client.append_qs('', 'field.actions.change', 'Change');
408+ qs = Y.lp.client.append_qs(qs, 'field.information_type', value);
409+ var sub_list_node = Y.one('#other-bug-subscribers');
410+ var subscribers_list = sub_list_node.getData('subscribers_loader');
411 var config = {
412+ method: "POST",
413+ headers: {'Accept': 'application/xhtml;application/json'},
414+ data: qs,
415 on: {
416- start: Y.bind(widget._uiSetWaiting),
417- end: Y.bind(widget._uiClearWaiting),
418- success: function(id, response) {
419- namespace.information_type_save_success(widget, value);
420+ start: function () {
421+ widget._uiSetWaiting();
422+ subscribers_list.subscribers_list.startActivity(
423+ 'Updating subscribers...');
424+ },
425+ end: function () {
426+ widget._uiClearWaiting();
427+ subscribers_list.subscribers_list.stopActivity();
428+ },
429+ success: function (id, response) {
430+ var result_data = null;
431+ if (response.responseText !== '') {
432+ result_data = Y.JSON.parse(response.responseText);
433+ }
434+ namespace.information_type_save_success(
435+ widget, value, subscribers_list, result_data);
436+ Y.lp.client.display_notifications(
437+ response.getResponseHeader('X-Lazr-Notifications'));
438 },
439 failure: error_handler.getFailureHandler()
440- },
441- parameters: {
442- information_type: value
443 }
444 };
445- lp_client.named_post(
446- LP.cache.bug.self_link, 'transitionToInformationType', config);
447+ lp_client.io_provider.io(submit_url, config);
448 };
449
450 var update_privacy_portlet = function(value) {
451- var description = information_type_descriptions[value];
452+ var description = information_type_value_from_key(
453+ value, 'value', 'description');
454 var desc_node = Y.one('#information-type-description');
455 if (Y.Lang.isValue(desc_node)) {
456 desc_node.set('text', description);
457@@ -74,35 +113,48 @@
458
459 namespace.get_information_type_banner_text = function(value) {
460 var text_template = "This page contains {info_type} information.";
461-
462- if (value === "User Data" && LP.cache.show_userdata_as_private) {
463- value = "Private";
464+ var info_type = information_type_value_from_key(value, 'value', 'name');
465+ if (info_type === "User Data" && LP.cache.show_userdata_as_private) {
466+ info_type = "Private";
467 }
468- return Y.Lang.substitute(text_template, {'info_type': value});
469+ return Y.Lang.substitute(text_template, {'info_type': info_type});
470 };
471
472-namespace.information_type_save_success = function(widget, value) {
473- LP.cache.bug.information_type = value;
474+namespace.information_type_save_success = function(widget, value,
475+ subscribers_list,
476+ subscribers_data) {
477+ LP.cache.bug.information_type =
478+ information_type_value_from_key(value, 'value', 'name');
479 update_privacy_banner(value);
480 widget._showSucceeded();
481- var subscription_ns = Y.lp.bugs.bugtask_index.portlets.subscription;
482- subscription_ns.update_subscription_status(skip_animation);
483+ if (Y.Lang.isObject(subscribers_data)) {
484+ var subscribers = subscribers_data.subscription_data;
485+ subscribers_list._loadSubscribersFromList(subscribers);
486+ var cache_data = subscribers_data.cache_data;
487+ var item;
488+ for (item in cache_data) {
489+ if (cache_data.hasOwnProperty(item)) {
490+ LP.cache[item] = cache_data[item];
491+ }
492+ }
493+ }
494+ var ns = Y.lp.bugs.bugtask_index.portlets.subscription;
495+ ns.update_subscription_status(skip_animation);
496 };
497
498 namespace.setup_information_type_choice = function(privacy_link, lp_client,
499 skip_anim) {
500 skip_animation = skip_anim;
501- Y.Array.each(LP.cache.information_types, function(info_type) {
502- information_type_descriptions[info_type.value] = info_type.description;
503- });
504+ var initial_value = information_type_value_from_key(
505+ LP.cache.bug.information_type, 'name', 'value');
506 var information_type = Y.one('#information-type');
507 var information_type_edit = new Y.ChoiceSource({
508 editicon: privacy_link,
509 contentBox: Y.one('#privacy'),
510 value_location: information_type,
511- value: LP.cache.bug.information_type,
512+ value: initial_value,
513 title: "Change information type",
514- items: LP.cache.information_types,
515+ items: LP.cache.information_type_data,
516 backgroundColor: '#FFFF99',
517 flashEnabled: false
518 });
519
520=== modified file 'lib/lp/bugs/javascript/tests/test_information_type_choice.html'
521--- lib/lp/bugs/javascript/tests/test_information_type_choice.html 2012-06-07 18:27:33 +0000
522+++ lib/lp/bugs/javascript/tests/test_information_type_choice.html 2012-07-04 01:25:24 +0000
523@@ -70,6 +70,10 @@
524 <script type="text/javascript"
525 src="../../../../../build/js/lp/bugs/bug_subscription_portlet.js"></script>
526 <script type="text/javascript"
527+ src="../../../../../build/js/lp/bugs/subscribers.js"></script>
528+ <script type="text/javascript"
529+ src="../../../../../build/js/lp/app/subscribers/subscribers_list.js"></script>
530+ <script type="text/javascript"
531 src="../../../../../build/js/lp/bugs/bugtask_index.js"></script>
532
533 <!-- The module under test. -->
534@@ -104,6 +108,7 @@
535 <a href="#" text="" class="menu-link-mute_subscription unmute"></a>
536 <a href="#" text="" class="menu-link-mute_subscription mute"></a>
537 </div>
538+ <div id="other-bug-subscribers"></div>
539 </script>
540 </body>
541 </html>
542
543=== modified file 'lib/lp/bugs/javascript/tests/test_information_type_choice.js'
544--- lib/lp/bugs/javascript/tests/test_information_type_choice.js 2012-06-27 14:05:07 +0000
545+++ lib/lp/bugs/javascript/tests/test_information_type_choice.js 2012-07-04 01:25:24 +0000
546@@ -22,13 +22,13 @@
547 information_type: 'Public',
548 self_link: '/bug/1'
549 },
550- private_types: ['Private', 'User Data'],
551- information_types: [
552- {'value': 'Public', 'name': 'Public',
553+ private_types: ['PRIVATE', 'USERDATA'],
554+ information_type_data: [
555+ {'value': 'PUBLIC', 'name': 'Public',
556 'description': 'Public Description'},
557- {'value': 'Private', 'name': 'Private',
558+ {'value': 'PRIVATE', 'name': 'Private',
559 'description': 'Private Description'},
560- {'value': 'User Data', 'name': 'User Data',
561+ {'value': 'USERDATA', 'name': 'User Data',
562 'description': 'User Data Description'}
563 ]
564 }
565@@ -42,6 +42,11 @@
566 var privacy_link = Y.one('#privacy-link');
567 this.widget = ns.setup_information_type_choice(
568 privacy_link, lp_client, true);
569+ Y.lp.bugs.subscribers.createBugSubscribersLoader({
570+ container_box: '#other-bug-subscribers',
571+ subscribers_details_view:
572+ '/+bug-portlet-subscribers-details'});
573+
574 },
575 tearDown: function () {
576 if (this.fixture !== null) {
577@@ -72,19 +77,39 @@
578 "Cannot locate the lp.bugs.information_type_choice module");
579 },
580
581+ // The save XHR call works as expected.
582 test_save_information_type: function() {
583 var mockio = new Y.lp.testing.mockio.MockIo();
584 var lp_client = new Y.lp.client.Launchpad({
585 io_provider: mockio
586 });
587- ns.save_information_type(this.widget, 'User Data', lp_client);
588- Y.Assert.areEqual('/api/devel/bug/1', mockio.last_request.url);
589+ var orig_save_success = ns.information_type_save_success;
590+ var save_success_called = false;
591+ ns.information_type_save_success = function(widget, value,
592+ subscribers_list,
593+ subscribers_data) {
594+ Y.Assert.areEqual('USERDATA', value);
595+ Y.Assert.areEqual(
596+ 'subscribers', subscribers_data.subscription_data);
597+ Y.Assert.areEqual(
598+ 'value', subscribers_data.cache_data.item);
599+ save_success_called = true;
600+ };
601+ ns.save_information_type(this.widget, 'USERDATA', lp_client);
602+ mockio.success({
603+ responseText: '{"subscription_data": "subscribers",' +
604+ '"cache_data": {"item": "value"}}',
605+ responseHeaders: {'Content-Type': 'application/json'}});
606+ Y.Assert.areEqual('/+secrecy', mockio.last_request.url);
607 Y.Assert.areEqual(
608- 'ws.op=transitionToInformationType&' +
609- 'information_type=User%20Data',
610+ 'field.actions.change=Change&' +
611+ 'field.information_type=USERDATA',
612 mockio.last_request.config.data);
613+ Y.Assert.isTrue(save_success_called);
614+ ns.information_type_save_success = orig_save_success;
615 },
616
617+ // Setting a private type shows the privacy banner.
618 test_information_type_save_success_private: function() {
619 var old_func = this._shim_privacy_banner();
620 var hide_flag = false;
621@@ -96,7 +121,7 @@
622 update_flag = true;
623 });
624
625- ns.information_type_save_success(this.widget, 'Private');
626+ ns.information_type_save_success(this.widget, 'PRIVATE');
627 var body = Y.one('body');
628 Y.Assert.isTrue(body.hasClass('private'));
629 Y.Assert.isTrue(hide_flag);
630@@ -105,6 +130,7 @@
631 this._unshim_privacy_banner(old_func);
632 },
633
634+ // Setting a private type hides the privacy banner.
635 test_information_type_save_success_public: function() {
636 var old_func = this._shim_privacy_banner();
637 var flag = false;
638@@ -114,7 +140,7 @@
639 var summary = Y.one('#information-type-summary');
640 summary.replaceClass('public', 'private');
641
642- ns.information_type_save_success(this.widget, 'Public');
643+ ns.information_type_save_success(this.widget, 'PUBLIC');
644 var body = Y.one('body');
645 Y.Assert.isTrue(body.hasClass('public'));
646 Y.Assert.isTrue(flag);
647@@ -122,16 +148,49 @@
648 this._unshim_privacy_banner(old_func);
649 },
650
651+ // A successful save updates the subscribers portlet.
652+ test_information_type_save_success_with_subscribers_data: function() {
653+ var old_func = this._shim_privacy_banner();
654+ var flag = false;
655+ Y.on('test:banner:hide', function() {
656+ flag = true;
657+ });
658+ var summary = Y.one('#information-type-summary');
659+ summary.replaceClass('public', 'private');
660+
661+ var load_subscribers_called = false;
662+ var subscribers_list = {
663+ _loadSubscribersFromList: function(subscription_data) {
664+ Y.Assert.areEqual('subscriber', subscription_data);
665+ load_subscribers_called = true;
666+ }
667+ };
668+ var subscribers_data = {
669+ subscription_data: 'subscriber',
670+ cache_data: {
671+ item1: 'value1',
672+ item2: 'value2'
673+ }
674+ };
675+ ns.information_type_save_success(
676+ this.widget, 'PUBLIC', subscribers_list, subscribers_data);
677+ Y.Assert.isTrue(load_subscribers_called);
678+ Y.Assert.areEqual('value1', window.LP.cache.item1);
679+ Y.Assert.areEqual('value2', window.LP.cache.item2);
680+ this._unshim_privacy_banner(old_func);
681+ },
682+
683+ // Selecting a new information type calls save correctly.
684 test_perform_update_information_type: function() {
685 var privacy_link = Y.one('#privacy-link');
686 privacy_link.simulate('click');
687 var private_choice = Y.one(
688- '.yui3-ichoicelist-content a[href="#Private"]');
689+ '.yui3-ichoicelist-content a[href="#USERDATA"]');
690 var orig_save_information_type = ns.save_information_type;
691 var function_called = false;
692 ns.save_information_type = function(widget, value, lp_client) {
693 Y.Assert.areEqual(
694- 'User Data', value); function_called = true; };
695+ 'USERDATA', value); function_called = true; };
696 private_choice.simulate('click');
697 var description_node = Y.one('#information-type-description');
698 Y.Assert.areEqual(
699@@ -142,20 +201,21 @@
700 ns.save_information_type = orig_save_information_type;
701 },
702
703+ // Test error handling when a save fails.
704 test_information_type_save_error: function() {
705 var mockio = new Y.lp.testing.mockio.MockIo();
706 var lp_client = new Y.lp.client.Launchpad({
707 io_provider: mockio
708 });
709- this.widget.set('value', 'User Data');
710- ns.save_information_type(this.widget, 'User Data', lp_client);
711+ this.widget.set('value', 'USERDATA');
712+ ns.save_information_type(this.widget, 'USERDATA', lp_client);
713 mockio.last_request.respond({
714 status: 500,
715 statusText: 'An error occurred'
716 });
717 // The original info type value from the cache should have been
718 // set back into the widget.
719- Y.Assert.areEqual('Public', this.widget.get('value'));
720+ Y.Assert.areEqual('PUBLIC', this.widget.get('value'));
721 var description_node = Y.one('#information-type-description');
722 Y.Assert.areEqual(
723 'Public Description', description_node.get('text'));
724@@ -167,5 +227,5 @@
725 }));
726
727 }, '0.1', {'requires': ['test', 'console', 'event', 'node-event-simulate',
728- 'lp.testing.mockio', 'lp.client',
729+ 'lp.testing.mockio', 'lp.client','lp.bugs.subscribers',
730 'lp.bugs.information_type_choice']});