Merge lp:~allenap/launchpad/dd-initseries-bug-727105-architecture-picker into lp:launchpad

Proposed by Gavin Panella
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
Reviewer Review Type Date Requested Status
Benji York (community) code Approve
Review via email: mp+54173@code.launchpad.net

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.architectures, a collection of DistroArchSeries
related to the series.

Creates a new javascript widget, ArchitecturesCheckBoxListWidget, that
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://launchpad.dev/ubuntu/hoary/+initseries

  - Change the options in the series drop-down box. Not all of them
    have any corresponding DistroArchSerieses.

To post a comment you must log in.
Revision history for this message
Benji York (benji) wrote :

This branch looks really good.

I only noticed one small thing: the "description" attribute of
"derived_from_series" would fit on one line if so desired.

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/canonical/launchpad/interfaces/_schema_circular_imports.py'
--- lib/canonical/launchpad/interfaces/_schema_circular_imports.py 2011-03-29 00:11:57 +0000
+++ lib/canonical/launchpad/interfaces/_schema_circular_imports.py 2011-03-30 16:15:31 +0000
@@ -30,12 +30,12 @@
30 patch_plain_parameter_type,30 patch_plain_parameter_type,
31 patch_reference_property,31 patch_reference_property,
32 )32 )
33from canonical.launchpad.interfaces.emailaddress import IEmailAddress
33from canonical.launchpad.interfaces.message import (34from canonical.launchpad.interfaces.message import (
34 IIndexedMessage,35 IIndexedMessage,
35 IMessage,36 IMessage,
36 IUserToUserEmail,37 IUserToUserEmail,
37 )38 )
38from canonical.launchpad.interfaces.emailaddress import IEmailAddress
39from canonical.launchpad.interfaces.temporaryblobstorage import (39from canonical.launchpad.interfaces.temporaryblobstorage import (
40 ITemporaryBlobStorage,40 ITemporaryBlobStorage,
41 ITemporaryStorageManager,41 ITemporaryStorageManager,
@@ -81,10 +81,7 @@
81 )81 )
82from lp.buildmaster.interfaces.buildfarmjob import IBuildFarmJob82from lp.buildmaster.interfaces.buildfarmjob import IBuildFarmJob
83from lp.buildmaster.interfaces.buildqueue import IBuildQueue83from lp.buildmaster.interfaces.buildqueue import IBuildQueue
84from lp.code.interfaces.branch import (84from lp.code.interfaces.branch import IBranch
85 IBranch,
86 IBranchSet,
87 )
88from lp.code.interfaces.branchmergeproposal import IBranchMergeProposal85from lp.code.interfaces.branchmergeproposal import IBranchMergeProposal
89from lp.code.interfaces.branchmergequeue import IBranchMergeQueue86from lp.code.interfaces.branchmergequeue import IBranchMergeQueue
90from lp.code.interfaces.branchsubscription import IBranchSubscription87from lp.code.interfaces.branchsubscription import IBranchSubscription
@@ -115,7 +112,9 @@
115 IHWSubmissionDevice,112 IHWSubmissionDevice,
116 IHWVendorID,113 IHWVendorID,
117 )114 )
118from lp.registry.interfaces.commercialsubscription import ICommercialSubscription115from lp.registry.interfaces.commercialsubscription import (
116 ICommercialSubscription,
117 )
119from lp.registry.interfaces.distribution import IDistribution118from lp.registry.interfaces.distribution import IDistribution
120from lp.registry.interfaces.distributionmirror import IDistributionMirror119from lp.registry.interfaces.distributionmirror import IDistributionMirror
121from lp.registry.interfaces.distributionsourcepackage import (120from lp.registry.interfaces.distributionsourcepackage import (
@@ -131,12 +130,13 @@
131from lp.registry.interfaces.gpg import IGPGKey130from lp.registry.interfaces.gpg import IGPGKey
132from lp.registry.interfaces.irc import IIrcID131from lp.registry.interfaces.irc import IIrcID
133from lp.registry.interfaces.jabber import IJabberID132from lp.registry.interfaces.jabber import IJabberID
134from lp.registry.interfaces.milestone import IHasMilestones133from lp.registry.interfaces.milestone import (
135from lp.registry.interfaces.milestone import IMilestone134 IHasMilestones,
135 IMilestone,
136 )
136from lp.registry.interfaces.person import (137from lp.registry.interfaces.person import (
137 IPerson,138 IPerson,
138 IPersonPublic,139 IPersonPublic,
139 IPersonSet,
140 ITeam,140 ITeam,
141 )141 )
142from lp.registry.interfaces.pillar import (142from lp.registry.interfaces.pillar import (
@@ -148,8 +148,10 @@
148 IProduct,148 IProduct,
149 IProductSet,149 IProductSet,
150 )150 )
151from lp.registry.interfaces.productrelease import IProductRelease151from lp.registry.interfaces.productrelease import (
152from lp.registry.interfaces.productrelease import IProductReleaseFile152 IProductRelease,
153 IProductReleaseFile,
154 )
153from lp.registry.interfaces.productseries import (155from lp.registry.interfaces.productseries import (
154 IProductSeries,156 IProductSeries,
155 ITimelineProductSeries,157 ITimelineProductSeries,
@@ -176,8 +178,8 @@
176 PackageUploadCustomFormat,178 PackageUploadCustomFormat,
177 PackageUploadStatus,179 PackageUploadStatus,
178 )180 )
181from lp.soyuz.interfaces.archive import IArchive
179from lp.soyuz.interfaces.archivedependency import IArchiveDependency182from lp.soyuz.interfaces.archivedependency import IArchiveDependency
180from lp.soyuz.interfaces.archive import IArchive
181from lp.soyuz.interfaces.archivepermission import IArchivePermission183from lp.soyuz.interfaces.archivepermission import IArchivePermission
182from lp.soyuz.interfaces.archivesubscriber import IArchiveSubscriber184from lp.soyuz.interfaces.archivesubscriber import IArchiveSubscriber
183from lp.soyuz.interfaces.binarypackagebuild import IBinaryPackageBuild185from lp.soyuz.interfaces.binarypackagebuild import IBinaryPackageBuild
@@ -215,6 +217,7 @@
215 ITranslationImportQueueEntry,217 ITranslationImportQueueEntry,
216 )218 )
217219
220
218IBranch['bug_branches'].value_type.schema = IBugBranch221IBranch['bug_branches'].value_type.schema = IBugBranch
219IBranch['linked_bugs'].value_type.schema = IBug222IBranch['linked_bugs'].value_type.schema = IBug
220IBranch['dependent_branches'].value_type.schema = IBranchMergeProposal223IBranch['dependent_branches'].value_type.schema = IBranchMergeProposal
@@ -462,6 +465,8 @@
462 IDistroSeries, 'getDistroArchSeries', IDistroArchSeries)465 IDistroSeries, 'getDistroArchSeries', IDistroArchSeries)
463patch_reference_property(466patch_reference_property(
464 IDistroSeries, 'main_archive', IArchive)467 IDistroSeries, 'main_archive', IArchive)
468patch_collection_property(
469 IDistroSeries, 'architectures', IDistroArchSeries)
465patch_reference_property(470patch_reference_property(
466 IDistroSeries, 'distribution', IDistribution)471 IDistroSeries, 'distribution', IDistribution)
467patch_choice_parameter_type(472patch_choice_parameter_type(
468473
=== modified file 'lib/canonical/launchpad/javascript/test.css'
--- lib/canonical/launchpad/javascript/test.css 2010-10-10 21:54:16 +0000
+++ lib/canonical/launchpad/javascript/test.css 2011-03-30 16:15:31 +0000
@@ -1,3 +1,5 @@
1@import url("../icing/yui/assets/skins/sam/skin.css");
2
1/* Taken and customized from testlogger.css */3/* Taken and customized from testlogger.css */
2.yui-console-entry-src { display:none; }4.yui-console-entry-src { display:none; }
35
46
=== modified file 'lib/lp/registry/browser/configure.zcml'
--- lib/lp/registry/browser/configure.zcml 2011-03-28 02:54:53 +0000
+++ lib/lp/registry/browser/configure.zcml 2011-03-30 16:15:31 +0000
@@ -1956,6 +1956,8 @@
1956 name="+reassign"1956 name="+reassign"
1957 template="../../../canonical/launchpad/templates/object-reassignment.pt"/>1957 template="../../../canonical/launchpad/templates/object-reassignment.pt"/>
1958 </browser:pages>1958 </browser:pages>
1959
1960 <!-- DistroSeries create and initialize. -->
1959 <browser:page1961 <browser:page
1960 name="+addseries"1962 name="+addseries"
1961 for="lp.registry.interfaces.distribution.IDistribution"1963 for="lp.registry.interfaces.distribution.IDistribution"
@@ -1964,7 +1966,6 @@
1964 permission="launchpad.Admin"1966 permission="launchpad.Admin"
1965 template="../../app/templates/generic-edit.pt">1967 template="../../app/templates/generic-edit.pt">
1966 </browser:page>1968 </browser:page>
1967
1968 <browser:page1969 <browser:page
1969 name="+addseries"1970 name="+addseries"
1970 for="lp.registry.interfaces.distribution.IDerivativeDistribution"1971 for="lp.registry.interfaces.distribution.IDerivativeDistribution"
@@ -1973,6 +1974,14 @@
1973 permission="launchpad.Append"1974 permission="launchpad.Append"
1974 template="../../app/templates/generic-edit.pt">1975 template="../../app/templates/generic-edit.pt">
1975 </browser:page>1976 </browser:page>
1977 <browser:page
1978 name="+initseries"
1979 for="lp.registry.interfaces.distroseries.IDistroSeries"
1980 class="lp.registry.browser.distroseries.DistroSeriesInitializeView"
1981 facet="overview"
1982 permission="launchpad.Admin"
1983 template="../templates/distroseries-initialize.pt">
1984 </browser:page>
19761985
1977 <browser:page1986 <browser:page
1978 for="lp.registry.interfaces.distribution.IDistribution"1987 for="lp.registry.interfaces.distribution.IDistribution"
19791988
=== modified file 'lib/lp/registry/browser/distroseries.py'
--- lib/lp/registry/browser/distroseries.py 2011-03-30 11:36:37 +0000
+++ lib/lp/registry/browser/distroseries.py 2011-03-30 16:15:31 +0000
@@ -11,10 +11,11 @@
11 'DistroSeriesBreadcrumb',11 'DistroSeriesBreadcrumb',
12 'DistroSeriesEditView',12 'DistroSeriesEditView',
13 'DistroSeriesFacets',13 'DistroSeriesFacets',
14 'DistroSeriesInitializeView',
14 'DistroSeriesLocalDifferences',15 'DistroSeriesLocalDifferences',
16 'DistroSeriesNavigation',
15 'DistroSeriesPackageSearchView',17 'DistroSeriesPackageSearchView',
16 'DistroSeriesPackagesView',18 'DistroSeriesPackagesView',
17 'DistroSeriesNavigation',
18 'DistroSeriesView',19 'DistroSeriesView',
19 ]20 ]
2021
@@ -510,7 +511,7 @@
510511
511512
512class DistroSeriesAddView(LaunchpadFormView):513class DistroSeriesAddView(LaunchpadFormView):
513 """A view to creat an `IDistrobutionSeries`."""514 """A view to create an `IDistroSeries`."""
514 schema = IDistroSeries515 schema = IDistroSeries
515 field_names = [516 field_names = [
516 'name', 'displayname', 'title', 'summary', 'description', 'version',517 'name', 'displayname', 'title', 'summary', 'description', 'version',
@@ -543,6 +544,38 @@
543 return canonical_url(self.context)544 return canonical_url(self.context)
544545
545546
547class IDistroSeriesInitializeForm(Interface):
548
549 derived_from_series = Choice(
550 title=_('Derived from distribution series'),
551 default=None,
552 vocabulary="DistroSeriesDerivation",
553 description=_(
554 "Select the distribution series you "
555 "want to derive from."),
556 required=True)
557
558
559class DistroSeriesInitializeView(LaunchpadFormView):
560 """A view to initialize an `IDistroSeries`."""
561
562 schema = IDistroSeriesInitializeForm
563 field_names = [
564 "derived_from_series",
565 ]
566
567 custom_widget('derived_from_series', LaunchpadDropdownWidget)
568
569 label = 'Initialize series'
570 page_title = label
571
572 @property
573 def next_url(self):
574 return canonical_url(self.context)
575
576 cancel_url = next_url
577
578
546class DistroSeriesPackagesView(LaunchpadView):579class DistroSeriesPackagesView(LaunchpadView):
547 """A View to show series package to upstream package relationships."""580 """A View to show series package to upstream package relationships."""
548581
@@ -626,8 +659,7 @@
626 for its own vocabulary, we set it up after all the others.659 for its own vocabulary, we set it up after all the others.
627 """660 """
628 super(DistroSeriesLocalDifferences, self).setUpFields()661 super(DistroSeriesLocalDifferences, self).setUpFields()
629 has_edit = check_permission('launchpad.Edit', self.context)662 check_permission('launchpad.Edit', self.context)
630
631 terms = [663 terms = [
632 SimpleTerm(diff, diff.source_package_name.name,664 SimpleTerm(diff, diff.source_package_name.name,
633 diff.source_package_name.name)665 diff.source_package_name.name)
634666
=== added file 'lib/lp/registry/browser/tests/test_distroseries.py'
--- lib/lp/registry/browser/tests/test_distroseries.py 1970-01-01 00:00:00 +0000
+++ lib/lp/registry/browser/tests/test_distroseries.py 2011-03-30 16:15:31 +0000
@@ -0,0 +1,21 @@
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"""Tests for `lp.registry.browser.distroseries`."""
5
6__metaclass__ = type
7
8from canonical.testing.layers import DatabaseFunctionalLayer
9from lp.testing import TestCaseWithFactory
10from lp.testing.views import create_initialized_view
11
12
13class TestDistroSeriesInitializeView(TestCaseWithFactory):
14
15 layer = DatabaseFunctionalLayer
16
17 def test_init(self):
18 # There exists a +initseries view for distroseries.
19 distroseries = self.factory.makeDistroSeries()
20 view = create_initialized_view(distroseries, "+initseries")
21 self.assertTrue(view)
022
=== modified file 'lib/lp/registry/interfaces/distroseries.py'
--- lib/lp/registry/interfaces/distroseries.py 2011-03-16 00:23:20 +0000
+++ lib/lp/registry/interfaces/distroseries.py 2011-03-30 16:15:31 +0000
@@ -32,6 +32,7 @@
32 webservice_error,32 webservice_error,
33 )33 )
34from lazr.restful.fields import (34from lazr.restful.fields import (
35 CollectionField,
35 Reference,36 Reference,
36 ReferenceChoice,37 ReferenceChoice,
37 )38 )
@@ -390,7 +391,11 @@
390 """391 """
391392
392 # DistroArchSeries lookup properties/methods.393 # DistroArchSeries lookup properties/methods.
393 architectures = Attribute("All architectures in this series.")394 architectures = exported(
395 CollectionField(
396 title=_("All architectures in this series."),
397 value_type=Reference(schema=Interface), # IDistroArchSeries.
398 readonly=True))
394399
395 enabled_architectures = Attribute(400 enabled_architectures = Attribute(
396 "All architectures in this series with the 'enabled' flag set.")401 "All architectures in this series with the 'enabled' flag set.")
397402
=== added file 'lib/lp/registry/javascript/distroseries.js'
--- lib/lp/registry/javascript/distroseries.js 1970-01-01 00:00:00 +0000
+++ lib/lp/registry/javascript/distroseries.js 2011-03-30 16:15:31 +0000
@@ -0,0 +1,274 @@
1/**
2 * Copyright 2011 Canonical Ltd. This software is licensed under the
3 * GNU Affero General Public License version 3 (see the file LICENSE).
4 *
5 * DistroSeries related stuff.
6 *
7 * @module registry
8 * @submodule distroseries
9 */
10
11YUI.add('lp.registry.distroseries.initseries', function(Y) {
12
13Y.log('loading lp.registry.distroseries.initseries');
14
15var namespace = Y.namespace('lp.registry.distroseries.initseries');
16
17
18/**
19 * A form row matching that which LaunchpadForm presents, containing a
20 * list of checkboxes, and an optional label and description.
21 *
22 * @class CheckBoxListWidget
23 */
24var CheckBoxListWidget = function() {
25 CheckBoxListWidget.superclass.constructor.apply(this, arguments);
26};
27
28Y.mix(CheckBoxListWidget, {
29
30 NAME: 'checkBoxListWidget',
31
32 ATTRS: {
33
34 /**
35 * The field name.
36 *
37 * @property name
38 */
39 name: {
40 setter: function(value, name) {
41 this.fieldNode.all("input").set("name", value);
42 }
43 },
44
45 /**
46 * An array of strings from which to choose.
47 *
48 * @property choices
49 */
50 choices: {
51 getter: function() {
52 return this.fieldNode.all("li > input").get("value");
53 },
54 setter: function(value, name) {
55 var choices = Y.Array.unique(value).sort();
56 var field_name = this.get("name");
57 var list = Y.Node.create("<ul />");
58 choices.forEach(
59 function(choice) {
60 var item = Y.Node.create(
61 "<li><input /> <label /></li>");
62 item.one("input")
63 .set("type", "checkbox")
64 .set("name", field_name)
65 .set("value", choice);
66 item.one("label")
67 .setAttribute(
68 "for", item.one("input").generateID())
69 .setStyle("font-weight", "normal")
70 .set("text", choice);
71 list.append(item);
72 }
73 );
74 this.fieldNode.empty().append(list);
75 }
76 },
77
78 /**
79 * The top label for the field.
80 *
81 * @property label
82 */
83 label: {
84 getter: function() {
85 return this.labelNode.get("text");
86 },
87 setter: function(value, name) {
88 this.labelNode.set("text", value);
89 }
90 },
91
92 /**
93 * A description shown near the field.
94 *
95 * @label description
96 */
97 description: {
98 getter: function() {
99 return this.descriptionNode.get("text");
100 },
101 setter: function(value, name) {
102 this.descriptionNode.set("text", value);
103 }
104 }
105 }
106
107});
108
109Y.extend(CheckBoxListWidget, Y.Widget, {
110
111 BOUNDING_TEMPLATE: "<tr></tr>",
112
113 CONTENT_TEMPLATE: '<td colspan="2"></td>',
114
115 initializer: function(config) {
116 this.labelNode = Y.Node.create("<label />");
117 this.fieldNode = Y.Node.create("<div></div>");
118 this.descriptionNode = Y.Node.create('<p class="formHelp" />');
119 },
120
121 renderUI: function() {
122 this.get("contentBox")
123 .append(this.labelNode)
124 .append(this.fieldNode)
125 .append(this.descriptionNode);
126 }
127
128});
129
130namespace.CheckBoxListWidget = CheckBoxListWidget;
131
132
133/**
134 * A special form of CheckBoxListWidget for choosing architecture tags.
135 *
136 * @class ArchitecturesCheckBoxListWidget
137 */
138var ArchitecturesCheckBoxListWidget = function() {
139 ArchitecturesCheckBoxListWidget
140 .superclass.constructor.apply(this, arguments);
141};
142
143Y.mix(ArchitecturesCheckBoxListWidget, {
144
145 NAME: 'architecturesCheckBoxListWidget',
146
147 ATTRS: {
148
149 /**
150 * The DistroSeries the choices in this field should
151 * reflect. Takes the form of a string, e.g. "ubuntu/hoary".
152 *
153 * @property distroSeries
154 */
155 distroSeries: {
156 setter: function(value, name) {
157 var path = value + "/architectures";
158 var on = {
159 start: Y.bind(this.showSpinner, this),
160 success: Y.bind(this.set, this, "distroArchSerieses"),
161 failure: this.error_handler.getFailureHandler(),
162 end: Y.bind(this.hideSpinner, this)
163 };
164 this.client.get(path, {on: on});
165 }
166 },
167
168 /**
169 * The DistroArchSerieses the choices in this field should
170 * reflect. Takes the form of a Y.lp.client.Collection.
171 *
172 * @property distroArchSerieses
173 */
174 distroArchSerieses: {
175 setter: function(value, name) {
176 this.set(
177 "choices", value.entries.map(
178 function(das) {
179 return das.get("architecture_tag");
180 }
181 )
182 );
183 if (value.entries.length == 0) {
184 this.fieldNode.append(
185 Y.Node.create('<p />').set(
186 "text", "The chosen series has no architectures!"));
187 }
188 Y.lazr.anim.green_flash({node: this.fieldNode}).run();
189 }
190 }
191 }
192
193});
194
195Y.extend(ArchitecturesCheckBoxListWidget, CheckBoxListWidget, {
196
197 initializer: function(config) {
198 this.client = new Y.lp.client.Launchpad();
199 this.error_handler = new Y.lp.client.ErrorHandler();
200 this.error_handler.clearProgressUI = Y.bind(this.hideSpinner, this);
201 this.error_handler.showError = Y.bind(this.showError, this);
202 this.spinner = Y.Node.create(
203 '<img src="/@@/spinner" alt="Loading..." />');
204 },
205
206 /**
207 * Show the spinner.
208 *
209 * @method showSpinner
210 */
211 showSpinner: function() {
212 this.fieldNode.empty().append(this.spinner);
213 },
214
215 /**
216 * Hide the spinner.
217 *
218 * @method hideSpinner
219 */
220 hideSpinner: function() {
221 this.spinner.remove();
222 },
223
224 /**
225 * Display an error.
226 *
227 * @method showError
228 */
229 showError: function(error) {
230 var message = Y.Node.create('<p />').set("text", error);
231 this.fieldNode.empty().append(message);
232 Y.lazr.anim.red_flash({node: message}).run();
233 }
234
235});
236
237namespace.ArchitecturesCheckBoxListWidget = ArchitecturesCheckBoxListWidget;
238
239
240/**
241 * Setup the widgets on the +initseries page.
242 *
243 * @function setup
244 */
245namespace.setup = function() {
246 var form_container = Y.one("#init-series-form-container");
247 var form_table_body = form_container.one("table.form > tbody");
248 var architecture_choice = new ArchitecturesCheckBoxListWidget()
249 .set("name", "field.architectures")
250 .set("label", "Architectures")
251 .set("description", (
252 "Choose the architectures you want to " +
253 "use from the parent series."))
254 .render(form_table_body);
255
256 // Wire up the distroseries select to the architectures widget.
257 var field_derived_from_series =
258 form_table_body.one("[name=field.derived_from_series]");
259 function update_architecture_choice() {
260 architecture_choice
261 .set("distroSeries", field_derived_from_series.get("value"));
262 }
263 field_derived_from_series.on("change", update_architecture_choice);
264
265 // Update the architectures widget for the selected distroseries.
266 update_architecture_choice();
267
268 // Show the form.
269 form_container.removeClass("unseen");
270};
271
272
273}, "0.1", {"requires": ["node", "dom", "io", "widget", "lp.client",
274 "lazr.anim", "array-extras"]});
0275
=== added file 'lib/lp/registry/javascript/tests/test_distroseries.html'
--- lib/lp/registry/javascript/tests/test_distroseries.html 1970-01-01 00:00:00 +0000
+++ lib/lp/registry/javascript/tests/test_distroseries.html 2011-03-30 16:15:31 +0000
@@ -0,0 +1,30 @@
1<!DOCTYPE
2 HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
3 "http://www.w3.org/TR/html4/strict.dtd">
4<html>
5 <head>
6 <title>Launchpad DistroSeries</title>
7 <!-- YUI 3.0 Setup -->
8 <script type="text/javascript"
9 src="../../../../canonical/launchpad/icing/yui/yui/yui.js"></script>
10 <script type="text/javascript"
11 src="../../../../canonical/launchpad/icing/lazr/build/lazr.js"></script>
12 <link rel="stylesheet"
13 href="../../../../canonical/launchpad/icing/yui/cssreset/reset.css"/>
14 <link rel="stylesheet"
15 href="../../../../canonical/launchpad/icing/yui/cssfonts/fonts.css"/>
16 <link rel="stylesheet"
17 href="../../../../canonical/launchpad/icing/yui/cssbase/base.css"/>
18 <link rel="stylesheet"
19 href="../../../../canonical/launchpad/javascript/test.css" />
20 <!-- Required modules -->
21 <script type="text/javascript" src="../../../app/javascript/client.js"></script>
22 <!-- The module under test -->
23 <script type="text/javascript" src="../distroseries.js"></script>
24 <!-- The test suite -->
25 <script type="text/javascript" src="test_distroseries.js"></script>
26 </head>
27 <body class="yui3-skin-sam">
28 <div id="log"></div>
29 </body>
30</html>
031
=== added file 'lib/lp/registry/javascript/tests/test_distroseries.js'
--- lib/lp/registry/javascript/tests/test_distroseries.js 1970-01-01 00:00:00 +0000
+++ lib/lp/registry/javascript/tests/test_distroseries.js 2011-03-30 16:15:31 +0000
@@ -0,0 +1,225 @@
1/* Copyright (c) 2011, Canonical Ltd. All rights reserved. */
2
3YUI({
4 base: '../../../../canonical/launchpad/icing/yui/',
5 filter: 'raw', combine: false, fetchCSS: false
6 }).use(
7 'test', 'console', 'lp.registry.distroseries.initseries',
8 function(Y) {
9
10 var Assert = Y.Assert;
11 var ArrayAssert = Y.ArrayAssert;
12
13 var suite = new Y.Test.Suite("distroseries.initseries Tests");
14 var initseries = Y.lp.registry.distroseries.initseries;
15 var console = new Y.Console({newestOnTop: false});
16 console.render('#log');
17
18 var testCheckBoxListWidget = {
19 name: 'TestCheckBoxListWidget',
20
21 setUp: function() {
22 this.container = Y.Node.create("<div />");
23 this.widget = new initseries.CheckBoxListWidget();
24 },
25
26 tearDown: function() {
27 this.container.remove();
28 },
29
30 testRender: function() {
31 this.widget.render(this.container);
32 Assert.isTrue(
33 this.container.contains(
34 this.widget.get("boundingBox")));
35 },
36
37 testRenderChoices: function() {
38 this.widget.set("choices", ["a", "b"]);
39 this.widget.render(this.container);
40 ArrayAssert.itemsAreEqual(
41 ["a", "b"],
42 this.container.all("li > input").get("value"));
43 ArrayAssert.itemsAreEqual(
44 ["a", "b"],
45 this.container.all("li > label").get("text"));
46 },
47
48 testRenderChoicesChange: function() {
49 this.widget.set("choices", ["a", "b"]);
50 this.widget.render(this.container);
51 this.widget.set("choices", ["c", "d", "e"]);
52 ArrayAssert.itemsAreEqual(
53 ["c", "d", "e"],
54 this.container.all("li > input").get("value"));
55 ArrayAssert.itemsAreEqual(
56 ["c", "d", "e"],
57 this.container.all("li > label").get("text"));
58 },
59
60 testRenderWithName: function() {
61 this.widget.set("name", "field");
62 this.widget.set("choices", ["a", "b"]);
63 this.widget.render(this.container);
64 ArrayAssert.itemsAreEqual(
65 ["field", "field"],
66 this.container.all("li > input").get("name"));
67 },
68
69 testRenderWithNameChange: function() {
70 this.widget.set("name", "field");
71 this.widget.set("choices", ["a", "b"]);
72 this.widget.render(this.container);
73 this.widget.set("name", "plain");
74 ArrayAssert.itemsAreEqual(
75 ["plain", "plain"],
76 this.container.all("li > input").get("name"));
77 },
78
79 testRenderLabel: function() {
80 this.widget.set("label", "Test label");
81 this.widget.render(this.container);
82 Assert.areEqual(
83 "Test label",
84 this.container.one("label").get("text"));
85 },
86
87 testRenderLabelChange: function() {
88 this.widget.set("label", "Test label");
89 this.widget.render(this.container);
90 this.widget.set("label", "Another label");
91 Assert.areEqual(
92 "Another label",
93 this.container.one("label").get("text"));
94 },
95
96 testRenderDescription: function() {
97 this.widget.set("description", "Test description.");
98 this.widget.render(this.container);
99 Assert.areEqual(
100 "Test description.",
101 this.container.one("p.formHelp").get("text"));
102 },
103
104 testRenderDescriptionChange: function() {
105 this.widget.set("description", "Test description.");
106 this.widget.render(this.container);
107 this.widget.set("description", "Another description.");
108 Assert.areEqual(
109 "Another description.",
110 this.container.one("p.formHelp").get("text"));
111 }
112
113 };
114
115 suite.add(new Y.Test.Case(testCheckBoxListWidget));
116
117 var testArchitecturesCheckBoxListWidget = {
118 name: 'TestArchitecturesCheckBoxListWidget',
119
120 setUp: function() {
121 this.container = Y.Node.create("<div />");
122 this.widget = new initseries.ArchitecturesCheckBoxListWidget();
123 },
124
125 tearDown: function() {
126 this.container.remove();
127 },
128
129 testSetDistroArchSeriesesUpdatesChoices: function() {
130 var distro_arch_serieses = [
131 {architecture_tag: "i386"},
132 {architecture_tag: "amd64"},
133 {architecture_tag: "i386"}
134 ];
135 var distro_arch_serieses_collection =
136 new Y.lp.client.Collection(
137 null, {entries: distro_arch_serieses}, null);
138 this.widget.set(
139 "distroArchSerieses",
140 distro_arch_serieses_collection);
141 ArrayAssert.itemsAreEqual(
142 ["amd64", "i386"],
143 this.widget.get("choices"));
144 },
145
146 testSetDistroSeriesInitiatesIO: function() {
147 var io = false;
148 this.widget.client = {
149 get: function(path, config) {
150 io = true;
151 Assert.areEqual("ubuntu/hoary/architectures", path);
152 Assert.isObject(config.on);
153 Assert.isFunction(config.on.success);
154 Assert.isFunction(config.on.failure);
155 }
156 };
157 this.widget.set("distroSeries", "ubuntu/hoary");
158 Assert.isTrue(io, "No IO initiated.");
159 },
160
161 testSetDistroSeriesUpdatesDistroArchSeries: function() {
162 var distro_arch_serieses = [
163 {architecture_tag: "i386"},
164 {architecture_tag: "amd64"},
165 {architecture_tag: "i386"}
166 ];
167 var distro_arch_serieses_collection =
168 new Y.lp.client.Collection(
169 null, {entries: distro_arch_serieses}, null);
170 this.widget.client = {
171 get: function(path, config) {
172 config.on.success(distro_arch_serieses_collection);
173 }
174 };
175 this.widget.set("distroSeries", "ubuntu/hoary");
176 ArrayAssert.itemsAreEqual(
177 ["amd64", "i386"],
178 this.widget.get("choices"));
179 },
180
181 testSetDistroSeriesSpinner: function() {
182 var widget = this.widget;
183 widget.client = {
184 get: function(path, config) {
185 Assert.isFalse(
186 widget.fieldNode.contains(widget.spinner));
187 config.on.start();
188 Assert.isTrue(
189 widget.fieldNode.contains(widget.spinner));
190 config.on.end();
191 Assert.isFalse(
192 widget.fieldNode.contains(widget.spinner));
193 }
194 };
195 this.widget.set("distroSeries", "ubuntu/hoary");
196 },
197
198 testSetDistroSeriesError: function() {
199 var widget = this.widget;
200 widget.client = {
201 get: function(path, config) {
202 config.on.failure(
203 null, {status: 404,
204 responseText: "Not found"});
205 Assert.areEqual(
206 "Not found",
207 widget.fieldNode.one("p").get("text"));
208 }
209 };
210 this.widget.set("distroSeries", "ubuntu/hoary");
211 }
212
213 };
214
215 testArchitecturesCheckBoxListWidget = Y.merge(
216 testCheckBoxListWidget, testArchitecturesCheckBoxListWidget);
217
218 suite.add(new Y.Test.Case(testArchitecturesCheckBoxListWidget));
219
220 Y.Test.Runner.add(suite);
221
222 Y.on('domready', function() {
223 Y.Test.Runner.run();
224 });
225});
0226
=== modified file 'lib/lp/registry/stories/webservice/xx-distroseries.txt'
--- lib/lp/registry/stories/webservice/xx-distroseries.txt 2011-01-26 19:35:17 +0000
+++ lib/lp/registry/stories/webservice/xx-distroseries.txt 2011-03-30 16:15:31 +0000
@@ -61,6 +61,7 @@
61 active_milestones_collection_link:61 active_milestones_collection_link:
62 u'http://.../ubuntu/hoary/active_milestones'62 u'http://.../ubuntu/hoary/active_milestones'
63 all_milestones_collection_link: u'http://.../ubuntu/hoary/all_milestones'63 all_milestones_collection_link: u'http://.../ubuntu/hoary/all_milestones'
64 architectures_collection_link: u'http://.../ubuntu/hoary/architectures'
64 bug_reported_acknowledgement: None65 bug_reported_acknowledgement: None
65 bug_reporting_guidelines: None66 bug_reporting_guidelines: None
66 changeslist: u'hoary-changes@ubuntu.com'67 changeslist: u'hoary-changes@ubuntu.com'
@@ -86,6 +87,7 @@
86 version: u'5.04'87 version: u'5.04'
87 web_link: u'http://launchpad.../ubuntu/hoary'88 web_link: u'http://launchpad.../ubuntu/hoary'
8889
90
89Creating a milestone on the distroseries91Creating a milestone on the distroseries
90----------------------------------------92----------------------------------------
9193
9294
=== added file 'lib/lp/registry/templates/distroseries-initialize.pt'
--- lib/lp/registry/templates/distroseries-initialize.pt 1970-01-01 00:00:00 +0000
+++ lib/lp/registry/templates/distroseries-initialize.pt 2011-03-30 16:15:31 +0000
@@ -0,0 +1,34 @@
1<html
2 xmlns="http://www.w3.org/1999/xhtml"
3 xmlns:tal="http://xml.zope.org/namespaces/tal"
4 xmlns:metal="http://xml.zope.org/namespaces/metal"
5 xmlns:i18n="http://xml.zope.org/namespaces/i18n"
6 metal:use-macro="view/macro:page/main_only"
7 i18n:domain="launchpad">
8 <metal:head-epilogue fill-slot="head_epilogue">
9 <style type="text/css">
10 .yui3-js-enabled .javascript-disabled { display: none; }
11 </style>
12 </metal:head-epilogue>
13 <body>
14 <div metal:fill-slot="main">
15 <div class="top-portlet">
16 This page allows you to initialize a distribution series.
17 </div>
18 <p class="error message javascript-disabled">
19 Javascript is required to use this page. Please enable
20 Javascript in your browser and reload this page. Alternatively,
21 please use the <code>deriveDistroSeries</code> API call via the
22 web service.
23 </p>
24 <div class="unseen" id="init-series-form-container">
25 <metal:form use-macro="context/@@launchpad_form/form" />
26 </div>
27 <script type="text/javascript">
28 LPS.use('lp.registry.distroseries.initseries', function(Y) {
29 Y.on('domready', Y.lp.registry.distroseries.initseries.setup);
30 });
31 </script>
32 </div>
33 </body>
34</html>