Merge lp:~wallyworld/launchpad/dupe-project-warning-317258 into lp:launchpad

Proposed by Ian Booth
Status: Merged
Approved by: Curtis Hovey
Approved revision: no longer in the source branch.
Merged at revision: 15751
Proposed branch: lp:~wallyworld/launchpad/dupe-project-warning-317258
Merge into: lp:launchpad
Diff against target: 240 lines (+82/-18)
8 files modified
lib/lp/bugs/browser/tests/test_bugs.py (+16/-3)
lib/lp/bugs/interfaces/malone.py (+3/-2)
lib/lp/bugs/javascript/bug_picker.js (+5/-2)
lib/lp/bugs/javascript/duplicates.js (+26/-0)
lib/lp/bugs/javascript/tests/test_bug_picker.js (+10/-4)
lib/lp/bugs/javascript/tests/test_duplicates.js (+14/-0)
lib/lp/code/javascript/tests/test_bugspeclinks.html (+2/-5)
lib/lp/systemhomes.py (+6/-2)
To merge this branch: bzr merge lp:~wallyworld/launchpad/dupe-project-warning-317258
Reviewer Review Type Date Requested Status
Curtis Hovey (community) code Approve
Review via email: mp+118287@code.launchpad.net

Commit message

Display a warning if a user wants to select a duplicate bug affecting a different project.

Description of the change

== Implementation ==

When a bug search is done for the bug picker, a new optional related_bug parameter is passed in. The json search results data contains a new 'different_pillars' attribute which is true if the search result bug and related bug do not have any target pillars in common.

The ui displays a warning if the bug data says that the proposed dupe and the context bug have no pillars in common.

== Demo and QA ==

http://people.canonical.com/~ianb/different-project-warning.png
http://people.canonical.com/~ianb/different-project-warning2.png

== Tests ==

Enhance bug picker and duplicate bug yui tests.
Enhance getBugData() unit tests.

== Lint ==

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/lp/systemhomes.py
  lib/lp/bugs/browser/tests/test_bugs.py
  lib/lp/bugs/interfaces/malone.py
  lib/lp/bugs/javascript/bug_picker.js
  lib/lp/bugs/javascript/duplicates.js
  lib/lp/bugs/javascript/tests/test_bug_picker.js
  lib/lp/bugs/javascript/tests/test_duplicates.js

To post a comment you must log in.
Revision history for this message
Curtis Hovey (sinzui) wrote :

I think the code is good, as is the text. I don't think we want two warning messages to have different font weights. I don't think we need to make the message bold.

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/bugs/browser/tests/test_bugs.py'
2--- lib/lp/bugs/browser/tests/test_bugs.py 2012-07-27 08:01:54 +0000
3+++ lib/lp/bugs/browser/tests/test_bugs.py 2012-08-07 05:02:24 +0000
4@@ -117,16 +117,19 @@
5 # we should get some valid content out of this
6 self.assertIn('Search all bugs', content)
7
8- def test_getBugData(self):
9+ def _assert_getBugData(self, related_bug=None):
10 # The getBugData method works as expected.
11 owner = self.factory.makePerson()
12+ product = self.factory.makeProduct()
13 bug = self.factory.makeBug(
14+ product=product,
15 owner=owner,
16 status=BugTaskStatus.INPROGRESS,
17 title='title', description='description',
18 information_type=InformationType.PRIVATESECURITY)
19 with person_logged_in(owner):
20- bug_data = getUtility(IMaloneApplication).getBugData(owner, bug.id)
21+ bug_data = getUtility(IMaloneApplication).getBugData(
22+ owner, bug.id, related_bug)
23 expected_bug_data = {
24 'id': bug.id,
25 'information_type': 'Private Security',
26@@ -137,6 +140,16 @@
27 'status_class': 'statusINPROGRESS',
28 'bug_summary': 'title',
29 'description': 'description',
30- 'bug_url': canonical_url(bug.default_bugtask)
31+ 'bug_url': canonical_url(bug.default_bugtask),
32+ 'different_pillars': related_bug is not None
33 }
34 self.assertEqual([expected_bug_data], bug_data)
35+
36+ def test_getBugData(self):
37+ # The getBugData method works as expected without a related_bug.
38+ self._assert_getBugData()
39+
40+ def test_getBugData_with_related_bug(self):
41+ # The getBugData method works as expected if related bug is specified.
42+ related_bug = self.factory.makeBug()
43+ self._assert_getBugData(related_bug)
44
45=== modified file 'lib/lp/bugs/interfaces/malone.py'
46--- lib/lp/bugs/interfaces/malone.py 2012-07-27 08:01:54 +0000
47+++ lib/lp/bugs/interfaces/malone.py 2012-08-07 05:02:24 +0000
48@@ -41,11 +41,12 @@
49
50 @call_with(user=REQUEST_USER)
51 @operation_parameters(
52- bug_id=copy_field(IBug['id'])
53+ bug_id=copy_field(IBug['id']),
54+ related_bug=Reference(schema=IBug)
55 )
56 @export_read_operation()
57 @operation_for_version('devel')
58- def getBugData(user, bug_id):
59+ def getBugData(user, bug_id, related_bug=None):
60 """Search bugtasks matching the specified criteria.
61
62 The only criteria currently supported is to search for a bugtask with
63
64=== modified file 'lib/lp/bugs/javascript/bug_picker.js'
65--- lib/lp/bugs/javascript/bug_picker.js 2012-08-03 12:47:11 +0000
66+++ lib/lp/bugs/javascript/bug_picker.js 2012-08-07 05:02:24 +0000
67@@ -116,7 +116,10 @@
68 = Y.lp.client.append_qs("", "ws.accept", "application.json");
69 qs_data = Y.lp.client.append_qs(qs_data, "ws.op", "getBugData");
70 qs_data = Y.lp.client.append_qs(qs_data, "bug_id", bug_id);
71-
72+ if (Y.Lang.isValue(LP.cache.bug)) {
73+ qs_data = Y.lp.client.append_qs(
74+ qs_data, "related_bug", LP.cache.bug.self_link);
75+ }
76 var that = this;
77 var config = {
78 on: {
79@@ -152,7 +155,7 @@
80 // Template for rendering the bug details.
81 _bug_details_template: function() {
82 return [
83- '<table><tbody><tr><td>',
84+ '<table class="confirm-bug-details"><tbody><tr><td>',
85 '<div id="client-listing">',
86 ' <div class="buglisting-col1">',
87 ' <div class="importance {{importance_class}}">',
88
89=== modified file 'lib/lp/bugs/javascript/duplicates.js'
90--- lib/lp/bugs/javascript/duplicates.js 2012-08-03 12:47:11 +0000
91+++ lib/lp/bugs/javascript/duplicates.js 2012-08-07 05:02:24 +0000
92@@ -41,6 +41,32 @@
93 });
94 },
95
96+ _syncResultsUI: function() {
97+ Y.lp.bugs.bug_picker.BugPicker.prototype._syncResultsUI.call(this);
98+ // Display warning about different project if required.
99+ var bug_data = this.get('results');
100+ if (!bug_data.length) {
101+ this._hide_bug_results();
102+ return;
103+ }
104+ bug_data = bug_data[0];
105+ if (!bug_data.different_pillars) {
106+ return;
107+ }
108+ var bug_details_table = this._results_box.one(
109+ 'table.confirm-bug-details tbody');
110+ var different_project_warning =
111+ '<p id="different-project-warning" ' +
112+ 'class="block-sprite large-warning">' +
113+ 'This bug affects a different project to the bug you ' +
114+ 'are specifying here.' +
115+ '</p>';
116+ var warning_node_row = Y.Node.create('<tr></tr>');
117+ warning_node_row.appendChild(
118+ Y.Node.create('<td></td>').setContent(different_project_warning));
119+ bug_details_table.appendChild(warning_node_row);
120+ },
121+
122 _bug_search_header: function() {
123 var search_header = '<p class="search-header">' +
124 'Marking this bug as a duplicate will, ' +
125
126=== modified file 'lib/lp/bugs/javascript/tests/test_bug_picker.js'
127--- lib/lp/bugs/javascript/tests/test_bug_picker.js 2012-08-03 12:47:11 +0000
128+++ lib/lp/bugs/javascript/tests/test_bug_picker.js 2012-08-07 05:02:24 +0000
129@@ -17,10 +17,15 @@
130 Y.Assert.areEqual(
131 'file:///api/devel/bugs',
132 this.mockio.last_request.url);
133- Y.Assert.areEqual(
134- this.mockio.last_request.config.data,
135+ var expected_data =
136 'ws.accept=application.json&ws.op=getBugData&' +
137- 'bug_id=' + bug_id);
138+ 'bug_id=' + bug_id;
139+ if (Y.Lang.isValue(LP.cache.bug)) {
140+ var bug_uri = encodeURIComponent('api/devel/bugs/1');
141+ expected_data += '&related_bug=' + bug_uri;
142+ }
143+ Y.Assert.areEqual(
144+ this.mockio.last_request.config.data, expected_data);
145 } else {
146 Y.Assert.areEqual(
147 '/api/devel/bugs/1', this.mockio.last_request.url);
148@@ -47,7 +52,8 @@
149 bug_summary: 'dupe title',
150 is_private: is_private,
151 duplicate_of_link: 'api/devel/bugs/' + bug_id,
152- self_link: 'api/devel/bugs/' + bug_id}];
153+ self_link: 'api/devel/bugs/' + bug_id,
154+ different_pillars: this.different_pillars}];
155 this.mockio.last_request.successJSON(expected_updated_entry);
156 this._assert_form_state(true);
157 },
158
159=== modified file 'lib/lp/bugs/javascript/tests/test_duplicates.js'
160--- lib/lp/bugs/javascript/tests/test_duplicates.js 2012-08-02 13:52:58 +0000
161+++ lib/lp/bugs/javascript/tests/test_duplicates.js 2012-08-07 05:02:24 +0000
162@@ -127,6 +127,20 @@
163 this._assert_form_state(false);
164 },
165
166+ // A warning is displayed for search results which are not targeted to
167+ // the same project.
168+ test_different_pillars: function() {
169+ this.widget = this._createWidget(false);
170+ this.different_pillars = true;
171+ this._assert_search_form_submission(4);
172+ this._assert_search_form_success(4);
173+ var privacy_message = Y.one('#different-project-warning');
174+ Y.Assert.areEqual(
175+ 'This bug affects a different project to the bug ' +
176+ 'you are specifying here.',
177+ privacy_message.get('text').trim());
178+ },
179+
180 // The expected data is submitted after searching for and selecting a
181 // bug.
182 _assert_dupe_submission: function(bug_id) {
183
184=== modified file 'lib/lp/code/javascript/tests/test_bugspeclinks.html'
185--- lib/lp/code/javascript/tests/test_bugspeclinks.html 2012-08-03 12:47:11 +0000
186+++ lib/lp/code/javascript/tests/test_bugspeclinks.html 2012-08-07 05:02:24 +0000
187@@ -61,11 +61,8 @@
188 <div id="fixture">
189 </div>
190 <script type="text/x-template" id="bugspec-links">
191- <a href="#" class="pick-bug js-action">Select a bug</a>
192- </script>
193- <div>
194 <div id="buglinks" class="actions">
195- <a href="https://foo/+linkbug" id="linkbug">
196+ <a href="#" id="linkbug" class="pick-bug js-action">
197 Link to bug report</a>
198 <div id="buglink-list">
199 <a id="delete-buglink-6"
200@@ -73,6 +70,6 @@
201 class="delete-buglink" title="Remove link"></a>
202 </div>
203 </div>
204- </div>
205+ </script>
206 </body>
207 </html>
208
209=== modified file 'lib/lp/systemhomes.py'
210--- lib/lp/systemhomes.py 2012-07-27 08:01:54 +0000
211+++ lib/lp/systemhomes.py 2012-08-07 05:02:24 +0000
212@@ -128,7 +128,7 @@
213 """See `IMaloneApplication`."""
214 return getUtility(IBugTaskSet).search(search_params)
215
216- def getBugData(self, user, bug_id):
217+ def getBugData(self, user, bug_id, related_bug=None):
218 """See `IMaloneApplication`."""
219 search_params = BugTaskSearchParams(user, bug=bug_id)
220 bugtasks = getUtility(IBugTaskSet).search(search_params)
221@@ -138,6 +138,9 @@
222 data = []
223 for bug in bugs:
224 bugtask = bug.default_bugtask
225+ different_pillars = related_bug and (
226+ set(bug.affected_pillars).isdisjoint(
227+ related_bug.affected_pillars)) or False
228 data.append({
229 'id': bug_id,
230 'information_type': bug.information_type.title,
231@@ -149,7 +152,8 @@
232 'status_class': 'status' + bugtask.status.name,
233 'bug_summary': bug.title,
234 'description': bug.description,
235- 'bug_url': canonical_url(bugtask)})
236+ 'bug_url': canonical_url(bugtask),
237+ 'different_pillars': different_pillars})
238 return data
239
240 def createBug(self, owner, title, description, target,