Merge lp:~allenap/launchpad/localpackagediffs-filter-by-package-set-bug-809786-split-formwidgets into lp:launchpad

Proposed by Gavin Panella
Status: Merged
Approved by: Gavin Panella
Approved revision: no longer in the source branch.
Merged at revision: 13463
Proposed branch: lp:~allenap/launchpad/localpackagediffs-filter-by-package-set-bug-809786-split-formwidgets
Merge into: lp:launchpad
Prerequisite: lp:~allenap/launchpad/localpackagediffs-filter-by-package-set-bug-809786-split
Diff against target: 2878 lines (+1371/-1241)
9 files modified
lib/lp/app/javascript/formwidgets/formwidgets.js (+626/-0)
lib/lp/app/javascript/formwidgets/tests/test_formwidgets.html (+47/-0)
lib/lp/app/javascript/formwidgets/tests/test_formwidgets.js (+605/-0)
lib/lp/registry/javascript/distroseries/initseries.js (+7/-6)
lib/lp/registry/javascript/distroseries/tests/test_initseries.html (+28/-26)
lib/lp/registry/javascript/distroseries/tests/test_initseries.js (+4/-4)
lib/lp/registry/javascript/distroseries/tests/test_widgets.html (+29/-23)
lib/lp/registry/javascript/distroseries/tests/test_widgets.js (+13/-569)
lib/lp/registry/javascript/distroseries/widgets.js (+12/-613)
To merge this branch: bzr merge lp:~allenap/launchpad/localpackagediffs-filter-by-package-set-bug-809786-split-formwidgets
Reviewer Review Type Date Requested Status
Gavin Panella (community) Approve
Review via email: mp+68237@code.launchpad.net

Commit message

[r=allenap][bug=809786] Split out the non-DistroSeries-specific widgets from lp.registry.distroseries.widgets into a new lp.all.formwidgets module.

Description of the change

This splits out the non-DistroSeries-specific widgets from the lp.registry.distroseries.widgets module into a new lp.all.formwidgets module. It also rearranges the on-disk layout of the .initseries and .widgets modules in Registry.

To post a comment you must log in.
Revision history for this message
Gavin Panella (allenap) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added directory 'lib/lp/app/javascript/formwidgets'
2=== added file 'lib/lp/app/javascript/formwidgets/formwidgets.js'
3--- lib/lp/app/javascript/formwidgets/formwidgets.js 1970-01-01 00:00:00 +0000
4+++ lib/lp/app/javascript/formwidgets/formwidgets.js 2011-07-18 11:56:36 +0000
5@@ -0,0 +1,626 @@
6+/**
7+ * Copyright 2011 Canonical Ltd. This software is licensed under the
8+ * GNU Affero General Public License version 3 (see the file LICENSE).
9+ *
10+ * Form Widgets.
11+ *
12+ * @module lp.app
13+ * @submodule formwidgets
14+ */
15+
16+YUI.add('lp.app.formwidgets', function(Y) {
17+
18+Y.log('loading lp.app.formwidgets');
19+
20+var namespace = Y.namespace('lp.app.formwidgets');
21+
22+
23+/**
24+ * A form row matching that which LaunchpadForm presents, containing a
25+ * field (defined in a subclass), and an optional label and
26+ * description.
27+ *
28+ * @class FormRowWidget
29+ */
30+var FormRowWidget;
31+
32+FormRowWidget = function() {
33+ FormRowWidget.superclass.constructor.apply(this, arguments);
34+};
35+
36+Y.mix(FormRowWidget, {
37+
38+ NAME: 'formRowWidget',
39+
40+ ATTRS: {
41+
42+ /**
43+ * The field name.
44+ *
45+ * @property name
46+ */
47+ name: {
48+ setter: function(value, name) {
49+ this.fieldNode.all("input, select").set("name", value);
50+ }
51+ },
52+
53+ /**
54+ * The top label for the field.
55+ *
56+ * @property label
57+ */
58+ label: {
59+ getter: function() {
60+ return this.labelNode.get("text");
61+ },
62+ setter: function(value, name) {
63+ this.labelNode.set("text", value);
64+ }
65+ },
66+
67+ /**
68+ * A dictionary {link:link, text:text} to populate
69+ * the pop-up help for the field.
70+ *
71+ * @property help
72+ */
73+ help: {
74+ getter: function() {
75+ return {link:this.helpNode.one('a')
76+ .get("href"),
77+ text:this.helpNode
78+ .one('.invisible-link')
79+ .get("text")};
80+ },
81+ setter: function(value, name) {
82+ if ((value.link !== undefined) &&
83+ (value.text !== undefined)) {
84+ this.helpNode.one('a').set("href", value.link);
85+ this.helpNode.one('.invisible-link')
86+ .set("text", value.text);
87+ this.helpNode.removeClass('unseen');
88+ }
89+ else {
90+ this.helpNode.addClass('unseen');
91+ }
92+ }
93+ },
94+
95+ /**
96+ * A description shown near the field.
97+ *
98+ * @label description
99+ */
100+ description: {
101+ getter: function() {
102+ return this.descriptionNode.get("text");
103+ },
104+ setter: function(value, name) {
105+ this.descriptionNode.set("text", value);
106+ }
107+ }
108+ }
109+
110+});
111+
112+Y.extend(FormRowWidget, Y.Widget, {
113+
114+ BOUNDING_TEMPLATE: "<tr></tr>",
115+
116+ CONTENT_TEMPLATE: '<td colspan="2"></td>',
117+
118+ initializer: function(config) {
119+ this.labelNode = Y.Node.create("<label />");
120+ this.helpNode = Y.Node.create(('<span class="helper unseen">'+
121+ '&nbsp;<a href=""' +
122+ 'target="help" class="sprite maybe">&nbsp;' +
123+ '<span class="invisible-link"></span></a></span>'));
124+ this.fieldNode = Y.Node.create("<div></div>");
125+ this.descriptionNode = Y.Node.create('<p class="formHelp" />');
126+ this.spinnerNode = Y.Node.create(
127+ '<img src="/@@/spinner" alt="Loading..." />');
128+ },
129+
130+ renderUI: function() {
131+ this.get("contentBox")
132+ .append(this.labelNode)
133+ .append(this.helpNode)
134+ .append(this.fieldNode)
135+ .append(this.descriptionNode);
136+ },
137+
138+ /**
139+ * Show the spinner.
140+ *
141+ * @method showSpinner
142+ */
143+ showSpinner: function() {
144+ this.fieldNode.empty().append(this.spinnerNode);
145+ },
146+
147+ /**
148+ * Hide the spinner.
149+ *
150+ * @method hideSpinner
151+ */
152+ hideSpinner: function() {
153+ this.spinnerNode.remove();
154+ },
155+
156+ /**
157+ * Display an error.
158+ *
159+ * @method showError
160+ */
161+ showError: function(error) {
162+ var message = Y.Node.create('<p />').set("text", error);
163+ this.fieldNode.empty().append(message);
164+ Y.lazr.anim.red_flash({node: message}).run();
165+ }
166+
167+});
168+
169+namespace.FormRowWidget = FormRowWidget;
170+
171+
172+/**
173+ * A form row matching that which LaunchpadForm presents, containing a
174+ * list of checkboxes, and an optional label and description.
175+ *
176+ * @class ChoiceListWidget
177+ */
178+var ChoiceListWidget;
179+
180+ChoiceListWidget = function() {
181+ ChoiceListWidget.superclass.constructor.apply(this, arguments);
182+};
183+
184+Y.mix(ChoiceListWidget, {
185+
186+ NAME: 'choiceListWidget',
187+
188+ ATTRS: {
189+
190+ /**
191+ * An array of strings from which to choose.
192+ *
193+ * @property choices
194+ */
195+ choices: {
196+ getter: function() {
197+ return this.fieldNode.all("li > input").get("value");
198+ },
199+ setter: function(value, name) {
200+ var choices = Y.Array.unique(value).sort();
201+ var list = Y.Node.create("<ul />");
202+ var self = this;
203+ choices.forEach(
204+ function(choice) {
205+ var item = self._createChoice(choice);
206+ list.append(item);
207+ }
208+ );
209+ this.fieldNode.empty().append(list);
210+ }
211+ },
212+
213+ /**
214+ * The current selection.
215+ *
216+ * @property choice
217+ */
218+ choice: {
219+ setter: function(value, name) {
220+ if (!Y.Lang.isArray(value)) {
221+ value = [value];
222+ }
223+ this.fieldNode.all("li > input").each(
224+ function(node) {
225+ node.set(
226+ "checked",
227+ value.indexOf(node.get("value")) >= 0);
228+ }
229+ );
230+ },
231+ getter: function() {
232+ var choice = [];
233+ this.fieldNode.all("li > input").each(
234+ function(node) {
235+ if (node.get("checked")) {
236+ choice.push(node.get("value"));
237+ }
238+ }
239+ );
240+ if (this.get("type") === "radio") {
241+ if (choice.length === 0) {
242+ choice = null;
243+ }
244+ else if (choice.length === 1) {
245+ choice = choice[0];
246+ }
247+ else {
248+ choice = undefined;
249+ }
250+ }
251+ return choice;
252+ }
253+ },
254+
255+ /**
256+ * The input type to display. Choose from "checkbox" or "radio".
257+ *
258+ * @property type
259+ */
260+ type: {
261+ value: "checkbox",
262+ setter: function(value, name) {
263+ this.fieldNode.all("li > input").set("type", value);
264+ }
265+ }
266+
267+ }
268+
269+});
270+
271+Y.extend(ChoiceListWidget, FormRowWidget, {
272+
273+ /**
274+ * Helper method to create an entry for the select widget.
275+ *
276+ * @method _createChoice
277+ */
278+ _createChoice: function(choice) {
279+ var field_name = this.get("name");
280+ var field_type = this.get("type");
281+ var item = Y.Node.create(
282+ "<li><input /> <label /></li>");
283+ item.one("input")
284+ .set("type", field_type)
285+ .set("name", field_name)
286+ .set("value", choice);
287+ item.one("label")
288+ .setAttribute(
289+ "for", item.one("input").generateID())
290+ .setStyle("font-weight", "normal")
291+ .set("text", choice);
292+ return item;
293+ },
294+
295+ /**
296+ * Remove a list of choices from the possible widget's choices.
297+ *
298+ * @method remove_choices
299+ */
300+ remove_choices: function(choices) {
301+ choices.forEach(
302+ function(choice) {
303+ this.fieldNode.all("select > option").each(
304+ function(option) { options.push(option); });
305+ this.fieldNode.all(
306+ "li input[value=" + choice + "]").each(
307+ function(li_input) {
308+ li_input.get('parentNode').remove();
309+ }
310+ );
311+ },
312+ this
313+ );
314+ Y.lazr.anim.green_flash({node: this.fieldNode}).run();
315+ },
316+
317+ _sorted_position: function(choice) {
318+ var options = [];
319+ this.fieldNode.all("input").each(
320+ function(node) {
321+ options.push(node.get('value'));
322+ }
323+ );
324+ options.push(choice);
325+ return options.sort().indexOf(choice);
326+ },
327+
328+ /**
329+ * Add new choices (if they are not already present).
330+ *
331+ * @method add_choices
332+ */
333+ add_choices: function(new_choices) {
334+ new_choices.forEach(
335+ function(choice) {
336+ if (this.fieldNode.all(
337+ "li > input[value=" + choice + "]").isEmpty()) {
338+ var list = this.fieldNode.one('ul');
339+ if (list === null) {
340+ list = Y.Node.create("<ul />");
341+ this.fieldNode.empty().append(list);
342+ }
343+ var option = this._createChoice(choice);
344+ var options = list.all('input');
345+ if (options.isEmpty()) {
346+ list.append(option);
347+ }
348+ else {
349+ var pos = this._sorted_position(choice);
350+ if (pos === 0) {
351+ list.prepend(option);
352+ }
353+ else {
354+ list.insertBefore(option, options.item(pos));
355+ }
356+ }
357+ }
358+ }, this
359+ );
360+ Y.lazr.anim.green_flash({node: this.fieldNode}).run();
361+ }
362+
363+});
364+
365+
366+namespace.ChoiceListWidget = ChoiceListWidget;
367+
368+
369+/**
370+ * A special form of FormRowWidget, containing a select control.
371+ *
372+ * @class SelectWidget
373+ */
374+var SelectWidget;
375+
376+SelectWidget = function() {
377+ SelectWidget.superclass.constructor.apply(this, arguments);
378+};
379+
380+Y.mix(SelectWidget, {
381+
382+ NAME: 'selectWidget',
383+
384+ ATTRS: {
385+
386+ /**
387+ * An array of objects from which to choose. Each object
388+ * should contain a value for "value", "text" and "data".
389+ *
390+ * @property choices
391+ */
392+ choices: {
393+ getter: function() {
394+ /* I think this is a YUI3 wart; I can't see any way to
395+ map() over a NodeList, so I must push the elements
396+ one by one into an array first. */
397+ var options = Y.Array([]);
398+ this.fieldNode.all("select > option").each(
399+ function(option) { options.push(option); });
400+ return options.map(
401+ function(option) {
402+ return {
403+ value: option.get("value"),
404+ text: option.get("text"),
405+ data: option.getData("data")
406+ };
407+ }
408+ );
409+ },
410+ setter: function(value, name) {
411+ var select = Y.Node.create("<select />");
412+ select.set("name", this.get("name"))
413+ .set("size", this.get("size"));
414+ if (this.get("multiple")) {
415+ select.set("multiple", "multiple");
416+ }
417+ var choices = Y.Array(value);
418+ choices.forEach(
419+ function(choice) {
420+ var option = Y.Node.create("<option />");
421+ option.set("value", choice.value)
422+ .set("text", choice.text)
423+ .setData("data", choice.data);
424+ select.append(option);
425+ }
426+ );
427+ if (choices.length > 0) {
428+ this.fieldNode.empty().append(select);
429+ }
430+ else {
431+ this.fieldNode.empty();
432+ }
433+ }
434+ },
435+
436+ /**
437+ * The current selection.
438+ *
439+ * @property choice
440+ */
441+ choice: {
442+ setter: function(value, name) {
443+ if (!Y.Lang.isArray(value)) {
444+ value = [value];
445+ }
446+ this.fieldNode.all("select > option").each(
447+ function(node) {
448+ node.set(
449+ "selected",
450+ value.indexOf(node.get("value")) >= 0);
451+ }
452+ );
453+ },
454+ getter: function() {
455+ var choice = [];
456+ this.fieldNode.all("select > option").each(
457+ function(node) {
458+ if (node.get("selected")) {
459+ choice.push(node.get("value"));
460+ }
461+ }
462+ );
463+ return choice;
464+ }
465+ },
466+
467+ /**
468+ * The number of rows to show in the select widget.
469+ *
470+ * @property size
471+ */
472+ size: {
473+ value: 1,
474+ setter: function(value, name) {
475+ this.fieldNode.all("select").set("size", value);
476+ }
477+ },
478+
479+ /**
480+ * Whether multiple rows can be selected.
481+ *
482+ * @property multiple
483+ */
484+ multiple: {
485+ value: false,
486+ setter: function(value, name) {
487+ value = value ? true : false;
488+ this.fieldNode.all("select").set("multiple", value);
489+ return value;
490+ }
491+ }
492+
493+ }
494+
495+});
496+
497+Y.extend(SelectWidget, FormRowWidget, {
498+
499+ _sorted_position: function(choice) {
500+ var options = [];
501+ this.fieldNode.all("option").each(
502+ function(node) {
503+ options.push(node.get('text'));
504+ }
505+ );
506+ options.push(choice);
507+ return options.sort().indexOf(choice);
508+ },
509+
510+ /**
511+ * Choose a size for the select control based on the number of
512+ * choices, up to an optional maximum size.
513+ *
514+ * @method autoSize
515+ */
516+ autoSize: function(maxSize) {
517+ var choiceCount = this.fieldNode.all("select > option").size();
518+ if (choiceCount === 0) {
519+ this.set("size", 1);
520+ }
521+ else if (maxSize === undefined) {
522+ this.set("size", choiceCount);
523+ }
524+ else if (choiceCount < maxSize) {
525+ this.set("size", choiceCount);
526+ }
527+ else {
528+ this.set("size", maxSize);
529+ }
530+ return this;
531+ }
532+
533+});
534+
535+namespace.SelectWidget = SelectWidget;
536+
537+
538+/**
539+ * A widget to encapsulate functionality around the form actions.
540+ *
541+ * @class FormActionsWidget
542+ */
543+var FormActionsWidget;
544+
545+FormActionsWidget = function() {
546+ FormActionsWidget
547+ .superclass.constructor.apply(this, arguments);
548+};
549+
550+FormActionsWidget.ATTRS = {
551+ duration: {
552+ value: 1.0
553+ },
554+
555+ height: {
556+ value: 0
557+ },
558+
559+ opacity: {
560+ value: 0
561+ }
562+};
563+
564+
565+Y.mix(FormActionsWidget, {
566+
567+ NAME: 'formActionsWidget',
568+
569+ HTML_PARSER: {
570+ submitButtonNode: "input[type=submit]"
571+ }
572+
573+});
574+
575+Y.extend(FormActionsWidget, Y.Widget, {
576+
577+ initializer: function(config) {
578+ this.client = new Y.lp.client.Launchpad();
579+ this.error_handler = new Y.lp.client.ErrorHandler();
580+ this.error_handler.clearProgressUI = Y.bind(this.hideSpinner, this);
581+ this.error_handler.showError = Y.bind(this.showError, this);
582+ this.submitButtonNode = config.submitButtonNode;
583+ this.spinnerNode = Y.Node.create(
584+ '<img src="/@@/spinner" alt="Loading..." />');
585+ },
586+
587+ /**
588+ * Show the spinner, and hide the submit button.
589+ *
590+ * @method showSpinner
591+ */
592+ showSpinner: function() {
593+ this.submitButtonNode.replace(this.spinnerNode);
594+ },
595+
596+ /**
597+ * Hide the spinner, and show the submit button again.
598+ *
599+ * @method hideSpinner
600+ */
601+ hideSpinner: function() {
602+ this.spinnerNode.replace(this.submitButtonNode);
603+ },
604+
605+ /**
606+ * Display an error.
607+ *
608+ * @method showError
609+ */
610+ showError: function(error) {
611+ Y.Node.create('<p class="error message" />')
612+ .appendTo(this.get("contentBox"))
613+ .set("text", error);
614+ },
615+
616+ /**
617+ * Remove all errors that have been previously displayed by showError.
618+ *
619+ * @method hideErrors
620+ */
621+ hideErrors: function(error) {
622+ this.get("contentBox").all("p.error.message").remove();
623+ }
624+
625+});
626+
627+namespace.FormActionsWidget = FormActionsWidget;
628+
629+
630+}, "0.1", {"requires": ["node", "dom", "io", "widget", "lp.client",
631+ "lazr.anim", "array-extras", "transition"]});
632
633=== added directory 'lib/lp/app/javascript/formwidgets/tests'
634=== added file 'lib/lp/app/javascript/formwidgets/tests/test_formwidgets.html'
635--- lib/lp/app/javascript/formwidgets/tests/test_formwidgets.html 1970-01-01 00:00:00 +0000
636+++ lib/lp/app/javascript/formwidgets/tests/test_formwidgets.html 2011-07-18 11:56:36 +0000
637@@ -0,0 +1,47 @@
638+<!DOCTYPE
639+ HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
640+ "http://www.w3.org/TR/html4/strict.dtd">
641+<html>
642+ <head>
643+ <title>Launchpad Form Widgets</title>
644+
645+ <!-- YUI and test setup -->
646+ <link rel="stylesheet" href="../../testing/test.css" />
647+ <script type="text/javascript"
648+ src="../../../../../canonical/launchpad/icing/yui/yui/yui.js"></script>
649+ <script type="text/javascript"
650+ src="../../testing/testrunner.js"></script>
651+
652+ <!-- Required modules -->
653+ <script type="text/javascript"
654+ src="../../client.js"></script>
655+ <script type="text/javascript"
656+ src="../../activator/activator.js"></script>
657+ <script type="text/javascript"
658+ src="../../anim/anim.js"></script>
659+ <script type="text/javascript"
660+ src="../../lazr/lazr.js"></script>
661+ <script type="text/javascript"
662+ src="../../overlay/overlay.js"></script>
663+ <!-- <script type="text/javascript" -->
664+ <!-- src="../../picker/picker.js"></script> -->
665+ <!-- <script type="text/javascript" -->
666+ <!-- src="../../picker/person_picker.js"></script> -->
667+ <!-- <script type="text/javascript" -->
668+ <!-- src="../../picker/picker_patcher.js"></script> -->
669+
670+ <!-- The module under test -->
671+ <script type="text/javascript"
672+ src="../formwidgets.js"></script>
673+
674+ <!-- The test suite -->
675+ <script type="text/javascript"
676+ src="test_formwidgets.js"></script>
677+
678+ </head>
679+ <body class="yui3-skin-sam">
680+ <ul id="suites">
681+ <li>lp.app.formwidgets.test</li>
682+ </ul>
683+ </body>
684+</html>
685
686=== added file 'lib/lp/app/javascript/formwidgets/tests/test_formwidgets.js'
687--- lib/lp/app/javascript/formwidgets/tests/test_formwidgets.js 1970-01-01 00:00:00 +0000
688+++ lib/lp/app/javascript/formwidgets/tests/test_formwidgets.js 2011-07-18 11:56:36 +0000
689@@ -0,0 +1,605 @@
690+/**
691+ * Copyright 2011 Canonical Ltd. This software is licensed under the
692+ * GNU Affero General Public License version 3 (see the file LICENSE).
693+ *
694+ * Tests for Form Widgets.
695+ *
696+ * @module lp.app.formwidgets
697+ * @submodule test
698+ */
699+
700+YUI.add('lp.app.formwidgets.test', function(Y) {
701+
702+ var namespace = Y.namespace('lp.app.formwidgets.test');
703+
704+ var Assert = Y.Assert;
705+ var ArrayAssert = Y.ArrayAssert;
706+
707+ var suite = new Y.Test.Suite("formwidgets Tests");
708+ var widgets = Y.lp.app.formwidgets;
709+
710+ var attrgetter = function(name) {
711+ return function(thing) {
712+ return thing[name];
713+ };
714+ };
715+
716+ var attrselect = function(name) {
717+ return function(things) {
718+ return Y.Array(things).map(attrgetter(name));
719+ };
720+ };
721+
722+ var testFormRowWidget = {
723+ name: 'TestFormRowWidget',
724+
725+ setUp: function() {
726+ this.container = Y.Node.create("<div />");
727+ this.widget = new widgets.FormRowWidget();
728+ },
729+
730+ tearDown: function() {
731+ this.container.remove(true);
732+ },
733+
734+ testRender: function() {
735+ this.widget.render(this.container);
736+ Assert.isTrue(
737+ this.container.contains(
738+ this.widget.get("boundingBox")));
739+ },
740+
741+ testRenderWithName: function() {
742+ this.widget.fieldNode.append(
743+ Y.Node.create("<input /><input />"));
744+ this.widget.set("name", "field");
745+ this.widget.render(this.container);
746+ ArrayAssert.itemsAreEqual(
747+ ["field", "field"],
748+ this.container.all("input").get("name"));
749+ },
750+
751+ testRenderWithNameChange: function() {
752+ this.widget.fieldNode.append(
753+ Y.Node.create("<input /><input />"));
754+ this.widget.set("name", "field");
755+ this.widget.render(this.container);
756+ this.widget.set("name", "plain");
757+ ArrayAssert.itemsAreEqual(
758+ ["plain", "plain"],
759+ this.container.all("input").get("name"));
760+ },
761+
762+ testRenderLabel: function() {
763+ this.widget.set("label", "Test label");
764+ this.widget.render(this.container);
765+ Assert.areEqual(
766+ "Test label",
767+ this.container.one("label").get("text"));
768+ },
769+
770+ testRenderLabelChange: function() {
771+ this.widget.set("label", "Test label");
772+ this.widget.render(this.container);
773+ this.widget.set("label", "Another label");
774+ Assert.areEqual(
775+ "Another label",
776+ this.container.one("label").get("text"));
777+ },
778+
779+ testRenderDescription: function() {
780+ this.widget.set("description", "Test description.");
781+ this.widget.render(this.container);
782+ Assert.areEqual(
783+ "Test description.",
784+ this.container.one("p.formHelp").get("text"));
785+ },
786+
787+ testRenderDescriptionChange: function() {
788+ this.widget.set("description", "Test description.");
789+ this.widget.render(this.container);
790+ this.widget.set("description", "Another description.");
791+ Assert.areEqual(
792+ "Another description.",
793+ this.container.one("p.formHelp").get("text"));
794+ },
795+
796+ testRenderHelp: function() {
797+ this.widget.set("help",
798+ {link: "http://test.com/test.html", text: "Help text"});
799+ this.widget.render(this.container);
800+ Assert.isFalse(this.container
801+ .one('span.helper').hasClass("unseen"));
802+ Assert.areEqual(
803+ "Help text",
804+ this.container.one("a")
805+ .one('span.invisible-link').get("text"));
806+ Assert.areEqual(
807+ "http://test.com/test.html",
808+ this.container.one("a").get('href'));
809+ },
810+
811+ testGetHelp: function() {
812+ this.widget.set("help",
813+ {link: "http://test.com/test.html", text: "Help text"});
814+ this.widget.render(this.container);
815+ Assert.areEqual(
816+ "http://test.com/test.html",
817+ this.widget.get("help").link);
818+ Assert.areEqual(
819+ "Help text",
820+ this.widget.get("help").text);
821+ },
822+
823+ testChangeHelp: function() {
824+ this.widget.set("help",
825+ {link: "http://test.com/test.html", text: "Help text"});
826+ this.widget.render(this.container);
827+ this.widget.set(
828+ "help",
829+ {link: "http://test.com/test2.html", text: "Help text2"});
830+ Assert.areEqual(
831+ "Help text2",
832+ this.container.one("a")
833+ .one('span.invisible-link').get("text"));
834+ Assert.areEqual(
835+ "http://test.com/test2.html",
836+ this.container.one("a").get('href'));
837+ },
838+
839+ testChangeHelpUndefined: function() {
840+ this.widget.set("help",
841+ {link: "http://test.com/test.html", text: "Help text"});
842+ this.widget.render(this.container);
843+ this.widget.set("help", {});
844+ Assert.isTrue(this.container
845+ .one('span.helper').hasClass("unseen"));
846+ },
847+
848+ testSpinner: function() {
849+ Assert.isFalse(
850+ this.widget.fieldNode.contains(this.widget.spinnerNode));
851+ this.widget.showSpinner();
852+ Assert.isTrue(
853+ this.widget.fieldNode.contains(this.widget.spinnerNode));
854+ this.widget.hideSpinner();
855+ Assert.isFalse(
856+ this.widget.fieldNode.contains(this.widget.spinnerNode));
857+ },
858+
859+ testShowError: function() {
860+ this.widget.showError("Unrealistic expectations.");
861+ Assert.areEqual(
862+ "Unrealistic expectations.",
863+ this.widget.fieldNode.one("p").get("text"));
864+ }
865+
866+ };
867+
868+ suite.add(new Y.Test.Case(testFormRowWidget));
869+
870+ var testChoiceListWidget = {
871+ name: 'TestChoiceListWidget',
872+
873+ setUp: function() {
874+ this.container = Y.Node.create("<div />");
875+ this.widget = new widgets.ChoiceListWidget();
876+ },
877+
878+ tearDown: function() {
879+ this.container.remove(true);
880+ },
881+
882+ testRenderChoices: function() {
883+ this.widget.set("choices", ["a", "b"]);
884+ this.widget.render(this.container);
885+ ArrayAssert.itemsAreEqual(
886+ ["a", "b"],
887+ this.container.all("li > input").get("value"));
888+ ArrayAssert.itemsAreEqual(
889+ ["a", "b"],
890+ this.container.all("li > label").get("text"));
891+ ArrayAssert.itemsAreEqual(
892+ ["checkbox", "checkbox"],
893+ this.container.all("li > input").getAttribute("type"));
894+ },
895+
896+ testRenderChoicesChange: function() {
897+ this.widget.set("choices", ["a", "b"]);
898+ this.widget.render(this.container);
899+ this.widget.set("choices", ["c", "d", "e"]);
900+ ArrayAssert.itemsAreEqual(
901+ ["c", "d", "e"],
902+ this.container.all("li > input").get("value"));
903+ ArrayAssert.itemsAreEqual(
904+ ["c", "d", "e"],
905+ this.container.all("li > label").get("text"));
906+ },
907+
908+ testRenderAddChoices: function() {
909+ this.widget.add_choices(["a", "b"]);
910+ this.widget.render(this.container);
911+ ArrayAssert.itemsAreEqual(
912+ ["a", "b"],
913+ this.container.all("li > input").get("value"));
914+ ArrayAssert.itemsAreEqual(
915+ ["a", "b"],
916+ this.container.all("li > label").get("text"));
917+ ArrayAssert.itemsAreEqual(
918+ ["checkbox", "checkbox"],
919+ this.container.all("li > input").getAttribute("type"));
920+ },
921+
922+ testRenderRemoveChoices: function() {
923+ this.widget.add_choices(["a", "b", "c", "d"]);
924+ this.widget.render(this.container);
925+ this.widget.remove_choices(["b", "d"]);
926+ ArrayAssert.itemsAreEqual(
927+ ["a", "c"],
928+ this.container.all("li > input").get("value"));
929+ ArrayAssert.itemsAreEqual(
930+ ["a", "c"],
931+ this.container.all("li > label").get("text"));
932+ },
933+
934+ testRenderChoicesChangeType: function() {
935+ this.widget.set("choices", ["a", "b"]);
936+ this.widget.render(this.container);
937+ this.widget.set("type", "radio");
938+ ArrayAssert.itemsAreEqual(
939+ ["radio", "radio"],
940+ this.container.all("li > input").getAttribute("type"));
941+
942+ },
943+
944+ testChoiceWithCheckBox: function() {
945+ this.widget
946+ .set("type", "checkbox")
947+ .set("choices", ["a", "b"]);
948+ ArrayAssert.itemsAreEqual(
949+ [], this.widget.get("choice"));
950+ this.widget.fieldNode.one("input[value=a]")
951+ .set("checked", "checked");
952+ ArrayAssert.itemsAreEqual(
953+ ["a"], this.widget.get("choice"));
954+ },
955+
956+ testChoiceWithRadio: function() {
957+ // When both radio buttons are checked (this is possible in some
958+ // broken DOMs/JS engined), choice is undefined.
959+ this.widget
960+ .set("type", "radio")
961+ .set("choices", ["a", "b"]);
962+ Assert.isNull(this.widget.get("choice"));
963+ this.widget.fieldNode.one("input[value=a]")
964+ .set("checked", "checked");
965+ Assert.areEqual("a", this.widget.get("choice"));
966+ this.widget.fieldNode.one("input[value=b]")
967+ .set("checked", "checked");
968+ if (this.widget.fieldNode.one("input[value=a]").get("checked")) {
969+ // This assertion can only be made if the DOM/JS is broken
970+ // in the host browser.
971+ Assert.isUndefined(this.widget.get("choice"));
972+ }
973+ else {
974+ // The host browser's DOM/JS is sane.
975+ ArrayAssert.itemsAreEqual(
976+ ["b"], this.widget.get("choice"));
977+ }
978+ },
979+
980+ testSetChoiceWithCheckBox: function() {
981+ this.widget
982+ .set("type", "checkbox")
983+ .set("choices", ["a", "b"])
984+ .set("choice", "a");
985+ ArrayAssert.itemsAreEqual(
986+ ["a"], this.widget.get("choice"));
987+ this.widget.set("choice", ["a"]);
988+ ArrayAssert.itemsAreEqual(
989+ ["a"], this.widget.get("choice"));
990+ this.widget.set("choice", ["a", "b"]);
991+ ArrayAssert.itemsAreEqual(
992+ ["a", "b"], this.widget.get("choice"));
993+ this.widget.set("choice", ["b", "c"]);
994+ ArrayAssert.itemsAreEqual(
995+ ["b"], this.widget.get("choice"));
996+ },
997+
998+ testSetChoiceWithRadio: function() {
999+ this.widget
1000+ .set("type", "radio")
1001+ .set("choices", ["a", "b"])
1002+ .set("choice", "a");
1003+ ArrayAssert.itemsAreEqual(
1004+ "a", this.widget.get("choice"));
1005+ this.widget.set("choice", ["a"]);
1006+ ArrayAssert.itemsAreEqual(
1007+ "a", this.widget.get("choice"));
1008+ this.widget.set("choice", "b");
1009+ ArrayAssert.itemsAreEqual(
1010+ "b", this.widget.get("choice"));
1011+ }
1012+
1013+ };
1014+
1015+ testChoiceListWidget = Y.merge(
1016+ testFormRowWidget, testChoiceListWidget);
1017+ suite.add(new Y.Test.Case(testChoiceListWidget));
1018+
1019+ var testSelectWidget = {
1020+ name: 'TestSelectWidget',
1021+
1022+ choices: [
1023+ {value: "a", text: "A", data: 123},
1024+ {value: "b", text: "B", data: 456},
1025+ {value: "c", text: "C", data: 789}
1026+ ],
1027+
1028+ setUp: function() {
1029+ this.container = Y.Node.create("<div />");
1030+ this.widget = new widgets.SelectWidget();
1031+ },
1032+
1033+ tearDown: function() {
1034+ this.container.remove(true);
1035+ },
1036+
1037+ testNameChange: function() {
1038+ this.widget
1039+ .set("name", "foo")
1040+ .set("choices", this.choices);
1041+ var select = this.widget.fieldNode.one("select");
1042+ Assert.areEqual("foo", select.get("name"));
1043+ this.widget
1044+ .set("name", "bar");
1045+ Assert.areEqual("bar", select.get("name"));
1046+ },
1047+
1048+ testChoices: function() {
1049+ this.widget.set("choices", this.choices);
1050+ var choices_observed = this.widget.get("choices");
1051+ /* We have to compare bit by bit ourselves because
1052+ Javascript is a language born in hell. */
1053+ ArrayAssert.itemsAreEqual(
1054+ attrselect("value")(this.choices),
1055+ attrselect("value")(choices_observed));
1056+ ArrayAssert.itemsAreEqual(
1057+ attrselect("text")(this.choices),
1058+ attrselect("text")(choices_observed));
1059+ ArrayAssert.itemsAreEqual(
1060+ attrselect("data")(this.choices),
1061+ attrselect("data")(choices_observed));
1062+ },
1063+
1064+ testRenderChoices: function() {
1065+ this.widget.set("choices", this.choices);
1066+ this.widget.render(this.container);
1067+ ArrayAssert.itemsAreEqual(
1068+ ["a", "b", "c"],
1069+ this.container.all("select > option").get("value"));
1070+ ArrayAssert.itemsAreEqual(
1071+ ["A", "B", "C"],
1072+ this.container.all("select > option").get("text"));
1073+ },
1074+
1075+ testRenderEmptyChoices: function() {
1076+ this.widget.fieldNode.append("something");
1077+ this.widget.set("choices", []);
1078+ this.widget.render(this.container);
1079+ Assert.isNull(this.container.one("select"));
1080+ Assert.isFalse(this.widget.fieldNode.hasChildNodes());
1081+ },
1082+
1083+ testRenderChoicesChange: function() {
1084+ var choices1 = [
1085+ {value: "a", text: "A", data: 123}
1086+ ];
1087+ this.widget.set("choices", choices1);
1088+ this.widget.render(this.container);
1089+ var choices2 = [
1090+ {value: "b", text: "B", data: 456},
1091+ {value: "c", text: "C", data: 789}
1092+ ];
1093+ this.widget.set("choices", choices2);
1094+ ArrayAssert.itemsAreEqual(
1095+ ["b", "c"],
1096+ this.container.all("select > option").get("value"));
1097+ ArrayAssert.itemsAreEqual(
1098+ ["B", "C"],
1099+ this.container.all("select > option").get("text"));
1100+ },
1101+
1102+ testChoice: function() {
1103+ this.widget
1104+ .set("choices", this.choices)
1105+ .set("multiple", true);
1106+ /* It would be better to deselect all options by default,
1107+ but this appears impossible; the browser seems to
1108+ select the first option when rendering. */
1109+ this.widget.fieldNode.all("option").set("selected", false);
1110+ ArrayAssert.itemsAreEqual(
1111+ [], this.widget.get("choice"));
1112+ this.widget.fieldNode.one("option[value=a]")
1113+ .set("selected", true);
1114+ ArrayAssert.itemsAreEqual(
1115+ ["a"], this.widget.get("choice"));
1116+ this.widget.fieldNode.one("option[value=c]")
1117+ .set("selected", true);
1118+ ArrayAssert.itemsAreEqual(
1119+ ["a", "c"], this.widget.get("choice"));
1120+ },
1121+
1122+ testSetChoice: function() {
1123+ this.widget
1124+ .set("multiple", true)
1125+ .set("choices", this.choices)
1126+ .set("choice", "a");
1127+ ArrayAssert.itemsAreEqual(
1128+ ["a"], this.widget.get("choice"));
1129+ this.widget.set("choice", ["a"]);
1130+ ArrayAssert.itemsAreEqual(
1131+ ["a"], this.widget.get("choice"));
1132+ this.widget.set("choice", ["a", "b"]);
1133+ ArrayAssert.itemsAreEqual(
1134+ ["a", "b"], this.widget.get("choice"));
1135+ this.widget.set("choice", ["b", "z"]);
1136+ ArrayAssert.itemsAreEqual(
1137+ ["b"], this.widget.get("choice"));
1138+ },
1139+
1140+ testSize: function() {
1141+ Assert.areEqual(1, this.widget.get("size"));
1142+ },
1143+
1144+ testRenderSize: function() {
1145+ this.widget
1146+ .set("choices", this.choices)
1147+ .set("size", 7)
1148+ .render(this.container);
1149+ Assert.areEqual(
1150+ 7, this.widget.fieldNode.one("select").get("size"));
1151+ },
1152+
1153+ testRenderSizeChange: function() {
1154+ this.widget
1155+ .set("choices", this.choices)
1156+ .set("size", 3)
1157+ .render(this.container)
1158+ .set("size", 5);
1159+ Assert.areEqual(
1160+ 5, this.widget.fieldNode.one("select").get("size"));
1161+ },
1162+
1163+ testAutoSize: function() {
1164+ this.widget.set("choices", this.choices);
1165+ /* Without argument, autoSize() sets the size to the same
1166+ as the number of choices. */
1167+ this.widget.autoSize();
1168+ Assert.areEqual(3, this.widget.get("size"));
1169+ },
1170+
1171+ testAutoSizeMoreChoicesThanMaxiumum: function() {
1172+ this.widget.set("choices", this.choices);
1173+ /* autoSize() sets the size to the same as the number of
1174+ choices unless there are more than the specified
1175+ maximum. */
1176+ this.widget.autoSize(2);
1177+ Assert.areEqual(2, this.widget.get("size"));
1178+ },
1179+
1180+ testAutoSizeFewerChoicesThanMaxiumum: function() {
1181+ this.widget.set("choices", this.choices);
1182+ /* autoSize() sets the size to the same as the number of
1183+ choices. */
1184+ this.widget.autoSize(5);
1185+ Assert.areEqual(3, this.widget.get("size"));
1186+ },
1187+
1188+ testMultiple: function() {
1189+ Assert.areEqual(false, this.widget.get("multiple"));
1190+ },
1191+
1192+ testRenderMultiple: function() {
1193+ this.widget
1194+ .set("choices", this.choices)
1195+ .set("multiple", true)
1196+ .render(this.container);
1197+ Assert.isTrue(
1198+ this.widget.fieldNode.one("select")
1199+ .hasAttribute("multiple"));
1200+ },
1201+
1202+ testRenderMultipleChange: function() {
1203+ this.widget
1204+ .set("choices", this.choices)
1205+ .set("multiple", true)
1206+ .render(this.container)
1207+ .set("multiple", false);
1208+ Assert.isFalse(
1209+ this.widget.fieldNode.one("select")
1210+ .hasAttribute("multiple"));
1211+ }
1212+
1213+ };
1214+
1215+ testSelectWidget = Y.merge(
1216+ testFormRowWidget, testSelectWidget);
1217+ suite.add(new Y.Test.Case(testSelectWidget));
1218+
1219+ var testFormActionsWidget = {
1220+ name: 'TestFormActionsWidget',
1221+
1222+ makeActionsDiv: function() {
1223+ var submit = Y.Node.create("<input />")
1224+ .set("type", "submit")
1225+ .set("value", "Initialize Series");
1226+ var cancel = Y.Node.create("<a>Cancel</a>");
1227+ var div = Y.Node.create("<div />")
1228+ .addClass("actions")
1229+ .append(submit)
1230+ .append(cancel);
1231+ return div;
1232+ },
1233+
1234+ setUp: function() {
1235+ this.actions = this.makeActionsDiv();
1236+ this.widget = new widgets.FormActionsWidget(
1237+ {srcNode: this.actions});
1238+ },
1239+
1240+ tearDown: function() {
1241+ this.actions.remove(true);
1242+ },
1243+
1244+ testInitializer: function() {
1245+ Assert.isTrue(
1246+ this.actions.one("input").compareTo(
1247+ this.widget.submitButtonNode));
1248+ },
1249+
1250+ testSpinner: function() {
1251+ Assert.isTrue(
1252+ this.actions.contains(this.widget.submitButtonNode));
1253+ Assert.isFalse(
1254+ this.actions.contains(this.widget.spinnerNode));
1255+ this.widget.showSpinner();
1256+ Assert.isFalse(
1257+ this.actions.contains(this.widget.submitButtonNode));
1258+ Assert.isTrue(
1259+ this.actions.contains(this.widget.spinnerNode));
1260+ this.widget.hideSpinner();
1261+ Assert.isTrue(
1262+ this.actions.contains(this.widget.submitButtonNode));
1263+ Assert.isFalse(
1264+ this.actions.contains(this.widget.spinnerNode));
1265+ },
1266+
1267+ testShowError: function() {
1268+ this.widget.showError("The Man From U.N.C.L.E.");
1269+ Assert.areEqual(
1270+ "The Man From U.N.C.L.E.",
1271+ this.actions.one("p.error.message").get("text"));
1272+ },
1273+
1274+ testHideErrors: function() {
1275+ this.widget.showError("The Man From U.N.C.L.E.");
1276+ this.widget.showError("The Woman From A.U.N.T.I.E.");
1277+ this.widget.hideErrors();
1278+ Assert.isNull(this.actions.one("p.error.message"));
1279+ }
1280+
1281+ };
1282+
1283+ suite.add(new Y.Test.Case(testFormActionsWidget));
1284+
1285+ // Exports.
1286+ namespace.testFormRowWidget = testFormRowWidget;
1287+ namespace.testChoiceListWidget = testChoiceListWidget;
1288+ namespace.testSelectWidget = testSelectWidget;
1289+ namespace.testFormActionsWidget = testFormActionsWidget;
1290+ namespace.suite = suite;
1291+
1292+}, "0.1", {"requires": [
1293+ 'test', 'console', 'node-event-simulate',
1294+ 'lp.app.formwidgets']});
1295
1296=== added directory 'lib/lp/registry/javascript/distroseries'
1297=== renamed file 'lib/lp/registry/javascript/distroseries.initseries.js' => 'lib/lp/registry/javascript/distroseries/initseries.js'
1298--- lib/lp/registry/javascript/distroseries.initseries.js 2011-07-18 11:56:35 +0000
1299+++ lib/lp/registry/javascript/distroseries/initseries.js 2011-07-18 11:56:36 +0000
1300@@ -2,10 +2,10 @@
1301 * Copyright 2011 Canonical Ltd. This software is licensed under the
1302 * GNU Affero General Public License version 3 (see the file LICENSE).
1303 *
1304- * DistroSeries related stuff.
1305+ * DistroSeries Initialization.
1306 *
1307- * @module registry
1308- * @submodule distroseries
1309+ * @module lp.registry.distroseries
1310+ * @submodule initseries
1311 */
1312
1313 YUI.add('lp.registry.distroseries.initseries', function(Y) {
1314@@ -15,6 +15,7 @@
1315 var namespace = Y.namespace('lp.registry.distroseries.initseries');
1316
1317 var widgets = Y.lp.registry.distroseries.widgets;
1318+var formwidgets = Y.lp.app.formwidgets;
1319
1320
1321 /**
1322@@ -35,7 +36,7 @@
1323
1324 });
1325
1326-Y.extend(DeriveDistroSeriesActionsWidget, widgets.FormActionsWidget, {
1327+Y.extend(DeriveDistroSeriesActionsWidget, formwidgets.FormActionsWidget, {
1328
1329 initializer: function(config) {
1330 this.context = config.context;
1331@@ -206,7 +207,7 @@
1332 .render(form_table_body);
1333 }
1334 var package_copy_options =
1335- new widgets.ChoiceListWidget()
1336+ new formwidgets.ChoiceListWidget()
1337 .set("name", "field.package_copy_options")
1338 .set("type", "radio")
1339 .set("label", "Copy options:")
1340@@ -305,4 +306,4 @@
1341
1342 }, "0.1", {"requires": ["node", "dom", "io", "widget", "array-extras",
1343 "transition", "lp.registry.distroseries.widgets",
1344- "lp.app.picker"]});
1345+ "lp.app.formwidgets", "lp.app.picker"]});
1346
1347=== added directory 'lib/lp/registry/javascript/distroseries/tests'
1348=== renamed file 'lib/lp/registry/javascript/tests/test_distroseries.initseries.html' => 'lib/lp/registry/javascript/distroseries/tests/test_initseries.html'
1349--- lib/lp/registry/javascript/tests/test_distroseries.initseries.html 2011-07-18 11:56:35 +0000
1350+++ lib/lp/registry/javascript/distroseries/tests/test_initseries.html 2011-07-18 11:56:36 +0000
1351@@ -3,46 +3,48 @@
1352 "http://www.w3.org/TR/html4/strict.dtd">
1353 <html>
1354 <head>
1355- <title>Launchpad DistroSeries</title>
1356+ <title>Launchpad DistroSeries Initialiazation</title>
1357
1358 <!-- YUI and test setup -->
1359- <link rel="stylesheet" href="../../../app/javascript/testing/test.css" />
1360- <script type="text/javascript"
1361- src="../../../../canonical/launchpad/icing/yui/yui/yui.js"></script>
1362- <script type="text/javascript"
1363- src="../../../app/javascript/testing/testrunner.js"></script>
1364+ <link rel="stylesheet" href="../../../../app/javascript/testing/test.css" />
1365+ <script type="text/javascript"
1366+ src="../../../../../canonical/launchpad/icing/yui/yui/yui.js"></script>
1367+ <script type="text/javascript"
1368+ src="../../../../app/javascript/testing/testrunner.js"></script>
1369
1370 <!-- Required modules -->
1371 <script type="text/javascript"
1372- src="../../../app/javascript/client.js"></script>
1373- <script type="text/javascript"
1374- src="../../../app/javascript/activator/activator.js"></script>
1375- <script type="text/javascript"
1376- src="../../../app/javascript/anim/anim.js"></script>
1377- <script type="text/javascript"
1378- src="../../../app/javascript/lazr/lazr.js"></script>
1379- <script type="text/javascript"
1380- src="../../../app/javascript/overlay/overlay.js"></script>
1381- <script type="text/javascript"
1382- src="../../../app/javascript/picker/picker.js"></script>
1383- <script type="text/javascript"
1384- src="../../../app/javascript/picker/person_picker.js"></script>
1385- <script type="text/javascript"
1386- src="../../../app/javascript/picker/picker_patcher.js"></script>
1387- <script type="text/javascript"
1388- src="../distroseries.widgets.js"></script>
1389+ src="../../../../app/javascript/client.js"></script>
1390+ <script type="text/javascript"
1391+ src="../../../../app/javascript/activator/activator.js"></script>
1392+ <script type="text/javascript"
1393+ src="../../../../app/javascript/anim/anim.js"></script>
1394+ <script type="text/javascript"
1395+ src="../../../../app/javascript/lazr/lazr.js"></script>
1396+ <script type="text/javascript"
1397+ src="../../../../app/javascript/overlay/overlay.js"></script>
1398+ <script type="text/javascript"
1399+ src="../../../../app/javascript/picker/picker.js"></script>
1400+ <script type="text/javascript"
1401+ src="../../../../app/javascript/picker/person_picker.js"></script>
1402+ <script type="text/javascript"
1403+ src="../../../../app/javascript/picker/picker_patcher.js"></script>
1404+ <script type="text/javascript"
1405+ src="../../../../app/javascript/formwidgets/formwidgets.js"></script>
1406+ <script type="text/javascript"
1407+ src="../widgets.js"></script>
1408
1409 <!-- The module under test -->
1410 <script type="text/javascript"
1411- src="../distroseries.initseries.js"></script>
1412+ src="../initseries.js"></script>
1413
1414 <!-- Required test modules -->
1415 <script type="text/javascript"
1416- src="test_distroseries.widgets.js"></script>
1417+ src="../../../../app/javascript/formwidgets/tests/test_formwidgets.js"></script>
1418
1419 <!-- The test suite -->
1420 <script type="text/javascript"
1421- src="test_distroseries.initseries.js"></script>
1422+ src="test_initseries.js"></script>
1423
1424 </head>
1425 <body class="yui3-skin-sam">
1426
1427=== renamed file 'lib/lp/registry/javascript/tests/test_distroseries.initseries.js' => 'lib/lp/registry/javascript/distroseries/tests/test_initseries.js'
1428--- lib/lp/registry/javascript/tests/test_distroseries.initseries.js 2011-07-18 11:56:35 +0000
1429+++ lib/lp/registry/javascript/distroseries/tests/test_initseries.js 2011-07-18 11:56:36 +0000
1430@@ -2,9 +2,9 @@
1431 * Copyright 2011 Canonical Ltd. This software is licensed under the
1432 * GNU Affero General Public License version 3 (see the file LICENSE).
1433 *
1434- * Tests for DistroSeries related stuff.
1435+ * Tests for DistroSeries Initialization.
1436 *
1437- * @module lp.registry.distroseries
1438+ * @module lp.registry.distroseries.initseries
1439 * @submodule test
1440 */
1441
1442@@ -137,7 +137,7 @@
1443 };
1444
1445 testDeriveDistroSeriesActionsWidget = Y.merge(
1446- Y.lp.registry.distroseries.widgets.test.testFormActionsWidget,
1447+ Y.lp.app.formwidgets.test.testFormActionsWidget,
1448 testDeriveDistroSeriesActionsWidget);
1449 suite.add(new Y.Test.Case(testDeriveDistroSeriesActionsWidget));
1450
1451@@ -228,5 +228,5 @@
1452
1453 }, "0.1", {"requires": [
1454 'test', 'console', 'node-event-simulate',
1455- 'lp.registry.distroseries.widgets.test',
1456+ 'lp.app.formwidgets.test',
1457 'lp.registry.distroseries.initseries']});
1458
1459=== renamed file 'lib/lp/registry/javascript/tests/test_distroseries.widgets.html' => 'lib/lp/registry/javascript/distroseries/tests/test_widgets.html'
1460--- lib/lp/registry/javascript/tests/test_distroseries.widgets.html 2011-07-18 11:56:35 +0000
1461+++ lib/lp/registry/javascript/distroseries/tests/test_widgets.html 2011-07-18 11:56:36 +0000
1462@@ -3,40 +3,46 @@
1463 "http://www.w3.org/TR/html4/strict.dtd">
1464 <html>
1465 <head>
1466- <title>Launchpad DistroSeries</title>
1467+ <title>Launchpad DistroSeries Widgets</title>
1468
1469 <!-- YUI and test setup -->
1470- <link rel="stylesheet" href="../../../app/javascript/testing/test.css" />
1471- <script type="text/javascript"
1472- src="../../../../canonical/launchpad/icing/yui/yui/yui.js"></script>
1473- <script type="text/javascript"
1474- src="../../../app/javascript/testing/testrunner.js"></script>
1475+ <link rel="stylesheet" href="../../../../app/javascript/testing/test.css" />
1476+ <script type="text/javascript"
1477+ src="../../../../../canonical/launchpad/icing/yui/yui/yui.js"></script>
1478+ <script type="text/javascript"
1479+ src="../../../../app/javascript/testing/testrunner.js"></script>
1480
1481 <!-- Required modules -->
1482 <script type="text/javascript"
1483- src="../../../app/javascript/client.js"></script>
1484- <script type="text/javascript"
1485- src="../../../app/javascript/activator/activator.js"></script>
1486- <script type="text/javascript"
1487- src="../../../app/javascript/anim/anim.js"></script>
1488- <script type="text/javascript"
1489- src="../../../app/javascript/lazr/lazr.js"></script>
1490- <script type="text/javascript"
1491- src="../../../app/javascript/overlay/overlay.js"></script>
1492- <script type="text/javascript"
1493- src="../../../app/javascript/picker/picker.js"></script>
1494- <script type="text/javascript"
1495- src="../../../app/javascript/picker/person_picker.js"></script>
1496- <script type="text/javascript"
1497- src="../../../app/javascript/picker/picker_patcher.js"></script>
1498+ src="../../../../app/javascript/client.js"></script>
1499+ <script type="text/javascript"
1500+ src="../../../../app/javascript/activator/activator.js"></script>
1501+ <script type="text/javascript"
1502+ src="../../../../app/javascript/anim/anim.js"></script>
1503+ <script type="text/javascript"
1504+ src="../../../../app/javascript/lazr/lazr.js"></script>
1505+ <script type="text/javascript"
1506+ src="../../../../app/javascript/overlay/overlay.js"></script>
1507+ <script type="text/javascript"
1508+ src="../../../../app/javascript/picker/picker.js"></script>
1509+ <script type="text/javascript"
1510+ src="../../../../app/javascript/picker/person_picker.js"></script>
1511+ <script type="text/javascript"
1512+ src="../../../../app/javascript/picker/picker_patcher.js"></script>
1513+ <script type="text/javascript"
1514+ src="../../../../app/javascript/formwidgets/formwidgets.js"></script>
1515
1516 <!-- The module under test -->
1517 <script type="text/javascript"
1518- src="../distroseries.widgets.js"></script>
1519+ src="../widgets.js"></script>
1520+
1521+ <!-- Required test modules -->
1522+ <script type="text/javascript"
1523+ src="../../../../app/javascript/formwidgets/tests/test_formwidgets.js"></script>
1524
1525 <!-- The test suite -->
1526 <script type="text/javascript"
1527- src="test_distroseries.widgets.js"></script>
1528+ src="test_widgets.js"></script>
1529
1530 </head>
1531 <body class="yui3-skin-sam">
1532
1533=== renamed file 'lib/lp/registry/javascript/tests/test_distroseries.widgets.js' => 'lib/lp/registry/javascript/distroseries/tests/test_widgets.js'
1534--- lib/lp/registry/javascript/tests/test_distroseries.widgets.js 2011-07-18 11:56:35 +0000
1535+++ lib/lp/registry/javascript/distroseries/tests/test_widgets.js 2011-07-18 11:56:36 +0000
1536@@ -2,9 +2,9 @@
1537 * Copyright 2011 Canonical Ltd. This software is licensed under the
1538 * GNU Affero General Public License version 3 (see the file LICENSE).
1539 *
1540- * Tests for DistroSeries related stuff.
1541+ * Tests for DistroSeries Widgets.
1542 *
1543- * @module lp.registry.distroseries
1544+ * @module lp.registry.distroseries.widgets
1545 * @submodule test
1546 */
1547
1548@@ -17,6 +17,7 @@
1549
1550 var suite = new Y.Test.Suite("distroseries.widgets Tests");
1551 var widgets = Y.lp.registry.distroseries.widgets;
1552+ var formwidgets = Y.lp.app.formwidgets;
1553
1554 var attrgetter = function(name) {
1555 return function(thing) {
1556@@ -30,303 +31,6 @@
1557 };
1558 };
1559
1560- var testFormRowWidget = {
1561- name: 'TestFormRowWidget',
1562-
1563- setUp: function() {
1564- this.container = Y.Node.create("<div />");
1565- this.widget = new widgets.FormRowWidget();
1566- },
1567-
1568- tearDown: function() {
1569- this.container.remove(true);
1570- },
1571-
1572- testRender: function() {
1573- this.widget.render(this.container);
1574- Assert.isTrue(
1575- this.container.contains(
1576- this.widget.get("boundingBox")));
1577- },
1578-
1579- testRenderWithName: function() {
1580- this.widget.fieldNode.append(
1581- Y.Node.create("<input /><input />"));
1582- this.widget.set("name", "field");
1583- this.widget.render(this.container);
1584- ArrayAssert.itemsAreEqual(
1585- ["field", "field"],
1586- this.container.all("input").get("name"));
1587- },
1588-
1589- testRenderWithNameChange: function() {
1590- this.widget.fieldNode.append(
1591- Y.Node.create("<input /><input />"));
1592- this.widget.set("name", "field");
1593- this.widget.render(this.container);
1594- this.widget.set("name", "plain");
1595- ArrayAssert.itemsAreEqual(
1596- ["plain", "plain"],
1597- this.container.all("input").get("name"));
1598- },
1599-
1600- testRenderLabel: function() {
1601- this.widget.set("label", "Test label");
1602- this.widget.render(this.container);
1603- Assert.areEqual(
1604- "Test label",
1605- this.container.one("label").get("text"));
1606- },
1607-
1608- testRenderLabelChange: function() {
1609- this.widget.set("label", "Test label");
1610- this.widget.render(this.container);
1611- this.widget.set("label", "Another label");
1612- Assert.areEqual(
1613- "Another label",
1614- this.container.one("label").get("text"));
1615- },
1616-
1617- testRenderDescription: function() {
1618- this.widget.set("description", "Test description.");
1619- this.widget.render(this.container);
1620- Assert.areEqual(
1621- "Test description.",
1622- this.container.one("p.formHelp").get("text"));
1623- },
1624-
1625- testRenderDescriptionChange: function() {
1626- this.widget.set("description", "Test description.");
1627- this.widget.render(this.container);
1628- this.widget.set("description", "Another description.");
1629- Assert.areEqual(
1630- "Another description.",
1631- this.container.one("p.formHelp").get("text"));
1632- },
1633-
1634- testRenderHelp: function() {
1635- this.widget.set("help",
1636- {link: "http://test.com/test.html", text: "Help text"});
1637- this.widget.render(this.container);
1638- Assert.isFalse(this.container
1639- .one('span.helper').hasClass("unseen"));
1640- Assert.areEqual(
1641- "Help text",
1642- this.container.one("a")
1643- .one('span.invisible-link').get("text"));
1644- Assert.areEqual(
1645- "http://test.com/test.html",
1646- this.container.one("a").get('href'));
1647- },
1648-
1649- testGetHelp: function() {
1650- this.widget.set("help",
1651- {link: "http://test.com/test.html", text: "Help text"});
1652- this.widget.render(this.container);
1653- Assert.areEqual(
1654- "http://test.com/test.html",
1655- this.widget.get("help").link);
1656- Assert.areEqual(
1657- "Help text",
1658- this.widget.get("help").text);
1659- },
1660-
1661- testChangeHelp: function() {
1662- this.widget.set("help",
1663- {link: "http://test.com/test.html", text: "Help text"});
1664- this.widget.render(this.container);
1665- this.widget.set(
1666- "help",
1667- {link: "http://test.com/test2.html", text: "Help text2"});
1668- Assert.areEqual(
1669- "Help text2",
1670- this.container.one("a")
1671- .one('span.invisible-link').get("text"));
1672- Assert.areEqual(
1673- "http://test.com/test2.html",
1674- this.container.one("a").get('href'));
1675- },
1676-
1677- testChangeHelpUndefined: function() {
1678- this.widget.set("help",
1679- {link: "http://test.com/test.html", text: "Help text"});
1680- this.widget.render(this.container);
1681- this.widget.set("help", {});
1682- Assert.isTrue(this.container
1683- .one('span.helper').hasClass("unseen"));
1684- },
1685-
1686- testSpinner: function() {
1687- Assert.isFalse(
1688- this.widget.fieldNode.contains(this.widget.spinnerNode));
1689- this.widget.showSpinner();
1690- Assert.isTrue(
1691- this.widget.fieldNode.contains(this.widget.spinnerNode));
1692- this.widget.hideSpinner();
1693- Assert.isFalse(
1694- this.widget.fieldNode.contains(this.widget.spinnerNode));
1695- },
1696-
1697- testShowError: function() {
1698- this.widget.showError("Unrealistic expectations.");
1699- Assert.areEqual(
1700- "Unrealistic expectations.",
1701- this.widget.fieldNode.one("p").get("text"));
1702- }
1703-
1704- };
1705-
1706- suite.add(new Y.Test.Case(testFormRowWidget));
1707-
1708- var testChoiceListWidget = {
1709- name: 'TestChoiceListWidget',
1710-
1711- setUp: function() {
1712- this.container = Y.Node.create("<div />");
1713- this.widget = new widgets.ChoiceListWidget();
1714- },
1715-
1716- tearDown: function() {
1717- this.container.remove(true);
1718- },
1719-
1720- testRenderChoices: function() {
1721- this.widget.set("choices", ["a", "b"]);
1722- this.widget.render(this.container);
1723- ArrayAssert.itemsAreEqual(
1724- ["a", "b"],
1725- this.container.all("li > input").get("value"));
1726- ArrayAssert.itemsAreEqual(
1727- ["a", "b"],
1728- this.container.all("li > label").get("text"));
1729- ArrayAssert.itemsAreEqual(
1730- ["checkbox", "checkbox"],
1731- this.container.all("li > input").getAttribute("type"));
1732- },
1733-
1734- testRenderChoicesChange: function() {
1735- this.widget.set("choices", ["a", "b"]);
1736- this.widget.render(this.container);
1737- this.widget.set("choices", ["c", "d", "e"]);
1738- ArrayAssert.itemsAreEqual(
1739- ["c", "d", "e"],
1740- this.container.all("li > input").get("value"));
1741- ArrayAssert.itemsAreEqual(
1742- ["c", "d", "e"],
1743- this.container.all("li > label").get("text"));
1744- },
1745-
1746- testRenderAddChoices: function() {
1747- this.widget.add_choices(["a", "b"]);
1748- this.widget.render(this.container);
1749- ArrayAssert.itemsAreEqual(
1750- ["a", "b"],
1751- this.container.all("li > input").get("value"));
1752- ArrayAssert.itemsAreEqual(
1753- ["a", "b"],
1754- this.container.all("li > label").get("text"));
1755- ArrayAssert.itemsAreEqual(
1756- ["checkbox", "checkbox"],
1757- this.container.all("li > input").getAttribute("type"));
1758- },
1759-
1760- testRenderRemoveChoices: function() {
1761- this.widget.add_choices(["a", "b", "c", "d"]);
1762- this.widget.render(this.container);
1763- this.widget.remove_choices(["b", "d"]);
1764- ArrayAssert.itemsAreEqual(
1765- ["a", "c"],
1766- this.container.all("li > input").get("value"));
1767- ArrayAssert.itemsAreEqual(
1768- ["a", "c"],
1769- this.container.all("li > label").get("text"));
1770- },
1771-
1772- testRenderChoicesChangeType: function() {
1773- this.widget.set("choices", ["a", "b"]);
1774- this.widget.render(this.container);
1775- this.widget.set("type", "radio");
1776- ArrayAssert.itemsAreEqual(
1777- ["radio", "radio"],
1778- this.container.all("li > input").getAttribute("type"));
1779-
1780- },
1781-
1782- testChoiceWithCheckBox: function() {
1783- this.widget
1784- .set("type", "checkbox")
1785- .set("choices", ["a", "b"]);
1786- ArrayAssert.itemsAreEqual(
1787- [], this.widget.get("choice"));
1788- this.widget.fieldNode.one("input[value=a]")
1789- .set("checked", "checked");
1790- ArrayAssert.itemsAreEqual(
1791- ["a"], this.widget.get("choice"));
1792- },
1793-
1794- testChoiceWithRadio: function() {
1795- // When both radio buttons are checked (this is possible in some
1796- // broken DOMs/JS engined), choice is undefined.
1797- this.widget
1798- .set("type", "radio")
1799- .set("choices", ["a", "b"]);
1800- Assert.isNull(this.widget.get("choice"));
1801- this.widget.fieldNode.one("input[value=a]")
1802- .set("checked", "checked");
1803- Assert.areEqual("a", this.widget.get("choice"));
1804- this.widget.fieldNode.one("input[value=b]")
1805- .set("checked", "checked");
1806- if (this.widget.fieldNode.one("input[value=a]").get("checked")) {
1807- // This assertion can only be made if the DOM/JS is broken
1808- // in the host browser.
1809- Assert.isUndefined(this.widget.get("choice"));
1810- }
1811- else {
1812- // The host browser's DOM/JS is sane.
1813- ArrayAssert.itemsAreEqual(
1814- ["b"], this.widget.get("choice"));
1815- }
1816- },
1817-
1818- testSetChoiceWithCheckBox: function() {
1819- this.widget
1820- .set("type", "checkbox")
1821- .set("choices", ["a", "b"])
1822- .set("choice", "a");
1823- ArrayAssert.itemsAreEqual(
1824- ["a"], this.widget.get("choice"));
1825- this.widget.set("choice", ["a"]);
1826- ArrayAssert.itemsAreEqual(
1827- ["a"], this.widget.get("choice"));
1828- this.widget.set("choice", ["a", "b"]);
1829- ArrayAssert.itemsAreEqual(
1830- ["a", "b"], this.widget.get("choice"));
1831- this.widget.set("choice", ["b", "c"]);
1832- ArrayAssert.itemsAreEqual(
1833- ["b"], this.widget.get("choice"));
1834- },
1835-
1836- testSetChoiceWithRadio: function() {
1837- this.widget
1838- .set("type", "radio")
1839- .set("choices", ["a", "b"])
1840- .set("choice", "a");
1841- ArrayAssert.itemsAreEqual(
1842- "a", this.widget.get("choice"));
1843- this.widget.set("choice", ["a"]);
1844- ArrayAssert.itemsAreEqual(
1845- "a", this.widget.get("choice"));
1846- this.widget.set("choice", "b");
1847- ArrayAssert.itemsAreEqual(
1848- "b", this.widget.get("choice"));
1849- }
1850-
1851- };
1852-
1853- testChoiceListWidget = Y.merge(
1854- testFormRowWidget, testChoiceListWidget);
1855- suite.add(new Y.Test.Case(testChoiceListWidget));
1856-
1857 var testParentSeriesListWidget = {
1858 name: 'TestParentSeriesListWidget',
1859
1860@@ -367,6 +71,7 @@
1861 },
1862
1863 testAddParentAddsLine: function() {
1864+ var parent;
1865 parent = {value: "4", title: "Hoary", api_uri: "ubuntu/hoary"};
1866 Assert.areEqual(
1867 0,
1868@@ -382,6 +87,7 @@
1869 },
1870
1871 testAddDuplicateParent: function() {
1872+ var parent;
1873 parent = {value: "4", title: "Hoary", api_uri: "ubuntu/hoary"};
1874 Assert.isTrue(
1875 this.widget.add_parent(parent),
1876@@ -396,6 +102,7 @@
1877 },
1878
1879 testParentOrdering: function() {
1880+ var parent;
1881 parent = {value: "4", title: "Hoary", api_uri: "ubuntu/hoary"};
1882 this.widget.add_parent(parent);
1883 parent = {value: "3", title: "Warty", api_uri: "ubuntu/warty"};
1884@@ -418,6 +125,7 @@
1885 },
1886
1887 testRemoveParent: function() {
1888+ var parent;
1889 parent = {value: "4", title: "Hoary", api_uri: "ubuntu/hoary"};
1890 this.widget.add_parent(parent);
1891 parent = {value: "3", title: "Warty", api_uri: "ubuntu/warty"};
1892@@ -460,7 +168,7 @@
1893 };
1894
1895 testParentSeriesListWidget = Y.merge(
1896- testFormRowWidget, testParentSeriesListWidget);
1897+ formwidgets.test.testFormRowWidget, testParentSeriesListWidget);
1898 suite.add(new Y.Test.Case(testParentSeriesListWidget));
1899
1900 var testArchitecturesChoiceListWidget = {
1901@@ -561,209 +269,10 @@
1902 };
1903
1904 testArchitecturesChoiceListWidget = Y.merge(
1905- testChoiceListWidget, testArchitecturesChoiceListWidget);
1906+ formwidgets.test.testChoiceListWidget,
1907+ testArchitecturesChoiceListWidget);
1908 suite.add(new Y.Test.Case(testArchitecturesChoiceListWidget));
1909
1910- var testSelectWidget = {
1911- name: 'TestSelectWidget',
1912-
1913- choices: [
1914- {value: "a", text: "A", data: 123},
1915- {value: "b", text: "B", data: 456},
1916- {value: "c", text: "C", data: 789}
1917- ],
1918-
1919- setUp: function() {
1920- this.container = Y.Node.create("<div />");
1921- this.widget = new widgets.SelectWidget();
1922- },
1923-
1924- tearDown: function() {
1925- this.container.remove(true);
1926- },
1927-
1928- testNameChange: function() {
1929- this.widget
1930- .set("name", "foo")
1931- .set("choices", this.choices);
1932- var select = this.widget.fieldNode.one("select");
1933- Assert.areEqual("foo", select.get("name"));
1934- this.widget
1935- .set("name", "bar");
1936- Assert.areEqual("bar", select.get("name"));
1937- },
1938-
1939- testChoices: function() {
1940- this.widget.set("choices", this.choices);
1941- var choices_observed = this.widget.get("choices");
1942- /* We have to compare bit by bit ourselves because
1943- Javascript is a language born in hell. */
1944- ArrayAssert.itemsAreEqual(
1945- attrselect("value")(this.choices),
1946- attrselect("value")(choices_observed));
1947- ArrayAssert.itemsAreEqual(
1948- attrselect("text")(this.choices),
1949- attrselect("text")(choices_observed));
1950- ArrayAssert.itemsAreEqual(
1951- attrselect("data")(this.choices),
1952- attrselect("data")(choices_observed));
1953- },
1954-
1955- testRenderChoices: function() {
1956- this.widget.set("choices", this.choices);
1957- this.widget.render(this.container);
1958- ArrayAssert.itemsAreEqual(
1959- ["a", "b", "c"],
1960- this.container.all("select > option").get("value"));
1961- ArrayAssert.itemsAreEqual(
1962- ["A", "B", "C"],
1963- this.container.all("select > option").get("text"));
1964- },
1965-
1966- testRenderEmptyChoices: function() {
1967- this.widget.fieldNode.append("something");
1968- this.widget.set("choices", []);
1969- this.widget.render(this.container);
1970- Assert.isNull(this.container.one("select"));
1971- Assert.isFalse(this.widget.fieldNode.hasChildNodes());
1972- },
1973-
1974- testRenderChoicesChange: function() {
1975- var choices1 = [
1976- {value: "a", text: "A", data: 123}
1977- ];
1978- this.widget.set("choices", choices1);
1979- this.widget.render(this.container);
1980- var choices2 = [
1981- {value: "b", text: "B", data: 456},
1982- {value: "c", text: "C", data: 789}
1983- ];
1984- this.widget.set("choices", choices2);
1985- ArrayAssert.itemsAreEqual(
1986- ["b", "c"],
1987- this.container.all("select > option").get("value"));
1988- ArrayAssert.itemsAreEqual(
1989- ["B", "C"],
1990- this.container.all("select > option").get("text"));
1991- },
1992-
1993- testChoice: function() {
1994- this.widget
1995- .set("choices", this.choices)
1996- .set("multiple", true);
1997- /* It would be better to deselect all options by default,
1998- but this appears impossible; the browser seems to
1999- select the first option when rendering. */
2000- this.widget.fieldNode.all("option").set("selected", false);
2001- ArrayAssert.itemsAreEqual(
2002- [], this.widget.get("choice"));
2003- this.widget.fieldNode.one("option[value=a]")
2004- .set("selected", true);
2005- ArrayAssert.itemsAreEqual(
2006- ["a"], this.widget.get("choice"));
2007- this.widget.fieldNode.one("option[value=c]")
2008- .set("selected", true);
2009- ArrayAssert.itemsAreEqual(
2010- ["a", "c"], this.widget.get("choice"));
2011- },
2012-
2013- testSetChoice: function() {
2014- this.widget
2015- .set("multiple", true)
2016- .set("choices", this.choices)
2017- .set("choice", "a");
2018- ArrayAssert.itemsAreEqual(
2019- ["a"], this.widget.get("choice"));
2020- this.widget.set("choice", ["a"]);
2021- ArrayAssert.itemsAreEqual(
2022- ["a"], this.widget.get("choice"));
2023- this.widget.set("choice", ["a", "b"]);
2024- ArrayAssert.itemsAreEqual(
2025- ["a", "b"], this.widget.get("choice"));
2026- this.widget.set("choice", ["b", "z"]);
2027- ArrayAssert.itemsAreEqual(
2028- ["b"], this.widget.get("choice"));
2029- },
2030-
2031- testSize: function() {
2032- Assert.areEqual(1, this.widget.get("size"));
2033- },
2034-
2035- testRenderSize: function() {
2036- this.widget
2037- .set("choices", this.choices)
2038- .set("size", 7)
2039- .render(this.container);
2040- Assert.areEqual(
2041- 7, this.widget.fieldNode.one("select").get("size"));
2042- },
2043-
2044- testRenderSizeChange: function() {
2045- this.widget
2046- .set("choices", this.choices)
2047- .set("size", 3)
2048- .render(this.container)
2049- .set("size", 5);
2050- Assert.areEqual(
2051- 5, this.widget.fieldNode.one("select").get("size"));
2052- },
2053-
2054- testAutoSize: function() {
2055- this.widget.set("choices", this.choices);
2056- /* Without argument, autoSize() sets the size to the same
2057- as the number of choices. */
2058- this.widget.autoSize();
2059- Assert.areEqual(3, this.widget.get("size"));
2060- },
2061-
2062- testAutoSizeMoreChoicesThanMaxiumum: function() {
2063- this.widget.set("choices", this.choices);
2064- /* autoSize() sets the size to the same as the number of
2065- choices unless there are more than the specified
2066- maximum. */
2067- this.widget.autoSize(2);
2068- Assert.areEqual(2, this.widget.get("size"));
2069- },
2070-
2071- testAutoSizeFewerChoicesThanMaxiumum: function() {
2072- this.widget.set("choices", this.choices);
2073- /* autoSize() sets the size to the same as the number of
2074- choices. */
2075- this.widget.autoSize(5);
2076- Assert.areEqual(3, this.widget.get("size"));
2077- },
2078-
2079- testMultiple: function() {
2080- Assert.areEqual(false, this.widget.get("multiple"));
2081- },
2082-
2083- testRenderMultiple: function() {
2084- this.widget
2085- .set("choices", this.choices)
2086- .set("multiple", true)
2087- .render(this.container);
2088- Assert.isTrue(
2089- this.widget.fieldNode.one("select")
2090- .hasAttribute("multiple"));
2091- },
2092-
2093- testRenderMultipleChange: function() {
2094- this.widget
2095- .set("choices", this.choices)
2096- .set("multiple", true)
2097- .render(this.container)
2098- .set("multiple", false);
2099- Assert.isFalse(
2100- this.widget.fieldNode.one("select")
2101- .hasAttribute("multiple"));
2102- }
2103-
2104- };
2105-
2106- testSelectWidget = Y.merge(
2107- testFormRowWidget, testSelectWidget);
2108- suite.add(new Y.Test.Case(testSelectWidget));
2109-
2110 var testPackagesetPickerWidget = {
2111 name: 'TestPackagesetPickerWidget',
2112
2113@@ -968,79 +477,14 @@
2114 };
2115
2116 testPackagesetPickerWidget = Y.merge(
2117- testSelectWidget, testPackagesetPickerWidget);
2118+ formwidgets.test.testSelectWidget, testPackagesetPickerWidget);
2119 suite.add(new Y.Test.Case(testPackagesetPickerWidget));
2120
2121- var testFormActionsWidget = {
2122- name: 'TestFormActionsWidget',
2123-
2124- makeActionsDiv: function() {
2125- var submit = Y.Node.create("<input />")
2126- .set("type", "submit")
2127- .set("value", "Initialize Series");
2128- var cancel = Y.Node.create("<a>Cancel</a>");
2129- var div = Y.Node.create("<div />")
2130- .addClass("actions")
2131- .append(submit)
2132- .append(cancel);
2133- return div;
2134- },
2135-
2136- setUp: function() {
2137- this.actions = this.makeActionsDiv();
2138- this.widget = new widgets.FormActionsWidget(
2139- {srcNode: this.actions});
2140- },
2141-
2142- tearDown: function() {
2143- this.actions.remove(true);
2144- },
2145-
2146- testInitializer: function() {
2147- Assert.isTrue(
2148- this.actions.one("input").compareTo(
2149- this.widget.submitButtonNode));
2150- },
2151-
2152- testSpinner: function() {
2153- Assert.isTrue(
2154- this.actions.contains(this.widget.submitButtonNode));
2155- Assert.isFalse(
2156- this.actions.contains(this.widget.spinnerNode));
2157- this.widget.showSpinner();
2158- Assert.isFalse(
2159- this.actions.contains(this.widget.submitButtonNode));
2160- Assert.isTrue(
2161- this.actions.contains(this.widget.spinnerNode));
2162- this.widget.hideSpinner();
2163- Assert.isTrue(
2164- this.actions.contains(this.widget.submitButtonNode));
2165- Assert.isFalse(
2166- this.actions.contains(this.widget.spinnerNode));
2167- },
2168-
2169- testShowError: function() {
2170- this.widget.showError("The Man From U.N.C.L.E.");
2171- Assert.areEqual(
2172- "The Man From U.N.C.L.E.",
2173- this.actions.one("p.error.message").get("text"));
2174- },
2175-
2176- testHideErrors: function() {
2177- this.widget.showError("The Man From U.N.C.L.E.");
2178- this.widget.showError("The Woman From A.U.N.T.I.E.");
2179- this.widget.hideErrors();
2180- Assert.isNull(this.actions.one("p.error.message"));
2181- }
2182-
2183- };
2184-
2185- suite.add(new Y.Test.Case(testFormActionsWidget));
2186
2187 // Exports.
2188- namespace.testFormActionsWidget = testFormActionsWidget;
2189 namespace.suite = suite;
2190
2191 }, "0.1", {"requires": [
2192 'test', 'console', 'node-event-simulate',
2193- 'lp.registry.distroseries.widgets']});
2194+ 'lp.registry.distroseries.widgets', 'lp.app.formwidgets',
2195+ 'lp.app.formwidgets.test']});
2196
2197=== renamed file 'lib/lp/registry/javascript/distroseries.widgets.js' => 'lib/lp/registry/javascript/distroseries/widgets.js'
2198--- lib/lp/registry/javascript/distroseries.widgets.js 2011-07-18 11:56:35 +0000
2199+++ lib/lp/registry/javascript/distroseries/widgets.js 2011-07-18 11:56:36 +0000
2200@@ -2,10 +2,10 @@
2201 * Copyright 2011 Canonical Ltd. This software is licensed under the
2202 * GNU Affero General Public License version 3 (see the file LICENSE).
2203 *
2204- * DistroSeries related stuff.
2205+ * DistroSeries Widgets.
2206 *
2207- * @module registry
2208- * @submodule distroseries
2209+ * @module lp.registry.distroseries
2210+ * @submodule widgets
2211 */
2212
2213 YUI.add('lp.registry.distroseries.widgets', function(Y) {
2214@@ -14,154 +14,8 @@
2215
2216 var namespace = Y.namespace('lp.registry.distroseries.widgets');
2217
2218-
2219-/**
2220- * A form row matching that which LaunchpadForm presents, containing a
2221- * field (defined in a subclass), and an optional label and
2222- * description.
2223- *
2224- * @class FormRowWidget
2225- */
2226-var FormRowWidget;
2227-
2228-FormRowWidget = function() {
2229- FormRowWidget.superclass.constructor.apply(this, arguments);
2230-};
2231-
2232-Y.mix(FormRowWidget, {
2233-
2234- NAME: 'formRowWidget',
2235-
2236- ATTRS: {
2237-
2238- /**
2239- * The field name.
2240- *
2241- * @property name
2242- */
2243- name: {
2244- setter: function(value, name) {
2245- this.fieldNode.all("input, select").set("name", value);
2246- }
2247- },
2248-
2249- /**
2250- * The top label for the field.
2251- *
2252- * @property label
2253- */
2254- label: {
2255- getter: function() {
2256- return this.labelNode.get("text");
2257- },
2258- setter: function(value, name) {
2259- this.labelNode.set("text", value);
2260- }
2261- },
2262-
2263- /**
2264- * A dictionary {link:link, text:text} to populate
2265- * the pop-up help for the field.
2266- *
2267- * @property help
2268- */
2269- help: {
2270- getter: function() {
2271- return {link:this.helpNode.one('a')
2272- .get("href"),
2273- text:this.helpNode
2274- .one('.invisible-link')
2275- .get("text")};
2276- },
2277- setter: function(value, name) {
2278- if ((value.link !== undefined) &&
2279- (value.text !== undefined)) {
2280- this.helpNode.one('a').set("href", value.link);
2281- this.helpNode.one('.invisible-link')
2282- .set("text", value.text);
2283- this.helpNode.removeClass('unseen');
2284- }
2285- else {
2286- this.helpNode.addClass('unseen');
2287- }
2288- }
2289- },
2290-
2291- /**
2292- * A description shown near the field.
2293- *
2294- * @label description
2295- */
2296- description: {
2297- getter: function() {
2298- return this.descriptionNode.get("text");
2299- },
2300- setter: function(value, name) {
2301- this.descriptionNode.set("text", value);
2302- }
2303- }
2304- }
2305-
2306-});
2307-
2308-Y.extend(FormRowWidget, Y.Widget, {
2309-
2310- BOUNDING_TEMPLATE: "<tr></tr>",
2311-
2312- CONTENT_TEMPLATE: '<td colspan="2"></td>',
2313-
2314- initializer: function(config) {
2315- this.labelNode = Y.Node.create("<label />");
2316- this.helpNode = Y.Node.create(('<span class="helper unseen">'+
2317- '&nbsp;<a href=""' +
2318- 'target="help" class="sprite maybe">&nbsp;' +
2319- '<span class="invisible-link"></span></a></span>'));
2320- this.fieldNode = Y.Node.create("<div></div>");
2321- this.descriptionNode = Y.Node.create('<p class="formHelp" />');
2322- this.spinnerNode = Y.Node.create(
2323- '<img src="/@@/spinner" alt="Loading..." />');
2324- },
2325-
2326- renderUI: function() {
2327- this.get("contentBox")
2328- .append(this.labelNode)
2329- .append(this.helpNode)
2330- .append(this.fieldNode)
2331- .append(this.descriptionNode);
2332- },
2333-
2334- /**
2335- * Show the spinner.
2336- *
2337- * @method showSpinner
2338- */
2339- showSpinner: function() {
2340- this.fieldNode.empty().append(this.spinnerNode);
2341- },
2342-
2343- /**
2344- * Hide the spinner.
2345- *
2346- * @method hideSpinner
2347- */
2348- hideSpinner: function() {
2349- this.spinnerNode.remove();
2350- },
2351-
2352- /**
2353- * Display an error.
2354- *
2355- * @method showError
2356- */
2357- showError: function(error) {
2358- var message = Y.Node.create('<p />').set("text", error);
2359- this.fieldNode.empty().append(message);
2360- Y.lazr.anim.red_flash({node: message}).run();
2361- }
2362-
2363-});
2364-
2365-namespace.FormRowWidget = FormRowWidget;
2366+var formwidgets = Y.lp.app.formwidgets;
2367+
2368
2369 /**
2370 * A table to display, order, delete the selected parent series. Each parent
2371@@ -249,7 +103,7 @@
2372 }
2373 });
2374
2375-Y.extend(ParentSeriesListWidget, FormRowWidget, {
2376+Y.extend(ParentSeriesListWidget, formwidgets.FormRowWidget, {
2377
2378 initializer: function() {
2379 ParentSeriesListWidget.superclass.initializer();
2380@@ -465,203 +319,6 @@
2381
2382
2383 /**
2384- * A form row matching that which LaunchpadForm presents, containing a
2385- * list of checkboxes, and an optional label and description.
2386- *
2387- * @class ChoiceListWidget
2388- */
2389-var ChoiceListWidget;
2390-
2391-ChoiceListWidget = function() {
2392- ChoiceListWidget.superclass.constructor.apply(this, arguments);
2393-};
2394-
2395-Y.mix(ChoiceListWidget, {
2396-
2397- NAME: 'choiceListWidget',
2398-
2399- ATTRS: {
2400-
2401- /**
2402- * An array of strings from which to choose.
2403- *
2404- * @property choices
2405- */
2406- choices: {
2407- getter: function() {
2408- return this.fieldNode.all("li > input").get("value");
2409- },
2410- setter: function(value, name) {
2411- var choices = Y.Array.unique(value).sort();
2412- var list = Y.Node.create("<ul />");
2413- var self = this;
2414- choices.forEach(
2415- function(choice) {
2416- var item = self._createChoice(choice);
2417- list.append(item);
2418- }
2419- );
2420- this.fieldNode.empty().append(list);
2421- }
2422- },
2423-
2424- /**
2425- * The current selection.
2426- *
2427- * @property choice
2428- */
2429- choice: {
2430- setter: function(value, name) {
2431- if (!Y.Lang.isArray(value)) {
2432- value = [value];
2433- }
2434- this.fieldNode.all("li > input").each(
2435- function(node) {
2436- node.set(
2437- "checked",
2438- value.indexOf(node.get("value")) >= 0);
2439- }
2440- );
2441- },
2442- getter: function() {
2443- var choice = [];
2444- this.fieldNode.all("li > input").each(
2445- function(node) {
2446- if (node.get("checked")) {
2447- choice.push(node.get("value"));
2448- }
2449- }
2450- );
2451- if (this.get("type") === "radio") {
2452- if (choice.length === 0) {
2453- choice = null;
2454- }
2455- else if (choice.length === 1) {
2456- choice = choice[0];
2457- }
2458- else {
2459- choice = undefined;
2460- }
2461- }
2462- return choice;
2463- }
2464- },
2465-
2466- /**
2467- * The input type to display. Choose from "checkbox" or "radio".
2468- *
2469- * @property type
2470- */
2471- type: {
2472- value: "checkbox",
2473- setter: function(value, name) {
2474- this.fieldNode.all("li > input").set("type", value);
2475- }
2476- }
2477-
2478- }
2479-
2480-});
2481-
2482-Y.extend(ChoiceListWidget, FormRowWidget, {
2483-
2484- /**
2485- * Helper method to create an entry for the select widget.
2486- *
2487- * @method _createChoice
2488- */
2489- _createChoice: function(choice) {
2490- var field_name = this.get("name");
2491- var field_type = this.get("type");
2492- var item = Y.Node.create(
2493- "<li><input /> <label /></li>");
2494- item.one("input")
2495- .set("type", field_type)
2496- .set("name", field_name)
2497- .set("value", choice);
2498- item.one("label")
2499- .setAttribute(
2500- "for", item.one("input").generateID())
2501- .setStyle("font-weight", "normal")
2502- .set("text", choice);
2503- return item;
2504- },
2505-
2506- /**
2507- * Remove a list of choices from the possible widget's choices.
2508- *
2509- * @method remove_choices
2510- */
2511- remove_choices: function(choices) {
2512- choices.forEach(
2513- function(choice) {
2514- this.fieldNode.all("select > option").each(
2515- function(option) { options.push(option); });
2516- this.fieldNode.all(
2517- "li input[value=" + choice + "]").each(
2518- function(li_input) {
2519- li_input.get('parentNode').remove();
2520- }
2521- );
2522- },
2523- this
2524- );
2525- Y.lazr.anim.green_flash({node: this.fieldNode}).run();
2526- },
2527-
2528- _sorted_position: function(choice) {
2529- var options = [];
2530- this.fieldNode.all("input").each(
2531- function(node) {
2532- options.push(node.get('value'));
2533- }
2534- );
2535- options.push(choice);
2536- return options.sort().indexOf(choice);
2537- },
2538-
2539- /**
2540- * Add new choices (if they are not already present).
2541- *
2542- * @method add_choices
2543- */
2544- add_choices: function(new_choices) {
2545- new_choices.forEach(
2546- function(choice) {
2547- if (this.fieldNode.all(
2548- "li > input[value=" + choice + "]").isEmpty()) {
2549- var list = this.fieldNode.one('ul');
2550- if (list === null) {
2551- list = Y.Node.create("<ul />");
2552- this.fieldNode.empty().append(list);
2553- }
2554- var option = this._createChoice(choice);
2555- var options = list.all('input');
2556- if (options.isEmpty()) {
2557- list.append(option);
2558- }
2559- else {
2560- var pos = this._sorted_position(choice);
2561- if (pos === 0) {
2562- list.prepend(option);
2563- }
2564- else {
2565- list.insertBefore(option, options.item(pos));
2566- }
2567- }
2568- }
2569- }, this
2570- );
2571- Y.lazr.anim.green_flash({node: this.fieldNode}).run();
2572- }
2573-
2574-});
2575-
2576-
2577-namespace.ChoiceListWidget = ChoiceListWidget;
2578-
2579-
2580-/**
2581 * A special form of ChoiceListWidget for choosing architecture tags.
2582 *
2583 * @class ArchitecturesChoiceListWidget
2584@@ -682,7 +339,7 @@
2585
2586 });
2587
2588-Y.extend(ArchitecturesChoiceListWidget, ChoiceListWidget, {
2589+Y.extend(ArchitecturesChoiceListWidget, formwidgets.ChoiceListWidget, {
2590
2591 initializer: function(config) {
2592 this.client = new Y.lp.client.Launchpad();
2593@@ -787,175 +444,6 @@
2594
2595
2596 /**
2597- * A special form of FormRowWidget, containing a select control.
2598- *
2599- * @class SelectWidget
2600- */
2601-var SelectWidget;
2602-
2603-SelectWidget = function() {
2604- SelectWidget.superclass.constructor.apply(this, arguments);
2605-};
2606-
2607-Y.mix(SelectWidget, {
2608-
2609- NAME: 'selectWidget',
2610-
2611- ATTRS: {
2612-
2613- /**
2614- * An array of objects from which to choose. Each object
2615- * should contain a value for "value", "text" and "data".
2616- *
2617- * @property choices
2618- */
2619- choices: {
2620- getter: function() {
2621- /* I think this is a YUI3 wart; I can't see any way to
2622- map() over a NodeList, so I must push the elements
2623- one by one into an array first. */
2624- var options = Y.Array([]);
2625- this.fieldNode.all("select > option").each(
2626- function(option) { options.push(option); });
2627- return options.map(
2628- function(option) {
2629- return {
2630- value: option.get("value"),
2631- text: option.get("text"),
2632- data: option.getData("data")
2633- };
2634- }
2635- );
2636- },
2637- setter: function(value, name) {
2638- var select = Y.Node.create("<select />");
2639- select.set("name", this.get("name"))
2640- .set("size", this.get("size"));
2641- if (this.get("multiple")) {
2642- select.set("multiple", "multiple");
2643- }
2644- var choices = Y.Array(value);
2645- choices.forEach(
2646- function(choice) {
2647- var option = Y.Node.create("<option />");
2648- option.set("value", choice.value)
2649- .set("text", choice.text)
2650- .setData("data", choice.data);
2651- select.append(option);
2652- }
2653- );
2654- if (choices.length > 0) {
2655- this.fieldNode.empty().append(select);
2656- }
2657- else {
2658- this.fieldNode.empty();
2659- }
2660- }
2661- },
2662-
2663- /**
2664- * The current selection.
2665- *
2666- * @property choice
2667- */
2668- choice: {
2669- setter: function(value, name) {
2670- if (!Y.Lang.isArray(value)) {
2671- value = [value];
2672- }
2673- this.fieldNode.all("select > option").each(
2674- function(node) {
2675- node.set(
2676- "selected",
2677- value.indexOf(node.get("value")) >= 0);
2678- }
2679- );
2680- },
2681- getter: function() {
2682- var choice = [];
2683- this.fieldNode.all("select > option").each(
2684- function(node) {
2685- if (node.get("selected")) {
2686- choice.push(node.get("value"));
2687- }
2688- }
2689- );
2690- return choice;
2691- }
2692- },
2693-
2694- /**
2695- * The number of rows to show in the select widget.
2696- *
2697- * @property size
2698- */
2699- size: {
2700- value: 1,
2701- setter: function(value, name) {
2702- this.fieldNode.all("select").set("size", value);
2703- }
2704- },
2705-
2706- /**
2707- * Whether multiple rows can be selected.
2708- *
2709- * @property multiple
2710- */
2711- multiple: {
2712- value: false,
2713- setter: function(value, name) {
2714- value = value ? true : false;
2715- this.fieldNode.all("select").set("multiple", value);
2716- return value;
2717- }
2718- }
2719-
2720- }
2721-
2722-});
2723-
2724-Y.extend(SelectWidget, FormRowWidget, {
2725-
2726- _sorted_position: function(choice) {
2727- var options = [];
2728- this.fieldNode.all("option").each(
2729- function(node) {
2730- options.push(node.get('text'));
2731- }
2732- );
2733- options.push(choice);
2734- return options.sort().indexOf(choice);
2735- },
2736-
2737- /**
2738- * Choose a size for the select control based on the number of
2739- * choices, up to an optional maximum size.
2740- *
2741- * @method autoSize
2742- */
2743- autoSize: function(maxSize) {
2744- var choiceCount = this.fieldNode.all("select > option").size();
2745- if (choiceCount === 0) {
2746- this.set("size", 1);
2747- }
2748- else if (maxSize === undefined) {
2749- this.set("size", choiceCount);
2750- }
2751- else if (choiceCount < maxSize) {
2752- this.set("size", choiceCount);
2753- }
2754- else {
2755- this.set("size", maxSize);
2756- }
2757- return this;
2758- }
2759-
2760-});
2761-
2762-namespace.SelectWidget = SelectWidget;
2763-
2764-
2765-/**
2766 * A special form of SelectWidget for choosing packagesets.
2767 *
2768 * @class PackagesetPickerWidget
2769@@ -1001,7 +489,7 @@
2770 });
2771
2772
2773-Y.extend(PackagesetPickerWidget, SelectWidget, {
2774+Y.extend(PackagesetPickerWidget, formwidgets.SelectWidget, {
2775
2776 /**
2777 * Add a distroseries: add its packagesets to the packageset picker.
2778@@ -1152,96 +640,7 @@
2779 namespace.PackagesetPickerWidget = PackagesetPickerWidget;
2780
2781
2782-/**
2783- * A widget to encapsulate functionality around the form actions.
2784- *
2785- * @class FormActionsWidget
2786- */
2787-var FormActionsWidget;
2788-
2789-FormActionsWidget = function() {
2790- FormActionsWidget
2791- .superclass.constructor.apply(this, arguments);
2792-};
2793-
2794-FormActionsWidget.ATTRS = {
2795- duration: {
2796- value: 1.0
2797- },
2798-
2799- height: {
2800- value: 0
2801- },
2802-
2803- opacity: {
2804- value: 0
2805- }
2806-};
2807-
2808-
2809-Y.mix(FormActionsWidget, {
2810-
2811- NAME: 'formActionsWidget',
2812-
2813- HTML_PARSER: {
2814- submitButtonNode: "input[type=submit]"
2815- }
2816-
2817-});
2818-
2819-Y.extend(FormActionsWidget, Y.Widget, {
2820-
2821- initializer: function(config) {
2822- this.client = new Y.lp.client.Launchpad();
2823- this.error_handler = new Y.lp.client.ErrorHandler();
2824- this.error_handler.clearProgressUI = Y.bind(this.hideSpinner, this);
2825- this.error_handler.showError = Y.bind(this.showError, this);
2826- this.submitButtonNode = config.submitButtonNode;
2827- this.spinnerNode = Y.Node.create(
2828- '<img src="/@@/spinner" alt="Loading..." />');
2829- },
2830-
2831- /**
2832- * Show the spinner, and hide the submit button.
2833- *
2834- * @method showSpinner
2835- */
2836- showSpinner: function() {
2837- this.submitButtonNode.replace(this.spinnerNode);
2838- },
2839-
2840- /**
2841- * Hide the spinner, and show the submit button again.
2842- *
2843- * @method hideSpinner
2844- */
2845- hideSpinner: function() {
2846- this.spinnerNode.replace(this.submitButtonNode);
2847- },
2848-
2849- /**
2850- * Display an error.
2851- *
2852- * @method showError
2853- */
2854- showError: function(error) {
2855- Y.Node.create('<p class="error message" />')
2856- .appendTo(this.get("contentBox"))
2857- .set("text", error);
2858- },
2859-
2860- /**
2861- * Remove all errors that have been previously displayed by showError.
2862- *
2863- * @method hideErrors
2864- */
2865- hideErrors: function(error) {
2866- this.get("contentBox").all("p.error.message").remove();
2867- }
2868-
2869-});
2870-
2871-namespace.FormActionsWidget = FormActionsWidget;
2872-
2873-}, "0.1", {"requires": ["node", "dom", "io", "widget", "lp.client",
2874- "lazr.anim", "array-extras", "transition"]});
2875+}, "0.1", {"requires": [
2876+ "node", "dom", "io", "widget", "lp.client",
2877+ "lp.app.formwidgets", "lazr.anim", "array-extras",
2878+ "transition"]});