Merge lp:~allenap/launchpad/dd-initseries-bug-727105-architecture-picker into lp:launchpad
- dd-initseries-bug-727105-architecture-picker
- Merge into devel
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Gavin Panella | ||||
Approved revision: | no longer in the source branch. | ||||
Merged at revision: | 12705 | ||||
Proposed branch: | lp:~allenap/launchpad/dd-initseries-bug-727105-architecture-picker | ||||
Merge into: | lp:launchpad | ||||
Prerequisite: | lp:~allenap/launchpad/dd-initseries-bug-727105-derivation-vocab | ||||
Diff against target: |
872 lines (+657/-18) 11 files modified
lib/canonical/launchpad/interfaces/_schema_circular_imports.py (+17/-12) lib/canonical/launchpad/javascript/test.css (+2/-0) lib/lp/registry/browser/configure.zcml (+10/-1) lib/lp/registry/browser/distroseries.py (+36/-4) lib/lp/registry/browser/tests/test_distroseries.py (+21/-0) lib/lp/registry/interfaces/distroseries.py (+6/-1) lib/lp/registry/javascript/distroseries.js (+274/-0) lib/lp/registry/javascript/tests/test_distroseries.html (+30/-0) lib/lp/registry/javascript/tests/test_distroseries.js (+225/-0) lib/lp/registry/stories/webservice/xx-distroseries.txt (+2/-0) lib/lp/registry/templates/distroseries-initialize.pt (+34/-0) |
||||
To merge this branch: | bzr merge lp:~allenap/launchpad/dd-initseries-bug-727105-architecture-picker | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Benji York (community) | code | Approve | |
Review via email: mp+54173@code.launchpad.net |
Commit message
Description of the change
Updates test.css to import sam/skin.css so that javascript unittests
are readable.
Registers the view and a custom template for the +initseries
page. There is a very minimal test for this right now; the view is
trivially simple so there's not much else to do.
Exports DistroSeries.
related to the series.
Creates a new javascript widget, ArchitecturesCh
shows checkboxes for a given set of choices. This extends another new
widget, CheckBoxListWidget, that will itself probably be rebased in
subsequent branches (the form on that page is not yet complete). The
new architectures widget is also plumbed in. To try it out:
- Go to https:/
- Change the options in the series drop-down box. Not all of them
have any corresponding DistroArchSerieses.
Preview Diff
1 | === modified file 'lib/canonical/launchpad/interfaces/_schema_circular_imports.py' |
2 | --- lib/canonical/launchpad/interfaces/_schema_circular_imports.py 2011-03-29 00:11:57 +0000 |
3 | +++ lib/canonical/launchpad/interfaces/_schema_circular_imports.py 2011-03-30 16:15:31 +0000 |
4 | @@ -30,12 +30,12 @@ |
5 | patch_plain_parameter_type, |
6 | patch_reference_property, |
7 | ) |
8 | +from canonical.launchpad.interfaces.emailaddress import IEmailAddress |
9 | from canonical.launchpad.interfaces.message import ( |
10 | IIndexedMessage, |
11 | IMessage, |
12 | IUserToUserEmail, |
13 | ) |
14 | -from canonical.launchpad.interfaces.emailaddress import IEmailAddress |
15 | from canonical.launchpad.interfaces.temporaryblobstorage import ( |
16 | ITemporaryBlobStorage, |
17 | ITemporaryStorageManager, |
18 | @@ -81,10 +81,7 @@ |
19 | ) |
20 | from lp.buildmaster.interfaces.buildfarmjob import IBuildFarmJob |
21 | from lp.buildmaster.interfaces.buildqueue import IBuildQueue |
22 | -from lp.code.interfaces.branch import ( |
23 | - IBranch, |
24 | - IBranchSet, |
25 | - ) |
26 | +from lp.code.interfaces.branch import IBranch |
27 | from lp.code.interfaces.branchmergeproposal import IBranchMergeProposal |
28 | from lp.code.interfaces.branchmergequeue import IBranchMergeQueue |
29 | from lp.code.interfaces.branchsubscription import IBranchSubscription |
30 | @@ -115,7 +112,9 @@ |
31 | IHWSubmissionDevice, |
32 | IHWVendorID, |
33 | ) |
34 | -from lp.registry.interfaces.commercialsubscription import ICommercialSubscription |
35 | +from lp.registry.interfaces.commercialsubscription import ( |
36 | + ICommercialSubscription, |
37 | + ) |
38 | from lp.registry.interfaces.distribution import IDistribution |
39 | from lp.registry.interfaces.distributionmirror import IDistributionMirror |
40 | from lp.registry.interfaces.distributionsourcepackage import ( |
41 | @@ -131,12 +130,13 @@ |
42 | from lp.registry.interfaces.gpg import IGPGKey |
43 | from lp.registry.interfaces.irc import IIrcID |
44 | from lp.registry.interfaces.jabber import IJabberID |
45 | -from lp.registry.interfaces.milestone import IHasMilestones |
46 | -from lp.registry.interfaces.milestone import IMilestone |
47 | +from lp.registry.interfaces.milestone import ( |
48 | + IHasMilestones, |
49 | + IMilestone, |
50 | + ) |
51 | from lp.registry.interfaces.person import ( |
52 | IPerson, |
53 | IPersonPublic, |
54 | - IPersonSet, |
55 | ITeam, |
56 | ) |
57 | from lp.registry.interfaces.pillar import ( |
58 | @@ -148,8 +148,10 @@ |
59 | IProduct, |
60 | IProductSet, |
61 | ) |
62 | -from lp.registry.interfaces.productrelease import IProductRelease |
63 | -from lp.registry.interfaces.productrelease import IProductReleaseFile |
64 | +from lp.registry.interfaces.productrelease import ( |
65 | + IProductRelease, |
66 | + IProductReleaseFile, |
67 | + ) |
68 | from lp.registry.interfaces.productseries import ( |
69 | IProductSeries, |
70 | ITimelineProductSeries, |
71 | @@ -176,8 +178,8 @@ |
72 | PackageUploadCustomFormat, |
73 | PackageUploadStatus, |
74 | ) |
75 | +from lp.soyuz.interfaces.archive import IArchive |
76 | from lp.soyuz.interfaces.archivedependency import IArchiveDependency |
77 | -from lp.soyuz.interfaces.archive import IArchive |
78 | from lp.soyuz.interfaces.archivepermission import IArchivePermission |
79 | from lp.soyuz.interfaces.archivesubscriber import IArchiveSubscriber |
80 | from lp.soyuz.interfaces.binarypackagebuild import IBinaryPackageBuild |
81 | @@ -215,6 +217,7 @@ |
82 | ITranslationImportQueueEntry, |
83 | ) |
84 | |
85 | + |
86 | IBranch['bug_branches'].value_type.schema = IBugBranch |
87 | IBranch['linked_bugs'].value_type.schema = IBug |
88 | IBranch['dependent_branches'].value_type.schema = IBranchMergeProposal |
89 | @@ -462,6 +465,8 @@ |
90 | IDistroSeries, 'getDistroArchSeries', IDistroArchSeries) |
91 | patch_reference_property( |
92 | IDistroSeries, 'main_archive', IArchive) |
93 | +patch_collection_property( |
94 | + IDistroSeries, 'architectures', IDistroArchSeries) |
95 | patch_reference_property( |
96 | IDistroSeries, 'distribution', IDistribution) |
97 | patch_choice_parameter_type( |
98 | |
99 | === modified file 'lib/canonical/launchpad/javascript/test.css' |
100 | --- lib/canonical/launchpad/javascript/test.css 2010-10-10 21:54:16 +0000 |
101 | +++ lib/canonical/launchpad/javascript/test.css 2011-03-30 16:15:31 +0000 |
102 | @@ -1,3 +1,5 @@ |
103 | +@import url("../icing/yui/assets/skins/sam/skin.css"); |
104 | + |
105 | /* Taken and customized from testlogger.css */ |
106 | .yui-console-entry-src { display:none; } |
107 | |
108 | |
109 | === modified file 'lib/lp/registry/browser/configure.zcml' |
110 | --- lib/lp/registry/browser/configure.zcml 2011-03-28 02:54:53 +0000 |
111 | +++ lib/lp/registry/browser/configure.zcml 2011-03-30 16:15:31 +0000 |
112 | @@ -1956,6 +1956,8 @@ |
113 | name="+reassign" |
114 | template="../../../canonical/launchpad/templates/object-reassignment.pt"/> |
115 | </browser:pages> |
116 | + |
117 | + <!-- DistroSeries create and initialize. --> |
118 | <browser:page |
119 | name="+addseries" |
120 | for="lp.registry.interfaces.distribution.IDistribution" |
121 | @@ -1964,7 +1966,6 @@ |
122 | permission="launchpad.Admin" |
123 | template="../../app/templates/generic-edit.pt"> |
124 | </browser:page> |
125 | - |
126 | <browser:page |
127 | name="+addseries" |
128 | for="lp.registry.interfaces.distribution.IDerivativeDistribution" |
129 | @@ -1973,6 +1974,14 @@ |
130 | permission="launchpad.Append" |
131 | template="../../app/templates/generic-edit.pt"> |
132 | </browser:page> |
133 | + <browser:page |
134 | + name="+initseries" |
135 | + for="lp.registry.interfaces.distroseries.IDistroSeries" |
136 | + class="lp.registry.browser.distroseries.DistroSeriesInitializeView" |
137 | + facet="overview" |
138 | + permission="launchpad.Admin" |
139 | + template="../templates/distroseries-initialize.pt"> |
140 | + </browser:page> |
141 | |
142 | <browser:page |
143 | for="lp.registry.interfaces.distribution.IDistribution" |
144 | |
145 | === modified file 'lib/lp/registry/browser/distroseries.py' |
146 | --- lib/lp/registry/browser/distroseries.py 2011-03-30 11:36:37 +0000 |
147 | +++ lib/lp/registry/browser/distroseries.py 2011-03-30 16:15:31 +0000 |
148 | @@ -11,10 +11,11 @@ |
149 | 'DistroSeriesBreadcrumb', |
150 | 'DistroSeriesEditView', |
151 | 'DistroSeriesFacets', |
152 | + 'DistroSeriesInitializeView', |
153 | 'DistroSeriesLocalDifferences', |
154 | + 'DistroSeriesNavigation', |
155 | 'DistroSeriesPackageSearchView', |
156 | 'DistroSeriesPackagesView', |
157 | - 'DistroSeriesNavigation', |
158 | 'DistroSeriesView', |
159 | ] |
160 | |
161 | @@ -510,7 +511,7 @@ |
162 | |
163 | |
164 | class DistroSeriesAddView(LaunchpadFormView): |
165 | - """A view to creat an `IDistrobutionSeries`.""" |
166 | + """A view to create an `IDistroSeries`.""" |
167 | schema = IDistroSeries |
168 | field_names = [ |
169 | 'name', 'displayname', 'title', 'summary', 'description', 'version', |
170 | @@ -543,6 +544,38 @@ |
171 | return canonical_url(self.context) |
172 | |
173 | |
174 | +class IDistroSeriesInitializeForm(Interface): |
175 | + |
176 | + derived_from_series = Choice( |
177 | + title=_('Derived from distribution series'), |
178 | + default=None, |
179 | + vocabulary="DistroSeriesDerivation", |
180 | + description=_( |
181 | + "Select the distribution series you " |
182 | + "want to derive from."), |
183 | + required=True) |
184 | + |
185 | + |
186 | +class DistroSeriesInitializeView(LaunchpadFormView): |
187 | + """A view to initialize an `IDistroSeries`.""" |
188 | + |
189 | + schema = IDistroSeriesInitializeForm |
190 | + field_names = [ |
191 | + "derived_from_series", |
192 | + ] |
193 | + |
194 | + custom_widget('derived_from_series', LaunchpadDropdownWidget) |
195 | + |
196 | + label = 'Initialize series' |
197 | + page_title = label |
198 | + |
199 | + @property |
200 | + def next_url(self): |
201 | + return canonical_url(self.context) |
202 | + |
203 | + cancel_url = next_url |
204 | + |
205 | + |
206 | class DistroSeriesPackagesView(LaunchpadView): |
207 | """A View to show series package to upstream package relationships.""" |
208 | |
209 | @@ -626,8 +659,7 @@ |
210 | for its own vocabulary, we set it up after all the others. |
211 | """ |
212 | super(DistroSeriesLocalDifferences, self).setUpFields() |
213 | - has_edit = check_permission('launchpad.Edit', self.context) |
214 | - |
215 | + check_permission('launchpad.Edit', self.context) |
216 | terms = [ |
217 | SimpleTerm(diff, diff.source_package_name.name, |
218 | diff.source_package_name.name) |
219 | |
220 | === added file 'lib/lp/registry/browser/tests/test_distroseries.py' |
221 | --- lib/lp/registry/browser/tests/test_distroseries.py 1970-01-01 00:00:00 +0000 |
222 | +++ lib/lp/registry/browser/tests/test_distroseries.py 2011-03-30 16:15:31 +0000 |
223 | @@ -0,0 +1,21 @@ |
224 | +# Copyright 2011 Canonical Ltd. This software is licensed under the |
225 | +# GNU Affero General Public License version 3 (see the file LICENSE). |
226 | + |
227 | +"""Tests for `lp.registry.browser.distroseries`.""" |
228 | + |
229 | +__metaclass__ = type |
230 | + |
231 | +from canonical.testing.layers import DatabaseFunctionalLayer |
232 | +from lp.testing import TestCaseWithFactory |
233 | +from lp.testing.views import create_initialized_view |
234 | + |
235 | + |
236 | +class TestDistroSeriesInitializeView(TestCaseWithFactory): |
237 | + |
238 | + layer = DatabaseFunctionalLayer |
239 | + |
240 | + def test_init(self): |
241 | + # There exists a +initseries view for distroseries. |
242 | + distroseries = self.factory.makeDistroSeries() |
243 | + view = create_initialized_view(distroseries, "+initseries") |
244 | + self.assertTrue(view) |
245 | |
246 | === modified file 'lib/lp/registry/interfaces/distroseries.py' |
247 | --- lib/lp/registry/interfaces/distroseries.py 2011-03-16 00:23:20 +0000 |
248 | +++ lib/lp/registry/interfaces/distroseries.py 2011-03-30 16:15:31 +0000 |
249 | @@ -32,6 +32,7 @@ |
250 | webservice_error, |
251 | ) |
252 | from lazr.restful.fields import ( |
253 | + CollectionField, |
254 | Reference, |
255 | ReferenceChoice, |
256 | ) |
257 | @@ -390,7 +391,11 @@ |
258 | """ |
259 | |
260 | # DistroArchSeries lookup properties/methods. |
261 | - architectures = Attribute("All architectures in this series.") |
262 | + architectures = exported( |
263 | + CollectionField( |
264 | + title=_("All architectures in this series."), |
265 | + value_type=Reference(schema=Interface), # IDistroArchSeries. |
266 | + readonly=True)) |
267 | |
268 | enabled_architectures = Attribute( |
269 | "All architectures in this series with the 'enabled' flag set.") |
270 | |
271 | === added file 'lib/lp/registry/javascript/distroseries.js' |
272 | --- lib/lp/registry/javascript/distroseries.js 1970-01-01 00:00:00 +0000 |
273 | +++ lib/lp/registry/javascript/distroseries.js 2011-03-30 16:15:31 +0000 |
274 | @@ -0,0 +1,274 @@ |
275 | +/** |
276 | + * Copyright 2011 Canonical Ltd. This software is licensed under the |
277 | + * GNU Affero General Public License version 3 (see the file LICENSE). |
278 | + * |
279 | + * DistroSeries related stuff. |
280 | + * |
281 | + * @module registry |
282 | + * @submodule distroseries |
283 | + */ |
284 | + |
285 | +YUI.add('lp.registry.distroseries.initseries', function(Y) { |
286 | + |
287 | +Y.log('loading lp.registry.distroseries.initseries'); |
288 | + |
289 | +var namespace = Y.namespace('lp.registry.distroseries.initseries'); |
290 | + |
291 | + |
292 | +/** |
293 | + * A form row matching that which LaunchpadForm presents, containing a |
294 | + * list of checkboxes, and an optional label and description. |
295 | + * |
296 | + * @class CheckBoxListWidget |
297 | + */ |
298 | +var CheckBoxListWidget = function() { |
299 | + CheckBoxListWidget.superclass.constructor.apply(this, arguments); |
300 | +}; |
301 | + |
302 | +Y.mix(CheckBoxListWidget, { |
303 | + |
304 | + NAME: 'checkBoxListWidget', |
305 | + |
306 | + ATTRS: { |
307 | + |
308 | + /** |
309 | + * The field name. |
310 | + * |
311 | + * @property name |
312 | + */ |
313 | + name: { |
314 | + setter: function(value, name) { |
315 | + this.fieldNode.all("input").set("name", value); |
316 | + } |
317 | + }, |
318 | + |
319 | + /** |
320 | + * An array of strings from which to choose. |
321 | + * |
322 | + * @property choices |
323 | + */ |
324 | + choices: { |
325 | + getter: function() { |
326 | + return this.fieldNode.all("li > input").get("value"); |
327 | + }, |
328 | + setter: function(value, name) { |
329 | + var choices = Y.Array.unique(value).sort(); |
330 | + var field_name = this.get("name"); |
331 | + var list = Y.Node.create("<ul />"); |
332 | + choices.forEach( |
333 | + function(choice) { |
334 | + var item = Y.Node.create( |
335 | + "<li><input /> <label /></li>"); |
336 | + item.one("input") |
337 | + .set("type", "checkbox") |
338 | + .set("name", field_name) |
339 | + .set("value", choice); |
340 | + item.one("label") |
341 | + .setAttribute( |
342 | + "for", item.one("input").generateID()) |
343 | + .setStyle("font-weight", "normal") |
344 | + .set("text", choice); |
345 | + list.append(item); |
346 | + } |
347 | + ); |
348 | + this.fieldNode.empty().append(list); |
349 | + } |
350 | + }, |
351 | + |
352 | + /** |
353 | + * The top label for the field. |
354 | + * |
355 | + * @property label |
356 | + */ |
357 | + label: { |
358 | + getter: function() { |
359 | + return this.labelNode.get("text"); |
360 | + }, |
361 | + setter: function(value, name) { |
362 | + this.labelNode.set("text", value); |
363 | + } |
364 | + }, |
365 | + |
366 | + /** |
367 | + * A description shown near the field. |
368 | + * |
369 | + * @label description |
370 | + */ |
371 | + description: { |
372 | + getter: function() { |
373 | + return this.descriptionNode.get("text"); |
374 | + }, |
375 | + setter: function(value, name) { |
376 | + this.descriptionNode.set("text", value); |
377 | + } |
378 | + } |
379 | + } |
380 | + |
381 | +}); |
382 | + |
383 | +Y.extend(CheckBoxListWidget, Y.Widget, { |
384 | + |
385 | + BOUNDING_TEMPLATE: "<tr></tr>", |
386 | + |
387 | + CONTENT_TEMPLATE: '<td colspan="2"></td>', |
388 | + |
389 | + initializer: function(config) { |
390 | + this.labelNode = Y.Node.create("<label />"); |
391 | + this.fieldNode = Y.Node.create("<div></div>"); |
392 | + this.descriptionNode = Y.Node.create('<p class="formHelp" />'); |
393 | + }, |
394 | + |
395 | + renderUI: function() { |
396 | + this.get("contentBox") |
397 | + .append(this.labelNode) |
398 | + .append(this.fieldNode) |
399 | + .append(this.descriptionNode); |
400 | + } |
401 | + |
402 | +}); |
403 | + |
404 | +namespace.CheckBoxListWidget = CheckBoxListWidget; |
405 | + |
406 | + |
407 | +/** |
408 | + * A special form of CheckBoxListWidget for choosing architecture tags. |
409 | + * |
410 | + * @class ArchitecturesCheckBoxListWidget |
411 | + */ |
412 | +var ArchitecturesCheckBoxListWidget = function() { |
413 | + ArchitecturesCheckBoxListWidget |
414 | + .superclass.constructor.apply(this, arguments); |
415 | +}; |
416 | + |
417 | +Y.mix(ArchitecturesCheckBoxListWidget, { |
418 | + |
419 | + NAME: 'architecturesCheckBoxListWidget', |
420 | + |
421 | + ATTRS: { |
422 | + |
423 | + /** |
424 | + * The DistroSeries the choices in this field should |
425 | + * reflect. Takes the form of a string, e.g. "ubuntu/hoary". |
426 | + * |
427 | + * @property distroSeries |
428 | + */ |
429 | + distroSeries: { |
430 | + setter: function(value, name) { |
431 | + var path = value + "/architectures"; |
432 | + var on = { |
433 | + start: Y.bind(this.showSpinner, this), |
434 | + success: Y.bind(this.set, this, "distroArchSerieses"), |
435 | + failure: this.error_handler.getFailureHandler(), |
436 | + end: Y.bind(this.hideSpinner, this) |
437 | + }; |
438 | + this.client.get(path, {on: on}); |
439 | + } |
440 | + }, |
441 | + |
442 | + /** |
443 | + * The DistroArchSerieses the choices in this field should |
444 | + * reflect. Takes the form of a Y.lp.client.Collection. |
445 | + * |
446 | + * @property distroArchSerieses |
447 | + */ |
448 | + distroArchSerieses: { |
449 | + setter: function(value, name) { |
450 | + this.set( |
451 | + "choices", value.entries.map( |
452 | + function(das) { |
453 | + return das.get("architecture_tag"); |
454 | + } |
455 | + ) |
456 | + ); |
457 | + if (value.entries.length == 0) { |
458 | + this.fieldNode.append( |
459 | + Y.Node.create('<p />').set( |
460 | + "text", "The chosen series has no architectures!")); |
461 | + } |
462 | + Y.lazr.anim.green_flash({node: this.fieldNode}).run(); |
463 | + } |
464 | + } |
465 | + } |
466 | + |
467 | +}); |
468 | + |
469 | +Y.extend(ArchitecturesCheckBoxListWidget, CheckBoxListWidget, { |
470 | + |
471 | + initializer: function(config) { |
472 | + this.client = new Y.lp.client.Launchpad(); |
473 | + this.error_handler = new Y.lp.client.ErrorHandler(); |
474 | + this.error_handler.clearProgressUI = Y.bind(this.hideSpinner, this); |
475 | + this.error_handler.showError = Y.bind(this.showError, this); |
476 | + this.spinner = Y.Node.create( |
477 | + '<img src="/@@/spinner" alt="Loading..." />'); |
478 | + }, |
479 | + |
480 | + /** |
481 | + * Show the spinner. |
482 | + * |
483 | + * @method showSpinner |
484 | + */ |
485 | + showSpinner: function() { |
486 | + this.fieldNode.empty().append(this.spinner); |
487 | + }, |
488 | + |
489 | + /** |
490 | + * Hide the spinner. |
491 | + * |
492 | + * @method hideSpinner |
493 | + */ |
494 | + hideSpinner: function() { |
495 | + this.spinner.remove(); |
496 | + }, |
497 | + |
498 | + /** |
499 | + * Display an error. |
500 | + * |
501 | + * @method showError |
502 | + */ |
503 | + showError: function(error) { |
504 | + var message = Y.Node.create('<p />').set("text", error); |
505 | + this.fieldNode.empty().append(message); |
506 | + Y.lazr.anim.red_flash({node: message}).run(); |
507 | + } |
508 | + |
509 | +}); |
510 | + |
511 | +namespace.ArchitecturesCheckBoxListWidget = ArchitecturesCheckBoxListWidget; |
512 | + |
513 | + |
514 | +/** |
515 | + * Setup the widgets on the +initseries page. |
516 | + * |
517 | + * @function setup |
518 | + */ |
519 | +namespace.setup = function() { |
520 | + var form_container = Y.one("#init-series-form-container"); |
521 | + var form_table_body = form_container.one("table.form > tbody"); |
522 | + var architecture_choice = new ArchitecturesCheckBoxListWidget() |
523 | + .set("name", "field.architectures") |
524 | + .set("label", "Architectures") |
525 | + .set("description", ( |
526 | + "Choose the architectures you want to " + |
527 | + "use from the parent series.")) |
528 | + .render(form_table_body); |
529 | + |
530 | + // Wire up the distroseries select to the architectures widget. |
531 | + var field_derived_from_series = |
532 | + form_table_body.one("[name=field.derived_from_series]"); |
533 | + function update_architecture_choice() { |
534 | + architecture_choice |
535 | + .set("distroSeries", field_derived_from_series.get("value")); |
536 | + } |
537 | + field_derived_from_series.on("change", update_architecture_choice); |
538 | + |
539 | + // Update the architectures widget for the selected distroseries. |
540 | + update_architecture_choice(); |
541 | + |
542 | + // Show the form. |
543 | + form_container.removeClass("unseen"); |
544 | +}; |
545 | + |
546 | + |
547 | +}, "0.1", {"requires": ["node", "dom", "io", "widget", "lp.client", |
548 | + "lazr.anim", "array-extras"]}); |
549 | |
550 | === added file 'lib/lp/registry/javascript/tests/test_distroseries.html' |
551 | --- lib/lp/registry/javascript/tests/test_distroseries.html 1970-01-01 00:00:00 +0000 |
552 | +++ lib/lp/registry/javascript/tests/test_distroseries.html 2011-03-30 16:15:31 +0000 |
553 | @@ -0,0 +1,30 @@ |
554 | +<!DOCTYPE |
555 | + HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" |
556 | + "http://www.w3.org/TR/html4/strict.dtd"> |
557 | +<html> |
558 | + <head> |
559 | + <title>Launchpad DistroSeries</title> |
560 | + <!-- YUI 3.0 Setup --> |
561 | + <script type="text/javascript" |
562 | + src="../../../../canonical/launchpad/icing/yui/yui/yui.js"></script> |
563 | + <script type="text/javascript" |
564 | + src="../../../../canonical/launchpad/icing/lazr/build/lazr.js"></script> |
565 | + <link rel="stylesheet" |
566 | + href="../../../../canonical/launchpad/icing/yui/cssreset/reset.css"/> |
567 | + <link rel="stylesheet" |
568 | + href="../../../../canonical/launchpad/icing/yui/cssfonts/fonts.css"/> |
569 | + <link rel="stylesheet" |
570 | + href="../../../../canonical/launchpad/icing/yui/cssbase/base.css"/> |
571 | + <link rel="stylesheet" |
572 | + href="../../../../canonical/launchpad/javascript/test.css" /> |
573 | + <!-- Required modules --> |
574 | + <script type="text/javascript" src="../../../app/javascript/client.js"></script> |
575 | + <!-- The module under test --> |
576 | + <script type="text/javascript" src="../distroseries.js"></script> |
577 | + <!-- The test suite --> |
578 | + <script type="text/javascript" src="test_distroseries.js"></script> |
579 | + </head> |
580 | + <body class="yui3-skin-sam"> |
581 | + <div id="log"></div> |
582 | + </body> |
583 | +</html> |
584 | |
585 | === added file 'lib/lp/registry/javascript/tests/test_distroseries.js' |
586 | --- lib/lp/registry/javascript/tests/test_distroseries.js 1970-01-01 00:00:00 +0000 |
587 | +++ lib/lp/registry/javascript/tests/test_distroseries.js 2011-03-30 16:15:31 +0000 |
588 | @@ -0,0 +1,225 @@ |
589 | +/* Copyright (c) 2011, Canonical Ltd. All rights reserved. */ |
590 | + |
591 | +YUI({ |
592 | + base: '../../../../canonical/launchpad/icing/yui/', |
593 | + filter: 'raw', combine: false, fetchCSS: false |
594 | + }).use( |
595 | + 'test', 'console', 'lp.registry.distroseries.initseries', |
596 | + function(Y) { |
597 | + |
598 | + var Assert = Y.Assert; |
599 | + var ArrayAssert = Y.ArrayAssert; |
600 | + |
601 | + var suite = new Y.Test.Suite("distroseries.initseries Tests"); |
602 | + var initseries = Y.lp.registry.distroseries.initseries; |
603 | + var console = new Y.Console({newestOnTop: false}); |
604 | + console.render('#log'); |
605 | + |
606 | + var testCheckBoxListWidget = { |
607 | + name: 'TestCheckBoxListWidget', |
608 | + |
609 | + setUp: function() { |
610 | + this.container = Y.Node.create("<div />"); |
611 | + this.widget = new initseries.CheckBoxListWidget(); |
612 | + }, |
613 | + |
614 | + tearDown: function() { |
615 | + this.container.remove(); |
616 | + }, |
617 | + |
618 | + testRender: function() { |
619 | + this.widget.render(this.container); |
620 | + Assert.isTrue( |
621 | + this.container.contains( |
622 | + this.widget.get("boundingBox"))); |
623 | + }, |
624 | + |
625 | + testRenderChoices: function() { |
626 | + this.widget.set("choices", ["a", "b"]); |
627 | + this.widget.render(this.container); |
628 | + ArrayAssert.itemsAreEqual( |
629 | + ["a", "b"], |
630 | + this.container.all("li > input").get("value")); |
631 | + ArrayAssert.itemsAreEqual( |
632 | + ["a", "b"], |
633 | + this.container.all("li > label").get("text")); |
634 | + }, |
635 | + |
636 | + testRenderChoicesChange: function() { |
637 | + this.widget.set("choices", ["a", "b"]); |
638 | + this.widget.render(this.container); |
639 | + this.widget.set("choices", ["c", "d", "e"]); |
640 | + ArrayAssert.itemsAreEqual( |
641 | + ["c", "d", "e"], |
642 | + this.container.all("li > input").get("value")); |
643 | + ArrayAssert.itemsAreEqual( |
644 | + ["c", "d", "e"], |
645 | + this.container.all("li > label").get("text")); |
646 | + }, |
647 | + |
648 | + testRenderWithName: function() { |
649 | + this.widget.set("name", "field"); |
650 | + this.widget.set("choices", ["a", "b"]); |
651 | + this.widget.render(this.container); |
652 | + ArrayAssert.itemsAreEqual( |
653 | + ["field", "field"], |
654 | + this.container.all("li > input").get("name")); |
655 | + }, |
656 | + |
657 | + testRenderWithNameChange: function() { |
658 | + this.widget.set("name", "field"); |
659 | + this.widget.set("choices", ["a", "b"]); |
660 | + this.widget.render(this.container); |
661 | + this.widget.set("name", "plain"); |
662 | + ArrayAssert.itemsAreEqual( |
663 | + ["plain", "plain"], |
664 | + this.container.all("li > input").get("name")); |
665 | + }, |
666 | + |
667 | + testRenderLabel: function() { |
668 | + this.widget.set("label", "Test label"); |
669 | + this.widget.render(this.container); |
670 | + Assert.areEqual( |
671 | + "Test label", |
672 | + this.container.one("label").get("text")); |
673 | + }, |
674 | + |
675 | + testRenderLabelChange: function() { |
676 | + this.widget.set("label", "Test label"); |
677 | + this.widget.render(this.container); |
678 | + this.widget.set("label", "Another label"); |
679 | + Assert.areEqual( |
680 | + "Another label", |
681 | + this.container.one("label").get("text")); |
682 | + }, |
683 | + |
684 | + testRenderDescription: function() { |
685 | + this.widget.set("description", "Test description."); |
686 | + this.widget.render(this.container); |
687 | + Assert.areEqual( |
688 | + "Test description.", |
689 | + this.container.one("p.formHelp").get("text")); |
690 | + }, |
691 | + |
692 | + testRenderDescriptionChange: function() { |
693 | + this.widget.set("description", "Test description."); |
694 | + this.widget.render(this.container); |
695 | + this.widget.set("description", "Another description."); |
696 | + Assert.areEqual( |
697 | + "Another description.", |
698 | + this.container.one("p.formHelp").get("text")); |
699 | + } |
700 | + |
701 | + }; |
702 | + |
703 | + suite.add(new Y.Test.Case(testCheckBoxListWidget)); |
704 | + |
705 | + var testArchitecturesCheckBoxListWidget = { |
706 | + name: 'TestArchitecturesCheckBoxListWidget', |
707 | + |
708 | + setUp: function() { |
709 | + this.container = Y.Node.create("<div />"); |
710 | + this.widget = new initseries.ArchitecturesCheckBoxListWidget(); |
711 | + }, |
712 | + |
713 | + tearDown: function() { |
714 | + this.container.remove(); |
715 | + }, |
716 | + |
717 | + testSetDistroArchSeriesesUpdatesChoices: function() { |
718 | + var distro_arch_serieses = [ |
719 | + {architecture_tag: "i386"}, |
720 | + {architecture_tag: "amd64"}, |
721 | + {architecture_tag: "i386"} |
722 | + ]; |
723 | + var distro_arch_serieses_collection = |
724 | + new Y.lp.client.Collection( |
725 | + null, {entries: distro_arch_serieses}, null); |
726 | + this.widget.set( |
727 | + "distroArchSerieses", |
728 | + distro_arch_serieses_collection); |
729 | + ArrayAssert.itemsAreEqual( |
730 | + ["amd64", "i386"], |
731 | + this.widget.get("choices")); |
732 | + }, |
733 | + |
734 | + testSetDistroSeriesInitiatesIO: function() { |
735 | + var io = false; |
736 | + this.widget.client = { |
737 | + get: function(path, config) { |
738 | + io = true; |
739 | + Assert.areEqual("ubuntu/hoary/architectures", path); |
740 | + Assert.isObject(config.on); |
741 | + Assert.isFunction(config.on.success); |
742 | + Assert.isFunction(config.on.failure); |
743 | + } |
744 | + }; |
745 | + this.widget.set("distroSeries", "ubuntu/hoary"); |
746 | + Assert.isTrue(io, "No IO initiated."); |
747 | + }, |
748 | + |
749 | + testSetDistroSeriesUpdatesDistroArchSeries: function() { |
750 | + var distro_arch_serieses = [ |
751 | + {architecture_tag: "i386"}, |
752 | + {architecture_tag: "amd64"}, |
753 | + {architecture_tag: "i386"} |
754 | + ]; |
755 | + var distro_arch_serieses_collection = |
756 | + new Y.lp.client.Collection( |
757 | + null, {entries: distro_arch_serieses}, null); |
758 | + this.widget.client = { |
759 | + get: function(path, config) { |
760 | + config.on.success(distro_arch_serieses_collection); |
761 | + } |
762 | + }; |
763 | + this.widget.set("distroSeries", "ubuntu/hoary"); |
764 | + ArrayAssert.itemsAreEqual( |
765 | + ["amd64", "i386"], |
766 | + this.widget.get("choices")); |
767 | + }, |
768 | + |
769 | + testSetDistroSeriesSpinner: function() { |
770 | + var widget = this.widget; |
771 | + widget.client = { |
772 | + get: function(path, config) { |
773 | + Assert.isFalse( |
774 | + widget.fieldNode.contains(widget.spinner)); |
775 | + config.on.start(); |
776 | + Assert.isTrue( |
777 | + widget.fieldNode.contains(widget.spinner)); |
778 | + config.on.end(); |
779 | + Assert.isFalse( |
780 | + widget.fieldNode.contains(widget.spinner)); |
781 | + } |
782 | + }; |
783 | + this.widget.set("distroSeries", "ubuntu/hoary"); |
784 | + }, |
785 | + |
786 | + testSetDistroSeriesError: function() { |
787 | + var widget = this.widget; |
788 | + widget.client = { |
789 | + get: function(path, config) { |
790 | + config.on.failure( |
791 | + null, {status: 404, |
792 | + responseText: "Not found"}); |
793 | + Assert.areEqual( |
794 | + "Not found", |
795 | + widget.fieldNode.one("p").get("text")); |
796 | + } |
797 | + }; |
798 | + this.widget.set("distroSeries", "ubuntu/hoary"); |
799 | + } |
800 | + |
801 | + }; |
802 | + |
803 | + testArchitecturesCheckBoxListWidget = Y.merge( |
804 | + testCheckBoxListWidget, testArchitecturesCheckBoxListWidget); |
805 | + |
806 | + suite.add(new Y.Test.Case(testArchitecturesCheckBoxListWidget)); |
807 | + |
808 | + Y.Test.Runner.add(suite); |
809 | + |
810 | + Y.on('domready', function() { |
811 | + Y.Test.Runner.run(); |
812 | + }); |
813 | +}); |
814 | |
815 | === modified file 'lib/lp/registry/stories/webservice/xx-distroseries.txt' |
816 | --- lib/lp/registry/stories/webservice/xx-distroseries.txt 2011-01-26 19:35:17 +0000 |
817 | +++ lib/lp/registry/stories/webservice/xx-distroseries.txt 2011-03-30 16:15:31 +0000 |
818 | @@ -61,6 +61,7 @@ |
819 | active_milestones_collection_link: |
820 | u'http://.../ubuntu/hoary/active_milestones' |
821 | all_milestones_collection_link: u'http://.../ubuntu/hoary/all_milestones' |
822 | + architectures_collection_link: u'http://.../ubuntu/hoary/architectures' |
823 | bug_reported_acknowledgement: None |
824 | bug_reporting_guidelines: None |
825 | changeslist: u'hoary-changes@ubuntu.com' |
826 | @@ -86,6 +87,7 @@ |
827 | version: u'5.04' |
828 | web_link: u'http://launchpad.../ubuntu/hoary' |
829 | |
830 | + |
831 | Creating a milestone on the distroseries |
832 | ---------------------------------------- |
833 | |
834 | |
835 | === added file 'lib/lp/registry/templates/distroseries-initialize.pt' |
836 | --- lib/lp/registry/templates/distroseries-initialize.pt 1970-01-01 00:00:00 +0000 |
837 | +++ lib/lp/registry/templates/distroseries-initialize.pt 2011-03-30 16:15:31 +0000 |
838 | @@ -0,0 +1,34 @@ |
839 | +<html |
840 | + xmlns="http://www.w3.org/1999/xhtml" |
841 | + xmlns:tal="http://xml.zope.org/namespaces/tal" |
842 | + xmlns:metal="http://xml.zope.org/namespaces/metal" |
843 | + xmlns:i18n="http://xml.zope.org/namespaces/i18n" |
844 | + metal:use-macro="view/macro:page/main_only" |
845 | + i18n:domain="launchpad"> |
846 | + <metal:head-epilogue fill-slot="head_epilogue"> |
847 | + <style type="text/css"> |
848 | + .yui3-js-enabled .javascript-disabled { display: none; } |
849 | + </style> |
850 | + </metal:head-epilogue> |
851 | + <body> |
852 | + <div metal:fill-slot="main"> |
853 | + <div class="top-portlet"> |
854 | + This page allows you to initialize a distribution series. |
855 | + </div> |
856 | + <p class="error message javascript-disabled"> |
857 | + Javascript is required to use this page. Please enable |
858 | + Javascript in your browser and reload this page. Alternatively, |
859 | + please use the <code>deriveDistroSeries</code> API call via the |
860 | + web service. |
861 | + </p> |
862 | + <div class="unseen" id="init-series-form-container"> |
863 | + <metal:form use-macro="context/@@launchpad_form/form" /> |
864 | + </div> |
865 | + <script type="text/javascript"> |
866 | + LPS.use('lp.registry.distroseries.initseries', function(Y) { |
867 | + Y.on('domready', Y.lp.registry.distroseries.initseries.setup); |
868 | + }); |
869 | + </script> |
870 | + </div> |
871 | + </body> |
872 | +</html> |
This branch looks really good.
I only noticed one small thing: the "description" attribute of from_series" would fit on one line if so desired.
"derived_