launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #10574
[Merge] lp:~wallyworld/launchpad/link-branch-to-bug-411409 into lp:launchpad
Ian Booth has proposed merging lp:~wallyworld/launchpad/link-branch-to-bug-411409 into lp:launchpad.
Requested reviews:
Curtis Hovey (sinzui)
Related bugs:
Bug #411409 in Launchpad itself: "AJAX "Link to a bug report" could be improved"
https://bugs.launchpad.net/launchpad/+bug/411409
Bug #877880 in Launchpad itself: ""link to a bug" ajax on branch page has poor failure mode"
https://bugs.launchpad.net/launchpad/+bug/877880
For more details, see:
https://code.launchpad.net/~wallyworld/launchpad/link-branch-to-bug-411409/+merge/118083
== Implementation ==
This branch replaces the current javascript used for the link bug to branch functionality and uses the new bug picker widget instead. It also does a few little cleanup things.
The link bug to branch functionality is a little screwed up (even before this mp) and another branch will be needed to fix it. The main issue is that when the page is first rendered, the TAL produces a list of linked bugs which renders the bug titles and a remove icon in a <ul>. When a new bug is linked, the entire list of linked bugs is replaced with some new HTML via an XHR call, but the replacement HTML has the linked bugs in a <table> also showing the status and importance of each linked bug. The TAL should be updated to match the enhanced rendering provided by the javascript.
This mp removes 2 unnecessary XHR calls when the branch page loads. It all looks pretty good now.
Like the case when adding a dupe, a warning is displayed when the user attempts to link a public branch to a private bug.
== QA ==
Go to a branch page and click the "Link to a bug report" link. Marvel at the new widget.
== Tests ==
Rewrite the test_bugspeclinks yui module to add in new tests for the widget and run the current extract_candidate_bug_id tests.
== Lint ==
Checking for conflicts and issues in changed files.
Linting changed files:
lib/canonical/launchpad/icing/css/components/bug_picker.css
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.html
lib/lp/code/javascript/branch.bugspeclinks.js
lib/lp/code/javascript/tests/test_bugspeclinks.html
lib/lp/code/javascript/tests/test_bugspeclinks.js
lib/lp/code/templates/branch-related-bugs-specs.pt
--
https://code.launchpad.net/~wallyworld/launchpad/link-branch-to-bug-411409/+merge/118083
Your team Launchpad code reviewers is subscribed to branch lp:launchpad.
=== modified file 'lib/canonical/launchpad/icing/css/components/bug_picker.css'
--- lib/canonical/launchpad/icing/css/components/bug_picker.css 2012-08-02 09:04:27 +0000
+++ lib/canonical/launchpad/icing/css/components/bug_picker.css 2012-08-03 12:55:39 +0000
@@ -1,8 +1,8 @@
/* The bug picker buttons should be left aligned, not centred. */
-.yui3-duplicatebugpickerwidget .yui3-picker-footer-slot {
+.yui3-bugpickerwidget .yui3-picker-footer-slot {
margin-left: 0;
}
-.yui3-duplicatebugpickerwidget .search-header {
+.yui3-bugpickerwidget .search-header {
margin-top: 6px;
}
=== modified file 'lib/lp/bugs/javascript/bug_picker.js'
--- lib/lp/bugs/javascript/bug_picker.js 2012-08-03 01:45:37 +0000
+++ lib/lp/bugs/javascript/bug_picker.js 2012-08-03 12:55:39 +0000
@@ -171,7 +171,7 @@
' <a href="{{bug_url}}" class="bugtitle sprite new-window">',
' <span class="bugnumber">#{{id}}</span> {{bug_summary}}</a>',
' <div class="buginfo-extra">',
- ' <p class="ellipsis line-block" style="max-width: 70em;">',
+ ' <p class="ellipsis line-block">',
' {{description}}</p>',
' </div>',
' </div>',
@@ -211,8 +211,6 @@
// The server may return multiple bugs but for now we only
// support displaying one of them.
bug_data = bug_data[0];
- var bug_id = bug_data.id;
- var bug_title = bug_data.bug_summary;
bug_data.private_warning
= this.get('public_context') && bug_data.is_private;
var private_warning_message
@@ -236,7 +234,7 @@
.on('click', function(e) {
e.halt();
that.fire(
- namespace.BugPicker.SAVE_DUPLICATE, bug_id, bug_title);
+ namespace.BugPicker.SAVE_DUPLICATE, bug_data);
});
},
=== modified file 'lib/lp/bugs/javascript/duplicates.js'
--- lib/lp/bugs/javascript/duplicates.js 2012-08-02 13:52:58 +0000
+++ lib/lp/bugs/javascript/duplicates.js 2012-08-03 12:55:39 +0000
@@ -28,8 +28,9 @@
Y.lp.bugs.bug_picker.BugPicker.SAVE_DUPLICATE, function(e) {
e.preventDefault();
that.set('progress', 100);
- var bug_id = e.details[0];
- var bug_title = e.details[1];
+ var bug_data = e.details[0];
+ var bug_id = bug_data.id;
+ var bug_title = bug_data.bug_summary;
that._submit_bug(bug_id, bug_title, this.save_button);
});
this.subscribe(
=== modified file 'lib/lp/bugs/javascript/tests/test_bug_picker.js'
--- lib/lp/bugs/javascript/tests/test_bug_picker.js 2012-08-02 13:52:58 +0000
+++ lib/lp/bugs/javascript/tests/test_bug_picker.js 2012-08-03 12:55:39 +0000
@@ -122,8 +122,9 @@
Y.lp.bugs.bug_picker.BugPicker.SAVE_DUPLICATE,
function(e) {
e.preventDefault();
- Y.Assert.areEqual(3, e.details[0]);
- Y.Assert.areEqual('dupe title', e.details[1]);
+ var bug_data = e.details[0];
+ Y.Assert.areEqual(3, bug_data.id);
+ Y.Assert.areEqual('dupe title', bug_data.bug_summary);
save_bug_called = true;
});
Y.one(
=== modified file 'lib/lp/bugs/javascript/tests/test_duplicates.html'
--- lib/lp/bugs/javascript/tests/test_duplicates.html 2012-08-02 09:04:27 +0000
+++ lib/lp/bugs/javascript/tests/test_duplicates.html 2012-08-03 12:55:39 +0000
@@ -34,7 +34,6 @@
<script type="text/javascript" src="../../../../../build/js/lp/app/lp.js"></script>
<script type="text/javascript" src="../../../../../build/js/lp/app/testing/mockio.js"></script>
<script type="text/javascript" src="../../../../../build/js/lp/app/activator/activator.js"></script>
- <script type="text/javascript" src="../../../../../build/js/lp/app/anim/anim.js"></script>
<script type="text/javascript" src="../../../../../build/js/lp/app/effects/effects.js"></script>
<script type="text/javascript" src="../../../../../build/js/lp/app/expander.js"></script>
<script type="text/javascript" src="../../../../../build/js/lp/app/extras/extras.js"></script>
=== modified file 'lib/lp/code/javascript/branch.bugspeclinks.js'
--- lib/lp/code/javascript/branch.bugspeclinks.js 2011-08-09 14:18:02 +0000
+++ lib/lp/code/javascript/branch.bugspeclinks.js 2012-08-03 12:55:39 +0000
@@ -1,26 +1,20 @@
-/* Copyright 2009 Canonical Ltd. This software is licensed under the
+/* Copyright 2012 Canonical Ltd. This software is licensed under the
* GNU Affero General Public License version 3 (see the file LICENSE).
*
- * Code for handling links to branches from bugs and specs.
+ * Provide functionality for picking a bug.
*
- * @module BranchLinks
- * @requires base, lp.anim, lazr.formoverlay
+ * @module bugs
+ * @submodule bug_picker
*/
-
YUI.add('lp.code.branch.bugspeclinks', function(Y) {
var namespace = Y.namespace('lp.code.branch.bugspeclinks');
-
-var lp_client; // The LP client
-
-var link_bug_overlay;
-
-var error_handler;
+var superclass = Y.lp.bugs.bug_picker.BugPicker;
/*
* Extract the best candidate for a bug number from the branch name.
*/
-function extract_candidate_bug_id(branch_name) {
+namespace.extract_candidate_bug_id = function(branch_name) {
// Extract all the runs of numbers in the branch name and sort by
// descending length.
var chunks = branch_name.split(/\D/g).sort(function (a, b) {
@@ -41,256 +35,257 @@
}
}
return null;
-}
-// Expose in the namespace so we can test it.
-namespace._extract_candidate_bug_id = extract_candidate_bug_id;
-
-/*
- * Connect the links to the javascript events.
- */
-namespace.connect_branchlinks = function() {
-
- error_handler = new Y.lp.client.ErrorHandler();
- error_handler.clearProgressUI = function() {
- destroy_temporary_spinner();
- };
- error_handler.showError = function(error_message) {
- alert('An unexpected error has occurred.');
- Y.log(error_message);
- };
-
- link_bug_overlay = new Y.lazr.FormOverlay({
- headerContent: '<h2>Link to a bug</h2>',
- form_submit_button: Y.Node.create(
- '<button type="submit" name="buglink.actions.change" ' +
- 'value="Change" class="lazr-pos lazr-btn">Ok</button>'),
- form_cancel_button: Y.Node.create(
- '<button type="button" name="buglink.actions.cancel" ' +
- 'class="lazr-neg lazr-btn">Cancel</button>'),
- centered: true,
- form_submit_callback: link_bug_to_branch,
- visible: false
- });
- link_bug_overlay.render();
- link_bug_overlay.loadFormContentAndRender('+linkbug/++form++');
- var linkbug_handle = Y.one('#linkbug');
- linkbug_handle.addClass('js-action');
- linkbug_handle.on('click', function(e) {
- e.preventDefault();
- link_bug_overlay.show();
- var field = Y.DOM.byId('field.bug');
- field.focus();
- var guessed_bug_id = extract_candidate_bug_id(LP.cache.context.name);
- if (Y.Lang.isValue(guessed_bug_id)) {
- field.value = guessed_bug_id;
- // Select the pre-filled bug number (if any) so that it will be
- // replaced by anything the user types (getting the guessed bug
- // number out of the way quickly if we guessed incorrectly).
- field.selectionStart = 0;
- field.selectionEnd = 999;
- }
- });
- connect_remove_links();
};
-/*
- * Connect the remove links of each bug link to the javascript functions to
- * remove the links.
- */
-function connect_remove_links() {
- Y.on('click', function(e) {
- e.preventDefault();
- var bugnumber = get_bugnumber_from_id(e.currentTarget.get('id'));
- unlink_bug_from_branch(bugnumber);
- }, '.delete-buglink');
-}
-
-/*
- * Link a specified bug to the branch.
- */
-function link_bug_to_branch(data) {
- link_bug_overlay.hide();
-
- create_temporary_spinner();
-
- var bugnumber = data['field.bug'];
- var existing = Y.one('#buglink-' + bugnumber);
- if (Y.Lang.isValue(existing)) {
- // Bug is already linked, don't do unneccessary requests.
- Y.lp.anim.green_flash({node: existing}).run();
- return;
- }
-
- get_bug_from_bugnumber(bugnumber, function(bug) {
-
- config = {
+
+/**
+ * A widget to allow a user to choose a bug to link to a branch.
+ */
+namespace.LinkedBugPicker = Y.Base.create(
+ "linkedBugPickerWidget", Y.lp.bugs.bug_picker.BugPicker, [], {
+ initializer: function(cfg) {
+ this.lp_client = new Y.lp.client.Launchpad(cfg);
+ },
+
+ bindUI: function() {
+ superclass.prototype.bindUI.apply(this, arguments);
+ var that = this;
+ this.subscribe(
+ Y.lp.bugs.bug_picker.BugPicker.SAVE_DUPLICATE, function(e) {
+ e.preventDefault();
+ that.set('progress', 100);
+ var bug_data = e.details[0];
+ var bug_id = bug_data.id;
+ that._link_bug_to_branch(bug_id, this.save_button);
+ });
+ this.after('visibleChange', function() {
+ if (this.get('visible')) {
+ var guessed_bug_id =
+ namespace.extract_candidate_bug_id(LP.cache.context.name);
+ if (Y.Lang.isValue(guessed_bug_id)) {
+ this._search_input.set('value', guessed_bug_id);
+ // Select the pre-filled bug number (if any) so that it will
+ // be replaced by anything the user types (getting the
+ // guessed bug number out of the way quickly if we guessed
+ // incorrectly).
+ this._search_input.set('selectionStart', 0);
+ this._search_input.set('selectionEnd', 999);
+ }
+ }
+ });
+ this._connect_remove_links();
+ },
+
+ /**
+ * Wire up the links to remove a bug link.
+ * @private
+ */
+ _connect_remove_links: function() {
+ var that = this;
+ Y.on('click', function(e) {
+ e.halt();
+ var bug_id = that._get_bug_id_from_remove_link(e.currentTarget);
+ var bug_link =
+ Y.lp.client.get_absolute_uri("/api/devel/bugs/" + bug_id);
+ that._unlink_bug_from_branch(bug_id, bug_link);
+ }, '#buglinks.actions .delete-buglink');
+ },
+
+ /*
+ * Get the bug id for the link element.
+ *
+ * Since we control the element id, we don't have to use crazy reqexes or
+ * something.
+ */
+ _get_bug_id_from_remove_link: function(link) {
+ var dom_id = link.get('id');
+ return dom_id.substr('delete-buglink-'.length, dom_id.length);
+ },
+
+ /**
+ * Link a specified bug to the branch.
+ * @param bug_id
+ * @param widget
+ * @private
+ */
+ _link_bug_to_branch: function(bug_id, widget) {
+ var existing = Y.one('#buglink-' + bug_id);
+ if (Y.Lang.isValue(existing)) {
+ // Bug is already linked, don't do unnecessary requests.
+ this._performDefaultSave();
+ Y.lp.anim.green_flash({node: existing}).run();
+ return;
+ }
+ var bug_link =
+ Y.lp.client.get_absolute_uri("/api/devel/bugs/" + bug_id);
+ var spinner;
+ var that = this;
+ var config = {
on: {
+ start: function() {
+ that.set('error', null);
+ spinner = that._show_bug_spinner(widget);
+ that._show_temporary_spinner();
+ },
success: function(entry) {
- // XXX: rockstar - linkBug still is returning BugBranches.
- // This means that I'll need to change this once I fix
- // that.
- var config = {
- on: {
- success: function(bugtasks) {
- update_bug_links(bug);
- }
- }
- };
- bug.follow_link('bug_tasks', config);
+ that._update_bug_links(bug_id, spinner);
},
- failure: error_handler.getFailureHandler()
+ failure: function(id, response) {
+ if (spinner !== null) {
+ spinner.remove(true);
+ }
+ that._hide_temporary_spinner();
+ that.set('error', response.responseText);
+ }
},
parameters: {
- bug: bug.get('self_link')
+ bug: bug_link
}
};
- set_up_lp_client();
- lp_client.named_post(
+ this.lp_client.named_post(
LP.cache.context.self_link, 'linkBug', config);
- });
-}
-
-/*
- * Update the list of bug links.
- */
-function update_bug_links(bug) {
-
- BUG_LINK_SNIPPET = '++bug-links';
- Y.io(BUG_LINK_SNIPPET, {
- on: {
- success: function(id, response) {
- destroy_temporary_spinner();
- Y.one('#linkbug')
- .set('innerHTML', 'Link to another bug report');
- Y.one('#buglink-list')
- .set('innerHTML', response.responseText);
- var new_buglink = Y.one('#buglink-' + bug.get('id'));
- var anim = Y.lp.anim.green_flash({node: new_buglink});
- anim.on('end', connect_remove_links);
- anim.run();
- },
- failure: function(id, response) {
- // At least remove the "Linking..." text
- destroy_temporary_spinner();
-
- alert('Unable to update bug links.');
- Y.log(response);
+ },
+
+ /**
+ * Update the list of bug links.
+ * @param bug_id
+ * @param spinner
+ * @private
+ */
+ _update_bug_links: function(bug_id, spinner) {
+ var that = this;
+ this.lp_client.io_provider.io('++bug-links', {
+ on: {
+ end: function() {
+ if (spinner !== null) {
+ spinner.remove(true);
+ }
+ that._hide_temporary_spinner();
+ },
+ success: function(id, response) {
+ that._link_bug_success(bug_id, response.responseText);
+ },
+ failure: function(id, response) {
+ that.set('error', response.responseText);
+ }
}
- }
- });
-
-}
-
-/*
- * Unlink a bug from the branch.
- */
-function unlink_bug_from_branch(bugnumber) {
- link_bug_overlay.hide();
-
- Y.one('#delete-buglink-' + bugnumber).get('children').set(
- 'src', '/@@/spinner');
- get_bug_from_bugnumber(bugnumber, function(bug) {
-
- config = {
+ });
+ },
+
+ /**
+ * A bug was linked successfully.
+ * @param bug_id
+ * @param buglink_content
+ * @private
+ */
+ _link_bug_success: function(bug_id, buglink_content) {
+ this._performDefaultSave();
+ Y.one('#linkbug').setContent('Link to another bug report');
+ Y.one('#buglink-list').setContent(buglink_content);
+ this._connect_remove_links();
+ var new_buglink = Y.one('#buglink-' + bug_id);
+ var anim = Y.lp.anim.green_flash({node: new_buglink});
+ anim.run();
+ },
+
+ /**
+ * Unlink a bug from the branch.
+ * @param bug_id
+ * @param bug_link
+ * @private
+ */
+ _unlink_bug_from_branch: function(bug_id, bug_link) {
+ var that = this;
+ var config = {
on: {
- success: function(updated_entry) {
- var element = Y.one('#buglink-' + bugnumber);
- var parent_element = element.get('parentNode');
- anim = Y.lp.anim.red_flash({node: element});
- anim.on('end', function() {
- parent_element.removeChild(element);
-
- // Check to see if that was the only bug linked.
- var buglinks = Y.all(".bug-branch-summary");
- if (!buglinks.size()) {
- Y.one('#linkbug').set('innerHTML',
- 'Link to a bug report');
- }
- });
- anim.run();
+ start: function() {
+ Y.one('#delete-buglink-' + bug_id).get('children').set(
+ 'src', '/@@/spinner');
},
- failure: function(id, response) {
- alert('An unexpected error has occurred.');
- Y.one('#delete-buglink-' + bugnumber).get('children').set(
+ end: function() {
+ Y.one('#delete-buglink-' + bug_id).get('children').set(
'src', '/@@/remove');
- Y.log(response.responseText);
+ },
+ success: function() {
+ that._unlink_bug_success(bug_id);
+ },
+ failure: function(id, response) {
+ that.set('error', response.responseText);
}
},
parameters: {
- bug: bug.get('self_link')
+ bug: bug_link
}
};
- set_up_lp_client();
- lp_client.named_post(
+ this.lp_client.named_post(
LP.cache.context.self_link, 'unlinkBug', config);
- });
-}
-
-
-/*
- * Get the bugnumber for the element id.
- *
- * Since we control the element id, we don't have to use crazy reqexes or
- * something.
- */
-function get_bugnumber_from_id(id) {
- return id.substr('remove-buglink-'.length, id.length);
-}
-
-/*
- * Get the bug representation from the bugnumber.
- *
- * XXX: rockstar - There is a better way to do this, I'm sure. I just need to
- * figure it out after everything else is done.
- */
-function get_bug_from_bugnumber(bugnumber, callback) {
- var bug_uri = '/bugs/' + bugnumber;
- config = {
- on: {
- success: callback
- }
- };
- set_up_lp_client();
- lp_client.get(bug_uri, config);
-}
-
-/*
- * Set up the lp_client.
- *
- * This would probably be better served in a place where everyone could get to
- * it, or at least so everything in code could get to it.
- */
-function set_up_lp_client() {
- if (lp_client === undefined) {
- lp_client = new Y.lp.client.Launchpad();
- }
-}
-
-/*
- * Show the temporary "Linking..." text
- */
-function create_temporary_spinner() {
- var temp_spinner = Y.Node.create([
- '<div id="temp-spinner">',
- '<img src="/@@/spinner"/>Linking...',
- '</div>'].join(''));
- var buglinks = Y.one('#buglinks');
- var last = Y.one('#linkbug').get('parentNode');
- if (last) {
- buglinks.insertBefore(temp_spinner, last);
- }
-}
-
-/*
- * Destroy the temporary "Linking..." text
- */
-function destroy_temporary_spinner() {
-
- var temp_spinner = Y.one('#temp-spinner');
- var spinner_parent = temp_spinner.get('parentNode');
- spinner_parent.removeChild(temp_spinner);
-
-}
-
-}, "0.1", {"requires": ["base", "lp.anim", "lazr.formoverlay",
+ },
+
+ /**
+ * A bug was unlinked successfully.
+ * @param bug_id
+ * @private
+ */
+ _unlink_bug_success: function(bug_id) {
+ var element = Y.one('#buglink-' + bug_id);
+ var parent_element = element.get('parentNode');
+ var anim = Y.lp.anim.red_flash({node: element});
+ var finish_update = function() {
+ parent_element.removeChild(element);
+ // Check to see if that was the only bug linked.
+ var buglinks = Y.all(".bug-branch-summary");
+ if (!buglinks.size()) {
+ Y.one('#linkbug')
+ .setContent('Link to a bug report');
+ }
+ };
+ if (this.get('use_animation')) {
+ anim.on('end', finish_update);
+ } else {
+ finish_update();
+ }
+ anim.run();
+ },
+
+ /*
+ * Show the temporary "Linking..." text.
+ */
+ _show_temporary_spinner: function() {
+ var temp_spinner = Y.Node.create([
+ '<div id="temp-spinner">',
+ '<img src="/@@/spinner"/>Linking...',
+ '</div>'].join(''));
+ var buglinks = Y.one('#buglinks');
+ var last = Y.one('#linkbug').get('parentNode');
+ if (last) {
+ buglinks.insertBefore(temp_spinner, last);
+ }
+ },
+
+ /*
+ * Destroy the temporary "Linking..." text.
+ */
+ _hide_temporary_spinner: function() {
+
+ var temp_spinner = Y.one('#temp-spinner');
+ var spinner_parent = temp_spinner.get('parentNode');
+ spinner_parent.removeChild(temp_spinner);
+
+ }
+}, {
+ ATTRS: {
+ header_text: {
+ value: 'Select bug to link'
+ },
+ save_link_text: {
+ value: "Link Bug"
+ },
+ private_warning_message: {
+ value:
+ 'Linking this public branch to a private bug means '+
+ 'that it won\'t be visible to contributors.'
+ }
+ }
+});
+}, "0.1", {"requires": ["base", "lp.anim", "lp.bugs.bug_picker",
"lp.client", "lp.client.plugins"]});
=== modified file 'lib/lp/code/javascript/tests/test_bugspeclinks.html'
--- lib/lp/code/javascript/tests/test_bugspeclinks.html 2012-07-02 12:58:02 +0000
+++ lib/lp/code/javascript/tests/test_bugspeclinks.html 2012-08-03 12:55:39 +0000
@@ -25,10 +25,23 @@
<link rel="stylesheet" href="../../../app/javascript/testing/test.css" />
<!-- Dependencies -->
- <script type="text/javascript"
- src="../../../../../build/js/lp/app/client.js"></script>
- <script type="text/javascript"
- src="../../../../../build/js/lp/app/overlay/overlay.js"></script>
+ <script type="text/javascript" src="../../../../../build/js/lp/app/mustache.js"></script>
+ <script type="text/javascript" src="../../../../../build/js/lp/app/anim/anim.js"></script>
+ <script type="text/javascript" src="../../../../../build/js/lp/app/client.js"></script>
+ <script type="text/javascript" src="../../../../../build/js/lp/app/errors.js"></script>
+ <script type="text/javascript" src="../../../../../build/js/lp/app/lp.js"></script>
+ <script type="text/javascript" src="../../../../../build/js/lp/app/testing/mockio.js"></script>
+ <script type="text/javascript" src="../../../../../build/js/lp/app/activator/activator.js"></script>
+ <script type="text/javascript" src="../../../../../build/js/lp/app/effects/effects.js"></script>
+ <script type="text/javascript" src="../../../../../build/js/lp/app/expander.js"></script>
+ <script type="text/javascript" src="../../../../../build/js/lp/app/extras/extras.js"></script>
+ <script type="text/javascript" src="../../../../../build/js/lp/app/formoverlay/formoverlay.js"></script>
+ <script type="text/javascript" src="../../../../../build/js/lp/app/formwidgets/formwidgets.js"></script>
+ <script type="text/javascript" src="../../../../../build/js/lp/app/inlineedit/editor.js"></script>
+ <script type="text/javascript" src="../../../../../build/js/lp/app/lazr/lazr.js"></script>
+ <script type="text/javascript" src="../../../../../build/js/lp/app/overlay/overlay.js"></script>
+ <script type="text/javascript" src="../../../../../build/js/lp/app/picker/picker.js"></script>
+ <script type="text/javascript" src="../../../../../build/js/lp/bugs/bug_picker.js"></script>
<!-- The module under test. -->
<script type="text/javascript" src="../branch.bugspeclinks.js"></script>
@@ -38,11 +51,28 @@
<!-- The test suite. -->
<script type="text/javascript" src="test_bugspeclinks.js"></script>
+ <script type="text/javascript" src="../../../bugs/javascript/tests/test_bug_picker.js"></script>
</head>
<body class="yui3-skin-sam">
<ul id="suites">
<li>lp.code.branch.bugspeclinks.test</li>
</ul>
+ <div id="fixture">
+ </div>
+ <script type="text/x-template" id="bugspec-links">
+ <a href="#" class="pick-bug js-action">Select a bug</a>
+ </script>
+ <div>
+ <div id="buglinks" class="actions">
+ <a href="https://foo/+linkbug" id="linkbug">
+ Link to bug report</a>
+ <div id="buglink-list">
+ <a id="delete-buglink-6"
+ href="+bug/6/+delete"
+ class="delete-buglink" title="Remove link"></a>
+ </div>
+ </div>
+ </div>
</body>
</html>
=== modified file 'lib/lp/code/javascript/tests/test_bugspeclinks.js'
--- lib/lp/code/javascript/tests/test_bugspeclinks.js 2012-07-02 12:58:02 +0000
+++ lib/lp/code/javascript/tests/test_bugspeclinks.js 2012-08-03 12:55:39 +0000
@@ -2,7 +2,7 @@
YUI.add('lp.code.branch.bugspeclinks.test', function (Y) {
var module = Y.lp.code.branch.bugspeclinks;
- var extract_candidate_bug_id = module._extract_candidate_bug_id;
+ var extract_candidate_bug_id = module.extract_candidate_bug_id;
var tests = Y.namespace('lp.code.branch.bugspeclinks.test');
tests.suite = new Y.Test.Suite('code.branch.bugspeclinks Tests');
@@ -59,8 +59,173 @@
}));
+ tests.suite.add(new Y.Test.Case(Y.merge(
+ Y.lp.bugs.bug_picker.test.common_bug_picker_tests,
+ {
+ name: 'Linked Bug Picker Tests',
+
+ setUp: function () {
+ window.LP = {
+ links: {},
+ cache: {
+ context: {
+ name: 'abranch',
+ self_link:
+ 'https://foo/api/devel/~fred/firefox/abranch'
+ }
+ }
+ };
+ this.mockio = new Y.lp.testing.mockio.MockIo();
+ this.lp_client = new Y.lp.client.Launchpad({
+ io_provider: this.mockio
+ });
+ },
+
+ tearDown: function () {
+ Y.one('#fixture').empty(true);
+ if (Y.Lang.isValue(this.widget)) {
+ this.widget.destroy();
+ }
+ delete this.mockio;
+ delete window.LP;
+ },
+
+ test_library_exists: function () {
+ Y.Assert.isObject(Y.lp.bugs.bug_picker,
+ "Could not locate the lp.code.branch.bugspeclinks module");
+ },
+
+ _createWidget: function() {
+ Y.one('#fixture').appendChild(
+ Y.Node.create(Y.one('#bugspec-links').getContent()));
+ var widget = new Y.lp.code.branch.bugspeclinks.LinkedBugPicker({
+ picker_activator: '.pick-bug',
+ private_warning_message:
+ 'You are selecting a private bug.',
+ use_animation: false,
+ io_provider: this.mockio
+ });
+ widget.render();
+ widget.hide();
+ return widget;
+ },
+
+ // The widget is created as expected.
+ test_create_widget: function() {
+ this.widget = this._createWidget();
+ Y.Assert.isInstanceOf(
+ Y.lp.code.branch.bugspeclinks.LinkedBugPicker,
+ this.widget,
+ "Linked bug picker failed to be instantiated");
+ Y.Assert.isFalse(this.widget.get('visible'));
+ Y.one('.pick-bug').simulate('click');
+ Y.Assert.isTrue(this.widget.get('visible'));
+ var remove_dupe = Y.one('.yui3-bugpickerwidget a.remove');
+ Y.Assert.isTrue(remove_dupe.hasClass('hidden'));
+ },
+
+ // The expected data is submitted after searching for and selecting a
+ // bug.
+ _assert_link_bug_submission: function(bug_id) {
+ this._assert_search_form_submission(bug_id);
+ this._assert_search_form_success(bug_id);
+ Y.one(
+ '.yui3-picker-footer-slot [name="field.actions.save"]')
+ .simulate('click');
+ this._assert_form_state(true);
+ Y.Assert.areEqual(
+ '/api/devel/~fred/firefox/abranch',
+ this.mockio.last_request.url);
+ var bug_uri = encodeURIComponent(
+ 'file:///api/devel/bugs/' + bug_id);
+ var expected_data =
+ 'ws.op=linkBug&bug=' + bug_uri;
+ Y.Assert.areEqual(
+ expected_data, this.mockio.last_request.config.data);
+ },
+
+ // Linking a bug works as expected.
+ test_picker_form_submission_success: function() {
+ this.widget = this._createWidget();
+ this._assert_link_bug_submission(3);
+ var success_called = false;
+ this.widget._link_bug_success =
+ function(bug_id, link_bug_content) {
+ Y.Assert.areEqual(3, bug_id);
+ Y.Assert.areEqual('<html></html>', link_bug_content);
+ success_called = true;
+ };
+ var bug_data = {
+ bug_link: "api/devel/bugs/3"
+ };
+ this.mockio.last_request.successJSON(bug_data);
+ Y.Assert.areEqual('++bug-links', this.mockio.last_request.url);
+ this.mockio.last_request.respond({
+ responseText: '<html></html>',
+ responseHeaders: {'Content-Type': 'text/html'}
+ });
+ Y.Assert.isTrue(success_called);
+ },
+
+ // A link failure is handled as expected.
+ test_picker_form_submission_failure: function() {
+ this.widget = this._createWidget();
+ this._assert_link_bug_submission(3);
+ this.mockio.respond({
+ status: 400,
+ responseText: 'There was an error',
+ responseHeaders: {'Content-Type': 'text/html'}});
+ Y.Assert.areEqual(
+ 'There was an error', this.widget.get('error'));
+ },
+
+ // Submitting an unlink request works as expected.
+ test_picker_form_submission_remove_buglink: function() {
+ this.widget = this._createWidget();
+ var success_called = false;
+ this.widget._unlink_bug_success =
+ function(bug_id) {
+ Y.Assert.areEqual(6, bug_id);
+ success_called = true;
+ };
+ Y.one('#delete-buglink-6').simulate('click');
+ this.mockio.success({
+ responseText: null,
+ responseHeaders: {'Content-Type': 'text/html'}});
+ Y.Assert.isTrue(success_called);
+ },
+
+ // The link bug success function works as expected.
+ test_link_bug_success: function() {
+ this.widget = this._createWidget();
+ var data = {
+ self_link: 'api/devel/bugs/1'};
+ var new_bug_entry = new Y.lp.client.Entry(
+ this.lp_client, data, data.self_link);
+ var link_html = '<div id="buglink-3"></div>';
+ this.widget._link_bug_success(3, link_html);
+ Y.Assert.areEqual(
+ 'Link to another bug report',
+ Y.one('#linkbug').get('text'));
+ Y.Assert.areEqual(
+ link_html, Y.one('#buglink-list').getContent());
+ },
+
+ // The unlink bug success function works as expected.
+ test_unlink_bug_success: function() {
+ this.widget = this._createWidget();
+ // Set up the bug data on the page.
+ Y.one('#linkbug').setContent('Link to another');
+ Y.one('#buglink-list').appendChild(
+ Y.Node.create('<div id="buglink-3"></div>'));
+ this.widget._unlink_bug_success(3);
+ Y.Assert.areEqual(
+ 'Link to a bug report', Y.one('#linkbug').getContent());
+ }
+ })));
}, '0.1', {
requires: ['test', 'lp.testing.helpers', 'console',
- 'lp.code.branch.bugspeclinks', 'node-event-simulate']
+ 'lp.code.branch.bugspeclinks', 'node-event-simulate',
+ 'lp.bugs.bug_picker', 'lp.bugs.bug_picker.test']
});
=== modified file 'lib/lp/code/templates/branch-related-bugs-specs.pt'
--- lib/lp/code/templates/branch-related-bugs-specs.pt 2012-06-16 13:12:41 +0000
+++ lib/lp/code/templates/branch-related-bugs-specs.pt 2012-08-03 12:55:39 +0000
@@ -76,7 +76,12 @@
var logged_in = LP.links['me'] !== undefined;
if (logged_in) {
- Y.lp.code.branch.bugspeclinks.connect_branchlinks();
+ var config = {
+ picker_activator: '#linkbug'
+ };
+ var linked_bug_picker = new Y.lp.code.branch.bugspeclinks.LinkedBugPicker(config);
+ linked_bug_picker.render();
+ linked_bug_picker.hide();
}
});
Follow ups