Merge lp:~deryck/launchpad/actually-save-info-type-on-project-create into lp:launchpad

Proposed by Deryck Hodge on 2012-10-03
Status: Merged
Approved by: Deryck Hodge on 2012-10-04
Approved revision: no longer in the source branch.
Merged at revision: 16088
Proposed branch: lp:~deryck/launchpad/actually-save-info-type-on-project-create
Merge into: lp:launchpad
Diff against target: 206 lines (+66/-14)
4 files modified
lib/lp/registry/browser/product.py (+6/-6)
lib/lp/registry/browser/tests/test_product.py (+40/-1)
lib/lp/registry/javascript/product_views.js (+13/-7)
lib/lp/registry/javascript/tests/test_product_views.js (+7/-0)
To merge this branch: bzr merge lp:~deryck/launchpad/actually-save-info-type-on-project-create
Reviewer Review Type Date Requested Status
Aaron Bentley (community) 2012-10-03 Approve on 2012-10-03
Review via email: mp+127863@code.launchpad.net

Commit Message

Ensure the project creation web UI doesn't error when saving, and ensure that it also saves information_type correctly, when the new private projects feature UI is enabled.

Description of the Change

This branch fixes a couple bugs discovered in the new private projects UI for project creation.

The JavaScript needed to be updated to fill in a value for license_info if the "Other/Proprietary" option was selected. This will prevent the form validation from throwing an error. I added a test inside the excellent test coverage for this new js code.

Also, the project creation web UI was not saving information type. This changes the form to save PUBLIC unless the feature flag is set, then we will save the value returned in form data. I added a couple tests here, too, that ensure that information_type is saved correctly whether the private projects feature flag is set or not/

There are some long lines of lint in the js file, which I'll cleanup before landing.

To post a comment you must log in.
Aaron Bentley (abentley) wrote :

This looks reasonable.

Setting information_type to PUBLIC is a bit of a DRY violation, since createProject takes care of that when unspecified or None. It's not the only DRY violation in this regard, as the EnumCol declares a default (but I can't figure out how to access it).

Similarly, declaring private_projects_flag is a DRY violation. I would prefer if you moved it to a module-level variable, since it's already used in two other places.

review: Approve
Deryck Hodge (deryck) wrote :

Sure, I can make those changes. Thanks for the review.

Deryck Hodge (deryck) wrote :

I realized now there's no need to hide the form data get behind a feature flag since None is an acceptable value. I've updated again, which makes this a little cleaner still.

Aaron Bentley (abentley) wrote :

Nice.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/registry/browser/product.py'
2--- lib/lp/registry/browser/product.py 2012-09-28 06:15:58 +0000
3+++ lib/lp/registry/browser/product.py 2012-10-04 12:58:22 +0000
4@@ -204,6 +204,7 @@
5
6 OR = ' OR '
7 SPACE = ' '
8+PRIVATE_PROJECTS_FLAG = 'disclosure.private_projects.enabled'
9
10
11 class ProductNavigation(
12@@ -1974,8 +1975,7 @@
13 hidden_names = ['__visited_steps__', 'license_info']
14 hidden_fields = self.form_fields.select(*hidden_names)
15
16- private_projects_flag = 'disclosure.private_projects.enabled'
17- private_projects = bool(getFeatureFlag(private_projects_flag))
18+ private_projects = bool(getFeatureFlag(PRIVATE_PROJECTS_FLAG))
19 if not private_projects or not IProductSet.providedBy(self.context):
20 hidden_names.extend([
21 'information_type', 'bug_supervisor', 'driver'])
22@@ -2018,8 +2018,7 @@
23 self.widgets['source_package_name'].visible = False
24 self.widgets['distroseries'].visible = False
25
26- private_projects_flag = 'disclosure.private_projects.enabled'
27- private_projects = bool(getFeatureFlag(private_projects_flag))
28+ private_projects = bool(getFeatureFlag(PRIVATE_PROJECTS_FLAG))
29
30 if private_projects and IProductSet.providedBy(self.context):
31 self.widgets['information_type'].value = InformationType.PUBLIC
32@@ -2091,8 +2090,7 @@
33 for error in errors:
34 self.errors.remove(error)
35
36- private_projects_flag = 'disclosure.private_projects.enabled'
37- private_projects = bool(getFeatureFlag(private_projects_flag))
38+ private_projects = bool(getFeatureFlag(PRIVATE_PROJECTS_FLAG))
39 if private_projects:
40 if data.get('information_type') != InformationType.PUBLIC:
41 for required_field in ('bug_supervisor', 'driver'):
42@@ -2118,6 +2116,7 @@
43 owner = getUtility(ILaunchpadCelebrities).registry_experts
44 else:
45 owner = data.get('owner')
46+
47 return getUtility(IProductSet).createProduct(
48 registrant=self.user,
49 bug_supervisor=data.get('bug_supervisor', None),
50@@ -2131,6 +2130,7 @@
51 homepageurl=data.get('homepageurl'),
52 licenses=data['licenses'],
53 license_info=data['license_info'],
54+ information_type=data.get('information_type'),
55 project=project)
56
57 def link_source_package(self, product, data):
58
59=== modified file 'lib/lp/registry/browser/tests/test_product.py'
60--- lib/lp/registry/browser/tests/test_product.py 2012-09-10 13:24:03 +0000
61+++ lib/lp/registry/browser/tests/test_product.py 2012-10-04 12:58:22 +0000
62@@ -11,7 +11,10 @@
63 from zope.schema.vocabulary import SimpleVocabulary
64
65 from lp.app.browser.lazrjs import vocabulary_to_choice_edit_items
66-from lp.app.enums import ServiceUsage
67+from lp.app.enums import (
68+ InformationType,
69+ ServiceUsage,
70+ )
71 from lp.registry.browser.product import (
72 ProjectAddStepOne,
73 ProjectAddStepTwo,
74@@ -25,6 +28,7 @@
75 License,
76 )
77 from lp.services.config import config
78+from lp.services.features.testing import FeatureFixture
79 from lp.services.webapp.publisher import canonical_url
80 from lp.testing import (
81 BrowserTestCase,
82@@ -106,6 +110,7 @@
83 'field.licenses': ['MIT'],
84 'field.license_info': '',
85 'field.disclaim_maintainer': 'off',
86+ 'field.information_type': 0,
87 }
88
89 def test_view_data_model(self):
90@@ -211,6 +216,40 @@
91 product = self.product_set.getByName('fnord')
92 self.assertEqual('registry', product.owner.name)
93
94+ def test_information_type_saved_new_product_default(self):
95+ # information_type should be PUBLIC by default for new projects.
96+ # if the private projects feature flag is not enabled.
97+ registrant = self.factory.makePerson()
98+ login_person(registrant)
99+ form = self.makeForm(action=2)
100+ form['field.information_type'] = 'PROPRIETARY'
101+ form['field.owner'] = registrant.name
102+ view = create_initialized_view(self.product_set, '+new', form=form)
103+ self.assertEqual(0, len(view.view.errors))
104+ product = self.product_set.getByName('fnord')
105+ self.assertEqual(InformationType.PUBLIC, product.information_type)
106+
107+ def test_information_type_saved_new_product_updated(self):
108+ # information_type will be updated if passed in via form data,
109+ # if the private projects feature flag is enabled.
110+ with FeatureFixture({u'disclosure.private_projects.enabled': u'on'}):
111+ registrant = self.factory.makePerson()
112+ login_person(registrant)
113+ form = self.makeForm(action=2)
114+ form['field.information_type'] = 'PROPRIETARY'
115+ form['field.owner'] = registrant.name
116+ form['field.driver'] = registrant.name
117+ form['field.maintainer'] = registrant.name
118+ form['field.bug_supervisor'] = registrant.name
119+ form['field.licenses'] = License.OTHER_PROPRIETARY.title
120+ form['field.license_info'] = 'Commericial Subscription'
121+ view = create_initialized_view(
122+ self.product_set, '+new', form=form)
123+ self.assertEqual(0, len(view.view.errors))
124+ product = self.product_set.getByName('fnord')
125+ self.assertEqual(
126+ InformationType.PROPRIETARY, product.information_type)
127+
128
129 class TestProductView(TestCaseWithFactory):
130 """Tests the ProductView."""
131
132=== modified file 'lib/lp/registry/javascript/product_views.js'
133--- lib/lp/registry/javascript/product_views.js 2012-09-24 14:10:10 +0000
134+++ lib/lp/registry/javascript/product_views.js 2012-10-04 12:58:22 +0000
135@@ -176,7 +176,8 @@
136 // selected task.
137 step_title.set('innerHTML', 'Step 2 (of 2) Registration details');
138
139- var reset_height = this.get('search_results').getComputedStyle('height');
140+ var reset_height = this.get('search_results').getComputedStyle(
141+ 'height');
142 this.get('search_results').setStyle('height', reset_height);
143 Y.lp.ui.effects.slide_in(this.get('search_results')).run();
144
145@@ -196,8 +197,10 @@
146 *
147 */
148 _information_type_change: function (value) {
149- var driver_cont = Y.one('input[name="field.driver"]').ancestor('td');
150- var bug_super_cont = Y.one('input[name="field.bug_supervisor"]').ancestor('td');
151+ var driver_cont = Y.one(
152+ 'input[name="field.driver"]').ancestor('td');
153+ var bug_super_cont = Y.one(
154+ 'input[name="field.bug_supervisor"]').ancestor('td');
155
156 // The license code is nested in another table.
157 var license = Y.one('input[name="field.licenses"]');
158@@ -218,6 +221,8 @@
159 var commercial = 'input[value="' + COMMERCIAL_LICENSE + '"]';
160 Y.one(commercial).set('checked', 'checked');
161 license.set('value', COMMERCIAL_LICENSE);
162+ Y.one('textarea[name="field.license_info"]').set(
163+ 'value', 'Launchpad 30-day trial commerical license');
164 } else {
165 driver_cont.hide();
166 bug_super_cont.hide();
167@@ -306,9 +311,9 @@
168 * @param {Object}
169 */
170 initialize: function (cfg) {
171- // The details button is only visible when JavaScript is enabled, but
172- // the H3 separator is only visible when JavaScript is disabled.
173- // Neither is displayed on the step 1 page.
174+ // The details button is only visible when JavaScript is enabled,
175+ // but the H3 separator is only visible when JavaScript is
176+ // disabled. Neither is displayed on the step 1 page.
177 this._show_separator(false);
178 },
179
180@@ -334,7 +339,8 @@
181 // If we've been here before (e.g. there was an error in
182 // submitting step 2), jump to continuing the registration.
183 var marker = this.get('marker');
184- if (marker && marker.getAttribute('value').search(/hidesearch/) >= 0) {
185+ if (marker &&
186+ marker.getAttribute('value').search(/hidesearch/) >= 0) {
187 this._complete_registration(null);
188 }
189 }
190
191=== modified file 'lib/lp/registry/javascript/tests/test_product_views.js'
192--- lib/lp/registry/javascript/tests/test_product_views.js 2012-09-24 14:10:10 +0000
193+++ lib/lp/registry/javascript/tests/test_product_views.js 2012-10-04 12:58:22 +0000
194@@ -135,6 +135,13 @@
195 var new_license = Y.one('input[name="field.licenses"]');
196 Y.Assert.areEqual('OTHER_PROPRIETARY', new_license.get('value'),
197 'License is updated to a commercial selection');
198+
199+ // license_info must also be filled in to ensure we don't
200+ // get form validation errors.
201+ var license_info = Y.one('textarea[name="field.license_info"]');
202+ Y.Assert.areEqual(
203+ 'Launchpad 30-day trial commerical license',
204+ license_info.get('value'));
205 }
206 }));
207