Merge lp:~abentley/launchpad/js-translation into lp:launchpad

Proposed by Aaron Bentley
Status: Merged
Merged at revision: 12718
Proposed branch: lp:~abentley/launchpad/js-translation
Merge into: lp:launchpad
Diff against target: 639 lines (+506/-16)
7 files modified
lib/lp/translations/browser/sourcepackage.py (+37/-3)
lib/lp/translations/browser/tests/test_sharing_details.py (+18/-4)
lib/lp/translations/javascript/sourcepackage_sharing_details.js (+233/-0)
lib/lp/translations/javascript/tests/test_sourcepackage_sharing_details.html (+34/-0)
lib/lp/translations/javascript/tests/test_sourcepackage_sharing_details.js (+105/-0)
lib/lp/translations/templates/sourcepackage-sharing-details.pt (+22/-9)
lib/lp/translations/windmill/tests/test_sourcepackage_sharing_details.py (+57/-0)
To merge this branch: bzr merge lp:~abentley/launchpad/js-translation
Reviewer Review Type Date Requested Status
Deryck Hodge (community) code Approve
Review via email: mp+54067@code.launchpad.net

Description of the change

= Summary =
Start implementing ajax support for sharing details page.

== Proposed fix ==

== Pre-implementation notes ==
Discussed with deryck

== Implementation details ==
Implemented TranslationSharingConfig, CheckItem and LinkCheckItem to represent
the state of the sharing config checklist. Updated the template so that the
branch-related text is always present in the DOM, with inactive text marked
with the "unseen" class.

Added pickers to select the branch, for the productseries. Implemented
TranslationSharingController to update the checklist in the model and the UI.

Setting the branch requires multiple steps of async IO, so I provided each step
a nested function. These were then chained together through their config
parameters so that each triggers the next.

Not yet implemented:
 - marking items disabled when they cannot be used
 - initializing the TranslationSharingController to the correct state

This is the first of a planned sequence of branches.

== Tests ==
Load lib/lp/translations/javascript/tests/test_sourcepackage_sharing_details.html in your browser.

bin/test sharing_details
bin/test --layer=WindmillLayer sharing_details

== Demo and Q/A ==
enable the feature flag
ensure you control a productseries that is linked to by a sourcepackage
go to the +sharing-details page.
Click the branch edit link. You should get an inline branch picker.
When you select a branch, the page should be updated correctly. You should be
able to confirm the branch link on the productseries page.

= Launchpad lint =

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/lp/translations/templates/sourcepackage-sharing-details.pt
  lib/lp/translations/javascript/tests/test_sourcepackage_sharing_details.js
  lib/lp/translations/templates/translation_sharing_config.pt
  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/translations/browser/tests/test_sharing_details.py
     331: Line exceeds 78 characters.

To post a comment you must log in.
Revision history for this message
Deryck Hodge (deryck) wrote :

Hi, Aaron.

This looks very good as a first branch in this work. Thanks!

Beyond the things I mentioned on IRC, I noticed you have several variables in the JavaScript test that are not bound locally with the var keyword. I'm assuming this is oversight and would ask you to fix this, unless you have a good reason to do so. While there is no negative consequence of this in the test that I see, I still think it's good practice unless you really mean the variables to be global. Especially since the behavior of js is opposite of Python in this regard.

Cheers,
deryck

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/lp/translations/browser/sourcepackage.py'
--- lib/lp/translations/browser/sourcepackage.py 2011-03-16 14:33:28 +0000
+++ lib/lp/translations/browser/sourcepackage.py 2011-03-21 18:13:21 +0000
@@ -11,6 +11,8 @@
11 'SourcePackageTranslationSharingStatus',11 'SourcePackageTranslationSharingStatus',
12 ]12 ]
1313
14import cgi
15
14from zope.publisher.interfaces import NotFound16from zope.publisher.interfaces import NotFound
1517
16from canonical.launchpad.webapp import (18from canonical.launchpad.webapp import (
@@ -148,6 +150,34 @@
148 'shared with the upstream project.')150 'shared with the upstream project.')
149151
150 @property152 @property
153 def branch_link(self):
154 if self.has_upstream_branch:
155 # Normally should use BranchFormatterAPI(branch).link(None), but
156 # on this page, that information is redundant.
157 title = cgi.escape(self.upstream_branch.unique_name)
158 url = canonical_url(self.upstream_branch)
159 else:
160 title = ''
161 url = '#'
162 return '<a class="sprite branch link" href="%s">%s</a>' % (url, title)
163
164 @property
165 def branch_incomplete_class(self):
166 classes = ['sprite', 'no']
167 if self.has_upstream_branch:
168 classes.append('unseen')
169 if not self.is_packaging_configured:
170 classes.append("lowlight")
171 return ' '.join(classes)
172
173 @property
174 def branch_complete_class(self):
175 classes = ['sprite', 'yes']
176 if not self.has_upstream_branch:
177 classes.append('unseen')
178 return ' '.join(classes)
179
180 @property
151 def is_packaging_configured(self):181 def is_packaging_configured(self):
152 """Is a packaging link defined for this branch?"""182 """Is a packaging link defined for this branch?"""
153 return self.context.direct_packaging is not None183 return self.context.direct_packaging is not None
@@ -162,11 +192,15 @@
162 return css_class + " lowlight"192 return css_class + " lowlight"
163193
164 @property194 @property
195 def upstream_branch(self):
196 if not self.is_packaging_configured:
197 return None
198 return self.context.direct_packaging.productseries.branch
199
200 @property
165 def has_upstream_branch(self):201 def has_upstream_branch(self):
166 """Does the upstream series have a source code branch?"""202 """Does the upstream series have a source code branch?"""
167 if not self.is_packaging_configured:203 return self.upstream_branch is not None
168 return False
169 return self.context.direct_packaging.productseries.branch is not None
170204
171 @property205 @property
172 def is_upstream_translations_enabled(self):206 def is_upstream_translations_enabled(self):
173207
=== modified file 'lib/lp/translations/browser/tests/test_sharing_details.py'
--- lib/lp/translations/browser/tests/test_sharing_details.py 2011-03-16 16:04:38 +0000
+++ lib/lp/translations/browser/tests/test_sharing_details.py 2011-03-21 18:13:21 +0000
@@ -3,6 +3,12 @@
33
4__metaclass__ = type4__metaclass__ = type
55
6
7from soupmatchers import (
8 HTMLContains,
9 Tag,
10)
11
6from canonical.launchpad.testing.pages import (12from canonical.launchpad.testing.pages import (
7 extract_text,13 extract_text,
8 find_tag_by_id,14 find_tag_by_id,
@@ -333,16 +339,22 @@
333 sourcepackage, no_login=True, rootsite="translations",339 sourcepackage, no_login=True, rootsite="translations",
334 view_name="+sharing-details")340 view_name="+sharing-details")
335341
342 def assertUnseen(self, browser, html_id):
343 branch_complete_unseen = Tag(html_id, 'li', attrs={
344 'id': html_id,
345 'class': lambda v: v and 'unseen' in v.split(' ')})
346 self.assertThat(browser.contents, HTMLContains(branch_complete_unseen))
347
336 def test_checklist_unconfigured(self):348 def test_checklist_unconfigured(self):
337 # Without a packaging link, sharing is completely unconfigured349 # Without a packaging link, sharing is completely unconfigured
338 sourcepackage = self._makeSourcePackage()350 sourcepackage = self._makeSourcePackage()
339 browser = self._getSharingDetailsViewBrowser(sourcepackage)351 browser = self._getSharingDetailsViewBrowser(sourcepackage)
340 checklist = find_tag_by_id(browser.contents, 'sharing-checklist')352 checklist = find_tag_by_id(browser.contents, 'sharing-checklist')
341 self.assertIsNot(None, checklist)353 self.assertIsNot(None, checklist)
354 self.assertUnseen(browser, 'branch-complete')
342 self.assertTextMatchesExpressionIgnoreWhitespace("""355 self.assertTextMatchesExpressionIgnoreWhitespace("""
343 Translation sharing configuration is incomplete.356 Translation sharing configuration is incomplete.
344 No upstream project series has been linked. Change upstream link357 .*
345 No source branch exists for the upstream series.
346 Translations are not enabled on the upstream series.358 Translations are not enabled on the upstream series.
347 Automatic synchronization of translations is not enabled.""",359 Automatic synchronization of translations is not enabled.""",
348 extract_text(checklist))360 extract_text(checklist))
@@ -353,11 +365,12 @@
353 browser = self._getSharingDetailsViewBrowser(packaging.sourcepackage)365 browser = self._getSharingDetailsViewBrowser(packaging.sourcepackage)
354 checklist = find_tag_by_id(browser.contents, 'sharing-checklist')366 checklist = find_tag_by_id(browser.contents, 'sharing-checklist')
355 self.assertIsNot(None, checklist)367 self.assertIsNot(None, checklist)
368 self.assertUnseen(browser, 'branch-complete')
356 self.assertTextMatchesExpressionIgnoreWhitespace("""369 self.assertTextMatchesExpressionIgnoreWhitespace("""
357 Translation sharing configuration is incomplete.370 Translation sharing configuration is incomplete.
358 Linked upstream series is .+ trunk series.371 Linked upstream series is .+ trunk series.
359 Change upstream link Remove upstream link372 Change upstream link Remove upstream link
360 No source branch exists for the upstream series.373 .*
361 Translations are not enabled on the upstream series.374 Translations are not enabled on the upstream series.
362 Automatic synchronization of translations is not enabled.""",375 Automatic synchronization of translations is not enabled.""",
363 extract_text(checklist))376 extract_text(checklist))
@@ -368,11 +381,12 @@
368 browser = self._getSharingDetailsViewBrowser(sourcepackage)381 browser = self._getSharingDetailsViewBrowser(sourcepackage)
369 checklist = find_tag_by_id(browser.contents, 'sharing-checklist')382 checklist = find_tag_by_id(browser.contents, 'sharing-checklist')
370 self.assertIsNot(None, checklist)383 self.assertIsNot(None, checklist)
384 self.assertUnseen(browser, 'branch-incomplete')
371 self.assertTextMatchesExpressionIgnoreWhitespace("""385 self.assertTextMatchesExpressionIgnoreWhitespace("""
372 Translation sharing with upstream is active.386 Translation sharing with upstream is active.
373 Linked upstream series is .+ trunk series.387 Linked upstream series is .+ trunk series.
374 Change upstream link Remove upstream link388 Change upstream link Remove upstream link
375 Upstream source branch is .+[.]389 .*
376 Translations are enabled on the upstream project.390 Translations are enabled on the upstream project.
377 Automatic synchronization of translations is enabled.""",391 Automatic synchronization of translations is enabled.""",
378 extract_text(checklist))392 extract_text(checklist))
379393
=== added file 'lib/lp/translations/javascript/sourcepackage_sharing_details.js'
--- lib/lp/translations/javascript/sourcepackage_sharing_details.js 1970-01-01 00:00:00 +0000
+++ lib/lp/translations/javascript/sourcepackage_sharing_details.js 2011-03-21 18:13:21 +0000
@@ -0,0 +1,233 @@
1/* Copyright 2011 Canonical Ltd. This software is licensed under the
2 * GNU Affero General Public License version 3 (see the file LICENSE).
3 */
4
5YUI.add('lp.translations.sourcepackage_sharing_details', function(Y) {
6var namespace = Y.namespace('lp.translations.sourcepackage_sharing_details');
7
8/**
9 * This class represents the state of a checklist item.
10 */
11function CheckItem(config){
12 CheckItem.superclass.constructor.apply(this, arguments);
13}
14CheckItem.ATTRS = {
15 // Optional reference to an item that must be completed before setting
16 // this.
17 dependency: {value: null},
18 // True if this item is enabled.
19 enabled: {
20 getter: function(){
21 if (Y.Lang.isNull(this.get('dependency'))){
22 return true;
23 }
24 return this.get('dependency').get('complete');
25 }
26 },
27 // The HTML identifier of the item.
28 identifier: null
29}
30Y.extend(CheckItem, Y.Base, {
31})
32
33namespace.CheckItem = CheckItem;
34
35/**
36 * This class reflects the state of a Checklist item that holds a link.
37 */
38function LinkCheckItem(){
39 LinkCheckItem.superclass.constructor.apply(this, arguments)
40}
41LinkCheckItem.ATTRS = {
42 // This item is complete if _text is set.
43 complete: {getter:
44 function() {
45 return !Y.Lang.isNull(this.get('_text'))
46 }
47 },
48 // _text is unset by default.
49 _text: {value: null},
50 // text is read-only.
51 text: {getter:
52 function(){
53 return this.get('_text');
54 }
55 },
56 // _url is unset by default.
57 _url: {value: null},
58 // text is read-only.
59 url: { getter:
60 function(){
61 return this.get('_url');
62 }
63 },
64}
65Y.extend(LinkCheckItem, CheckItem, {
66 /**
67 * Set the text and URL of the link for this LinkCheckItem.
68 */
69 set_link: function(text, url){
70 this.set('_text', text);
71 this.set('_url', url);
72 }
73});
74
75namespace.LinkCheckItem = LinkCheckItem;
76
77/**
78 * This class represents the state of the translation sharing config
79 * checklist.
80 */
81function TranslationSharingConfig (config){
82 TranslationSharingConfig.superclass.constructor.apply(this, arguments)
83}
84Y.extend(TranslationSharingConfig, Y.Base, {
85 initializer: function(){
86 var product_series = new LinkCheckItem(
87 {identifier: 'product-series'});
88 this.set('product_series', product_series);
89 var usage = new CheckItem(
90 {identifier: 'usage', dependency: product_series});
91 this.set('translation_usage', usage);
92 var branch = new LinkCheckItem(
93 {identifier: 'branch', dependency: this.get('product_series')});
94 this.set('branch', branch);
95 var autoimport = new CheckItem(
96 {identifier: 'autoimport', dependency: branch});
97 this.set('autoimport', autoimport);
98 this.set('all_items', [product_series, usage, branch, autoimport]);
99 }
100});
101namespace.TranslationSharingConfig = TranslationSharingConfig;
102
103/**
104 * This class is the controller for updating the TranslationSharingConfig.
105 * It handles updating the HTML and the DB model.
106 */
107function TranslationSharingController (config){
108 TranslationSharingController.superclass.constructor.apply(
109 this, arguments);
110}
111Y.extend(TranslationSharingController, Y.Base, {
112 initializer: function(source_package){
113 this.set('source_package', source_package);
114 this.set('productseries_link', source_package['productseries_link']);
115 this.set('tsconfig', new TranslationSharingConfig());
116 },
117 /*
118 * Select the specified branch as the translation branch.
119 *
120 * @param branch_summary {Object} An object containing api_url, css,
121 * description, value, title
122 */
123 select_branch: function(branch_summary){
124 var that = this;
125 var lp_client = new Y.lp.client.Launchpad();
126 var error_handler = new Y.lp.client.ErrorHandler();
127 error_handler.showError = function(error_msg) {
128 Y.lp.app.errors.display_error(Y.one('#branch'), error_msg);
129 }
130 /**
131 * Return an LP client config using error_handler.
132 *
133 * @param next {Object} A callback to call on success.
134 */
135 function get_config(next){
136 var config = {
137 on:{
138 success: next,
139 failure: error_handler.getFailureHandler()
140 }
141 };
142 return config;
143 }
144 /**
145 * Return an LP client config that will call the specified callbacks
146 * in sequence, using error_handler.
147 *
148 * @param next {Object} A callback to call on success.
149 */
150 function chain_config(){
151 var last_config;
152 // Each callback is bound to the next, so we use reverse order.
153 for(var i = arguments.length-1; i >= 0; i--){
154 if (i == arguments.length - 1)
155 callback = arguments[i];
156 else
157 callback = Y.bind(arguments[i], this, last_config);
158 last_config = get_config(callback);
159 }
160 return last_config;
161 }
162
163 /* Here begin a series of methods which each represent a step in
164 * setting the branch. They each take a config to use in an lp_client
165 * call, except the last one. This allows them to be chained
166 * together.
167 *
168 * They take full advantage of their access to variables in the
169 * closure, such as "that" and "branch_summary".
170 */
171 function get_productseries(config){
172 lp_client.get(that.get('productseries_link'), config);
173 }
174 function save_branch(config, productseries){
175 productseries.set('branch_link', branch_summary['api_uri']);
176 productseries.lp_save(config);
177 }
178 function get_branch(config){
179 lp_client.get(branch_summary['api_uri'], config);
180 }
181 function set_link(branch){
182 that.get('tsconfig').get('branch').set_link(
183 branch.get('unique_name'), branch.get('web_link'));
184 that.update();
185 }
186 get_productseries(chain_config(save_branch, get_branch, set_link));
187 },
188 /**
189 * Update the display of all checklist items.
190 */
191 update: function(){
192 var branch = this.get('tsconfig').get('branch');
193 this.update_check(branch);
194 },
195 /**
196 * Update the display of a single checklist item.
197 */
198 update_check: function(check){
199 var complete = Y.one('#' + check.get('identifier') + '-complete');
200 var link = complete.one('a.link');
201 link.set('href', check.get('url'));
202 link.set('text', check.get('text'));
203 complete.toggleClass('unseen', !check.get('complete'));
204 var incomplete = Y.one('#' + check.get('identifier') + '-incomplete');
205 incomplete.toggleClass('unseen', check.get('complete'));
206 },
207});
208namespace.TranslationSharingController = TranslationSharingController;
209
210
211/**
212 * Method to prepare the AJAX translation sharing config functionality.
213 */
214namespace.prepare = function(source_package){
215 var sharing_controller = new namespace.TranslationSharingController(
216 source_package);
217 sharing_controller.update();
218 var config = {
219 picker_activator: '#branch-incomplete-picker a',
220 header : 'Select translation branch',
221 step_title: 'Search',
222 save: Y.bind('select_branch', sharing_controller)
223 };
224 var picker = Y.lp.app.picker.create('Branch', config);
225 var config = {
226 picker_activator: '#branch-complete-picker a',
227 header : 'Select translation branch',
228 step_title: 'Search',
229 save: Y.bind('select_branch', sharing_controller)
230 };
231 var picker2 = Y.lp.app.picker.create('Branch', config);
232};
233}, "0.1", {"requires": ['lp.app.errors', 'lp.app.picker', 'oop']})
0234
=== added directory 'lib/lp/translations/javascript/tests'
=== added file 'lib/lp/translations/javascript/tests/test_sourcepackage_sharing_details.html'
--- lib/lp/translations/javascript/tests/test_sourcepackage_sharing_details.html 1970-01-01 00:00:00 +0000
+++ lib/lp/translations/javascript/tests/test_sourcepackage_sharing_details.html 2011-03-21 18:13:21 +0000
@@ -0,0 +1,34 @@
1<html>
2 <head>
3 <title>Launchpad translationsharingconfig</title>
4 <!-- YUI 3.0 Setup -->
5 <script type="text/javascript" src="../../../../canonical/launchpad/icing/yui/yui/yui.js"></script>
6 <script type="text/javascript"
7 src="../../../../canonical/launchpad/icing/lazr/build/lazr.js"></script>
8 <link rel="stylesheet"
9 href="../../../../canonical/launchpad/icing/yui/cssreset/reset.css"/>
10 <link rel="stylesheet"
11 href="../../../../canonical/launchpad/icing/yui/cssfonts/fonts.css"/>
12 <link rel="stylesheet"
13 href="../../../../canonical/launchpad/icing/yui/cssbase/base.css"/>
14 <link rel="stylesheet"
15 href="../../../../canonical/launchpad/javascript/test.css" />
16
17 <!-- The module under test -->
18 <script type="text/javascript" src="../sourcepackage_sharing_details.js"></script>
19
20 <!-- The test suite -->
21 <script type="text/javascript" src="test_sourcepackage_sharing_details.js"></script>
22 </head>
23 <body class="yui3-skin-sam">
24
25 <!-- The example markup required by the script to run -->
26 <div id="expected-id">
27 <div id="branch-complete">Branch selected: <a href="#" class="link"></a>
28 </div>
29 <div id="branch-incomplete">No branch selected.</div>
30 </div>
31 <!-- The test output -->
32 <div id="log"></div>
33 </body>
34</html>
035
=== added file 'lib/lp/translations/javascript/tests/test_sourcepackage_sharing_details.js'
--- lib/lp/translations/javascript/tests/test_sourcepackage_sharing_details.js 1970-01-01 00:00:00 +0000
+++ lib/lp/translations/javascript/tests/test_sourcepackage_sharing_details.js 2011-03-21 18:13:21 +0000
@@ -0,0 +1,105 @@
1/* Copyright 2011 Canonical Ltd. This software is licensed under the
2 * GNU Affero General Public License version 3 (see the file LICENSE).
3 */
4
5YUI({
6 base: '../../../../canonical/launchpad/icing/yui/',
7 filter: 'raw', combine: false,
8 fetchCSS: false
9 }).use('test', 'console', 'lp.translations.sourcepackage_sharing_details',
10 function(Y) {
11 var suite = new Y.Test.Suite("sourcepackage_sharing_details Tests");
12 var namespace = Y.lp.translations.sourcepackage_sharing_details
13 var TranslationSharingConfig = namespace.TranslationSharingConfig
14 var TranslationSharingController = namespace.TranslationSharingController
15 var CheckItem = (
16 Y.lp.translations.sourcepackage_sharing_details.CheckItem)
17 var LinkCheckItem = (
18 Y.lp.translations.sourcepackage_sharing_details.LinkCheckItem)
19
20 suite.add(new Y.Test.Case({
21 // Test the setup method.
22 name: 'setup',
23
24 test_translation_usage_enabled: function() {
25 var sharing_config = new TranslationSharingConfig();
26 var usage = sharing_config.get('translation_usage')
27 Y.Assert.isFalse(usage.get('enabled'));
28 sharing_config.get('product_series').set_link('ps', 'http://')
29 Y.Assert.isTrue(usage.get('enabled'));
30 },
31 test_branch: function() {
32 var sharing_config = new TranslationSharingConfig();
33 var product_series = sharing_config.get('product_series')
34 Y.Assert.isFalse(product_series.get('complete'));
35 Y.Assert.isFalse(sharing_config.get('branch').get('enabled'));
36 product_series.set_link('ps', 'http://')
37 Y.Assert.isTrue(sharing_config.get('branch').get('enabled'));
38 },
39 test_autoimport: function() {
40 var sharing_config = new TranslationSharingConfig();
41 Y.Assert.isFalse(sharing_config.get('autoimport').get('enabled'));
42 sharing_config.get('branch').set_link('br', 'http://foo')
43 Y.Assert.isTrue(sharing_config.get('autoimport').get('enabled'));
44 },
45 test_LinkCheckItem_contents: function() {
46 var lci = new LinkCheckItem()
47 Y.Assert.isNull(lci.get('text'));
48 Y.Assert.isNull(lci.get('url'));
49 lci.set_link('mytext', 'http://example.com');
50 Y.Assert.areEqual('mytext', lci.get('text'));
51 Y.Assert.areEqual('http://example.com', lci.get('url'));
52 },
53 test_LinkCheckItem_complete: function() {
54 var lci = new LinkCheckItem()
55 Y.Assert.isFalse(lci.get('complete'));
56 lci.set_link('text', 'http://example.com');
57 Y.Assert.isTrue(lci.get('complete'));
58 },
59 test_CheckItem_enabled: function() {
60 var ci = new CheckItem();
61 Y.Assert.isTrue(ci.get('enabled'));
62 },
63 test_CheckItem_enabled_dependency: function(){
64 var lci = new LinkCheckItem()
65 var ci = new CheckItem({dependency: lci})
66 Y.Assert.isFalse(ci.get('enabled'))
67 lci.set_link('text', 'http://example.com');
68 Y.Assert.isTrue(ci.get('enabled'))
69 },
70 test_CheckItem_identifier: function(){
71 var ci = new CheckItem({identifier: 'id1'});
72 Y.Assert.areEqual('id1', ci.get('identifier'))
73 },
74 test_update_branch: function(){
75 var complete = Y.one('#branch-complete')
76 var incomplete = Y.one('#branch-incomplete')
77 var link = Y.one('#branch-complete a')
78 Y.Assert.areEqual('', link.get('text'))
79 Y.Assert.areNotEqual('http:///', link.get('href'))
80 Y.Assert.isFalse(complete.hasClass('unseen'))
81 Y.Assert.isFalse(incomplete.hasClass('unseen'))
82 var ctrl = new TranslationSharingController({})
83 ctrl.update()
84 Y.Assert.isTrue(complete.hasClass('unseen'))
85 Y.Assert.isFalse(incomplete.hasClass('unseen'))
86 ctrl.get('tsconfig').get('branch').set_link('a', 'http:///')
87 ctrl.update()
88 Y.Assert.isFalse(complete.hasClass('unseen'))
89 Y.Assert.isTrue(incomplete.hasClass('unseen'))
90 var link = Y.one('#branch-complete a')
91 Y.Assert.areEqual('a', link.get('text'))
92 Y.Assert.areEqual('http:///', link.get('href'))
93 }
94 }));
95
96 // Lock, stock, and two smoking barrels.
97 Y.Test.Runner.add(suite);
98
99 var console = new Y.Console({newestOnTop: false});
100 console.render('#log');
101
102 Y.on('domready', function() {
103 Y.Test.Runner.run();
104 });
105});
0106
=== modified file 'lib/lp/translations/templates/sourcepackage-sharing-details.pt'
--- lib/lp/translations/templates/sourcepackage-sharing-details.pt 2011-03-15 19:15:18 +0000
+++ lib/lp/translations/templates/sourcepackage-sharing-details.pt 2011-03-21 18:13:21 +0000
@@ -7,6 +7,16 @@
7 i18n:domain="launchpad"7 i18n:domain="launchpad"
8>8>
9 <body>9 <body>
10 <metal:block fill-slot="head_epilogue">
11 <script type="text/javascript">
12 LPS.use('lp.translations.sourcepackage_sharing_details', function(Y) {
13 Y.on('domready', function() {
14 Y.lp.translations.sourcepackage_sharing_details.prepare(
15 LP.cache['context']);
16 });
17 });
18 </script>
19 </metal:block>
10 <div metal:fill-slot="heading">20 <div metal:fill-slot="heading">
11 <h1>Translation sharing details</h1>21 <h1>Translation sharing details</h1>
12 </div>22 </div>
@@ -34,19 +44,22 @@
34 <a tal:replace="structure context/menu:overview/edit_packaging/fmt:icon" />44 <a tal:replace="structure context/menu:overview/edit_packaging/fmt:icon" />
35 <a tal:replace="structure context/menu:overview/remove_packaging/fmt:icon" />45 <a tal:replace="structure context/menu:overview/remove_packaging/fmt:icon" />
36 </li>46 </li>
37 <li tal:attributes="class view/no_item_class"47 <li tal:attributes="class view/branch_incomplete_class"
38 tal:condition="not:view/has_upstream_branch">48 id="branch-incomplete">
39 No source branch exists for the upstream series.49 No source branch exists for the upstream series.
50 <span id="branch-incomplete-picker">
40 <a tal:condition="view/is_packaging_configured"51 <a tal:condition="view/is_packaging_configured"
41 tal:replace="structure context/productseries/menu:overview/set_branch/fmt:icon" />52 tal:replace="structure context/productseries/menu:overview/set_branch/fmt:icon" />
53 </span>
42 </li>54 </li>
43 <li class="sprite yes"55 <li tal:attributes="class view/branch_complete_class"
44 tal:condition="view/has_upstream_branch">56 id="branch-complete">
45 Upstream source branch is57 Upstream source branch is
46 <a tal:replace="structure context/productseries/branch/fmt:link">58 <a tal:replace="structure view/branch_link">lp:gimp</a>.
47 lp:gimp</a>.59 <span id="branch-complete-picker">
48 <a tal:condition="view/is_packaging_configured"60 <a tal:condition="view/is_packaging_configured"
49 tal:replace="structure context/productseries/menu:overview/set_branch/fmt:icon" />61 tal:replace="structure context/productseries/menu:overview/set_branch/fmt:icon" />
62 </span>
50 </li>63 </li>
51 <li tal:attributes="class view/no_item_class"64 <li tal:attributes="class view/no_item_class"
52 tal:condition="not:view/is_upstream_translations_enabled">65 tal:condition="not:view/is_upstream_translations_enabled">
5366
=== added file 'lib/lp/translations/windmill/tests/test_sourcepackage_sharing_details.py'
--- lib/lp/translations/windmill/tests/test_sourcepackage_sharing_details.py 1970-01-01 00:00:00 +0000
+++ lib/lp/translations/windmill/tests/test_sourcepackage_sharing_details.py 2011-03-21 18:13:21 +0000
@@ -0,0 +1,57 @@
1# Copyright 2010 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4"""Tests for sharing details page."""
5
6
7__metaclass__ = type
8
9
10import transaction
11
12from canonical.launchpad.webapp import canonical_url
13from lp.testing import (
14 feature_flags,
15 set_feature_flag,
16 WindmillTestCase,
17)
18from lp.testing.windmill import (
19 lpuser,
20)
21from lp.testing.windmill.constants import (
22 FOR_ELEMENT,
23 PAGE_LOAD,
24)
25from lp.testing.windmill.widgets import (
26 search_and_select_picker_widget,
27)
28from lp.translations.windmill.testing import (
29 TranslationsWindmillLayer,
30)
31
32
33class TestSharingDetails(WindmillTestCase):
34
35 layer = TranslationsWindmillLayer
36
37 def test_set_branch(self):
38 packaging = self.factory.makePackagingLink()
39 self.useContext(feature_flags())
40 set_feature_flag(u'translations.sharing_information.enabled', u'on')
41 transaction.commit()
42 url = canonical_url(
43 packaging.sourcepackage, rootsite='translations',
44 view_name='+sharing-details')
45 self.client.open(url=url)
46 self.client.waits.forPageLoad(timeout=PAGE_LOAD)
47 lpuser.TRANSLATIONS_ADMIN.ensure_login(self.client)
48 self.client.waits.forElement(
49 id='branch-incomplete', timeout=FOR_ELEMENT)
50 self.client.click(xpath='//*[@id="branch-incomplete-picker"]/a')
51 search_and_select_picker_widget(self.client, 'firefox', 1)
52 self.client.waits.forElementProperty(
53 classname="unseen", option='id|branch-incomplete',
54 timeout=FOR_ELEMENT)
55 transaction.commit()
56 branch = packaging.productseries.branch
57 self.assertEqual('~name12/firefox/main', branch.unique_name)