← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~rharding/launchpad/bug_yui35_four into lp:launchpad

 

Richard Harding has proposed merging lp:~rharding/launchpad/bug_yui35_four into lp:launchpad.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~rharding/launchpad/bug_yui35_four/+merge/112609

= Summary =

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

== Implementation Notes ==

Updating the tests is mostly just reorganizing them to add an actual module. 

== Tests ==

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

== Lint ==

Updated for lint since we indented a lot of code.

== LoC Qualification ==

It's a negative LoC change.
-- 
https://code.launchpad.net/~rharding/launchpad/bug_yui35_four/+merge/112609
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~rharding/launchpad/bug_yui35_four into lp:launchpad.
=== modified file 'lib/lp/bugs/javascript/filebug_dupefinder.js'
--- lib/lp/bugs/javascript/filebug_dupefinder.js	2012-06-26 00:15:11 +0000
+++ lib/lp/bugs/javascript/filebug_dupefinder.js	2012-06-28 18:18:19 +0000
@@ -210,7 +210,7 @@
 
         // Copy the value from the search field into the title field
         // on the filebug form.
-        Y.one('#bug-reporting-form input[name=field.title]').set(
+        Y.one('#bug-reporting-form input[name="field.title"]').set(
             'value', search_field.get('value'));
 
         // Finally, change the label on the search button and show it again.

=== modified file 'lib/lp/bugs/javascript/tests/test_filebug_dupefinder.html'
--- lib/lp/bugs/javascript/tests/test_filebug_dupefinder.html	2012-03-14 04:41:36 +0000
+++ lib/lp/bugs/javascript/tests/test_filebug_dupefinder.html	2012-06-28 18:18:19 +0000
@@ -54,7 +54,7 @@
     <body class="yui3-skin-sam">
         <ul id="suites">
             <!-- <li>lp.large_indicator.test</li> -->
-            <li>lp.filebug_dupefinder.test</li>
+            <li>lp.bugs.filebug_dupefinder.test</li>
         </ul>
 
         <!-- Example markup required by test suite -->

=== modified file 'lib/lp/bugs/javascript/tests/test_filebug_dupefinder.js'
--- lib/lp/bugs/javascript/tests/test_filebug_dupefinder.js	2012-02-10 17:21:16 +0000
+++ lib/lp/bugs/javascript/tests/test_filebug_dupefinder.js	2012-06-28 18:18:19 +0000
@@ -1,341 +1,333 @@
-YUI({
-    base: '../../../../canonical/launchpad/icing/yui/',
-    filter: 'raw', combine: false, fetchCSS: false
-    }).use('test', 'console', 'lp.bugs.filebug_dupefinder',
-        'node-event-simulate', function(Y) {
-
-var suite = new Y.Test.Suite("lp.bugs.filebug_dupefinder Tests");
-var module = Y.lp.bugs.filebug_dupefinder;
-
-/*
- * A wrapper for the Y.Event.simulate() function.  The wrapper accepts
- * CSS selectors and Node instances instead of raw nodes.
- */
-function simulate(widget, selector, evtype, options) {
-    var node_to_use = widget;
-    if (selector !== undefined) {
-        node_to_use = widget.one(selector);
-    }
-    var rawnode = Y.Node.getDOMNode(node_to_use);
-    Y.Event.simulate(rawnode, evtype, options);
-}
-
-/**
- * A stub io handler.
- */
-function IOStub(test_case){
-    if (!(this instanceof IOStub)) {
-        throw new Error("Constructor called as a function");
-    }
-    this.calls = [];
-    this.io = function(url, config) {
-        this.calls.push(url);
-        var response = {responseText: ''};
-        // We may have been passed text to use in the response.
-        if (Y.Lang.isValue(arguments.callee.responseText)) {
-            response.responseText = arguments.callee.responseText;
-        }
-        // We currently only support calling the success handler.
-        config.on.success(undefined, response, arguments.callee.args);
-        // After calling the handler, resume the test.
-        if (Y.Lang.isFunction(arguments.callee.doAfter)) {
-            test_case.resume(arguments.callee.doAfter);
-        }
-    };
-}
-
-suite.add(new Y.Test.Case({
-    name: 'Test filebug form manipulation.',
-
-    setUp: function() {
-        // Reset the HTML elements.
-        Y.one("#possible-duplicates").set('innerHTML', '');
-        Y.one(Y.DOM.byId('field.comment')).set('value', '');
-        var node = Y.one(Y.DOM.byId('field.search'));
-        if (node !== null) {
-            node.set('value', '');
-        }
-        node = Y.one(Y.DOM.byId('field.title'));
-        if (node !== null) {
-            node.set('value', '');
-        }
-        Y.one('#filebug-form-container').addClass('transparent')
-                .setStyles({opacity: '0', display: 'none'});
-
-        this.config = {};
-        this.config.yio = new IOStub(this);
-        module.setup_config(this.config);
-    },
-
-    tearDown: function() {
-        Y.one('#filebug-form').set(
-                'action', 'https://bugs.launchpad.dev/foo/+filebug');
-    },
-
-    /**
-     * Some helper functions
-     */
-    selectNode: function(node, selector) {
-        if (!Y.Lang.isValue(node)) {
-            node = Y.one('#test-root');
-        }
-        var node_to_use = node;
-        if (Y.Lang.isValue(selector)) {
-            node_to_use = node.one(selector);
-        }
-        return node_to_use;
-    },
-
-    assertIsVisible: function(node, selector) {
-        node = this.selectNode(node, selector);
-        Y.Assert.areNotEqual('none', node.getStyle('display'));
-    },
-
-    assertIsNotVisible: function(node, selector) {
-        node = this.selectNode(node, selector);
-        Y.Assert.areEqual('none', node.getStyle('display'));
-    },
-
-    assertNodeText: function(node, selector, text) {
-        node = this.selectNode(node, selector);
-        Y.Assert.areEqual(text, node.get('innerHTML'));
-    },
-
-
-    /**
-     * A user first searches for duplicate bugs. If there are no duplicates
-     * the file bug form should be visible for bug details to be entered.
-     */
-    test_no_dups_search_shows_filebug_form: function() {
-        // filebug container should not initially be visible
-        this.assertIsNotVisible(null, '#filebug-form-container');
-        var search_text = Y.one(Y.DOM.byId('field.search'));
-        search_text.set('value', 'foo');
-        var search_button = Y.one(Y.DOM.byId('field.actions.search'));
-        // The search button should initially say 'Next'
-        Y.Assert.areEqual('Next', search_button.get('value'));
-        this.config.yio.io.responseText = 'No similar bug reports.';
-        this.config.yio.io.doAfter = function() {
-            // Check the expected io calls have been made.
-            Y.ArrayAssert.itemsAreEqual(
-                ['https://bugs.launchpad.dev/' +
-                 'foo/+filebug-show-similar?title=foo'],
-                this.config.yio.calls);
-            // filebug container should be visible after the dup search
-            this.assertIsVisible(null, '#filebug-form-container');
-            var dups_node = Y.one("#possible-duplicates");
-            this.assertNodeText(
-                    dups_node, undefined, 'No similar bug reports.');
+/* Copyright (c) 2012, Canonical Ltd. All rights reserved. */
+YUI.add('lp.bugs.filebug_dupefinder.test', function (Y) {
+    var module = Y.lp.bugs.filebug_dupefinder;
+
+    /*
+     * A wrapper for the Y.Event.simulate() function.  The wrapper accepts
+     * CSS selectors and Node instances instead of raw nodes.
+     */
+    function simulate(widget, selector, evtype, options) {
+        var node_to_use = widget;
+        if (selector !== undefined) {
+            node_to_use = widget.one(selector);
+        }
+        var rawnode = Y.Node.getDOMNode(node_to_use);
+        Y.Event.simulate(rawnode, evtype, options);
+    }
+
+    /**
+     * A stub io handler.
+     */
+    function IOStub(test_case){
+        if (!(this instanceof IOStub)) {
+            throw new Error("Constructor called as a function");
+        }
+        this.calls = [];
+        this.io = function(url, config) {
+            this.calls.push(url);
+            var response = {responseText: ''};
+            // We may have been passed text to use in the response.
+            if (Y.Lang.isValue(arguments.callee.responseText)) {
+                response.responseText = arguments.callee.responseText;
+            }
+            // We currently only support calling the success handler.
+            config.on.success(undefined, response, arguments.callee.args);
+            // After calling the handler, resume the test.
+            if (Y.Lang.isFunction(arguments.callee.doAfter)) {
+                test_case.resume(arguments.callee.doAfter);
+            }
         };
-        simulate(search_button, undefined, 'click');
-        this.wait();
-    },
-
-    /**
-     * A user first searches for duplicate bugs. If there are duplicates
-     * the dups should be listed and the file bug form should not be visible.
-     */
-    test_dups_search_shows_dup_info: function() {
-        // filebug container should not initially be visible
-        this.assertIsNotVisible(null, '#filebug-form-container');
-        var search_text = Y.one(Y.DOM.byId('field.search'));
-        search_text.set('value', 'foo');
-        var search_button = Y.one(Y.DOM.byId('field.actions.search'));
-        this.config.yio.io.responseText = ([
-                '<table><tr><td id="bug-details-expander" ',
-                'class="bug-already-reported-expander"></td></tr></table>',
-                '<input type="button" value="No, I need to report a new bug"',
-                ' name="field.bug_already_reported_as"',
-                ' id="bug-not-already-reported" style="display: block">',
-                ].join(''));
-        this.config.yio.io.doAfter = function() {
-            // filebug container should not be visible when there are dups
+    }
+
+    // Configure the javascript module under test. In production, the
+    // setup_dupe_finder() is called from the page template. We need to pass
+    // in a stub io handler here so that the XHR call made during set up is
+    // ignored.
+    var config = {};
+    config.yio = new IOStub();
+    module.setup_config(config);
+    module.setup_dupe_finder();
+
+    var tests = Y.namespace('lp.bugs.filebug_dupefinder.test');
+    tests.suite = new Y.Test.Suite('bugs.filebug_dupefinder Tests');
+
+    tests.suite.add(new Y.Test.Case({
+        name: 'Test filebug form manipulation.',
+
+        setUp: function() {
+            // Reset the HTML elements.
+            Y.one("#possible-duplicates").set('innerHTML', '');
+            Y.one(Y.DOM.byId('field.comment')).set('value', '');
+            var node = Y.one(Y.DOM.byId('field.search'));
+            if (node !== null) {
+                node.set('value', '');
+            }
+            node = Y.one(Y.DOM.byId('field.title'));
+            if (node !== null) {
+                node.set('value', '');
+            }
+            Y.one('#filebug-form-container').addClass('transparent')
+                    .setStyles({opacity: '0', display: 'none'});
+
+            this.config = {};
+            this.config.yio = new IOStub(this);
+            module.setup_config(this.config);
+        },
+
+        tearDown: function() {
+            Y.one('#filebug-form').set(
+                    'action', 'https://bugs.launchpad.dev/foo/+filebug');
+        },
+
+        /**
+         * Some helper functions
+         */
+        selectNode: function(node, selector) {
+            if (!Y.Lang.isValue(node)) {
+                node = Y.one('#test-root');
+            }
+            var node_to_use = node;
+            if (Y.Lang.isValue(selector)) {
+                node_to_use = node.one(selector);
+            }
+            return node_to_use;
+        },
+
+        assertIsVisible: function(node, selector) {
+            node = this.selectNode(node, selector);
+            Y.Assert.areNotEqual('none', node.getStyle('display'));
+        },
+
+        assertIsNotVisible: function(node, selector) {
+            node = this.selectNode(node, selector);
+            Y.Assert.areEqual('none', node.getStyle('display'));
+        },
+
+        assertNodeText: function(node, selector, text) {
+            node = this.selectNode(node, selector);
+            Y.Assert.areEqual(text, node.get('innerHTML'));
+        },
+
+
+        /**
+         * A user first searches for duplicate bugs. If there are no duplicates
+         * the file bug form should be visible for bug details to be entered.
+         */
+        test_no_dups_search_shows_filebug_form: function() {
+            // filebug container should not initially be visible
             this.assertIsNotVisible(null, '#filebug-form-container');
-            // we should have a 'new bug' button
-            this.assertIsVisible(null, '#bug-not-already-reported');
-            // The search button should say 'Check again'
-            Y.Assert.areEqual('Check again', search_button.get('value'));
-        };
-        simulate(search_button, undefined, 'click');
-        this.wait();
-    },
-
-    /**
-     * A user first searches for duplicate bugs. They can start typing in some
-     * detail. They can search again for dups and their input should be
-     * retained.
-     */
-    test_dups_search_retains_user_input_when_no_dups: function() {
-        // filebug container should not initially be visible
-        this.assertIsNotVisible(null, '#filebug-form-container');
-        var search_text = Y.one(Y.DOM.byId('field.search'));
-        search_text.set('value', 'foo');
-        var search_button = Y.one(Y.DOM.byId('field.actions.search'));
-        this.config.yio.io.responseText = 'No similar bug reports.';
-        this.config.yio.io.doAfter = function() {
-            var comment_text = Y.one(Y.DOM.byId('field.comment'));
-            comment_text.set('value', 'an error occurred');
+            var search_text = Y.one(Y.DOM.byId('field.search'));
+            search_text.set('value', 'foo');
+            var search_button = Y.one(Y.DOM.byId('field.actions.search'));
+            // The search button should initially say 'Next'
+            Y.Assert.areEqual('Next', search_button.get('value'));
+            this.config.yio.io.responseText = 'No similar bug reports.';
             this.config.yio.io.doAfter = function() {
-                // The user input should be retained
-                Y.Assert.areEqual(
-                    'an error occurred', comment_text.get('value'));
+                // Check the expected io calls have been made.
+                Y.ArrayAssert.itemsAreEqual(
+                    ['https://bugs.launchpad.dev/' +
+                     'foo/+filebug-show-similar?title=foo'],
+                    this.config.yio.calls);
+                // filebug container should be visible after the dup search
+                this.assertIsVisible(null, '#filebug-form-container');
+                var dups_node = Y.one("#possible-duplicates");
+                this.assertNodeText(
+                        dups_node, undefined, 'No similar bug reports.');
             };
             simulate(search_button, undefined, 'click');
             this.wait();
-        };
-        simulate(search_button, undefined, 'click');
-        this.wait();
-    },
+        },
 
-    /**
-     * A user first searches for duplicate bugs and there are none.
-     * They can start typing in some detail. They can search again for dups
-     * and their input should be retained even when there are dups and they
-     * have to click the "No, this is a new bug" button.
-     */
-    test_dups_search_retains_user_input_when_dups: function() {
-        // filebug container should not initially be visible
-        this.assertIsNotVisible(null, '#filebug-form-container');
-        var search_text = Y.one(Y.DOM.byId('field.search'));
-        search_text.set('value', 'foo');
-        var search_button = Y.one(Y.DOM.byId('field.actions.search'));
-        this.config.yio.io.responseText = 'No similar bug reports.';
-        this.config.yio.io.doAfter = function() {
-            var comment_text = Y.one(Y.DOM.byId('field.comment'));
-            comment_text.set('value', 'an error occurred');
+        /**
+         * A user first searches for duplicate bugs. If there are duplicates
+         * the dups should be listed and the file bug form should not be
+         * visible.
+         */
+        test_dups_search_shows_dup_info: function() {
+            // filebug container should not initially be visible
+            this.assertIsNotVisible(null, '#filebug-form-container');
+            var search_text = Y.one(Y.DOM.byId('field.search'));
+            search_text.set('value', 'foo');
+            var search_button = Y.one(Y.DOM.byId('field.actions.search'));
             this.config.yio.io.responseText = ([
-                    '<img id="bug-details-expander" ',
-                    'class="bug-already-reported-expander" ',
-                    'src="/@@/treeCollapsed">',
-                    '<input type="button" value="No, I need to report a bug"',
-                    ' name="field.bug_already_reported_as"',
+                    '<table><tr><td id="bug-details-expander" ',
+                    'class="bug-already-reported-expander"></td></tr></table>',
+                    '<input type="button" value="No, I need to report a new',
+                    ' bug" name="field.bug_already_reported_as"',
                     ' id="bug-not-already-reported" style="display: block">'
                     ].join(''));
             this.config.yio.io.doAfter = function() {
-                var new_bug_button = Y.one('#bug-not-already-reported');
-                simulate(new_bug_button, undefined, 'click');
-                // filebug container should be visible
-                this.assertIsVisible(null, '#filebug-form-container');
-                // The user input should be retained
-                Y.Assert.areEqual(
-                    'an error occurred', comment_text.get('value'));
-            };
-            simulate(search_button, undefined, 'click');
-            this.wait();
-        };
-        simulate(search_button, undefined, 'click');
-        this.wait();
-    },
-
-    /**
-     * The filebug form url is correctly set when the page loads.
-     */
-    test_project_initial_filebug_form_action: function() {
-        Y.Assert.areEqual(
-            'https://bugs.launchpad.dev/foo/+filebug',
-            Y.one('#filebug-form').get('action'));
-    },
-
-    add_project_selector: function() {
-        var project_selector = Y.Node.create([
-        '<tr>',
-        '    <td>',
-        '        <label for="field.product">Project:</label>',
-        '        <select size="1" name="field.product" id="field.product">',
-        '            <option value="foo">Foo</option>',
-        '            <option value="bar">Bar</option>',
-        '        </select>',
-        '    </td>',
-        '</tr>'
-        ].join(''));
-        Y.one('#search-field').insert(project_selector, 'before');
-        module.setup_product_urls();
-    },
-
-    /**
-     * The filebug form url is correctly updated when the project changes.
-     */
-    test_project_change_filebug_form_action: function() {
-        this.add_project_selector();
-        var project = Y.one(Y.DOM.byId('field.product'));
-        project.set('value', 'bar');
-        simulate(project, undefined, 'change');
-        Y.Assert.areEqual(
-            'https://bugs.launchpad.dev/bar/+filebug',
-            Y.one('#filebug-search-form').get('action'));
-    },
-
-    /**
-     * A user first searches for duplicate bugs and there are none.
-     * They can start typing in some detail. They change the project and
-     * perform a new search. Their input should be retained.
-     */
-    test_project_change_retains_user_input_after_dups_serach: function() {
-        Y.one(Y.DOM.byId('field.product')).set('value', 'foo');
-        module.setup_product_urls();
-        // filebug container should not initially be visible
-        this.assertIsNotVisible(null, '#filebug-form-container');
-        var search_text = Y.one(Y.DOM.byId('field.search'));
-        search_text.set('value', 'foo');
-        var search_button = Y.one(Y.DOM.byId('field.actions.search'));
-        this.config.yio.io.responseText = 'No similar bug reports.';
-        this.config.yio.io.doAfter = function() {
-            var comment_text = Y.one(Y.DOM.byId('field.comment'));
-            comment_text.set('value', 'an error occurred');
-
-            this.config.yio.io.responseText = 'Bug filing details';
+                // filebug container should not be visible when there are dups
+                this.assertIsNotVisible(null, '#filebug-form-container');
+                // we should have a 'new bug' button
+                this.assertIsVisible(null, '#bug-not-already-reported');
+                // The search button should say 'Check again'
+                Y.Assert.areEqual('Check again', search_button.get('value'));
+            };
+            simulate(search_button, undefined, 'click');
+            this.wait();
+        },
+
+        /**
+         * A user first searches for duplicate bugs. They can start typing in
+         * some detail. They can search again for dups and their input should
+         * be retained.
+         */
+        test_dups_search_retains_user_input_when_no_dups: function() {
+            // filebug container should not initially be visible
+            this.assertIsNotVisible(null, '#filebug-form-container');
+            var search_text = Y.one(Y.DOM.byId('field.search'));
+            search_text.set('value', 'foo');
+            var search_button = Y.one(Y.DOM.byId('field.actions.search'));
+            this.config.yio.io.responseText = 'No similar bug reports.';
+            this.config.yio.io.doAfter = function() {
+                var comment_text = Y.one(Y.DOM.byId('field.comment'));
+                comment_text.set('value', 'an error occurred');
+                this.config.yio.io.doAfter = function() {
+                    // The user input should be retained
+                    Y.Assert.areEqual(
+                        'an error occurred', comment_text.get('value'));
+                };
+                simulate(search_button, undefined, 'click');
+                this.wait();
+            };
+            simulate(search_button, undefined, 'click');
+            this.wait();
+        },
+
+        /**
+         * A user first searches for duplicate bugs and there are none.
+         * They can start typing in some detail. They can search again for dups
+         * and their input should be retained even when there are dups and they
+         * have to click the "No, this is a new bug" button.
+         */
+        test_dups_search_retains_user_input_when_dups: function() {
+            // filebug container should not initially be visible
+            this.assertIsNotVisible(null, '#filebug-form-container');
+            var search_text = Y.one(Y.DOM.byId('field.search'));
+            search_text.set('value', 'foo');
+            var search_button = Y.one(Y.DOM.byId('field.actions.search'));
+            this.config.yio.io.responseText = 'No similar bug reports.';
+            this.config.yio.io.doAfter = function() {
+                var comment_text = Y.one(Y.DOM.byId('field.comment'));
+                comment_text.set('value', 'an error occurred');
+                this.config.yio.io.responseText = ([
+                        '<img id="bug-details-expander" ',
+                        'class="bug-already-reported-expander" ',
+                        'src="/@@/treeCollapsed">',
+                        '<input type="button" value="No, I need to report a',
+                        ' bug" name="field.bug_already_reported_as"',
+                        ' id="bug-not-already-reported" style="display: block">'
+                        ].join(''));
+                this.config.yio.io.doAfter = function() {
+                    var new_bug_button = Y.one('#bug-not-already-reported');
+                    simulate(new_bug_button, undefined, 'click');
+                    // filebug container should be visible
+                    this.assertIsVisible(null, '#filebug-form-container');
+                    // The user input should be retained
+                    Y.Assert.areEqual(
+                        'an error occurred', comment_text.get('value'));
+                };
+                simulate(search_button, undefined, 'click');
+                this.wait();
+            };
+            simulate(search_button, undefined, 'click');
+            this.wait();
+        },
+
+        /**
+         * The filebug form url is correctly set when the page loads.
+         */
+        test_project_initial_filebug_form_action: function() {
+            Y.Assert.areEqual(
+                'https://bugs.launchpad.dev/foo/+filebug',
+                Y.one('#filebug-form').get('action'));
+        },
+
+        add_project_selector: function() {
+            var project_selector = Y.Node.create([
+            '<tr>',
+            '    <td>',
+            '        <label for="field.product">Project:</label>',
+            '        <select size="1" name="field.product" id="field.product">',
+            '            <option value="foo">Foo</option>',
+            '            <option value="bar">Bar</option>',
+            '        </select>',
+            '    </td>',
+            '</tr>'
+            ].join(''));
+            Y.one('#search-field').insert(project_selector, 'before');
+            module.setup_product_urls();
+        },
+
+        /**
+         * The filebug form url is correctly updated when the project changes.
+         */
+        test_project_change_filebug_form_action: function() {
+            this.add_project_selector();
             var project = Y.one(Y.DOM.byId('field.product'));
             project.set('value', 'bar');
             simulate(project, undefined, 'change');
-            // filebug container should be visible
-            this.assertIsVisible(null, '#filebug-form-container');
-
-            // Search button should day 'Check again' because we have already
-            // done a search.
-            var search_button = (Y.one(Y.DOM.byId('field.actions.search')));
-            Y.Assert.areEqual('Check again', search_button.get('value'));
-
+            Y.Assert.areEqual(
+                'https://bugs.launchpad.dev/bar/+filebug',
+                Y.one('#filebug-search-form').get('action'));
+        },
+
+        /**
+         * A user first searches for duplicate bugs and there are none.
+         * They can start typing in some detail. They change the project and
+         * perform a new search. Their input should be retained.
+         */
+        test_project_change_retains_user_input_after_dups_serach: function() {
+            Y.one(Y.DOM.byId('field.product')).set('value', 'foo');
+            module.setup_product_urls();
+            // filebug container should not initially be visible
+            this.assertIsNotVisible(null, '#filebug-form-container');
+            var search_text = Y.one(Y.DOM.byId('field.search'));
+            search_text.set('value', 'foo');
+            var search_button = Y.one(Y.DOM.byId('field.actions.search'));
             this.config.yio.io.responseText = 'No similar bug reports.';
             this.config.yio.io.doAfter = function() {
+                var comment_text = Y.one(Y.DOM.byId('field.comment'));
+                comment_text.set('value', 'an error occurred');
+
+                this.config.yio.io.responseText = 'Bug filing details';
+                var project = Y.one(Y.DOM.byId('field.product'));
+                project.set('value', 'bar');
+                simulate(project, undefined, 'change');
                 // filebug container should be visible
                 this.assertIsVisible(null, '#filebug-form-container');
-                // The user input should be retained
-                Y.Assert.areEqual(
-                    'an error occurred', comment_text.get('value'));
-                Y.ArrayAssert.itemsAreEqual(
-                        ['https://bugs.launchpad.dev/' +
-                         'foo/+filebug-show-similar?title=foo',
-                         'https://bugs.launchpad.dev/' +
-                         'bar/+filebug-show-similar?title=foo'],
-                        this.config.yio.calls);
+
+                // Search button should day 'Check again' because we have
+                // already done a search.
+                var search_button = (Y.one(Y.DOM.byId('field.actions.search')));
+                Y.Assert.areEqual('Check again', search_button.get('value'));
+
+                this.config.yio.io.responseText = 'No similar bug reports.';
+                this.config.yio.io.doAfter = function() {
+                    // filebug container should be visible
+                    this.assertIsVisible(null, '#filebug-form-container');
+                    // The user input should be retained
+                    Y.Assert.areEqual(
+                        'an error occurred', comment_text.get('value'));
+                    Y.ArrayAssert.itemsAreEqual(
+                            ['https://bugs.launchpad.dev/' +
+                             'foo/+filebug-show-similar?title=foo',
+                             'https://bugs.launchpad.dev/' +
+                             'bar/+filebug-show-similar?title=foo'],
+                            this.config.yio.calls);
+                };
+                simulate(search_button, undefined, 'click');
+                this.wait();
             };
             simulate(search_button, undefined, 'click');
             this.wait();
-        };
-        simulate(search_button, undefined, 'click');
-        this.wait();
-    }
-
-}));
-
-var handle_complete = function(data) {
-    window.status = '::::' + JSON.stringify(data);
-    };
-Y.Test.Runner.on('complete', handle_complete);
-Y.Test.Runner.add(suite);
-
-var console = new Y.Console({newestOnTop: false});
-console.render('#log');
-
-// Configure the javascript module under test. In production, the
-// setup_dupe_finder() is called from the page template. We need to pass in
-// a stub io handler here so that the XHR call made during set up is ignored.
-var config = {};
-config.yio = new IOStub();
-module.setup_config(config);
-module.setup_dupe_finder();
-
-Y.on('domready', function(e) {
-    Y.Test.Runner.run();
-});
+        }
+
+    }));
+
+
+}, '0.1', {
+    requires: ['test', 'lp.testing.helpers', 'console',
+        'lp.bugs.filebug_dupefinder', 'node-event-simulate']
 });

=== modified file 'lib/lp/bugs/javascript/tests/test_me_too.html'
--- lib/lp/bugs/javascript/tests/test_me_too.html	2012-03-27 04:36:24 +0000
+++ lib/lp/bugs/javascript/tests/test_me_too.html	2012-06-28 18:18:19 +0000
@@ -58,7 +58,7 @@
     <body class="yui3-skin-sam">
         <ul id="suites">
             <!-- <li>lp.large_indicator.test</li> -->
-            <li>lp.bugtask_index.test</li>
+            <li>lp.bugs.bugtask_index.test_me_too</li>
         </ul>
     </body>
 </html>

=== modified file 'lib/lp/bugs/javascript/tests/test_me_too.js'
--- lib/lp/bugs/javascript/tests/test_me_too.js	2011-06-08 16:16:32 +0000
+++ lib/lp/bugs/javascript/tests/test_me_too.js	2012-06-28 18:18:19 +0000
@@ -1,219 +1,204 @@
-/* Copyright (c) 2008, Canonical Ltd. All rights reserved. */
-
-YUI({
-    base: '../../../../canonical/launchpad/icing/yui/',
-    filter: 'raw',
-    combine: false,
-    fetchCSS: false
-      }).use('event', 'lp.bugs.bugtask_index', 'lp.client', 'node',
-             'test', 'widget-stack', 'console', function(Y) {
-
-// Local aliases
-var Assert = Y.Assert,
-    ArrayAssert = Y.ArrayAssert;
-
-/*
- * A wrapper for the Y.Event.simulate() function.  The wrapper accepts
- * CSS selectors and Node instances instead of raw nodes.
- */
-function simulate(widget, selector, evtype, options) {
-    var rawnode = Y.Node.getDOMNode(widget.one(selector));
-    Y.Event.simulate(rawnode, evtype, options);
-}
-
-var suite = new Y.Test.Suite("Bugtask Me-Too Choice Edit Tests");
-
-suite.add(new Y.Test.Case({
-
-    name: 'me_too_choice_edit_basics',
-
-    setUp: function() {
-        // Monkeypatch LP to avoid network traffic and to make
-        // some things work as expected.
-        Y.lp.client.Launchpad.prototype.named_post =
-          function(url, func, config) {
-            config.on.success();
-          };
-        LP = {
-          'cache': {
-            'bug': {
-              self_link: "http://bugs.example.com/bugs/1234";
-          }}};
-        // add the in-page HTML
-        var inpage = Y.Node.create([
-            '<span id="affectsmetoo">',
-            '  <span class="static">',
-            '   <img src="https://bugs.launchpad.net/@@/flame-icon"; alt=""/>',
-            '    This bug affects me too',
-            '    <a href="+affectsmetoo">',
-            '      <img class="editicon" alt="Edit"',
-            '           src="https://bugs.launchpad.net/@@/edit"; />',
-            '    </a>',
-            '  </span>',
-            '  <span class="dynamic unseen">',
-            '    <img class="editicon" alt="Edit"',
-            '         src="https://bugs.launchpad.net/@@/edit"; />',
-            '    <a href="+affectsmetoo" class="js-action"',
-            '      ><span class="value">Does this bug affect you?</span></a>',
-            '   <img src="https://bugs.launchpad.net/@@/flame-icon"; alt=""/>',
-            '  </span>',
-            '</span>'].join(''));
-        Y.one("body").appendChild(inpage);
-        var me_too_content = Y.one('#affectsmetoo');
-        this.config = {
-            contentBox: me_too_content, value: null,
-            elementToFlash: me_too_content, others_affected_count: 5
-        };
-        this.choice_edit = new Y.lp.bugs.bugtask_index._MeTooChoiceSource(
-            this.config);
-        this.choice_edit.render();
-    },
-
-    tearDown: function() {
-        var status = Y.one("document").one("#affectsmetoo");
-        if (status) {
-            status.get("parentNode").removeChild(status);
+/* Copyright (c) 2008-2012 Canonical Ltd. All rights reserved. */
+
+YUI.add('lp.bugs.bugtask_index.test_me_too', function (Y) {
+    // Local aliases
+    var Assert = Y.Assert,
+        ArrayAssert = Y.ArrayAssert;
+
+    /*
+     * A wrapper for the Y.Event.simulate() function.  The wrapper accepts
+     * CSS selectors and Node instances instead of raw nodes.
+     */
+    function simulate(widget, selector, evtype, options) {
+        var rawnode = Y.Node.getDOMNode(widget.one(selector));
+        Y.Event.simulate(rawnode, evtype, options);
+    }
+
+    var tests = Y.namespace('lp.bugs.bugtask_index.test_me_too');
+    tests.suite = new Y.Test.Suite('MeToo Tests');
+
+    tests.suite.add(new Y.Test.Case({
+
+        name: 'me_too_choice_edit_basics',
+
+        setUp: function() {
+            // Monkeypatch LP to avoid network traffic and to make
+            // some things work as expected.
+            Y.lp.client.Launchpad.prototype.named_post =
+              function(url, func, config) {
+                config.on.success();
+              };
+            LP = {
+              'cache': {
+                'bug': {
+                  self_link: "http://bugs.example.com/bugs/1234";
+              }}};
+            // add the in-page HTML
+            var inpage = Y.Node.create([
+                '<span id="affectsmetoo">',
+                '  <span class="static">',
+                '   <img src="https://bugs.launchpad.net/@@/flame-icon"; alt=""/>',
+                '    This bug affects me too',
+                '    <a href="+affectsmetoo">',
+                '      <img class="editicon" alt="Edit"',
+                '           src="https://bugs.launchpad.net/@@/edit"; />',
+                '    </a>',
+                '  </span>',
+                '  <span class="dynamic unseen">',
+                '    <img class="editicon" alt="Edit"',
+                '         src="https://bugs.launchpad.net/@@/edit"; />',
+                '    <a href="+affectsmetoo" class="js-action"',
+                '      ><span class="value">Does this bug affect you?</span></a>',
+                '   <img src="https://bugs.launchpad.net/@@/flame-icon"; alt=""/>',
+                '  </span>',
+                '</span>'].join(''));
+            Y.one("body").appendChild(inpage);
+            var me_too_content = Y.one('#affectsmetoo');
+            this.config = {
+                contentBox: me_too_content, value: null,
+                elementToFlash: me_too_content, others_affected_count: 5
+            };
+            this.choice_edit = new Y.lp.bugs.bugtask_index._MeTooChoiceSource(
+                this.config);
+            this.choice_edit.render();
+        },
+
+        tearDown: function() {
+            var status = Y.one("document").one("#affectsmetoo");
+            if (status) {
+                status.get("parentNode").removeChild(status);
+            }
+        },
+
+        /**
+         * The choice edit should be displayed inline.
+         */
+        test_is_inline: function() {
+            var display =
+                this.choice_edit.get('boundingBox').getStyle('display');
+            Assert.areEqual(
+                display, 'inline',
+                "Not displayed inline, display is: " + display);
+        },
+
+        /**
+         * The .static area should be hidden by adding the "unseen" class.
+         */
+        test_hide_static: function() {
+            var static_area = this.choice_edit.get('contentBox').one('.static');
+            Assert.isTrue(
+                static_area.hasClass('unseen'), "Static area is not hidden.");
+        },
+
+        /**
+         * The .dynamic area should be shown by removing the "unseen" class.
+         */
+        test_hide_dynamic: function() {
+            var dynamic_area =
+                this.choice_edit.get('contentBox').one('.dynamic');
+            Assert.isFalse(
+                dynamic_area.hasClass('unseen'), "Dynamic area is hidden.");
+        },
+
+        /**
+         * The UI should be in a waiting state while the save process is
+         * executing and return to a non-waiting state once it has
+         * finished.
+         */
+        test_ui_waiting_for_success: function() {
+            this.do_test_ui_waiting('success');
+        },
+
+        /**
+         * The UI should be in a waiting state while the save process is
+         * executing and return to a non-waiting state even if the process
+         * fails.
+         */
+        test_ui_waiting_for_failure: function() {
+            this.do_test_ui_waiting('failure');
+        },
+
+        /**
+         * Helper function that does the leg work for the
+         * test_ui_waiting_* methods.
+         */
+        do_test_ui_waiting: function(callback) {
+            var edit_icon = this.choice_edit.get('editicon');
+            // The spinner should not be displayed at first.
+            Assert.isNull(
+                edit_icon.get('src').match(/\/spinner$/),
+                "The edit icon is displaying a spinner at rest.");
+            // The spinner should not be displayed after opening the
+            // choice list.
+            simulate(this.choice_edit.get('boundingBox'), '.value', 'click');
+            Assert.isNull(
+                edit_icon.get('src').match(/\/spinner$/),
+                "The edit icon is displaying a spinner after opening the " +
+                "choice list.");
+            // The spinner should be visible during the interval between a
+            // choice being made and a response coming back from Launchpad
+            // that the choice has been saved.
+            var edit_icon_src_during_save;
+            // Patch the named_post method to simulate success or failure,
+            // as determined by the callback argument. We cannot make
+            // assertions in this method because exceptions are swallowed
+            // somewhere. Instead, we save something testable to a local
+            // var.
+            Y.lp.client.Launchpad.prototype.named_post =
+                function(url, func, config) {
+                    edit_icon_src_during_save = edit_icon.get('src');
+                    config.on[callback]();
+                };
+            simulate(this.choice_edit._choice_list.get('boundingBox'),
+                     'li a[href$=true]', 'click');
+            Assert.isNotNull(
+                edit_icon_src_during_save.match(/\/spinner$/),
+                "The edit icon is not displaying a spinner during save.");
+            // The spinner should not be displayed once a choice has been
+            // saved.
+            Assert.isNull(
+                edit_icon.get('src').match(/\/spinner$/),
+                "The edit icon is displaying a spinner once the choice has " +
+                "been made.");
+        },
+
+        test__getSourceNames: function() {
+            var names;
+            // No other users affected.
+            names = this.choice_edit._getSourceNames(0);
+            Assert.areEqual(
+                'This bug affects you', names[true]);
+            Assert.areEqual(
+                "This bug doesn't affect you", names[false]);
+            // 1 other user affected.
+            names = this.choice_edit._getSourceNames(1);
+            Assert.areEqual(
+                'This bug affects you and 1 other person', names[true]);
+            Assert.areEqual(
+                'This bug affects 1 person, but not you', names[false]);
+            // 2 other users affected.
+            names = this.choice_edit._getSourceNames(2);
+            Assert.areEqual(
+                'This bug affects you and 2 other people', names[true]);
+            Assert.areEqual(
+                'This bug affects 2 people, but not you', names[false]);
+        },
+
+        test_new_names_are_applied: function() {
+            var names = {};
+            Y.each(this.choice_edit.get('items'), function(item) {
+                names[item.value] = item.source_name;
+            });
+            Assert.areEqual(
+                'This bug affects you and 5 other people', names[true]);
+            Assert.areEqual(
+                'This bug affects 5 people, but not you', names[false]);
         }
-    },
-
-    /**
-     * The choice edit should be displayed inline.
-     */
-    test_is_inline: function() {
-        var display = this.choice_edit.get('boundingBox').getStyle('display');
-        Assert.areEqual(
-            display, 'inline',
-            "Not displayed inline, display is: " + display);
-    },
-
-    /**
-     * The .static area should be hidden by adding the "unseen" class.
-     */
-    test_hide_static: function() {
-        var static_area = this.choice_edit.get('contentBox').one('.static');
-        Assert.isTrue(
-            static_area.hasClass('unseen'), "Static area is not hidden.");
-    },
-
-    /**
-     * The .dynamic area should be shown by removing the "unseen" class.
-     */
-    test_hide_dynamic: function() {
-        var dynamic_area = this.choice_edit.get('contentBox').one('.dynamic');
-        Assert.isFalse(
-            dynamic_area.hasClass('unseen'), "Dynamic area is hidden.");
-    },
-
-    /**
-     * The UI should be in a waiting state while the save process is
-     * executing and return to a non-waiting state once it has
-     * finished.
-     */
-    test_ui_waiting_for_success: function() {
-        this.do_test_ui_waiting('success');
-    },
-
-    /**
-     * The UI should be in a waiting state while the save process is
-     * executing and return to a non-waiting state even if the process
-     * fails.
-     */
-    test_ui_waiting_for_failure: function() {
-        this.do_test_ui_waiting('failure');
-    },
-
-    /**
-     * Helper function that does the leg work for the
-     * test_ui_waiting_* methods.
-     */
-    do_test_ui_waiting: function(callback) {
-        var edit_icon = this.choice_edit.get('editicon');
-        // The spinner should not be displayed at first.
-        Assert.isNull(
-            edit_icon.get('src').match(/\/spinner$/),
-            "The edit icon is displaying a spinner at rest.");
-        // The spinner should not be displayed after opening the
-        // choice list.
-        simulate(this.choice_edit.get('boundingBox'), '.value', 'click');
-        Assert.isNull(
-            edit_icon.get('src').match(/\/spinner$/),
-            "The edit icon is displaying a spinner after opening the " +
-            "choice list.");
-        // The spinner should be visible during the interval between a
-        // choice being made and a response coming back from Launchpad
-        // that the choice has been saved.
-        var edit_icon_src_during_save;
-        // Patch the named_post method to simulate success or failure,
-        // as determined by the callback argument. We cannot make
-        // assertions in this method because exceptions are swallowed
-        // somewhere. Instead, we save something testable to a local
-        // var.
-        Y.lp.client.Launchpad.prototype.named_post =
-            function(url, func, config) {
-                edit_icon_src_during_save = edit_icon.get('src');
-                config.on[callback]();
-            };
-        simulate(this.choice_edit._choice_list.get('boundingBox'),
-                 'li a[href$=true]', 'click');
-        Assert.isNotNull(
-            edit_icon_src_during_save.match(/\/spinner$/),
-            "The edit icon is not displaying a spinner during save.");
-        // The spinner should not be displayed once a choice has been
-        // saved.
-        Assert.isNull(
-            edit_icon.get('src').match(/\/spinner$/),
-            "The edit icon is displaying a spinner once the choice has " +
-            "been made.");
-    },
-
-    test__getSourceNames: function() {
-        var names;
-        // No other users affected.
-        names = this.choice_edit._getSourceNames(0);
-        Assert.areEqual(
-            'This bug affects you', names[true]);
-        Assert.areEqual(
-            "This bug doesn't affect you", names[false]);
-        // 1 other user affected.
-        names = this.choice_edit._getSourceNames(1);
-        Assert.areEqual(
-            'This bug affects you and 1 other person', names[true]);
-        Assert.areEqual(
-            'This bug affects 1 person, but not you', names[false]);
-        // 2 other users affected.
-        names = this.choice_edit._getSourceNames(2);
-        Assert.areEqual(
-            'This bug affects you and 2 other people', names[true]);
-        Assert.areEqual(
-            'This bug affects 2 people, but not you', names[false]);
-    },
-
-    test_new_names_are_applied: function() {
-        var names = {};
-        Y.each(this.choice_edit.get('items'), function(item) {
-            names[item.value] = item.source_name;
-        });
-        Assert.areEqual(
-            'This bug affects you and 5 other people', names[true]);
-        Assert.areEqual(
-            'This bug affects 5 people, but not you', names[false]);
-    }
-
-}));
-
-var handle_complete = function(data) {
-    window.status = '::::' + JSON.stringify(data);
-    };
-Y.Test.Runner.on('complete', handle_complete);
-Y.Test.Runner.add(suite);
-
-var yconsole = new Y.Console({
-    newestOnTop: false
-});
-yconsole.render('#log');
-
-Y.on('domready', function() {
-    Y.Test.Runner.run();
-});
-
+
+    }));
+
+}, '0.1', {
+    requires: ['test', 'lp.testing.helpers', 'console',
+        'lp.bugs.bugtask_index', 'lp.client', 'node', 'widget-stack',
+        'event']
 });

=== modified file 'lib/lp/bugs/javascript/tests/test_pre_search.html'
--- lib/lp/bugs/javascript/tests/test_pre_search.html	2012-03-27 04:36:24 +0000
+++ lib/lp/bugs/javascript/tests/test_pre_search.html	2012-06-28 18:18:19 +0000
@@ -53,7 +53,7 @@
     <body class="yui3-skin-sam">
         <ul id="suites">
             <!-- <li>lp.large_indicator.test</li> -->
-            <li>lp.bugtask_index.test</li>
+            <li>lp.bugs.bugtask_index.test_pre_search</li>
         </ul>
     </body>
 </html>

=== modified file 'lib/lp/bugs/javascript/tests/test_pre_search.js'
--- lib/lp/bugs/javascript/tests/test_pre_search.js	2011-07-28 18:59:52 +0000
+++ lib/lp/bugs/javascript/tests/test_pre_search.js	2012-06-28 18:18:19 +0000
@@ -1,106 +1,80 @@
-/* Copyright (c) 2011, Canonical Ltd. All rights reserved. */
-
-YUI({
-    base: '../../../../canonical/launchpad/icing/yui/',
-    filter: 'raw',
-    combine: false,
-    fetchCSS: false
-      }).use('event', 'lp.bugs.bugtask_index', 'lp.client', 'node',
-             'test', 'widget-stack', 'console', function(Y) {
-
-// Local aliases
-var Assert = Y.Assert,
-    ArrayAssert = Y.ArrayAssert;
-
-// A picker implementation that records method calls for testing.
-function FauxPicker() {
-    this.events = [];
-}
-
-FauxPicker.prototype.get = function(name) {
-    this.events.push('get ' + name);
-    return 47;
-};
-
-FauxPicker.prototype.set = function(name, value) {
-    this.events.push('set ' + name + ' = ' + value);
-};
-
-FauxPicker.prototype.fire = function(name, value) {
-    this.events.push('fire ' + name + ' with ' + value);
-};
-
-
-var suite = new Y.Test.Suite("Link a related branch preemptive search.");
-var module = Y.lp.bugs.bugtask_index;
-
-suite.add(new Y.Test.Case({
-
-    name: 'pre_search',
-
-    setUp: function() {
-    },
-
-    tearDown: function() {
-    },
-
-    /**
-     * A loading message is added to the footer slot.
-     */
-    test_loading_message: function() {
-        picker = new FauxPicker();
-        module._do_pre_search(picker, 'BUG-ID');
-        ArrayAssert.contains(
-            'set footer_slot = Loading suggestions...',
-            picker.events);
-    },
-
-    /**
-     * Because some bug numbers are short strings, the minimum search
-     * character limit has to be set to zero and then reset to its original
-     * value.
-     */
-    test_min_search_length: function() {
-        picker = new FauxPicker();
-        module._do_pre_search(picker, 'BUG-ID');
-        ArrayAssert.contains(
-            'get min_search_chars',
-            picker.events);
-        ArrayAssert.contains(
-            'set min_search_chars = 47',
-            picker.events);
-    },
-
-    /**
-     * After the search event is fired, search_mode has to be (immediately)
-     * disbled so the user can enter a search.
-     */
-    test_disable_search_mode: function() {
-        picker = new FauxPicker();
-        module._do_pre_search(picker, 'BUG-ID');
-        ArrayAssert.contains(
-            'fire search with BUG-ID',
-            picker.events);
-        ArrayAssert.contains(
-            'set search_mode = false',
-            picker.events);
+/* Copyright (c) 2011-2012, Canonical Ltd. All rights reserved. */
+
+YUI.add('lp.bugs.bugtask_index.test_pre_search', function (Y) {
+    var module = Y.lp.bugs.bugtask_index;
+    // Local aliases
+    var Assert = Y.Assert,
+        ArrayAssert = Y.ArrayAssert;
+
+    // A picker implementation that records method calls for testing.
+    function FauxPicker() {
+        this.events = [];
     }
 
-}));
-
-var handle_complete = function(data) {
-    window.status = '::::' + JSON.stringify(data);
-    };
-Y.Test.Runner.on('complete', handle_complete);
-Y.Test.Runner.add(suite);
-
-var yconsole = new Y.Console({
-    newestOnTop: false
-});
-yconsole.render('#log');
-
-Y.on('domready', function() {
-    Y.Test.Runner.run();
-});
-
+    FauxPicker.prototype.get = function(name) {
+        this.events.push('get ' + name);
+        return 47;
+    };
+
+    FauxPicker.prototype.set = function(name, value) {
+        this.events.push('set ' + name + ' = ' + value);
+    };
+
+    FauxPicker.prototype.fire = function(name, value) {
+        this.events.push('fire ' + name + ' with ' + value);
+    };
+
+    var tests = Y.namespace('lp.bugs.bugtask_index.test_pre_search');
+    tests.suite = new Y.Test.Suite('Pre Search Tests');
+    tests.suite.add(new Y.Test.Case({
+
+        name: 'pre_search',
+
+        /**
+         * A loading message is added to the footer slot.
+         */
+        test_loading_message: function() {
+            picker = new FauxPicker();
+            module._do_pre_search(picker, 'BUG-ID');
+            ArrayAssert.contains(
+                'set footer_slot = Loading suggestions...',
+                picker.events);
+        },
+
+        /**
+         * Because some bug numbers are short strings, the minimum search
+         * character limit has to be set to zero and then reset to its original
+         * value.
+         */
+        test_min_search_length: function() {
+            picker = new FauxPicker();
+            module._do_pre_search(picker, 'BUG-ID');
+            ArrayAssert.contains(
+                'get min_search_chars',
+                picker.events);
+            ArrayAssert.contains(
+                'set min_search_chars = 47',
+                picker.events);
+        },
+
+        /**
+         * After the search event is fired, search_mode has to be (immediately)
+         * disbled so the user can enter a search.
+         */
+        test_disable_search_mode: function() {
+            picker = new FauxPicker();
+            module._do_pre_search(picker, 'BUG-ID');
+            ArrayAssert.contains(
+                'fire search with BUG-ID',
+                picker.events);
+            ArrayAssert.contains(
+                'set search_mode = false',
+                picker.events);
+        }
+
+    }));
+
+}, '0.1', {
+    requires: ['test', 'lp.testing.helpers', 'console', 'event',
+    'lp.bugs.bugtask_index', 'lp.client', 'node', 'widget-stack']
 });


Follow ups