← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~abentley/launchpad/json-init into lp:launchpad

 

Aaron Bentley has proposed merging lp:~abentley/launchpad/json-init into lp:launchpad.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~abentley/launchpad/json-init/+merge/56643

= Summary =
This branch initializes the values of objects from the page cache.

This is the first of 3 javascript branches for the sharing details page.

== Pre-implementation notes ==
Discussed with deryck, leonardr

== Implementation details ==
In the view, add the objects to the request's JSONRequestCache.  This pushes
them onto the page, like me and context.

Convert the cache from pure javascript mappings to LP.entry objects, so they
match the results of web service calls.

Update the picker so that its config can contain an optional "context" object.
If supplied, this is used to change the URL used to retrieve the vocabulary.

Add 'complete' attribute to CheckItems, so they can be used in the page
context.

Add mappings of value to title for usage and autoimport enumerations.

Add setters for branch, productseries, usage and autoimport_mode, which update
both TranslationSharingController and TranslationSharingConfig.

Use a single branch picker, updated to use the BranchRestrictedOnProduct
vocabulary.

Much lint fixing.

== Tests ==
bin/test --layer=WindmillLayer sharing_details
firefox lib/lp/translations/javascript/tests/test_sourcepackage_sharing_details.html

== Demo and Q/A ==
For a given sourcepackage, go to its translations page and click "Edit sharing
details".  The displayed values should match the actual values.

= Launchpad lint =

I avoided introducing new problems.  These were existing problems.  I didn't
want to bite off even more to chew.

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/lp/translations/templates/sourcepackage-sharing-details.pt
  lib/lp/app/javascript/client.js
  lib/lp/translations/javascript/tests/test_sourcepackage_sharing_details.js
  lib/lp/app/javascript/picker.js
  lib/lp/translations/browser/tests/test_sharing_details.py
  lib/lp/translations/windmill/tests/test_sourcepackage_sharing_details.py
  lib/lp/translations/javascript/sourcepackage_sharing_details.js
  lib/lp/translations/javascript/tests/test_sourcepackage_sharing_details.html
  lib/lp/translations/browser/sourcepackage.py

./lib/lp/app/javascript/client.js
      30: Expected '===' and instead saw '=='.
      33: Expected '===' and instead saw '=='.
      54: Move 'var' declarations to the top of the function.
      54: Stopping.  (5% scanned).
       0: JSLINT had a fatal error.
       4: Line exceeds 78 characters.
     191: Line exceeds 78 characters.
     570: Line exceeds 78 characters.
     618: Line exceeds 78 characters.
     745: Line exceeds 78 characters.
     747: Line exceeds 78 characters.
./lib/lp/app/javascript/picker.js
      47: Expected ';' and instead saw 'if'.
      96: Expected '!==' and instead saw '!='.
     227: Expected '!==' and instead saw '!='.
     228: Expected '{' and instead saw 'temp_spinner'.
     261: Expected '!==' and instead saw '!='.
     274: 'header' used out of scope.
     275: 'step_title' used out of scope.
     323: Move 'var' declarations to the top of the function.
     323: Stopping.  (85% scanned).
       0: JSLINT had a fatal error.
     198: Line exceeds 78 characters.
-- 
https://code.launchpad.net/~abentley/launchpad/json-init/+merge/56643
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~abentley/launchpad/json-init into lp:launchpad.
=== modified file 'lib/lp/app/javascript/client.js'
--- lib/lp/app/javascript/client.js	2011-04-05 12:10:01 +0000
+++ lib/lp/app/javascript/client.js	2011-04-06 20:19:28 +0000
@@ -227,6 +227,8 @@
   var full_uri = module.get_absolute_uri(original_uri);
   for (var name in LP.cache) {
     var cached_object = LP.cache[name];
+    if (!Y.Lang.isValue(cached_object))
+        continue;
     if (cached_object['self_link'] == full_uri) {
       Y.log(name + ' cached object has been updated.');
       update_cached_object(name, cached_object, entry);

=== modified file 'lib/lp/app/javascript/picker.js'
--- lib/lp/app/javascript/picker.js	2011-03-31 04:52:03 +0000
+++ lib/lp/app/javascript/picker.js	2011-04-06 20:19:28 +0000
@@ -5,6 +5,10 @@
 var BATCH_SIZE = 6;
 var MAX_BATCHES = 20;
 
+function get_pathname(url) {
+     return Y.Node.create('<a>junk</a>').set('href', url).get('pathname');
+}
+
 /* Add a picker widget which will PATCH a given attribute on
  * a given resource.
  *
@@ -337,7 +341,12 @@
         // The uri needs to be relative, so that the vocabulary
         // has the same context as the form. Some vocabularies
         // use the context to limit the results to the same project.
-        var uri = '@@+huge-vocabulary?' + qs;
+
+        var uri = '';
+        if (Y.Lang.isValue(config['context'])){
+            uri += get_pathname(config['context'].get('web_link')) + '/';
+        }
+        uri += '@@+huge-vocabulary?' + qs;
 
         Y.io(uri, {
             headers: {'Accept': 'application/json'},

=== modified file 'lib/lp/translations/browser/sourcepackage.py'
--- lib/lp/translations/browser/sourcepackage.py	2011-04-01 09:15:59 +0000
+++ lib/lp/translations/browser/sourcepackage.py	2011-04-06 20:19:28 +0000
@@ -11,6 +11,8 @@
     'SourcePackageTranslationSharingStatus',
     ]
 
+
+from lazr.restful.interfaces import IJSONRequestCache
 from zope.publisher.interfaces import NotFound
 
 from canonical.launchpad.webapp import (
@@ -136,6 +138,12 @@
                 'Translations are currently being linked by a background '
                 'job. When that job has finished, translations will be '
                 'shared with the upstream project.')
+        cache = IJSONRequestCache(self.request)
+        cache.objects.update({
+            'productseries': self.context.productseries,
+            'upstream_branch': self.upstream_branch,
+            'product': self.product,
+        })
 
     @property
     def branch_link(self):
@@ -239,6 +247,12 @@
         return self.context.direct_packaging.productseries.branch
 
     @property
+    def product(self):
+        if self.context.productseries is None:
+            return None
+        return self.context.productseries.product
+
+    @property
     def has_upstream_branch(self):
         """Does the upstream series have a source code branch?"""
         return self.upstream_branch is not None

=== modified file 'lib/lp/translations/browser/tests/test_sharing_details.py'
--- lib/lp/translations/browser/tests/test_sharing_details.py	2011-03-31 19:42:48 +0000
+++ lib/lp/translations/browser/tests/test_sharing_details.py	2011-04-06 20:19:28 +0000
@@ -5,6 +5,8 @@
 
 
 import re
+
+from lazr.restful.interfaces import IJSONRequestCache
 from soupmatchers import (
     HTMLContains,
     Tag,
@@ -36,6 +38,13 @@
 from lp.translations.model.translationpackagingjob import TranslationMergeJob
 
 
+def make_initialized_view(sourcepackage):
+    view = SourcePackageTranslationSharingDetailsView(
+        sourcepackage, LaunchpadTestRequest())
+    view.initialize()
+    return view
+
+
 class ConfigureScenarioMixin:
     """Provide a method for project configuration."""
 
@@ -101,9 +110,7 @@
             productseries=self.productseries, name='shared-template')
         self.upstream_only_template = self.factory.makePOTemplate(
             productseries=self.productseries, name='upstream-only')
-        self.view = SourcePackageTranslationSharingDetailsView(
-            self.sourcepackage, LaunchpadTestRequest())
-        self.view.initialize()
+        self.view = make_initialized_view(self.sourcepackage)
 
     def configureSharing(self,
             set_upstream_branch=False,
@@ -319,6 +326,29 @@
             ]
         self.assertEqual(expected, self.view.template_info())
 
+    def getCacheObjects(self):
+        view = make_initialized_view(self.sourcepackage)
+        view.initialize()
+        cache = IJSONRequestCache(view.request)
+        return cache.objects
+
+    def test_cache_contents_no_productseries(self):
+        objects = self.getCacheObjects()
+        self.assertIs(None, objects['productseries'])
+
+    def test_cache_contents_no_branch(self):
+        self.configureSharing()
+        objects = self.getCacheObjects()
+        self.assertEqual(self.productseries, objects['productseries'])
+        self.assertEqual(self.productseries.product, objects['product'])
+        self.assertIs(None, objects['upstream_branch'])
+
+    def test_cache_contents_branch(self):
+        self.configureSharing(set_upstream_branch=True)
+        objects = self.getCacheObjects()
+        self.assertEqual(
+            self.productseries.branch, objects['upstream_branch'])
+
     def _getExpectedTranslationSettingsLink(self, id, series, visible):
         if series is None:
             url = '#'
@@ -730,6 +760,16 @@
         self.assertUnseen(browser, 'upstream-sync-incomplete')
         self.assertSeen(browser, 'upstream-sync-complete')
 
+    def test_cache_javascript(self):
+        # Cache object entries propagate into the javascript.
+        sourcepackage = self.makeFullyConfiguredSharing()[0]
+        anon_browser = self._getSharingDetailsViewBrowser(sourcepackage)
+        # Anonymous users don't get cached objects due to bug #740208
+        self.assertNotIn("LP.cache['productseries'] =", anon_browser.contents)
+        browser = self._getSharingDetailsViewBrowser(
+            sourcepackage, user=self.user)
+        self.assertIn("LP.cache['productseries'] =", browser.contents)
+
     def test_potlist_only_ubuntu(self):
         # Without a packaging link, only Ubuntu templates are listed.
         sourcepackage = self._makeSourcePackage()
@@ -876,8 +916,8 @@
                 'id': 'upstream-translations-incomplete',
                 'href': '#',
                 'class': 'sprite edit unseen',
-                }
-            )
+                },
+        )
         self.assertThat(browser.contents, HTMLContains(matcher))
         matcher = Tag(
             'upstream-translations-complete', 'a',
@@ -885,8 +925,8 @@
                 'id': 'upstream-translations-complete',
                 'href': '#',
                 'class': 'sprite edit unseen',
-                }
-            )
+                },
+        )
         self.assertThat(browser.contents, HTMLContains(matcher))
 
     def test_upstream_sync_link(self):
@@ -900,8 +940,8 @@
                 'id': 'translation-synchronisation-incomplete',
                 'href': '#',
                 'class': 'sprite edit unseen',
-                }
-            )
+                },
+        )
         self.assertThat(browser.contents, HTMLContains(matcher))
         matcher = Tag(
             'translation-synchronisation-complete', 'a',
@@ -909,8 +949,8 @@
                 'id': 'translation-synchronisation-complete',
                 'href': '#',
                 'class': 'sprite edit unseen',
-                }
-            )
+                },
+        )
         self.assertThat(browser.contents, HTMLContains(matcher))
 
 
@@ -925,12 +965,6 @@
         self.useFixture(FeatureFixture(
             {'translations.sharing_information.enabled': 'on'}))
 
-    def _makeInitializedView(self, sourcepackage):
-        view = SourcePackageTranslationSharingDetailsView(
-            sourcepackage, LaunchpadTestRequest())
-        view.initialize()
-        return view
-
     def _getNotifications(self, view):
         notifications = view.request.response.notifications
         return [extract_text(notification.message)
@@ -945,7 +979,7 @@
         # When sharing is fully configured but no upstream templates are
         # found, a message is displayed.
         sourcepackage = self.makeFullyConfiguredSharing()[0]
-        view = self._makeInitializedView(sourcepackage)
+        view = make_initialized_view(sourcepackage)
         self.assertIn(
             self.no_templates_message, self._getNotifications(view))
 
@@ -954,7 +988,7 @@
         # message should be displayed.
         sourcepackage, productseries = self.makeFullyConfiguredSharing()
         self.factory.makePOTemplate(productseries=productseries)
-        view = self._makeInitializedView(sourcepackage)
+        view = make_initialized_view(sourcepackage)
         self.assertNotIn(
             self.no_templates_message, self._getNotifications(view))
 
@@ -965,7 +999,7 @@
         productseries = packaging.productseries
         sourcepackage = packaging.sourcepackage
         self.factory.makePOTemplate(productseries=productseries)
-        view = self._makeInitializedView(sourcepackage)
+        view = make_initialized_view(sourcepackage)
         self.assertNotIn(
             self.no_templates_message, self._getNotifications(view))
 
@@ -978,7 +1012,7 @@
         # When a merge job is running, a message is displayed.
         sourcepackage = self.makeFullyConfiguredSharing(
             suppress_merge_job=False)[0]
-        view = self._makeInitializedView(sourcepackage)
+        view = make_initialized_view(sourcepackage)
         self.assertIn(
             self.job_running_message, self._getNotifications(view))
 
@@ -987,6 +1021,6 @@
         sourcepackage = self.makeFullyConfiguredSharing(
             suppress_merge_job=False)[0]
         self.endMergeJob(sourcepackage)
-        view = self._makeInitializedView(sourcepackage)
+        view = make_initialized_view(sourcepackage)
         self.assertNotIn(
             self.job_running_message, self._getNotifications(view))

=== modified file 'lib/lp/translations/javascript/sourcepackage_sharing_details.js'
--- lib/lp/translations/javascript/sourcepackage_sharing_details.js	2011-03-18 19:37:05 +0000
+++ lib/lp/translations/javascript/sourcepackage_sharing_details.js	2011-04-06 20:19:28 +0000
@@ -12,6 +12,7 @@
     CheckItem.superclass.constructor.apply(this, arguments);
 }
 CheckItem.ATTRS = {
+    complete: {value: false},
     // Optional reference to an item that must be completed before setting
     // this.
     dependency: {value: null},
@@ -26,9 +27,9 @@
     },
     // The HTML identifier of the item.
     identifier: null
-}
+};
 Y.extend(CheckItem, Y.Base, {
-})
+});
 
 namespace.CheckItem = CheckItem;
 
@@ -36,13 +37,13 @@
  * This class reflects the state of a Checklist item that holds a link.
  */
 function LinkCheckItem(){
-    LinkCheckItem.superclass.constructor.apply(this, arguments)
+    LinkCheckItem.superclass.constructor.apply(this, arguments);
 }
 LinkCheckItem.ATTRS = {
     // This item is complete if _text is set.
     complete: {getter:
         function() {
-            return !Y.Lang.isNull(this.get('_text'))
+            return !Y.Lang.isNull(this.get('_text'));
         }
     },
     // _text is unset by default.
@@ -60,8 +61,8 @@
         function(){
             return this.get('_url');
         }
-    },
-}
+    }
+};
 Y.extend(LinkCheckItem, CheckItem, {
     /**
      * Set the text and URL of the link for this LinkCheckItem.
@@ -73,13 +74,43 @@
 });
 
 namespace.LinkCheckItem = LinkCheckItem;
+namespace.convert_cache = function(lp_client, cache){
+    var new_cache = {};
+    var value;
+    var key;
+    for (key in cache){
+        if (cache.hasOwnProperty(key)){
+            value = cache[key];
+            if (value !== null){
+                value = lp_client.wrap_resource(value.self_link, value);
+            }
+            new_cache[key] = value;
+        }
+    }
+    return new_cache;
+};
+
+
+namespace.autoimport_modes = {
+    no_import: 'None',
+    import_templates: 'Import template files',
+    import_translations: 'Import template and translation files'
+};
+
+
+namespace.usage = {
+    unknown: 'Unknown',
+    launchpad: 'Launchpad',
+    external: 'External',
+    no_applicable: 'Not Applicable'
+};
 
 /**
  * This class represents the state of the translation sharing config
  * checklist.
  */
 function TranslationSharingConfig (config){
-    TranslationSharingConfig.superclass.constructor.apply(this, arguments)
+    TranslationSharingConfig.superclass.constructor.apply(this, arguments);
 }
 Y.extend(TranslationSharingConfig, Y.Base, {
     initializer: function(){
@@ -88,7 +119,7 @@
         this.set('product_series', product_series);
         var usage = new CheckItem(
             {identifier: 'usage', dependency: product_series});
-        this.set('translation_usage', usage);
+        this.set('translations_usage', usage);
         var branch = new LinkCheckItem(
             {identifier: 'branch', dependency: this.get('product_series')});
         this.set('branch', branch);
@@ -111,8 +142,9 @@
 Y.extend(TranslationSharingController, Y.Base, {
     initializer: function(source_package){
         this.set('source_package', source_package);
-        this.set('productseries_link', source_package['productseries_link']);
         this.set('tsconfig', new TranslationSharingConfig());
+        this.set('productseries', null);
+        this.set('branch', null);
     },
     /*
      * Select the specified branch as the translation branch.
@@ -120,13 +152,52 @@
      * @param branch_summary {Object} An object containing api_url, css,
      * description, value, title
      */
+    configure: function(config){
+        this.set_productseries(config.productseries);
+        var autoimport_mode = namespace.autoimport_modes.no_import;
+        var translations_usage = namespace.usage.unknown;
+        if (!Y.Lang.isNull(config.productseries)){
+            autoimport_mode = config.productseries.get(
+                'translations_autoimport_mode');
+            translations_usage = config.product.get(
+                'translations_usage');
+        }
+        this.set_autoimport_mode(autoimport_mode);
+        this.set_translations_usage(translations_usage);
+        this.set_branch(config.upstream_branch);
+    },
+    set_productseries: function(productseries) {
+        if (Y.Lang.isValue(productseries)){
+            this.set('productseries', productseries);
+            this.get('tsconfig').get('product_series').set_link(
+                productseries.get('title'), productseries.get('web_link'));
+        }
+    },
+    set_branch: function(branch){
+        this.set('branch', branch);
+        if (Y.Lang.isValue(branch)){
+            this.get('tsconfig').get('branch').set_link(
+                branch.get('unique_name'), branch.get('web_link'));
+        }
+    },
+    set_autoimport_mode: function(mode){
+        complete = (mode === namespace.autoimport_modes.import_translations);
+        this.get('tsconfig').get('autoimport').set('complete', complete);
+    },
+    set_translations_usage: function(usage){
+        complete = (
+            usage === namespace.usage.launchpad ||
+            usage === namespace.usage.external);
+        var usage_check = this.get('tsconfig').get('translations_usage');
+        usage_check.set('complete', complete);
+    },
     select_branch: function(branch_summary){
         var that = this;
         var lp_client = new Y.lp.client.Launchpad();
         var error_handler = new Y.lp.client.ErrorHandler();
         error_handler.showError = function(error_msg) {
             Y.lp.app.errors.display_error(Y.one('#branch'), error_msg);
-        }
+        };
         /**
          * Return an LP client config using error_handler.
          *
@@ -149,12 +220,15 @@
          */
         function chain_config(){
             var last_config;
+            var i;
             // Each callback is bound to the next, so we use reverse order.
-            for(var i = arguments.length-1; i >= 0; i--){
-                if (i == arguments.length - 1)
+            for(i = arguments.length-1; i >= 0; i--){
+                if (i === arguments.length - 1) {
                     callback = arguments[i];
-                else
+                }
+                else {
                     callback = Y.bind(arguments[i], this, last_config);
+                }
                 last_config = get_config(callback);
             }
             return last_config;
@@ -168,22 +242,23 @@
          * They take full advantage of their access to variables in the
          * closure, such as "that" and "branch_summary".
          */
-        function get_productseries(config){
-            lp_client.get(that.get('productseries_link'), config);
-        }
-        function save_branch(config, productseries){
-            productseries.set('branch_link', branch_summary['api_uri']);
+        function save_branch(config){
+            var productseries = that.get('productseries');
+            productseries.set('branch_link', branch_summary.api_uri);
             productseries.lp_save(config);
         }
         function get_branch(config){
-            lp_client.get(branch_summary['api_uri'], config);
+            lp_client.get(branch_summary.api_uri, config);
         }
         function set_link(branch){
-            that.get('tsconfig').get('branch').set_link(
-                branch.get('unique_name'), branch.get('web_link'));
+            that.set_branch(branch);
             that.update();
+            var check = that.get('tsconfig').get('branch');
+            var element = Y.one(that.check_selector(check, true));
+            var anim = Y.lazr.anim.green_flash({node: element});
+            anim.run();
         }
-        get_productseries(chain_config(save_branch, get_branch, set_link));
+        save_branch(chain_config(get_branch, set_link));
     },
     /**
      * Update the display of all checklist items.
@@ -192,18 +267,23 @@
         var branch = this.get('tsconfig').get('branch');
         this.update_check(branch);
     },
+    check_selector: function(check, complete){
+        var completion = complete ? '-complete' : '-incomplete';
+        return '#' + check.get('identifier') + completion;
+    },
     /**
      * Update the display of a single checklist item.
      */
     update_check: function(check){
-        var complete = Y.one('#' + check.get('identifier') + '-complete');
+        var complete = Y.one(this.check_selector(check, true));
         var link = complete.one('a.link');
         link.set('href', check.get('url'));
         link.set('text', check.get('text'));
         complete.toggleClass('unseen', !check.get('complete'));
-        var incomplete = Y.one('#' + check.get('identifier') + '-incomplete');
+        var incomplete = Y.one(this.check_selector(check, false));
         incomplete.toggleClass('unseen', check.get('complete'));
-    },
+        incomplete.toggleClass('lowlight', !check.get('enabled'));
+    }
 });
 namespace.TranslationSharingController = TranslationSharingController;
 
@@ -211,23 +291,30 @@
 /**
  * Method to prepare the AJAX translation sharing config functionality.
  */
-namespace.prepare = function(source_package){
+namespace.prepare = function(source_package, cache){
     var sharing_controller = new namespace.TranslationSharingController(
         source_package);
+    var lp_client = new Y.lp.client.Launchpad();
+    cache = namespace.convert_cache(lp_client, cache);
+    sharing_controller.configure(cache);
     sharing_controller.update();
     var config = {
         picker_activator: '#branch-incomplete-picker a',
         header : 'Select translation branch',
         step_title: 'Search',
-        save: Y.bind('select_branch', sharing_controller)
-    };
-    var picker = Y.lp.app.picker.create('Branch', config);
-    var config = {
-        picker_activator: '#branch-complete-picker a',
-        header : 'Select translation branch',
-        step_title: 'Search',
-        save: Y.bind('select_branch', sharing_controller)
-    };
-    var picker2 = Y.lp.app.picker.create('Branch', config);
+        save: Y.bind('select_branch', sharing_controller),
+        context: cache.product
+    };
+    var picker = Y.lp.app.picker.create('BranchRestrictedOnProduct', config);
+    var element = Y.one('#branch-complete-picker a');
+    /* Copy-pasted because picker can't normally be activated by two different
+     * elements. */
+    element.on('click', function(e) {
+        e.halt();
+        this.show();
+    }, picker);
+    element.addClass(picker.get('picker_activator_css_class'));
+
 };
-}, "0.1", {"requires": ['lp.app.errors', 'lp.app.picker', 'oop']})
+}, "0.1", {"requires": ['lp.app.errors', 'lp.app.picker', 'oop', 'lp.client']}
+);

=== modified file 'lib/lp/translations/javascript/tests/test_sourcepackage_sharing_details.html'
--- lib/lp/translations/javascript/tests/test_sourcepackage_sharing_details.html	2011-03-18 19:27:42 +0000
+++ lib/lp/translations/javascript/tests/test_sourcepackage_sharing_details.html	2011-04-06 20:19:28 +0000
@@ -13,6 +13,7 @@
       href="../../../../canonical/launchpad/icing/yui/cssbase/base.css"/>
     <link rel="stylesheet"
       href="../../../../canonical/launchpad/javascript/test.css" />
+    <script type="text/javascript" src="../../../app/javascript/client.js"></script>
 
     <!-- The module under test -->
     <script type="text/javascript" src="../sourcepackage_sharing_details.js"></script>

=== modified file 'lib/lp/translations/javascript/tests/test_sourcepackage_sharing_details.js'
--- lib/lp/translations/javascript/tests/test_sourcepackage_sharing_details.js	2011-03-21 18:03:20 +0000
+++ lib/lp/translations/javascript/tests/test_sourcepackage_sharing_details.js	2011-04-06 20:19:28 +0000
@@ -9,41 +9,42 @@
     }).use('test', 'console', 'lp.translations.sourcepackage_sharing_details',
     function(Y) {
     var suite = new Y.Test.Suite("sourcepackage_sharing_details Tests");
-    var namespace = Y.lp.translations.sourcepackage_sharing_details
-    var TranslationSharingConfig = namespace.TranslationSharingConfig
-    var TranslationSharingController = namespace.TranslationSharingController
+    var namespace = Y.lp.translations.sourcepackage_sharing_details;
+    var TranslationSharingConfig = namespace.TranslationSharingConfig;
+    var TranslationSharingController = namespace.TranslationSharingController;
     var CheckItem = (
-      Y.lp.translations.sourcepackage_sharing_details.CheckItem)
+      Y.lp.translations.sourcepackage_sharing_details.CheckItem);
     var LinkCheckItem = (
-      Y.lp.translations.sourcepackage_sharing_details.LinkCheckItem)
+      Y.lp.translations.sourcepackage_sharing_details.LinkCheckItem);
+    var test_ns = Y.lp.translations.sourcepackage_sharing_details;
 
     suite.add(new Y.Test.Case({
         // Test the setup method.
         name: 'setup',
 
-        test_translation_usage_enabled: function() {
+        test_translations_usage_enabled: function() {
             var sharing_config = new TranslationSharingConfig();
-            var usage = sharing_config.get('translation_usage')
+            var usage = sharing_config.get('translations_usage');
             Y.Assert.isFalse(usage.get('enabled'));
-            sharing_config.get('product_series').set_link('ps', 'http://')
+            sharing_config.get('product_series').set_link('ps', 'http://');
             Y.Assert.isTrue(usage.get('enabled'));
         },
         test_branch: function() {
             var sharing_config = new TranslationSharingConfig();
-            var product_series = sharing_config.get('product_series')
+            var product_series = sharing_config.get('product_series');
             Y.Assert.isFalse(product_series.get('complete'));
             Y.Assert.isFalse(sharing_config.get('branch').get('enabled'));
-            product_series.set_link('ps', 'http://')
+            product_series.set_link('ps', 'http://');
             Y.Assert.isTrue(sharing_config.get('branch').get('enabled'));
         },
         test_autoimport: function() {
             var sharing_config = new TranslationSharingConfig();
             Y.Assert.isFalse(sharing_config.get('autoimport').get('enabled'));
-            sharing_config.get('branch').set_link('br', 'http://foo')
+            sharing_config.get('branch').set_link('br', 'http://foo');
             Y.Assert.isTrue(sharing_config.get('autoimport').get('enabled'));
         },
         test_LinkCheckItem_contents: function() {
-            var lci = new LinkCheckItem()
+            var lci = new LinkCheckItem();
             Y.Assert.isNull(lci.get('text'));
             Y.Assert.isNull(lci.get('url'));
             lci.set_link('mytext', 'http://example.com');
@@ -51,7 +52,7 @@
             Y.Assert.areEqual('http://example.com', lci.get('url'));
         },
         test_LinkCheckItem_complete: function() {
-            var lci = new LinkCheckItem()
+            var lci = new LinkCheckItem();
             Y.Assert.isFalse(lci.get('complete'));
             lci.set_link('text', 'http://example.com');
             Y.Assert.isTrue(lci.get('complete'));
@@ -61,35 +62,122 @@
             Y.Assert.isTrue(ci.get('enabled'));
         },
         test_CheckItem_enabled_dependency: function(){
-            var lci = new LinkCheckItem()
-            var ci = new CheckItem({dependency: lci})
-            Y.Assert.isFalse(ci.get('enabled'))
+            var lci = new LinkCheckItem();
+            var ci = new CheckItem({dependency: lci});
+            Y.Assert.isFalse(ci.get('enabled'));
             lci.set_link('text', 'http://example.com');
-            Y.Assert.isTrue(ci.get('enabled'))
+            Y.Assert.isTrue(ci.get('enabled'));
         },
         test_CheckItem_identifier: function(){
             var ci = new CheckItem({identifier: 'id1'});
-            Y.Assert.areEqual('id1', ci.get('identifier'))
+            Y.Assert.areEqual('id1', ci.get('identifier'));
+        },
+        test_configure_empty: function(){
+            var ctrl = new TranslationSharingController({});
+            var cache = {
+                productseries: null,
+                upstream_branch: null
+            };
+            ctrl.configure(cache);
+        },
+        test_configure: function(){
+            var cache = {
+                product: {
+                    translations_usage: test_ns.usage.launchpad,
+                    resource_type_link: 'http://product'
+                },
+                productseries: {
+                    title: 'title1',
+                    web_link: 'http://web1',
+                    translations_autoimport_mode: (
+                        test_ns.autoimport_modes.import_translations),
+                    resource_type_link: 'productseries'
+                },
+                upstream_branch: {
+                    unique_name: 'title2',
+                    web_link: 'http://web2',
+                    resource_type_link: 'branch'
+                }
+            };
+            var ctrl = new TranslationSharingController({});
+            var lp_client = new Y.lp.client.Launchpad();
+            cache = test_ns.convert_cache(lp_client, cache);
+            ctrl.configure(cache);
+            var tsconfig = ctrl.get('tsconfig');
+            Y.Assert.areEqual(
+                tsconfig.get('product_series').get('text'), 'title1');
+            Y.Assert.areEqual(
+                tsconfig.get('product_series').get('url'), 'http://web1');
+            Y.Assert.areEqual(
+                tsconfig.get('branch').get('text'), 'title2');
+            Y.Assert.isTrue(tsconfig.get('autoimport').get('complete'));
+            Y.Assert.isTrue(
+                tsconfig.get('translations_usage').get('complete'));
+        },
+        test_convert_cache: function(){
+            var cache = {
+                foo: {
+                    self_link: 'http://foo',
+                    resource_type_link: 'http://foo_type'
+                },
+                bar: null
+            };
+            var lp_client = new Y.lp.client.Launchpad();
+            cache = test_ns.convert_cache(lp_client, cache);
+            Y.Assert.isNull(cache.bar);
+            Y.Assert.areEqual('http://foo', cache.foo.get('self_link'));
         },
         test_update_branch: function(){
-            var complete = Y.one('#branch-complete')
-            var incomplete = Y.one('#branch-incomplete')
-            var link = Y.one('#branch-complete a')
-            Y.Assert.areEqual('', link.get('text'))
-            Y.Assert.areNotEqual('http:///', link.get('href'))
-            Y.Assert.isFalse(complete.hasClass('unseen'))
-            Y.Assert.isFalse(incomplete.hasClass('unseen'))
-            var ctrl = new TranslationSharingController({})
-            ctrl.update()
-            Y.Assert.isTrue(complete.hasClass('unseen'))
-            Y.Assert.isFalse(incomplete.hasClass('unseen'))
-            ctrl.get('tsconfig').get('branch').set_link('a', 'http:///')
-            ctrl.update()
-            Y.Assert.isFalse(complete.hasClass('unseen'))
-            Y.Assert.isTrue(incomplete.hasClass('unseen'))
-            var link = Y.one('#branch-complete a')
-            Y.Assert.areEqual('a', link.get('text'))
-            Y.Assert.areEqual('http:///', link.get('href'))
+            var complete = Y.one('#branch-complete');
+            var incomplete = Y.one('#branch-incomplete');
+            var link = Y.one('#branch-complete a');
+            Y.Assert.areEqual('', link.get('text'));
+            Y.Assert.areNotEqual('http:///', link.get('href'));
+            Y.Assert.isFalse(complete.hasClass('unseen'));
+            Y.Assert.isFalse(incomplete.hasClass('unseen'));
+            var ctrl = new TranslationSharingController({});
+            ctrl.update();
+            Y.Assert.isTrue(complete.hasClass('unseen'));
+            Y.Assert.isFalse(incomplete.hasClass('unseen'));
+            ctrl.get('tsconfig').get('branch').set_link('a', 'http:///');
+            ctrl.update();
+            Y.Assert.isFalse(complete.hasClass('unseen'));
+            Y.Assert.isTrue(incomplete.hasClass('unseen'));
+            link = Y.one('#branch-complete a');
+            Y.Assert.areEqual('a', link.get('text'));
+            Y.Assert.areEqual('http:///', link.get('href'));
+        },
+        test_update_check_disabled: function(){
+            var incomplete = Y.one('#branch-incomplete');
+            var ctrl = new TranslationSharingController({});
+            var branch = ctrl.get('tsconfig').get('branch');
+            ctrl.update_check(branch);
+            Y.Assert.isTrue(incomplete.hasClass('lowlight'));
+            var product_series = ctrl.get('tsconfig').get('product_series');
+            product_series.set_link('a', 'http://');
+            ctrl.update_check(branch);
+            Y.Assert.isFalse(incomplete.hasClass('lowlight'));
+        },
+        test_set_autoimport_mode: function(){
+            var ctrl = new TranslationSharingController({});
+            var check = ctrl.get('tsconfig').get('autoimport');
+            Y.Assert.isFalse(check.get('complete'));
+            ctrl.set_autoimport_mode('Import template and translation files');
+            Y.Assert.isTrue(check.get('complete'));
+            ctrl.set_autoimport_mode('Import template files');
+            Y.Assert.isFalse(check.get('complete'));
+        },
+        test_set_translations_usage: function(){
+            var ctrl = new TranslationSharingController({});
+            var check = ctrl.get('tsconfig').get('translations_usage');
+            ctrl.set_translations_usage('Unknown');
+            Y.Assert.isFalse(check.get('complete'));
+            ctrl.set_translations_usage('Launchpad');
+            Y.Assert.isTrue(check.get('complete'));
+            ctrl.set_translations_usage('Not Applicable');
+            Y.Assert.isFalse(check.get('complete'));
+            ctrl.set_translations_usage('External');
+            Y.Assert.isTrue(check.get('complete'));
         }
     }));
 

=== modified file 'lib/lp/translations/templates/sourcepackage-sharing-details.pt'
--- lib/lp/translations/templates/sourcepackage-sharing-details.pt	2011-03-31 19:42:48 +0000
+++ lib/lp/translations/templates/sourcepackage-sharing-details.pt	2011-04-06 20:19:28 +0000
@@ -12,7 +12,7 @@
         LPS.use('lp.translations.sourcepackage_sharing_details', function(Y) {
           Y.on('domready', function() {
               Y.lp.translations.sourcepackage_sharing_details.prepare(
-                LP.cache['context']);
+                LP.cache['context'], LP.cache);
           });
         });
       </script>

=== modified file 'lib/lp/translations/windmill/tests/test_sourcepackage_sharing_details.py'
--- lib/lp/translations/windmill/tests/test_sourcepackage_sharing_details.py	2011-04-05 00:47:45 +0000
+++ lib/lp/translations/windmill/tests/test_sourcepackage_sharing_details.py	2011-04-06 20:19:28 +0000
@@ -34,6 +34,8 @@
 
     def test_set_branch(self):
         packaging = self.factory.makePackagingLink()
+        branch = self.factory.makeProductBranch(
+            product=packaging.productseries.product, name='product-branch')
         self.useContext(feature_flags())
         set_feature_flag(u'translations.sharing_information.enabled', u'on')
         transaction.commit()
@@ -44,10 +46,9 @@
         client.waits.forElement(
             id='branch-incomplete', timeout=FOR_ELEMENT)
         client.click(xpath='//*[@id="branch-incomplete-picker"]/a')
-        search_and_select_picker_widget(client, 'firefox', 1)
-        client.waits.forElement(
-            xpath=u'//*[@id="branch-incomplete" and contains(@class, "unseen")]',
+        search_and_select_picker_widget(client, 'product-branch', 1)
+        client.waits.forElementProperty(
+            id='branch-incomplete', option='className|sprite no unseen',
             timeout=FOR_ELEMENT)
         transaction.commit()
-        branch = packaging.productseries.branch
-        self.assertEqual('~name12/firefox/main', branch.unique_name)
+        self.assertEqual(branch, packaging.productseries.branch)