launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #10205
[Merge] lp:~wallyworld/launchpad/private-dupe-bug-warning-943497 into lp:launchpad
Ian Booth has proposed merging lp:~wallyworld/launchpad/private-dupe-bug-warning-943497 into lp:launchpad.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
Related bugs:
Bug #943497 in Launchpad itself: "warn when you are going to mark a public bug as a duplicate of a private one"
https://bugs.launchpad.net/launchpad/+bug/943497
For more details, see:
https://code.launchpad.net/~wallyworld/launchpad/private-dupe-bug-warning-943497/+merge/116248
== Implementation ==
Factor out the javascript to handle setting bug duplicates from bugtask_index into it's own yui module. It's now a widget built using standard yui patterns and has tests for the first time. There were also a couple of other clean up things done as drive bys.
This is the first step in warning fixing bug 943497. I didn't want to add new functionality to the code soup that existed before this branch.
About half the added lines are for the new yui tests.
== Tests ==
Add new yui tests module test_mark_bug_duplicate
== Lint ==
Checking for conflicts and issues in changed files.
Linting changed files:
lib/lp/bugs/javascript/bugtask_index.js
lib/lp/bugs/javascript/filebug_dupefinder.js
lib/lp/bugs/javascript/mark_bug_duplicate.js
lib/lp/bugs/javascript/tests/test_bugtask_delete.html
lib/lp/bugs/javascript/tests/test_mark_bug_duplicate.html
lib/lp/bugs/javascript/tests/test_mark_bug_duplicate.js
--
https://code.launchpad.net/~wallyworld/launchpad/private-dupe-bug-warning-943497/+merge/116248
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~wallyworld/launchpad/private-dupe-bug-warning-943497 into lp:launchpad.
=== modified file 'lib/lp/bugs/javascript/bugtask_index.js'
--- lib/lp/bugs/javascript/bugtask_index.js 2012-07-21 03:09:16 +0000
+++ lib/lp/bugs/javascript/bugtask_index.js 2012-07-24 03:47:21 +0000
@@ -14,13 +14,6 @@
// Override for testing
namespace.ANIM_DURATION = 1;
-// lazr.FormOverlay objects.
-var duplicate_form_overlay;
-var privacy_form_overlay;
-
-// The url of the page used to update bug duplicates.
-var update_dupe_url;
-
// The launchpad js client used.
var lp_client;
@@ -30,15 +23,7 @@
// The bug itself, taken from cache.
var bug_repr;
-// Overlay related vars.
-var submit_button_html =
- '<button type="submit" name="field.actions.change" ' +
- 'value="Change" class="lazr-pos lazr-btn" >OK</button>';
-var cancel_button_html =
- '<button type="button" name="field.actions.cancel" ' +
- 'class="lazr-neg lazr-btn" >Cancel</button>';
var privacy_link;
-var privacy_spinner;
var link_branch_link;
namespace.setup_bugtask_index = function() {
@@ -54,62 +39,12 @@
}
setup_client_and_bug();
-
- // Look for the 'Mark as duplicate' links or the
- // 'change duplicate bug' link.
- var update_dupe_link = Y.one(
- '.menu-link-mark-dupe, #change_duplicate_bug');
-
- if (update_dupe_link) {
- // First things first, pre-load the mark-dupe form.
- update_dupe_url = update_dupe_link.get('href');
- var mark_dupe_form_url = update_dupe_url + '/++form++';
-
- var form_header = '<p>Marking this bug as a duplicate will,' +
- ' by default, hide it from search results ' +
- 'listings.</p>';
-
- var has_dupes = Y.one('#portlet-duplicates');
- if (has_dupes !== null) {
- form_header = form_header +
- '<p class="block-sprite large-warning"> ' +
- '<strong>Note:</strong> ' +
- 'This bug has duplicates of its own. ' +
- 'If you go ahead, they too will become duplicates of ' +
- 'the bug you specify here. This cannot be undone.' +
- '</p>';
- }
-
- duplicate_form_overlay = new Y.lazr.FormOverlay({
- headerContent: '<h2>Mark bug report as duplicate</h2>',
- form_header: form_header,
- form_submit_button: Y.Node.create(submit_button_html),
- form_cancel_button: Y.Node.create(cancel_button_html),
- centered: true,
- form_submit_callback: update_bug_duplicate,
- visible: false
- });
- duplicate_form_overlay.render('#duplicate-form-container');
- duplicate_form_overlay.loadFormContentAndRender(
- mark_dupe_form_url);
-
- // Add an on-click handler to any links found that displays
- // the form overlay.
- update_dupe_link.on('click', function(e) {
- // Only go ahead if we have received the form content by the
- // time the user clicks:
- if (duplicate_form_overlay){
- e.preventDefault();
- duplicate_form_overlay.show();
- Y.DOM.byId('field.duplicateof').focus();
- }
- });
- // Add a class denoting them as js-action links.
- update_dupe_link.addClass('js-action');
- }
+ var dup_widget = new Y.lp.bugs.mark_bug_duplicate.MarkBugDuplicate({
+ srcNode: '#duplicate-actions',
+ lp_bug_entry: lp_bug_entry
+ });
privacy_link = Y.one('#privacy-link');
-
if (privacy_link) {
Y.lp.bugs.information_type_choice.setup_information_type_choice(
privacy_link, lp_client);
@@ -137,147 +72,6 @@
}
}
-/*
- * Update the bug duplicate via the LP API
- */
-function update_bug_duplicate(data) {
- // XXX noodles 2009-03-17 bug=336866 It seems the etag
- // returned by lp_save() is incorrect. Remove it for now
- // so that the second save does not result in a '412
- // precondition failed' error.
- //
- // XXX deryck 2009-04-29 bug=369293 Also, this has to
- // happen before *any* call to lp_save now that bug
- // subscribing can be done inline. Named operations
- // don't return new objects, making the cached bug's
- // etag invalid as well.
- lp_bug_entry.removeAttr('http_etag');
-
- // Hide the formoverlay:
- duplicate_form_overlay.hide();
-
- // Hide the dupe edit icon if it exists.
- var dupe_edit_icon = Y.one('#change_duplicate_bug');
- if (dupe_edit_icon !== null) {
- dupe_edit_icon.setStyle('display', 'none');
- }
-
- // Add the spinner...
- var dupe_span = Y.one('#mark-duplicate-text');
- dupe_span.removeClass('sprite bug-dupe');
- dupe_span.addClass('update-in-progress-message');
-
- // Set the new duplicate link on the bug entry.
- var new_dup_url = null;
- var new_dup_id = Y.Lang.trim(data['field.duplicateof'][0]);
- if (new_dup_id !== '') {
- var self_link = lp_bug_entry.get('self_link');
- var last_slash_index = self_link.lastIndexOf('/');
- new_dup_url = self_link.slice(0, last_slash_index+1) + new_dup_id;
- }
- var old_dup_url = lp_bug_entry.get('duplicate_of_link');
- lp_bug_entry.set('duplicate_of_link', new_dup_url);
-
- // Create a config for the lp_save method
- config = {
- on: {
- success: function(updated_entry) {
- dupe_span.removeClass('update-in-progress-message');
- lp_bug_entry = updated_entry;
-
- if (new_dup_url !== null) {
- dupe_span.set('innerHTML', [
- '<a id="change_duplicate_bug" ',
- 'title="Edit or remove linked duplicate bug" ',
- 'class="sprite edit action-icon">Edit</a>',
- 'Duplicate of <a>bug #</a>'].join(""));
- dupe_span.all('a').item(0)
- .set('href', update_dupe_url);
- dupe_span.all('a').item(1)
- .set('href', '/bugs/' + new_dup_id)
- .appendChild(document.createTextNode(new_dup_id));
- var has_dupes = Y.one('#portlet-duplicates');
- if (has_dupes !== null) {
- has_dupes.get('parentNode').removeChild(has_dupes);
- }
- show_comment_on_duplicate_warning();
- } else {
- dupe_span.addClass('sprite bug-dupe');
- dupe_span.set('innerHTML', [
- '<a class="menu-link-mark-dupe js-action">',
- 'Mark as duplicate</a>'].join(""));
- dupe_span.one('a').set('href', update_dupe_url);
- hide_comment_on_duplicate_warning();
- }
- Y.lp.anim.green_flash({
- node: dupe_span,
- duration: namespace.ANIM_DURATION
- }).run();
- // ensure the new link is hooked up correctly:
- dupe_span.one('a').on(
- 'click', function(e){
- e.preventDefault();
- duplicate_form_overlay.show();
- Y.DOM.byId('field.duplicateof').focus();
- });
- },
- failure: function(id, request) {
- dupe_span.removeClass('update-in-progress-message');
- if (request.status === 400) {
- duplicate_form_overlay.showError(
- new_dup_id + ' is not a valid bug number or' +
- ' nickname.');
- } else {
- duplicate_form_overlay.showError(request.responseText);
- }
- duplicate_form_overlay.show();
-
- // Reset the lp_bug_entry.duplicate_of_link as it wasn't
- // updated.
- lp_bug_entry.set('duplicate_of_link', old_dup_url);
-
- }
- }
- };
-
- // And save the updated entry.
- lp_bug_entry.lp_save(config);
-}
-
-/*
- * Ensure that a warning about adding a comment to a duplicate bug
- * is displayed.
- *
- * @method show_comment_on_duplicate_warning
- */
-var show_comment_on_duplicate_warning = function() {
- var duplicate_warning = Y.one('#warning-comment-on-duplicate');
- if (duplicate_warning === null) {
- var container = Y.one('#add-comment-form');
- var first_node = container.get('firstChild');
- duplicate_warning = Y.Node.create(
- ['<div class="warning message"',
- 'id="warning-comment-on-duplicate">',
- 'Remember, this bug report is a duplicate. ',
- 'Comment here only if you think the duplicate status is wrong.',
- '</div>'].join(''));
- container.insertBefore(duplicate_warning, first_node);
- }
-};
-
-/*
- * Ensure that no warning about adding a comment to a duplicate bug
- * is displayed.
- *
- * @method hide_comment_on_duplicate_warning
- */
-var hide_comment_on_duplicate_warning = function() {
- var duplicate_warning = Y.one('#warning-comment-on-duplicate');
- if (duplicate_warning !== null) {
- duplicate_warning.ancestor().removeChild(duplicate_warning);
- }
-};
-
/**
* Do a preemptive search for branches that contain the current bug's ID.
*/
@@ -1332,4 +1126,5 @@
"lp.app.widgets.expander", "lp.client", "escape",
"lp.client.plugins", "lp.app.errors",
"lp.app.banner.privacy",
- "lp.app.confirmationoverlay"]});
+ "lp.app.confirmationoverlay",
+ "lp.bugs.mark_bug_duplicate"]});
=== modified file 'lib/lp/bugs/javascript/filebug_dupefinder.js'
--- lib/lp/bugs/javascript/filebug_dupefinder.js 2012-07-07 14:00:30 +0000
+++ lib/lp/bugs/javascript/filebug_dupefinder.js 2012-07-24 03:47:21 +0000
@@ -298,7 +298,7 @@
// Alter the overlay's properties to make sure it submits correctly
// and to the right place.
- form_node = subscribe_form_overlay.form_node;
+ var form_node = subscribe_form_overlay.form_node;
form_node.set('action', form.get('action'));
form_node.set('method', 'post');
=== added file 'lib/lp/bugs/javascript/mark_bug_duplicate.js'
--- lib/lp/bugs/javascript/mark_bug_duplicate.js 1970-01-01 00:00:00 +0000
+++ lib/lp/bugs/javascript/mark_bug_duplicate.js 2012-07-24 03:47:21 +0000
@@ -0,0 +1,267 @@
+/* Copyright 2012 Canonical Ltd. This software is licensed under the
+ * GNU Affero General Public License version 3 (see the file LICENSE).
+ *
+ * Provide functionality for marking a bug as a duplicate.
+ *
+ * @module bugs
+ * @submodule mark_bug_duplicate
+ */
+YUI.add('lp.bugs.mark_bug_duplicate', function(Y) {
+
+var namespace = Y.namespace('lp.bugs.mark_bug_duplicate');
+
+// Overlay related vars.
+var submit_button_html =
+ '<button type="submit" name="field.actions.change" ' +
+ 'value="Change" class="lazr-pos lazr-btn" >OK</button>';
+var cancel_button_html =
+ '<button type="button" name="field.actions.cancel" ' +
+ 'class="lazr-neg lazr-btn" >Cancel</button>';
+
+/**
+ * Manage the process of marking a bug as a duplicate.
+ * This widget does no rendering itself; it is used to enhance existing HTML.
+ */
+namespace.MarkBugDuplicate = Y.Base.create("markBugDupeWidget", Y.Widget, [], {
+ initializer: function(cfg) {
+ var update_dupe_link = cfg.update_dupe_link;
+ if (update_dupe_link) {
+ // First things first, pre-load the mark-dupe form.
+ var mark_dupe_form_url =
+ update_dupe_link.get('href') + '/++form++';
+
+ var form_header = '<p>Marking this bug as a duplicate will,' +
+ ' by default, hide it from search results ' +
+ 'listings.</p>';
+
+ var duplicates_div = this.get('duplicates_div');
+ if (Y.Lang.isValue(duplicates_div)) {
+ form_header = form_header +
+ '<p class="block-sprite large-warning">' +
+ '<strong>Note:</strong> ' +
+ 'This bug has duplicates of its own. ' +
+ 'If you go ahead, they too will become duplicates of ' +
+ 'the bug you specify here. This cannot be undone.' +
+ '</p>';
+ }
+
+ this.duplicate_form_overlay = new Y.lazr.FormOverlay({
+ headerContent: '<h2>Mark bug report as duplicate</h2>',
+ form_header: form_header,
+ form_submit_button: Y.Node.create(submit_button_html),
+ form_cancel_button: Y.Node.create(cancel_button_html),
+ centered: true,
+ form_submit_callback:
+ Y.bind(this.update_bug_duplicate, this),
+ visible: false,
+ io_provider: cfg.io_provider
+ });
+ this.duplicate_form_overlay.render('#duplicate-form-container');
+ this.duplicate_form_overlay.loadFormContentAndRender(
+ mark_dupe_form_url);
+
+ // Add an on-click handler to any links found that displays
+ // the form overlay.
+ var that = this;
+ update_dupe_link.on('click', function(e) {
+ // Only go ahead if we have received the form content by the
+ // time the user clicks:
+ if (that.duplicate_form_overlay){
+ e.preventDefault();
+ that.duplicate_form_overlay.show();
+ Y.DOM.byId('field.duplicateof').focus();
+ }
+ });
+ // Add a class denoting them as js-action links.
+ update_dupe_link.addClass('js-action');
+ }
+ },
+
+ // Bug was successfully marked as a duplicate, update the UI.
+ update_bug_duplicate_success: function(updated_entry, new_dup_url,
+ new_dup_id) {
+ updated_entry.set('duplicate_of_link', new_dup_url);
+ this.set('lp_bug_entry', updated_entry);
+
+ var dupe_span = this.get('dupe_span');
+ var update_dup_url = dupe_span.one('a').get('href');
+ if (Y.Lang.isValue(new_dup_url)) {
+ dupe_span.setContent([
+ '<a id="change_duplicate_bug" ',
+ 'title="Edit or remove linked duplicate bug" ',
+ 'class="sprite edit action-icon">Edit</a>',
+ 'Duplicate of <a>bug #</a>'].join(""));
+ dupe_span.all('a').item(0) .set('href', update_dup_url);
+ dupe_span.all('a').item(1)
+ .set('href', '/bugs/' + new_dup_id)
+ .appendChild(document.createTextNode(new_dup_id));
+ var duplicates_div = this.get('duplicates_div');
+ Y.log(duplicates_div)
+ if (Y.Lang.isValue(duplicates_div)) {
+ duplicates_div.remove(true);
+ }
+ this.show_comment_on_duplicate_warning();
+ } else {
+ dupe_span.addClass('sprite bug-dupe');
+ dupe_span.setContent([
+ '<a class="menu-link-mark-dupe js-action">',
+ 'Mark as duplicate</a>'].join(""));
+ dupe_span.one('a').set('href', update_dup_url);
+ this.hide_comment_on_duplicate_warning();
+ }
+ Y.lp.anim.green_flash({
+ node: dupe_span,
+ duration: this.get('anim_duration')
+ }).run();
+ // ensure the new link is hooked up correctly:
+ var that = this;
+ dupe_span.one('a').on(
+ 'click', function(e){
+ e.preventDefault();
+ that.duplicate_form_overlay.show();
+ Y.DOM.byId('field.duplicateof').focus();
+ });
+ },
+
+ // There was an error marking a bug as a duplicate.
+ update_bug_duplicate_failure: function(response, old_dup_url, new_dup_id) {
+ // Reset the lp_bug_entry.duplicate_of_link as it wasn't
+ // updated.
+ this.get('lp_bug_entry').set('duplicate_of_link', old_dup_url);
+ if (response.status === 400) {
+ this.duplicate_form_overlay.showError(
+ new_dup_id + ' is not a valid bug number or nickname.');
+ } else {
+ this.duplicate_form_overlay.showError(response.responseText);
+ }
+ this.duplicate_form_overlay.show();
+ },
+
+ /**
+ * Update the bug duplicate via the LP API
+ */
+ update_bug_duplicate: function(data) {
+ // XXX noodles 2009-03-17 bug=336866 It seems the etag
+ // returned by lp_save() is incorrect. Remove it for now
+ // so that the second save does not result in a '412
+ // precondition failed' error.
+ //
+ // XXX deryck 2009-04-29 bug=369293 Also, this has to
+ // happen before *any* call to lp_save now that bug
+ // subscribing can be done inline. Named operations
+ // don't return new objects, making the cached bug's
+ // etag invalid as well.
+ var lp_bug_entry = this.get('lp_bug_entry');
+ lp_bug_entry.removeAttr('http_etag');
+
+ // Hide the formoverlay:
+ this.duplicate_form_overlay.hide();
+
+ var new_dup_url = null;
+ var new_dup_id = Y.Lang.trim(data['field.duplicateof'][0]);
+ if (new_dup_id !== '') {
+ var self_link = lp_bug_entry.get('self_link');
+ var last_slash_index = self_link.lastIndexOf('/');
+ new_dup_url = self_link.slice(0, last_slash_index+1) + new_dup_id;
+ }
+ var old_dup_url = lp_bug_entry.get('duplicate_of_link');
+ lp_bug_entry.set('duplicate_of_link', new_dup_url);
+
+ var dupe_span = this.get('dupe_span');
+ var that = this;
+ var config = {
+ on: {
+ start: function() {
+ dupe_span.removeClass('sprite bug-dupe');
+ dupe_span.addClass('update-in-progress-message');
+ },
+ end: function() {
+ dupe_span.removeClass('update-in-progress-message');
+ },
+ success: function(updated_entry) {
+ that.update_bug_duplicate_success(
+ updated_entry, new_dup_url, new_dup_id);
+ },
+ failure: function(id, response) {
+ that.update_bug_duplicate_failure(
+ response, old_dup_url, new_dup_id);
+ }
+ }
+ };
+ // And save the updated entry.
+ lp_bug_entry.lp_save(config);
+ },
+
+ /*
+ * Ensure that a warning about adding a comment to a duplicate bug
+ * is displayed.
+ *
+ * @method show_comment_on_duplicate_warning
+ */
+ show_comment_on_duplicate_warning: function() {
+ var duplicate_warning = Y.one('#warning-comment-on-duplicate');
+ if (duplicate_warning === null) {
+ var container = Y.one('#add-comment-form');
+ var first_node = container.get('firstChild');
+ duplicate_warning = Y.Node.create(
+ ['<div class="warning message"',
+ 'id="warning-comment-on-duplicate">',
+ 'Remember, this bug report is a duplicate. ',
+ 'Comment here only if you think the duplicate status ',
+ 'is wrong.',
+ '</div>'].join(''));
+ container.insertBefore(duplicate_warning, first_node);
+ }
+ },
+
+ /*
+ * Ensure that no warning about adding a comment to a duplicate bug
+ * is displayed.
+ *
+ * @method hide_comment_on_duplicate_warning
+ */
+ hide_comment_on_duplicate_warning: function() {
+ var duplicate_warning = Y.one('#warning-comment-on-duplicate');
+ if (duplicate_warning !== null) {
+ duplicate_warning.ancestor().removeChild(duplicate_warning);
+ }
+ }
+}, {
+ HTML_PARSER: {
+ // Look for the 'Mark as duplicate' links or the
+ // 'change duplicate bug' link.
+ update_dupe_link: '.menu-link-mark-dupe, #change_duplicate_bug',
+ // The rendered duplicate information.
+ dupe_span: '#mark-duplicate-text'
+ },
+ ATTRS: {
+ io_provider: {
+
+ },
+ // The launchpad client entry for the current bug.
+ lp_bug_entry: {
+ value: null
+ },
+ // The link used to update the duplicate bug number.
+ update_dupe_link: {
+ },
+ // The rendered duplicate information.
+ dupe_span: {
+ },
+ // Div containing duplicates of this bug.
+ duplicates_div: {
+ getter: function() {
+ return Y.one('#portlet-duplicates');
+ }
+ },
+ // Override for testing.
+ anim_duration: {
+ value: 1
+ }
+ }
+});
+
+}, "0.1", {"requires": [
+ "base", "io", "oop", "node", "event", "json", "lazr.formoverlay",
+ "lazr.effects", "lp.app.widgets.expander",
+ "lp.app.formwidgets.resizing_textarea", "plugin"]});
=== modified file 'lib/lp/bugs/javascript/tests/test_bugtask_delete.html'
--- lib/lp/bugs/javascript/tests/test_bugtask_delete.html 2012-05-15 14:48:22 +0000
+++ lib/lp/bugs/javascript/tests/test_bugtask_delete.html 2012-07-24 03:47:21 +0000
@@ -26,7 +26,6 @@
<!-- Dependencies -->
<script type="text/javascript" src="../../../../../build/js/lp/app/client.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>
=== added file 'lib/lp/bugs/javascript/tests/test_mark_bug_duplicate.html'
--- lib/lp/bugs/javascript/tests/test_mark_bug_duplicate.html 1970-01-01 00:00:00 +0000
+++ lib/lp/bugs/javascript/tests/test_mark_bug_duplicate.html 2012-07-24 03:47:21 +0000
@@ -0,0 +1,89 @@
+<!DOCTYPE html>
+<!--
+Copyright 2012 Canonical Ltd. This software is licensed under the
+GNU Affero General Public License version 3 (see the file LICENSE).
+-->
+
+<html>
+ <head>
+ <title>lp.bugs.mark_bug_duplicate Tests</title>
+
+ <!-- YUI and test setup -->
+ <script type="text/javascript"
+ src="../../../../../build/js/yui/yui/yui.js">
+ </script>
+ <link rel="stylesheet"
+ href="../../../../../build/js/yui/console/assets/console-core.css" />
+ <link rel="stylesheet"
+ href="../../../../../build/js/yui/console/assets/skins/sam/console.css" />
+ <link rel="stylesheet"
+ href="../../../../../build/js/yui/test/assets/skins/sam/test.css" />
+
+ <script type="text/javascript"
+ src="../../../../../build/js/lp/app/testing/testrunner.js"></script>
+ <script type="text/javascript"
+ src="../../../../../build/js/lp/app/testing/helpers.js"></script>
+
+ <link rel="stylesheet" href="../../../app/javascript/testing/test.css" />
+
+ <!-- Dependencies -->
+ <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/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>
+ <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>
+
+ <!-- The module under test. -->
+ <script type="text/javascript" src="../mark_bug_duplicate.js"></script>
+
+ <!-- Placeholder for any css asset for this module. -->
+ <!-- <link rel="stylesheet" href="../assets/lp.bugs.mark_bug_duplicate-core.css" /> -->
+
+ <!-- The test suite -->
+ <script type="text/javascript" src="test_mark_bug_duplicate.js"></script>
+
+ </head>
+ <body class="yui3-skin-sam">
+ <ul id="suites">
+ <li>lp.bugs.mark_bug_duplicate.test</li>
+ </ul>
+ <div id="fixture">
+ </div>
+ <script type="text/x-template" id="no-existing-duplicate">
+ <div><ul id="duplicate-actions">
+ <li class="sprite bug-dupe" id="mark-duplicate-text">
+ <a href="http://foo/+duplicate"
+ class="menu-link-mark-dupe js-action">
+ Mark as duplicate</a>
+ </li>
+ </ul></div>
+ <div id="duplicate-form-container"></div>
+ <div id="add-comment-form"></div>
+ <div id="portlet-duplicates"></div>
+ </script>
+ <script type="text/x-template" id="existing-duplicate">
+ <div><ul id="duplicate-actions">
+ <li class="" id="mark-duplicate-text">
+ <a class="sprite edit action-icon"
+ title="Edit or remove linked duplicate bug"
+ id="change_duplicate_bug"
+ href="http://foo/+duplicate">Edit</a>
+ Duplicate of <a href="http://foo/bugs/1234">bug #1234
+ </a>
+ </li>
+ </ul></div>
+ <div id="duplicate-form-container"></div>
+ <div id="add-comment-form"></div>
+ </script>
+ </body>
+</html>
=== added file 'lib/lp/bugs/javascript/tests/test_mark_bug_duplicate.js'
--- lib/lp/bugs/javascript/tests/test_mark_bug_duplicate.js 1970-01-01 00:00:00 +0000
+++ lib/lp/bugs/javascript/tests/test_mark_bug_duplicate.js 2012-07-24 03:47:21 +0000
@@ -0,0 +1,273 @@
+/* Copyright (c) 2012 Canonical Ltd. All rights reserved. */
+
+YUI.add('lp.bugs.mark_bug_duplicate.test', function (Y) {
+
+ var tests = Y.namespace('lp.bugs.mark_bug_duplicate.test');
+ tests.suite = new Y.Test.Suite('lp.bugs.mark_bug_duplicate Tests');
+
+ tests.suite.add(new Y.Test.Case({
+ name: 'lp.bugs.mark_bug_duplicate_tests',
+
+ setUp: function () {
+ window.LP = {
+ links: {},
+ cache: {
+ bug: {
+ self_link: 'api/devel/bugs/1',
+ duplicate_of_link: null
+ }
+ }
+ };
+ this.mockio = new Y.lp.testing.mockio.MockIo();
+ this.lp_client = new Y.lp.client.Launchpad({
+ io_provider: this.mockio
+ });
+ var bug_repr = window.LP.cache.bug;
+ this.lp_bug_entry = new Y.lp.client.Entry(
+ this.lp_client, bug_repr, bug_repr.self_link);
+ },
+
+ tearDown: function () {
+ Y.one('#fixture').empty(true);
+ delete this.lp_bug_entry;
+ delete this.mockio;
+ delete window.LP;
+ },
+
+ test_library_exists: function () {
+ Y.Assert.isObject(Y.lp.bugs.mark_bug_duplicate,
+ "Could not locate the lp.bugs.mark_bug_duplicate module");
+ },
+
+ _createWidget: function(existing_duplicate) {
+ var fixture_id;
+ if (existing_duplicate) {
+ fixture_id = "existing-duplicate";
+ } else {
+ fixture_id = "no-existing-duplicate";
+ }
+ Y.one('#fixture').appendChild(
+ Y.Node.create(Y.one('#' + fixture_id).getContent()));
+ var widget = new Y.lp.bugs.mark_bug_duplicate.MarkBugDuplicate({
+ srcNode: '#duplicate-actions',
+ lp_bug_entry: this.lp_bug_entry,
+ anim_duration: 0,
+ io_provider: this.mockio
+ });
+ Y.Assert.areEqual(
+ 'http://foo/+duplicate/++form++',
+ this.mockio.last_request.url);
+ this.mockio.success({
+ responseText: this._fake_duplicate_form(),
+ responseHeaders: {'Content-Type': 'text/html'}});
+ return widget;
+ },
+
+ _fake_duplicate_form: function() {
+ return [
+ '<div>',
+ '<input type="text" value="" name="field.duplicateof"',
+ 'id="field.duplicateof" class="textType">',
+ '</div>'
+ ].join('');
+ },
+
+ // The widget is created when there are no bug duplicates.
+ test_widget_creation_no_duplicate_exists: function() {
+ this.widget = this._createWidget(false);
+ Y.Assert.isInstanceOf(
+ Y.lp.bugs.mark_bug_duplicate.MarkBugDuplicate,
+ this.widget,
+ "Mark bug duplicate failed to be instantiated");
+ var url = this.widget.get('update_dupe_link').get('href');
+ Y.Assert.areEqual('http://foo/+duplicate', url);
+ Y.Assert.isNotNull(
+ Y.one('#mark-duplicate-text a.menu-link-mark-dupe'));
+ },
+
+ // The widget is created when there are bug duplicates.
+ test_widget_creation_duplicate_exists: function() {
+ this.widget = this._createWidget(true);
+ Y.Assert.isInstanceOf(
+ Y.lp.bugs.mark_bug_duplicate.MarkBugDuplicate,
+ this.widget,
+ "Mark bug duplicate failed to be instantiated");
+ var url = this.widget.get('update_dupe_link').get('href');
+ Y.Assert.areEqual('http://foo/+duplicate', url);
+ },
+
+ // The duplicate entry form renders and submits the expected data.
+ _assert_duplicate_form_submission: function(bug_id) {
+ var form = Y.one('#duplicate-form-container');
+ Y.Assert.isTrue(
+ form.one('div.pretty-overlay-window')
+ .hasClass('yui3-lazr-formoverlay-hidden'));
+ this.widget.get('update_dupe_link').simulate('click');
+ Y.Assert.isFalse(
+ Y.one('#duplicate-form-container div.pretty-overlay-window')
+ .hasClass('yui3-lazr-formoverlay-hidden'));
+ Y.DOM.byId('field.duplicateof').value = bug_id;
+ form.one('.lazr-pos').simulate('click');
+ Y.Assert.areEqual(
+ '/api/devel/bugs/1',
+ this.mockio.last_request.url);
+ var expected_link = '{}';
+ if (bug_id !== '') {
+ expected_link =
+ '{"duplicate_of_link":"api/devel/bugs/' + bug_id + '"}';
+ }
+ Y.Assert.areEqual(
+ expected_link, this.mockio.last_request.config.data);
+ },
+
+ // Submitting a bug dupe works as expected.
+ test_duplicate_form_submission_success: function() {
+ this.widget = this._createWidget(false);
+ var success_called = false;
+ this.widget.update_bug_duplicate_success =
+ function(updated_entry, new_dup_url, new_dup_id) {
+ Y.Assert.areEqual(
+ expected_updated_entry.duplicate_of_link,
+ updated_entry.duplicate_of_link);
+ Y.Assert.areEqual('api/devel/bugs/3', new_dup_url);
+ Y.Assert.areEqual(3, new_dup_id);
+ success_called = true;
+ };
+ this._assert_duplicate_form_submission(3);
+ var expected_updated_entry = {
+ lp_original_uri: 'api/devel/bugs/1',
+ duplicate_of_link: 'api/devel/bugs/3',
+ self_link: 'api/devel/bugs/1'};
+ this.mockio.last_request.successJSON(expected_updated_entry);
+ Y.Assert.isTrue(success_called);
+ },
+
+ // A submission failure is handled as expected.
+ test_duplicate_form_submission_failure: function() {
+ this.widget = this._createWidget(false);
+ var failure_called = false;
+ this.widget.update_bug_duplicate_failure =
+ function(response, old_dup_url, new_dup_id) {
+ Y.Assert.areEqual(
+ 'There was an error', response.responseText);
+ Y.Assert.areEqual(null, old_dup_url);
+ Y.Assert.areEqual(3, new_dup_id);
+ failure_called = true;
+ };
+ this._assert_duplicate_form_submission(3);
+ this.mockio.respond({
+ status: 400,
+ responseText: 'There was an error',
+ responseHeaders: {'Content-Type': 'text/html'}});
+ Y.Assert.isTrue(failure_called);
+ },
+
+ // Submitting a dupe removal request works as expected.
+ test_duplicate_form_submission_remove_dupe: function() {
+ this.widget = this._createWidget(true);
+ var success_called = false;
+ this.widget.update_bug_duplicate_success =
+ function(updated_entry, new_dup_url, new_dup_id) {
+ Y.Assert.areEqual(expected_updated_entry, updated_entry);
+ Y.Assert.areEqual(null, new_dup_url);
+ Y.Assert.areEqual('', new_dup_id);
+ success_called = true;
+ };
+ this._assert_duplicate_form_submission('');
+ var expected_updated_entry =
+ '{"duplicate_of_link":""}';
+ this.mockio.success({
+ responseText: expected_updated_entry,
+ responseHeaders: {'Content-Type': 'text/html'}});
+ Y.Assert.isTrue(success_called);
+ },
+
+ // The mark bug duplicate success function works as expected.
+ test_update_bug_duplicate_success: function() {
+ this.widget = this._createWidget(false);
+ var data = {
+ self_link: 'api/devel/bugs/1'};
+ var new_bug_entry = new Y.lp.client.Entry(
+ this.lp_client, data, data.self_link);
+ this.widget.update_bug_duplicate_success(
+ new_bug_entry, 'api/devel/bugs/3', 3);
+ // Test the updated bug entry.
+ Y.Assert.areEqual(
+ 'api/devel/bugs/3',
+ this.widget.get('lp_bug_entry').get('duplicate_of_link'));
+ // Test the Change Duplicate link.
+ Y.Assert.isNotNull(
+ Y.one('#mark-duplicate-text #change_duplicate_bug'));
+ // Test the duplicate warning message.
+ Y.Assert.isNotNull(Y.one('#warning-comment-on-duplicate'));
+ // Any previously listed duplicates are removed.
+ Y.Assert.isNull(Y.one('#portlet-duplicates'));
+ },
+
+ // The remove bug duplicate success function works as expected.
+ test_remove_bug_duplicate_success: function() {
+ this.widget = this._createWidget(true);
+ var data = {
+ self_link: 'api/devel/bugs/1'};
+ var new_bug_entry = new Y.lp.client.Entry(
+ this.lp_client, data, data.self_link);
+ this.widget.update_bug_duplicate_success(new_bug_entry, null, '');
+ // Test the updated bug entry.
+ Y.Assert.isNull(
+ this.widget.get('lp_bug_entry').get('duplicate_of_link'));
+ // Test the Mark as Duplicate link.
+ Y.Assert.isNotNull(
+ Y.one('#mark-duplicate-text .menu-link-mark-dupe'));
+ // Test the duplicate warning message is gone.
+ Y.Assert.isNull(Y.one('#warning-comment-on-duplicate'));
+ },
+
+ // The remove bug duplicate error function works as expected for
+ // generic errors.
+ test_update_bug_duplicate_generic_failure: function() {
+ this.widget = this._createWidget(false);
+ 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 response = {
+ status: 500,
+ responseText: 'An error occurred'
+ };
+ this.widget.update_bug_duplicate_failure(response, null, 3);
+ Y.Assert.isFalse(
+ Y.one('#duplicate-form-container div.pretty-overlay-window')
+ .hasClass('yui3-lazr-formoverlay-hidden'));
+ var error_msg = Y.one('.yui3-lazr-formoverlay-errors ul li');
+ Y.Assert.areEqual('An error occurred', error_msg.get('text'));
+ },
+
+ // The remove bug duplicate error function works as expected for
+ // invalid bug errors.
+ test_update_bug_duplicate_invalid_bug_failure: function() {
+ this.widget = this._createWidget(false);
+ 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 response = {
+ status: 400,
+ responseText: 'An error occurred'
+ };
+ this.widget.update_bug_duplicate_failure(response, null, 3);
+ Y.Assert.isFalse(
+ Y.one('#duplicate-form-container div.pretty-overlay-window')
+ .hasClass('yui3-lazr-formoverlay-hidden'));
+ var error_msg = Y.one('.yui3-lazr-formoverlay-errors ul li');
+ Y.Assert.areEqual(
+ '3 is not a valid bug number or nickname.',
+ error_msg.get('text'));
+ }
+ }));
+
+}, '0.1', {
+ requires: [
+ 'test', 'lp.testing.helpers', 'event', 'node-event-simulate',
+ 'console', 'lp.client', 'lp.testing.mockio', 'lp.anim',
+ 'lp.bugs.mark_bug_duplicate']
+});
Follow ups