Merge lp:~rharding/launchpad/combo_yui_tests into lp:launchpad

Proposed by Richard Harding
Status: Merged
Approved by: Richard Harding
Approved revision: no longer in the source branch.
Merged at revision: 14809
Proposed branch: lp:~rharding/launchpad/combo_yui_tests
Merge into: lp:launchpad
Prerequisite: lp:~stevenk/launchpad/combo-url
Diff against target: 5654 lines (+2801/-2652)
18 files modified
lib/lp/app/javascript/activator/tests/test_activator.html (+46/-24)
lib/lp/app/javascript/activator/tests/test_activator.js (+17/-11)
lib/lp/app/javascript/anim/tests/test_anim.html (+45/-26)
lib/lp/app/javascript/anim/tests/test_anim.js (+187/-195)
lib/lp/app/javascript/autocomplete/tests/test_autocomplete.html (+44/-21)
lib/lp/app/javascript/autocomplete/tests/test_autocomplete.js (+562/-567)
lib/lp/app/javascript/indicator/tests/test_indicator.html (+42/-23)
lib/lp/app/javascript/indicator/tests/test_indicator.js (+5/-5)
lib/lp/app/javascript/inlineedit/tests/test_inline_edit.html (+52/-25)
lib/lp/app/javascript/inlineedit/tests/test_inline_edit.js (+1003/-990)
lib/lp/app/javascript/inlinehelp/tests/test_inlinehelp.html (+41/-25)
lib/lp/app/javascript/inlinehelp/tests/test_inlinehelp.js (+3/-5)
lib/lp/app/javascript/ordering/tests/test_orderby_widget.html (+42/-23)
lib/lp/app/javascript/ordering/tests/test_orderby_widget.js (+374/-380)
lib/lp/app/javascript/overlay/tests/test_overlay.html (+44/-21)
lib/lp/app/javascript/overlay/tests/test_overlay.js (+235/-213)
standard_test_template.html (+40/-24)
standard_test_template.js (+19/-74)
To merge this branch: bzr merge lp:~rharding/launchpad/combo_yui_tests
Reviewer Review Type Date Requested Status
Ian Booth (community) code Approve
Review via email: mp+91478@code.launchpad.net

Commit message

[r=wallyworld][no-qa] Start the process of updating the JS tests to the new build dir used for the combo loader.

Description of the change

= Summary =
Note: This change depends on the branch from Steve in order to make sure the build directory is populated correctly with both YUI and LP JS code. Otherwise the links will fail and tests won't run/load.

With the move to convoy we have a concept of a js build directory. We should be running tests against that directory so that we can easily switch/test different scenarios.

One example is to run our tests from one version of YUI to another. Since the build directory can contain the following:
yui-3.3
yui-3.4
yui (symlink to the current version of yui)

We'd want the tests to be pulling from the yui symlink.

While we're updating tests, we also want to make sure that tests are using a common format for defining/running using our testrunner.js.

This branch starts the work of updating the default test templates for JS tests and the process of updating the modules to use this format pulling from the build directory.

== Implementation Details ==
All files that aren't under direct test should be pulled from the build directory. In this way any tests that fail to work with the build setup should fail and help bring to light issues.

== Tests ==
lib/lp/app/javascript/activator/tests/test_activator.html
lib/lp/app/javascript/anim/tests/test_anim.html
lib/lp/app/javascript/autocomplete/tests/test_autocomplete.html
lib/lp/app/javascript/indicator/tests/test_indicator.html
lib/lp/app/javascript/inlineedit/tests/test_inline_edit.html
lib/lp/app/javascript/inlinehelp/tests/test_inlinehelp.html
lib/lp/app/javascript/ordering/tests/test_orderby_widget.html
lib/lp/app/javascript/overlay/tests/test_overlay.html

== Demo and Q/A ==
We're not touching any production code, so this is a matter of tests passing

To post a comment you must log in.
Revision history for this message
Ian Booth (wallyworld) wrote :

Most excellent, thanks for cleaning up the test templates.

A small typo in each html file to fix:

64 + <!-- Any css assert for this module. -->

Should be 'asset'

Also, why is

65 + <!-- <link rel="stylesheet" href="../assets/activator-core.css" /> -->

Commented out?

review: Approve (code)
Revision history for this message
Richard Harding (rharding) wrote :

Thanks for looking at this Ian. I'll get the typo fixed up.

The commented out CSS is just left from the updated JS test template. Since each file started out as that template, I didn't remove it from each. When I do the global replace of ${LIBRARY} with the name, it ends up as a commented out line. I left it just so that it's consistent and will be there for copy/paste/etc.

Revision history for this message
Ian Booth (wallyworld) wrote :

>
> The commented out CSS is just left from the updated JS test template. Since each file started out as that template, I didn't remove it from each. When I do the global replace of ${LIBRARY} with the name, it ends up as a commented out line. I left it just so that it's consistent and will be there for copy/paste/etc.

No problem. Maybe you could improve the comment in the html so others
know that it's a placeholder and is commented out on purpose.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/app/javascript/activator/tests/test_activator.html'
2--- lib/lp/app/javascript/activator/tests/test_activator.html 2011-08-10 08:43:17 +0000
3+++ lib/lp/app/javascript/activator/tests/test_activator.html 2012-02-15 12:51:42 +0000
4@@ -1,27 +1,49 @@
5-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
6- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
7+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
8+ "http://www.w3.org/TR/html4/strict.dtd">
9+<!--
10+Copyright 2012 Canonical Ltd. This software is licensed under the
11+GNU Affero General Public License version 3 (see the file LICENSE).
12+-->
13+
14 <html>
15 <head>
16- <title>Activator</title>
17-
18- <!-- YUI and test setup -->
19- <script type="text/javascript"
20- src="../../../../../canonical/launchpad/icing/yui/yui/yui.js">
21- </script>
22- <link rel="stylesheet" href="../../../../app/javascript/testing/test.css" />
23- <script type="text/javascript"
24- src="../../../../app/javascript/testing/testrunner.js"></script>
25-
26- <!-- The module under test -->
27- <script type="text/javascript" src="../activator.js"></script>
28- <script type="text/javascript" src="../../anim/anim.js"></script>
29- <script type="text/javascript" src="../../lazr/lazr.js"></script>
30- <script type="text/javascript" src="../../extras/extras.js"></script>
31-
32- <!-- The test suite -->
33- <script type="text/javascript" src="test_activator.js"></script>
34-
35-</head>
36-<body class="yui3-skin-sam">
37-</body>
38+ <title>Activator Tests</title>
39+
40+ <!-- YUI and test setup -->
41+ <script type="text/javascript"
42+ src="../../../../../../build/js/yui/yui/yui.js">
43+ </script>
44+ <link rel="stylesheet"
45+ href="../../../../../../build/js/yui/console/assets/console-core.css" />
46+ <link rel="stylesheet"
47+ href="../../../../../../build/js/yui/console/assets/skins/sam/console.css" />
48+ <link rel="stylesheet"
49+ href="../../../../../../build/js/yui/test/assets/skins/sam/test.css" />
50+
51+ <script type="text/javascript"
52+ src="../../../../../../build/js/lp/app/testing/testrunner.js"></script>
53+
54+ <link rel="stylesheet" href="../../../../app/javascript/testing/test.css" />
55+
56+ <!-- Dependencies -->
57+ <script type="text/javascript" src="../../../../../../build/js/lp/app/anim/anim.js"></script>
58+ <script type="text/javascript" src="../../../../../../build/js/lp/app/lazr/lazr.js"></script>
59+ <script type="text/javascript" src="../../../../../../build/js/lp/app/extras/extras.js"></script>
60+
61+ <!-- The module under test. -->
62+ <script type="text/javascript" src="../activator.js"></script>
63+
64+ <!-- Placeholder for any css asset for this module. -->
65+ <!-- <link rel="stylesheet" href="../assets/activator-core.css" /> -->
66+
67+ <!-- The test suite. -->
68+ <script type="text/javascript" src="test_activator.js"></script>
69+
70+ </head>
71+ <body class="yui3-skin-sam">
72+ <ul id="suites">
73+ <!-- <li>lp.large_indicator.test</li> -->
74+ <li>lp.activator.test</li>
75+ </ul>
76+ </body>
77 </html>
78
79=== modified file 'lib/lp/app/javascript/activator/tests/test_activator.js'
80--- lib/lp/app/javascript/activator/tests/test_activator.js 2011-07-08 05:12:39 +0000
81+++ lib/lp/app/javascript/activator/tests/test_activator.js 2012-02-15 12:51:42 +0000
82@@ -1,7 +1,9 @@
83-/* Copyright (c) 2009, Canonical Ltd. All rights reserved. */
84-
85-YUI().use('lp.testing.runner', 'test', 'console', 'node', 'lazr.activator',
86- 'event', 'event-simulate', function(Y) {
87+/* Copyright (c) 2012, Canonical Ltd. All rights reserved. */
88+
89+YUI.add('lp.activator.test', function (Y) {
90+
91+var tests = Y.namespace('lp.activator.test');
92+tests.suite = new Y.Test.Suite('Activator Tests');
93
94 var Assert = Y.Assert; // For easy access to isTrue(), etc.
95
96@@ -25,12 +27,9 @@
97 widget.destroy();
98 }
99
100-var suite = new Y.Test.Suite("Activator Tests");
101-
102-
103-suite.add(new Y.Test.Case({
104-
105- name: 'activator_basics',
106+tests.suite.add(new Y.Test.Case({
107+ name: 'activator_tests',
108+
109
110 setUp: function() {
111 this.workspace = Y.one('#workspace');
112@@ -68,6 +67,11 @@
113 this.workspace.set('innerHTML', '');
114 },
115
116+ test_library_exists: function () {
117+ Y.Assert.isObject(Y.lazr.activator,
118+ "We should be able to locate the lazr.activator module");
119+ },
120+
121 test_correct_animation_node: function() {
122 // Check that the correct animation node is used.
123 // First check the default.
124@@ -268,6 +272,8 @@
125 }
126 }));
127
128-Y.lp.testing.Runner.run(suite);
129
130+}, '0.1', {
131+ 'requires': ['test', 'console', 'node', 'lazr.activator', 'event',
132+ 'event-simulate']
133 });
134
135=== modified file 'lib/lp/app/javascript/anim/tests/test_anim.html'
136--- lib/lp/app/javascript/anim/tests/test_anim.html 2011-08-09 14:18:02 +0000
137+++ lib/lp/app/javascript/anim/tests/test_anim.html 2012-02-15 12:51:42 +0000
138@@ -1,29 +1,48 @@
139-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
140- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
141+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
142+ "http://www.w3.org/TR/html4/strict.dtd">
143+<!--
144+Copyright 2012 Canonical Ltd. This software is licensed under the
145+GNU Affero General Public License version 3 (see the file LICENSE).
146+-->
147+
148 <html>
149 <head>
150- <title>Anim</title>
151-
152- <!-- YUI and test setup -->
153- <script type="text/javascript"
154- src="../../../../../canonical/launchpad/icing/yui/yui/yui.js">
155- </script>
156- <link rel="stylesheet" href="../../../../app/javascript/testing/test.css" />
157- <script type="text/javascript"
158- src="../../../../app/javascript/testing/testrunner.js"></script>
159- <script type="text/javascript"
160- src="../../extras/extras.js"></script>
161-
162- <!-- The module under test -->
163- <script type="text/javascript" src="../anim.js"></script>
164- <script type="text/javascript" src="../../lazr/lazr.js"></script>
165-
166- <!-- The test suite -->
167- <script type="text/javascript" src="test_anim.js"></script>
168- </head>
169-<body class="yui3-skin-sam">
170- <ul id="suites">
171- <li>lp.anim.test</li>
172- </ul>
173-</body>
174+ <title>Anim Tests</title>
175+
176+ <!-- YUI and test setup -->
177+ <script type="text/javascript"
178+ src="../../../../../../build/js/yui/yui/yui.js">
179+ </script>
180+ <link rel="stylesheet"
181+ href="../../../../../../build/js/yui/console/assets/console-core.css" />
182+ <link rel="stylesheet"
183+ href="../../../../../../build/js/yui/console/assets/skins/sam/console.css" />
184+ <link rel="stylesheet"
185+ href="../../../../../../build/js/yui/test/assets/skins/sam/test.css" />
186+
187+ <script type="text/javascript"
188+ src="../../../../../../build/js/lp/app/testing/testrunner.js"></script>
189+
190+ <link rel="stylesheet" href="../../../../app/javascript/testing/test.css" />
191+
192+ <!-- Dependencies -->
193+ <script type="text/javascript" src="../../../../../../build/js/lp/app/extras/extras.js"></script>
194+ <script type="text/javascript" src="../../../../../../build/js/lp/app/lazr/lazr.js"></script>
195+
196+ <!-- The module under test. -->
197+ <script type="text/javascript" src="../anim.js"></script>
198+
199+ <!-- Placeholder for any css asset for this module. -->
200+ <!-- <link rel="stylesheet" href="../assets/anim-core.css" /> -->
201+
202+ <!-- The test suite. -->
203+ <script type="text/javascript" src="test_anim.js"></script>
204+
205+ </head>
206+ <body class="yui3-skin-sam">
207+ <ul id="suites">
208+ <!-- <li>lp.large_indicator.test</li> -->
209+ <li>lp.anim.test</li>
210+ </ul>
211+ </body>
212 </html>
213
214=== modified file 'lib/lp/app/javascript/anim/tests/test_anim.js'
215--- lib/lp/app/javascript/anim/tests/test_anim.js 2011-08-10 08:41:49 +0000
216+++ lib/lp/app/javascript/anim/tests/test_anim.js 2012-02-15 12:51:42 +0000
217@@ -1,202 +1,194 @@
218 /* Copyright (c) 2009-2011, Canonical Ltd. All rights reserved. */
219
220 YUI.add('lp.anim.test', function(Y) {
221-
222-var namespace = Y.namespace('lp.anim.test');
223-
224-var Assert = Y.Assert, // For easy access to isTrue(), etc.
225- ArrayAssert = Y.ArrayAssert;
226-
227-var suite = new Y.Test.Suite("Anim Tests");
228-
229-var TestAnim = {
230- name: 'TestAnim',
231-
232- setUp: function() {
233- this.workspace = Y.Node.create(
234- '<div id="workspace">'
235- + '<table id="anim-table">'
236- + '<tr id="anim-table-tr">'
237- + '<td id="anim-table-td1" style="background: #eeeeee">foo</td>'
238- + '<td id="anim-table-td2" style="background: #eeeeee">bar</td>'
239- + '</tr></table></div>'
240- );
241- Y.one(document.body).append(this.workspace);
242- },
243-
244- tearDown: function() {
245- this.workspace.remove(true);
246- },
247-
248- test_resolveNodeListFrom_selector: function() {
249- var selector = '#anim-table-td1';
250- var nodelist = namespace.resolveNodeListFrom(selector);
251- Assert.isInstanceOf(Y.NodeList, nodelist);
252- ArrayAssert.itemsAreSame(
253- [Y.one(selector)], nodelist._nodes.map(Y.one));
254- },
255-
256- test_resolveNodeListFrom_node: function() {
257- var node = Y.one('#anim-table-td1');
258- var nodelist = namespace.resolveNodeListFrom(node);
259- Assert.isInstanceOf(Y.NodeList, nodelist);
260- ArrayAssert.itemsAreSame(
261- [node], nodelist._nodes.map(Y.one));
262- },
263-
264- test_resolveNodeListFrom_node_list: function() {
265- var nodelist_orig = Y.all('#anim-table td');
266- var nodelist = namespace.resolveNodeListFrom(nodelist_orig);
267- Assert.isInstanceOf(Y.NodeList, nodelist);
268- Assert.areSame(nodelist, nodelist_orig);
269- },
270-
271- test_resolveNodeListFrom_anythine_else: function() {
272- var succeed = true;
273- try {
274- var nodelist = namespace.resolveNodeListFrom(
275- {crazy: true, broken: 'definitely'});
276- } catch(e) {
277- succeed = false;
278+ var Assert = Y.Assert, // For easy access to isTrue(), etc.
279+ ArrayAssert = Y.ArrayAssert;
280+ var tests = Y.namespace('lp.anim.test');
281+
282+ tests.suite = new Y.Test.Suite('Anim Tets');
283+ tests.suite.add(new Y.Test.Case({
284+ name: 'TestAnim',
285+
286+ setUp: function() {
287+ this.workspace = Y.Node.create(
288+ '<div id="workspace">'
289+ + '<table id="anim-table">'
290+ + '<tr id="anim-table-tr">'
291+ + '<td id="anim-table-td1" style="background: #eeeeee">foo</td>'
292+ + '<td id="anim-table-td2" style="background: #eeeeee">bar</td>'
293+ + '</tr></table></div>'
294+ );
295+ Y.one(document.body).append(this.workspace);
296+ },
297+
298+ tearDown: function() {
299+ this.workspace.remove(true);
300+ },
301+
302+ test_resolveNodeListFrom_selector: function() {
303+ var selector = '#anim-table-td1';
304+ var nodelist = tests.resolveNodeListFrom(selector);
305+ Assert.isInstanceOf(Y.NodeList, nodelist);
306+ ArrayAssert.itemsAreSame(
307+ [Y.one(selector)], nodelist._nodes.map(Y.one));
308+ },
309+
310+ test_resolveNodeListFrom_node: function() {
311+ var node = Y.one('#anim-table-td1');
312+ var nodelist = tests.resolveNodeListFrom(node);
313+ Assert.isInstanceOf(Y.NodeList, nodelist);
314+ ArrayAssert.itemsAreSame(
315+ [node], nodelist._nodes.map(Y.one));
316+ },
317+
318+ test_resolveNodeListFrom_node_list: function() {
319+ var nodelist_orig = Y.all('#anim-table td');
320+ var nodelist = tests.resolveNodeListFrom(nodelist_orig);
321+ Assert.isInstanceOf(Y.NodeList, nodelist);
322+ Assert.areSame(nodelist, nodelist_orig);
323+ },
324+
325+ test_resolveNodeListFrom_anythine_else: function() {
326+ var succeed = true;
327+ try {
328+ var nodelist = tests.resolveNodeListFrom(
329+ {crazy: true, broken: 'definitely'});
330+ } catch(e) {
331+ succeed = false;
332+ }
333+ Assert.isFalse(succeed, "Somehow, we're cleverer than we thought.");
334+ },
335+
336+ test_green_flash_td1: function() {
337+ // works as expected on a single node,
338+ // without coercion into a NodeList here
339+ var node = Y.one('#anim-table-td1');
340+ var bgcolor = node.getStyle('backgroundColor');
341+ var anim = Y.lp.anim.green_flash(
342+ {node: node,
343+ to: {backgroundColor: bgcolor},
344+ duration: 0.2}
345+ );
346+ anim.run();
347+ this.wait(function() {
348+ Assert.areEqual(
349+ bgcolor,
350+ node.getStyle('backgroundColor'),
351+ 'background colors do not match'
352+ );
353+ }, 500
354+ );
355+ },
356+
357+ test_green_flash_td1_by_selector: function() {
358+ // works as expected on a single node selector,
359+ // without coercion into a NodeList here
360+ var node = Y.one('#anim-table-td1');
361+ var bgcolor = node.getStyle('backgroundColor');
362+ var anim = Y.lp.anim.green_flash(
363+ {node: '#anim-table-td1',
364+ to: {backgroundColor: bgcolor},
365+ duration: 0.2}
366+ );
367+ anim.run();
368+ this.wait(function() {
369+ Assert.areEqual(
370+ bgcolor,
371+ node.getStyle('backgroundColor'),
372+ 'background colors do not match'
373+ );
374+ }, 500
375+ );
376+ },
377+
378+ test_green_flash_multi: function() {
379+ // works with a native NodeList as well
380+ var nodelist = Y.all('#anim-table td');
381+ var red = '#ff0000';
382+ var backgrounds = [];
383+ Y.each(nodelist, function(n) {
384+ backgrounds.push({bg: n.getStyle('backgroundColor'), node: n});
385+ });
386+ var anim = Y.lp.anim.green_flash(
387+ {node: nodelist,
388+ to: {backgroundColor: red},
389+ duration: 5}
390+ );
391+ anim.run();
392+ this.wait(function() {
393+ Assert.areNotEqual(
394+ backgrounds[0].node.getStyle('backgroundColor'),
395+ red,
396+ 'background of 0 has mysteriously jumped to the end color.'
397+ );
398+ Assert.areNotEqual(
399+ backgrounds[1].node.getStyle('backgroundColor'),
400+ red,
401+ 'background of 1 has mysteriously jumped to the end color.'
402+ );
403+ Assert.areNotEqual(
404+ backgrounds[0].node.getStyle('backgroundColor'),
405+ backgrounds[0].bg,
406+ 'background of 0 has not changed at all.'
407+ );
408+ Assert.areNotEqual(
409+ backgrounds[1].node.getStyle('backgroundColor'),
410+ backgrounds[1].bg,
411+ 'background of 1 has not changed at all.'
412+ );
413+ }, 1500);
414+ },
415+
416+ test_green_flash_multi_by_selector: function() {
417+ // works with a native NodeList as well
418+ var nodelist = Y.all('#anim-table td');
419+ var red = '#ff0000';
420+ var backgrounds = [];
421+ Y.each(nodelist, function(n) {
422+ backgrounds.push({bg: n.getStyle('backgroundColor'), node: n});
423+ });
424+ var anim = Y.lp.anim.green_flash(
425+ {node: '#anim-table td',
426+ to: {backgroundColor: red},
427+ duration: 2}
428+ );
429+ anim.run();
430+ this.wait(function() {
431+ Assert.areNotEqual(
432+ backgrounds[0].node.getStyle('backgroundColor'),
433+ red,
434+ 'background of 0 has mysteriously jumped to the end color.'
435+ );
436+ Assert.areNotEqual(
437+ backgrounds[1].node.getStyle('backgroundColor'),
438+ red,
439+ 'background of 1 has mysteriously jumped to the end color.'
440+ );
441+ Assert.areNotEqual(
442+ backgrounds[0].node.getStyle('backgroundColor'),
443+ backgrounds[0].bg,
444+ 'background of 0 has not changed at all.'
445+ );
446+ Assert.areNotEqual(
447+ backgrounds[1].node.getStyle('backgroundColor'),
448+ backgrounds[1].bg,
449+ 'background of 1 has not changed at all.'
450+ );
451+ }, 500);
452+ },
453+
454+ test_on: function() {
455+ // Anim.on delegates to its component animations.
456+ var targets = [];
457+ var anim = new Y.lp.anim.Anim({node: "#anim-table td"});
458+ Assert.areSame(2, anim._anims.length);
459+ anim.on("start", function(event) { targets.push(event.target); });
460+ anim.run();
461+ Assert.areSame(2, targets.length);
462+ ArrayAssert.containsItems(anim._anims, targets);
463 }
464- Assert.isFalse(succeed, "Somehow, we're cleverer than we thought.");
465- },
466-
467- test_green_flash_td1: function() {
468- // works as expected on a single node,
469- // without coercion into a NodeList here
470- var node = Y.one('#anim-table-td1');
471- var bgcolor = node.getStyle('backgroundColor');
472- var anim = Y.lp.anim.green_flash(
473- {node: node,
474- to: {backgroundColor: bgcolor},
475- duration: 0.2}
476- );
477- anim.run();
478- this.wait(function() {
479- Assert.areEqual(
480- bgcolor,
481- node.getStyle('backgroundColor'),
482- 'background colors do not match'
483- );
484- }, 500
485- );
486- },
487-
488- test_green_flash_td1_by_selector: function() {
489- // works as expected on a single node selector,
490- // without coercion into a NodeList here
491- var node = Y.one('#anim-table-td1');
492- var bgcolor = node.getStyle('backgroundColor');
493- var anim = Y.lp.anim.green_flash(
494- {node: '#anim-table-td1',
495- to: {backgroundColor: bgcolor},
496- duration: 0.2}
497- );
498- anim.run();
499- this.wait(function() {
500- Assert.areEqual(
501- bgcolor,
502- node.getStyle('backgroundColor'),
503- 'background colors do not match'
504- );
505- }, 500
506- );
507- },
508-
509- test_green_flash_multi: function() {
510- // works with a native NodeList as well
511- var nodelist = Y.all('#anim-table td');
512- var red = '#ff0000';
513- var backgrounds = [];
514- Y.each(nodelist, function(n) {
515- backgrounds.push({bg: n.getStyle('backgroundColor'), node: n});
516- });
517- var anim = Y.lp.anim.green_flash(
518- {node: nodelist,
519- to: {backgroundColor: red},
520- duration: 5}
521- );
522- anim.run();
523- this.wait(function() {
524- Assert.areNotEqual(
525- backgrounds[0].node.getStyle('backgroundColor'),
526- red,
527- 'background of 0 has mysteriously jumped to the end color.'
528- );
529- Assert.areNotEqual(
530- backgrounds[1].node.getStyle('backgroundColor'),
531- red,
532- 'background of 1 has mysteriously jumped to the end color.'
533- );
534- Assert.areNotEqual(
535- backgrounds[0].node.getStyle('backgroundColor'),
536- backgrounds[0].bg,
537- 'background of 0 has not changed at all.'
538- );
539- Assert.areNotEqual(
540- backgrounds[1].node.getStyle('backgroundColor'),
541- backgrounds[1].bg,
542- 'background of 1 has not changed at all.'
543- );
544- }, 1500);
545- },
546-
547- test_green_flash_multi_by_selector: function() {
548- // works with a native NodeList as well
549- var nodelist = Y.all('#anim-table td');
550- var red = '#ff0000';
551- var backgrounds = [];
552- Y.each(nodelist, function(n) {
553- backgrounds.push({bg: n.getStyle('backgroundColor'), node: n});
554- });
555- var anim = Y.lp.anim.green_flash(
556- {node: '#anim-table td',
557- to: {backgroundColor: red},
558- duration: 2}
559- );
560- anim.run();
561- this.wait(function() {
562- Assert.areNotEqual(
563- backgrounds[0].node.getStyle('backgroundColor'),
564- red,
565- 'background of 0 has mysteriously jumped to the end color.'
566- );
567- Assert.areNotEqual(
568- backgrounds[1].node.getStyle('backgroundColor'),
569- red,
570- 'background of 1 has mysteriously jumped to the end color.'
571- );
572- Assert.areNotEqual(
573- backgrounds[0].node.getStyle('backgroundColor'),
574- backgrounds[0].bg,
575- 'background of 0 has not changed at all.'
576- );
577- Assert.areNotEqual(
578- backgrounds[1].node.getStyle('backgroundColor'),
579- backgrounds[1].bg,
580- 'background of 1 has not changed at all.'
581- );
582- }, 500);
583- },
584-
585- test_on: function() {
586- // Anim.on delegates to its component animations.
587- var targets = [];
588- var anim = new Y.lp.anim.Anim({node: "#anim-table td"});
589- Assert.areSame(2, anim._anims.length);
590- anim.on("start", function(event) { targets.push(event.target); });
591- anim.run();
592- Assert.areSame(2, targets.length);
593- ArrayAssert.containsItems(anim._anims, targets);
594- }
595-
596-};
597-
598-suite.add(new Y.Test.Case(TestAnim));
599-
600-// Exports.
601-namespace.suite = suite;
602+
603+ }));
604
605 }, "0.1", {"requires": [
606 'test', 'node', 'lp.anim', 'event',
607
608=== modified file 'lib/lp/app/javascript/autocomplete/tests/test_autocomplete.html'
609--- lib/lp/app/javascript/autocomplete/tests/test_autocomplete.html 2011-07-08 06:06:15 +0000
610+++ lib/lp/app/javascript/autocomplete/tests/test_autocomplete.html 2012-02-15 12:51:42 +0000
611@@ -1,24 +1,47 @@
612-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
613- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
614+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
615+ "http://www.w3.org/TR/html4/strict.dtd">
616+<!--
617+Copyright 2012 Canonical Ltd. This software is licensed under the
618+GNU Affero General Public License version 3 (see the file LICENSE).
619+-->
620+
621 <html>
622 <head>
623- <title>autocomplete unit tests</title>
624-
625- <!-- YUI and test setup -->
626- <script type="text/javascript"
627- src="../../../../../canonical/launchpad/icing/yui/yui/yui.js">
628- </script>
629- <link rel="stylesheet" href="../../../../app/javascript/testing/test.css" />
630- <script type="text/javascript"
631- src="../../../../app/javascript/testing/testrunner.js"></script>
632-
633- <!-- The module under test -->
634- <script type="text/javascript" src="../autocomplete.js"></script>
635-
636- <!-- The test suite -->
637- <script type="text/javascript" src="test_autocomplete.js"></script>
638-
639-</head>
640-<body class="yui3-skin-sam">
641-</body>
642+ <title>Autocomplete Tests</title>
643+
644+ <!-- YUI and test setup -->
645+ <script type="text/javascript"
646+ src="../../../../../../build/js/yui/yui/yui.js">
647+ </script>
648+ <link rel="stylesheet"
649+ href="../../../../../../build/js/yui/console/assets/console-core.css" />
650+ <link rel="stylesheet"
651+ href="../../../../../../build/js/yui/console/assets/skins/sam/console.css" />
652+ <link rel="stylesheet"
653+ href="../../../../../../build/js/yui/test/assets/skins/sam/test.css" />
654+
655+ <script type="text/javascript"
656+ src="../../../../../../build/js/lp/app/testing/testrunner.js"></script>
657+
658+ <link rel="stylesheet" href="../../../../app/javascript/testing/test.css" />
659+
660+ <!-- Dependencies -->
661+ <!-- <script type="text/javascript" src="../../../../../../build/js/lp/..."></script> -->
662+
663+ <!-- The module under test. -->
664+ <script type="text/javascript" src="../autocomplete.js"></script>
665+
666+ <!-- Placeholder for any css asset for this module. -->
667+ <!-- <link rel="stylesheet" href="../assets/autocomplete-core.css" /> -->
668+
669+ <!-- The test suite. -->
670+ <script type="text/javascript" src="test_autocomplete.js"></script>
671+
672+ </head>
673+ <body class="yui3-skin-sam">
674+ <ul id="suites">
675+ <!-- <li>lp.large_indicator.test</li> -->
676+ <li>lp.autocomplete.test</li>
677+ </ul>
678+ </body>
679 </html>
680
681=== modified file 'lib/lp/app/javascript/autocomplete/tests/test_autocomplete.js'
682--- lib/lp/app/javascript/autocomplete/tests/test_autocomplete.js 2011-07-08 05:12:39 +0000
683+++ lib/lp/app/javascript/autocomplete/tests/test_autocomplete.js 2012-02-15 12:51:42 +0000
684@@ -1,569 +1,564 @@
685-/* Copyright (c) 2009, Canonical Ltd. All rights reserved. */
686-
687-YUI().use('lp.testing.runner', 'test', 'console', 'node', 'lazr.autocomplete',
688- 'event', 'event-simulate', function(Y) {
689-
690-/*****************************
691- *
692- * Helper methods and aliases
693- *
694- */
695-var Assert = Y.Assert;
696-
697-/* Helper function to clean up a dynamically added widget instance. */
698-function cleanup_widget(widget) {
699- // Nuke the boundingBox, but only if we've touched the DOM.
700- if (widget.get('rendered')) {
701- var bb = widget.get('boundingBox');
702- bb.get('parentNode').removeChild(bb);
703- }
704- // Kill the widget itself.
705- widget.destroy();
706-}
707-
708-/* A helper to create a simple text input box */
709-function make_input(value) {
710- var input = document.createElement('input');
711- input.setAttribute('type', 'text');
712- input.setAttribute('value', value || '');
713- Y.one('body').appendChild(input);
714- return input;
715-}
716-
717-/* A helper to destroy a generic input: make_input()'s inverse */
718-function kill_input(input) {
719- Y.one('body').removeChild(input);
720-}
721-
722-
723-/****************************
724- *
725- * Tests
726- *
727- */
728-
729-var suite = new Y.Test.Suite('autocomplete Test Suite');
730-
731-
732-suite.add(new Y.Test.Case({
733-
734- name:'test widget setup',
735-
736- setUp: function() {
737- this.input = make_input();
738- },
739-
740- tearDown: function() {
741- kill_input(this.input);
742- },
743-
744- test_widget_starts_hidden: function() {
745- var autocomp = new Y.lazr.AutoComplete({ input: this.input });
746- autocomp.render();
747- Assert.isFalse(
748- autocomp.get('visible'),
749- "The widget should start out hidden.");
750- }
751-}));
752-
753-
754-suite.add(new Y.Test.Case({
755-
756- name:'test display of matching results',
757-
758- setUp: function() {
759- this.input = make_input();
760- this.autocomp = new Y.lazr.AutoComplete({
761- input: this.input
762- });
763- },
764-
765- tearDown: function() {
766- cleanup_widget(this.autocomp);
767- kill_input(this.input);
768- },
769-
770- /* A helper to option the completions list for a given input string. */
771- complete_input: function(value) {
772- this.input.value = value;
773- var last_charcode = value.charCodeAt(value.length - 1);
774- Y.Event.simulate(this.input, 'keyup', { keyCode: last_charcode });
775- },
776-
777- /* Extract the matching text from the widget's autocompletion list. */
778- get_completions: function() {
779- if (!this.autocomp.get('rendered')) {
780- Y.fail("Tried find matches for an unrendered widget.");
781- return;
782- }
783-
784- var matches = [];
785- this.autocomp
786- .get('boundingBox')
787- .all('.item')
788- .each(function(item) {
789- matches.push(item.get('text'));
790- });
791- return matches;
792- },
793-
794- test_autocomplete_is_visible_if_results_match: function() {
795- this.autocomp.set('data', ['aaa']);
796- this.autocomp.render();
797-
798- // We want to match the one and only data set element.
799- this.complete_input('aa');
800- Assert.isTrue(
801- this.autocomp.get('visible'),
802- "The widget should be visible if matching input was found.");
803- },
804-
805- test_autocomplete_is_hidden_if_no_query_is_given: function() {
806- this.autocomp.set('data', ['aaa']);
807- this.autocomp.render();
808-
809- // We want to simulate an empty input field, but some action triggers
810- // matching.
811- this.complete_input('');
812- Assert.isFalse(
813- this.autocomp.get('visible'),
814- "The widget should be hidden if the input field is empty.");
815- },
816-
817- test_autocomplete_is_hidden_if_results_do_not_match: function() {
818- this.autocomp.set('data', ['bbb']);
819- this.autocomp.render();
820-
821- if (this.autocomp.get('visible')) {
822- Y.fail("The autocomplete widget should start out hidden.");
823- }
824-
825-
826- // 'aa' shouldn't match any of the data.
827- this.complete_input('aa');
828- Assert.isFalse(
829- this.autocomp.get('visible'),
830- "The widget should be hidden if the query doesn't match any " +
831- "possible completions.");
832- },
833-
834- test_display_should_contain_all_matches: function() {
835- var data = [
836- 'aaa',
837- 'baa'
838- ];
839-
840- this.autocomp.set('data', data);
841- this.autocomp.render();
842-
843- // Trigger autocompletion, should match all data items.
844- this.complete_input('aa');
845-
846- // Grab the now-open menu
847- var option_list = Y.one('.yui3-autocomplete-list');
848- Assert.isObject(option_list,
849- "The list of completion options should be open.");
850-
851- Y.ArrayAssert.itemsAreEqual(
852- this.get_completions(),
853- data,
854- "Every autocomplete item should be present in the available " +
855- "match keys.");
856- },
857-
858- test_display_is_updated_with_new_completions: function() {
859- // Create two pieces of data, each narrower than the other.
860- this.autocomp.set('data', ['aaa', 'aab']);
861- this.autocomp.render();
862-
863- // Trigger autocompletion for the loosest matches
864- this.complete_input('aa');
865- // Complete the narrower set
866- this.complete_input('aaa');
867-
868- var completions = this.get_completions();
869-
870- Y.ArrayAssert.itemsAreEqual(
871- ['aaa'],
872- completions,
873- "'aaa' should be the data item displayed after narrowing the " +
874- "search with the query 'aaa'.");
875- },
876-
877- test_matching_text_in_item_is_marked: function() {
878- this.autocomp.set('data', ['aaa']);
879- this.autocomp.render();
880-
881- // Display the matching input.
882- var query = 'aa';
883- this.complete_input(query);
884-
885- // Grab the matching item
886- var matching_text = this.autocomp
887- .get('boundingBox')
888- .one('.item .matching-text');
889-
890- Assert.isNotNull(matching_text,
891- "Some of the matching item's text should be marked as matching.");
892-
893- Assert.areEqual(
894- query,
895- matching_text.get('text'),
896- "The matching text should be the same as the query text.");
897- },
898-
899- test_escape_key_should_close_completions_list: function() {
900- this.autocomp.set('data', ['aaa']);
901- this.autocomp.render();
902-
903- // Open the completions list
904- this.complete_input('aa');
905-
906- // Hit the escape key to close the list
907- Y.Event.simulate(this.input, 'keydown', { keyCode: 27 });
908-
909- Assert.isFalse(
910- this.autocomp.get('visible'),
911- "The list of completions should be closed after pressing the " +
912- "escape key.");
913- }
914-}));
915-
916-suite.add(new Y.Test.Case({
917-
918- name:'test result text marking method',
919-
920- test_match_at_beginning_should_be_marked: function() {
921- var autocomp = new Y.lazr.AutoComplete();
922- var marked_text = autocomp.markMatchingText('aabb', 'aa', 0);
923-
924- Assert.areEqual(
925- '<span class="matching-text">aa</span>bb',
926- marked_text,
927- "The text at the beginning of the result should have been " +
928- "marked.");
929- },
930-
931- test_match_in_middle_should_be_marked: function() {
932- var autocomp = new Y.lazr.AutoComplete();
933- var marked_text = autocomp.markMatchingText('baab', 'aa', 1);
934-
935- Assert.areEqual(
936- 'b<span class="matching-text">aa</span>b',
937- marked_text,
938- "The text in the middle of the result should have been " +
939- "marked.");
940- },
941-
942- test_match_at_end_should_be_marked: function() {
943- var autocomp = new Y.lazr.AutoComplete();
944- var marked_text = autocomp.markMatchingText('bbaa', 'aa', 2);
945-
946- Assert.areEqual(
947- 'bb<span class="matching-text">aa</span>',
948- marked_text,
949- "The text at the end of the result should have been " +
950- "marked.");
951- }
952-}));
953-
954-
955-suite.add(new Y.Test.Case({
956-
957- name:'test query parsing',
958-
959- setUp: function() {
960- this.autocomplete = new Y.lazr.AutoComplete({
961- delimiter: ' '
962- });
963- },
964-
965- test_space_for_delimiter: function() {
966- Assert.areEqual(
967- 'b',
968- this.autocomplete.parseQuery('a b').text,
969- "Input should be split around the 'space' character.");
970- Assert.isNull(
971- this.autocomplete.parseQuery(' '),
972- "Space for input and delimiter should not parse.");
973- },
974-
975- test_parsed_query_is_stripped_of_leading_whitespace: function() {
976- this.autocomplete.set('delimiter', ',');
977-
978- Assert.areEqual(
979- 'a',
980- this.autocomplete.parseQuery(' a').text,
981- "Leading whitespace at the start of the input string should " +
982- "be stripped.");
983-
984- Assert.areEqual(
985- 'b',
986- this.autocomplete.parseQuery('a, b').text,
987- "Leading whitespace between the last separator and the current " +
988- "query should be stripped.");
989- },
990-
991- test_query_is_taken_from_middle_of_input: function() {
992- // Pick a caret position that is in the middle of the second result.
993- var input = "aaa bbb ccc";
994- var caret = 6;
995-
996- Assert.areEqual(
997- 'bbb',
998- this.autocomplete.parseQuery(input, caret).text,
999- "The current query should be picked out of the middle of the " +
1000- "text input if the caret has been positioned there.");
1001- },
1002-
1003- test_query_is_taken_from_beginning_of_input: function() {
1004- // Pick a caret position that is in the first input's query
1005- var input = "aaa bbb";
1006- var caret = 2;
1007-
1008- Assert.areEqual(
1009- 'aaa',
1010- this.autocomplete.parseQuery(input, caret).text,
1011- "The first block of text should become the current query if " +
1012- "the caret is positioned within it.");
1013- }
1014-}));
1015-
1016-suite.add(new Y.Test.Case({
1017-
1018- name:'test results matching algorithm',
1019-
1020- /* A helper function to determine if two match result items are equal */
1021- matches_are_equal: function(a, b) {
1022- if (typeof a == 'undefined') {
1023- Assert.fail("Match set 'a' is of type 'undefined'!");
1024- }
1025- if (typeof b == 'undefined') {
1026- Assert.fail("Match set 'b' is of type 'undefined'!");
1027- }
1028- return (a.text == b.text) && (a.offset == b.offset);
1029- },
1030-
1031- test_no_matches_returns_an_empty_array: function() {
1032- var autocomplete = new Y.lazr.AutoComplete({
1033- data: ['ccc']
1034- });
1035-
1036- var matches = autocomplete.findMatches('aa');
1037- Y.ArrayAssert.isEmpty(matches,
1038- "No data should have matched the query 'aa'");
1039- },
1040-
1041- test_match_last_item: function() {
1042- var autocomplete = new Y.lazr.AutoComplete({
1043- data: [
1044- 'ccc',
1045+/* Copyright (c) 2012, Canonical Ltd. All rights reserved. */
1046+
1047+YUI.add('lp.autocomplete.test', function (Y) {
1048+ var tests = Y.namespace('lp.autocomplete.test');
1049+ tests.suite = new Y.Test.Suite('autocomplete Tests');
1050+
1051+ /*****************************
1052+ *
1053+ * Helper methods and aliases
1054+ *
1055+ */
1056+ var Assert = Y.Assert;
1057+
1058+ /* Helper function to clean up a dynamically added widget instance. */
1059+ function cleanup_widget(widget) {
1060+ // Nuke the boundingBox, but only if we've touched the DOM.
1061+ if (widget.get('rendered')) {
1062+ var bb = widget.get('boundingBox');
1063+ bb.get('parentNode').removeChild(bb);
1064+ }
1065+ // Kill the widget itself.
1066+ widget.destroy();
1067+ }
1068+
1069+ /* A helper to create a simple text input box */
1070+ function make_input(value) {
1071+ var input = document.createElement('input');
1072+ input.setAttribute('type', 'text');
1073+ input.setAttribute('value', value || '');
1074+ Y.one('body').appendChild(input);
1075+ return input;
1076+ }
1077+
1078+ /* A helper to destroy a generic input: make_input()'s inverse */
1079+ function kill_input(input) {
1080+ Y.one('body').removeChild(input);
1081+ }
1082+
1083+ tests.suite.add(new Y.Test.Case({
1084+ name:'test widget setup',
1085+
1086+ setUp: function() {
1087+ this.input = make_input();
1088+ },
1089+
1090+ tearDown: function() {
1091+ kill_input(this.input);
1092+ },
1093+
1094+ test_library_exists: function () {
1095+ Y.Assert.isObject(Y.lazr.AutoComplete,
1096+ "We should be able to locate the lazr.autocomplete module");
1097+ },
1098+
1099+ test_widget_starts_hidden: function() {
1100+ var autocomp = new Y.lazr.AutoComplete({ input: this.input });
1101+ autocomp.render();
1102+ Assert.isFalse(
1103+ autocomp.get('visible'),
1104+ "The widget should start out hidden.");
1105+ }
1106+ }));
1107+
1108+ tests.suite.add(new Y.Test.Case({
1109+
1110+ name:'test display of matching results',
1111+
1112+ setUp: function() {
1113+ this.input = make_input();
1114+ this.autocomp = new Y.lazr.AutoComplete({
1115+ input: this.input
1116+ });
1117+ },
1118+
1119+ tearDown: function() {
1120+ cleanup_widget(this.autocomp);
1121+ kill_input(this.input);
1122+ },
1123+
1124+ /* A helper to option the completions list for a given input string. */
1125+ complete_input: function(value) {
1126+ this.input.value = value;
1127+ var last_charcode = value.charCodeAt(value.length - 1);
1128+ Y.Event.simulate(this.input, 'keyup', { keyCode: last_charcode });
1129+ },
1130+
1131+ /* Extract the matching text from the widget's autocompletion list. */
1132+ get_completions: function() {
1133+ if (!this.autocomp.get('rendered')) {
1134+ Y.fail("Tried find matches for an unrendered widget.");
1135+ return;
1136+ }
1137+
1138+ var matches = [];
1139+ this.autocomp
1140+ .get('boundingBox')
1141+ .all('.item')
1142+ .each(function(item) {
1143+ matches.push(item.get('text'));
1144+ });
1145+ return matches;
1146+ },
1147+
1148+ test_autocomplete_is_visible_if_results_match: function() {
1149+ this.autocomp.set('data', ['aaa']);
1150+ this.autocomp.render();
1151+
1152+ // We want to match the one and only data set element.
1153+ this.complete_input('aa');
1154+ Assert.isTrue(
1155+ this.autocomp.get('visible'),
1156+ "The widget should be visible if matching input was found.");
1157+ },
1158+
1159+ test_autocomplete_is_hidden_if_no_query_is_given: function() {
1160+ this.autocomp.set('data', ['aaa']);
1161+ this.autocomp.render();
1162+
1163+ // We want to simulate an empty input field, but some action
1164+ // triggers matching.
1165+ this.complete_input('');
1166+ Assert.isFalse(
1167+ this.autocomp.get('visible'),
1168+ "The widget should be hidden if the input field is empty.");
1169+ },
1170+
1171+ test_autocomplete_is_hidden_if_results_do_not_match: function() {
1172+ this.autocomp.set('data', ['bbb']);
1173+ this.autocomp.render();
1174+
1175+ if (this.autocomp.get('visible')) {
1176+ Y.fail("The autocomplete widget should start out hidden.");
1177+ }
1178+
1179+
1180+ // 'aa' shouldn't match any of the data.
1181+ this.complete_input('aa');
1182+ Assert.isFalse(
1183+ this.autocomp.get('visible'),
1184+ "The widget should be hidden if the query doesn't match any " +
1185+ "possible completions.");
1186+ },
1187+
1188+ test_display_should_contain_all_matches: function() {
1189+ var data = [
1190+ 'aaa',
1191+ 'baa'
1192+ ];
1193+
1194+ this.autocomp.set('data', data);
1195+ this.autocomp.render();
1196+
1197+ // Trigger autocompletion, should match all data items.
1198+ this.complete_input('aa');
1199+
1200+ // Grab the now-open menu
1201+ var option_list = Y.one('.yui3-autocomplete-list');
1202+ Assert.isObject(option_list,
1203+ "The list of completion options should be open.");
1204+
1205+ Y.ArrayAssert.itemsAreEqual(
1206+ this.get_completions(),
1207+ data,
1208+ "Every autocomplete item should be present in the available " +
1209+ "match keys.");
1210+ },
1211+
1212+ test_display_is_updated_with_new_completions: function() {
1213+ // Create two pieces of data, each narrower than the other.
1214+ this.autocomp.set('data', ['aaa', 'aab']);
1215+ this.autocomp.render();
1216+
1217+ // Trigger autocompletion for the loosest matches
1218+ this.complete_input('aa');
1219+ // Complete the narrower set
1220+ this.complete_input('aaa');
1221+
1222+ var completions = this.get_completions();
1223+
1224+ Y.ArrayAssert.itemsAreEqual(
1225+ ['aaa'],
1226+ completions,
1227+ "'aaa' should be the data item displayed after narrowing the " +
1228+ "search with the query 'aaa'.");
1229+ },
1230+
1231+ test_matching_text_in_item_is_marked: function() {
1232+ this.autocomp.set('data', ['aaa']);
1233+ this.autocomp.render();
1234+
1235+ // Display the matching input.
1236+ var query = 'aa';
1237+ this.complete_input(query);
1238+
1239+ // Grab the matching item
1240+ var matching_text = this.autocomp
1241+ .get('boundingBox')
1242+ .one('.item .matching-text');
1243+
1244+ Assert.isNotNull(matching_text,
1245+ "Some of the matching item's text should be marked matching.");
1246+
1247+ Assert.areEqual(
1248+ query,
1249+ matching_text.get('text'),
1250+ "The matching text should be the same as the query text.");
1251+ },
1252+
1253+ test_escape_key_should_close_completions_list: function() {
1254+ this.autocomp.set('data', ['aaa']);
1255+ this.autocomp.render();
1256+
1257+ // Open the completions list
1258+ this.complete_input('aa');
1259+
1260+ // Hit the escape key to close the list
1261+ Y.Event.simulate(this.input, 'keydown', { keyCode: 27 });
1262+
1263+ Assert.isFalse(
1264+ this.autocomp.get('visible'),
1265+ "The list of completions should be closed after pressing the " +
1266+ "escape key.");
1267+ }
1268+ }));
1269+
1270+ tests.suite.add(new Y.Test.Case({
1271+
1272+ name:'test result text marking method',
1273+
1274+ test_match_at_beginning_should_be_marked: function() {
1275+ var autocomp = new Y.lazr.AutoComplete();
1276+ var marked_text = autocomp.markMatchingText('aabb', 'aa', 0);
1277+
1278+ Assert.areEqual(
1279+ '<span class="matching-text">aa</span>bb',
1280+ marked_text,
1281+ "The text at the beginning of the result should have been " +
1282+ "marked.");
1283+ },
1284+
1285+ test_match_in_middle_should_be_marked: function() {
1286+ var autocomp = new Y.lazr.AutoComplete();
1287+ var marked_text = autocomp.markMatchingText('baab', 'aa', 1);
1288+
1289+ Assert.areEqual(
1290+ 'b<span class="matching-text">aa</span>b',
1291+ marked_text,
1292+ "The text in the middle of the result should have been " +
1293+ "marked.");
1294+ },
1295+
1296+ test_match_at_end_should_be_marked: function() {
1297+ var autocomp = new Y.lazr.AutoComplete();
1298+ var marked_text = autocomp.markMatchingText('bbaa', 'aa', 2);
1299+
1300+ Assert.areEqual(
1301+ 'bb<span class="matching-text">aa</span>',
1302+ marked_text,
1303+ "The text at the end of the result should have been " +
1304+ "marked.");
1305+ }
1306+ }));
1307+
1308+
1309+ tests.suite.add(new Y.Test.Case({
1310+
1311+ name:'test query parsing',
1312+
1313+ setUp: function() {
1314+ this.autocomplete = new Y.lazr.AutoComplete({
1315+ delimiter: ' '
1316+ });
1317+ },
1318+
1319+ test_space_for_delimiter: function() {
1320+ Assert.areEqual(
1321+ 'b',
1322+ this.autocomplete.parseQuery('a b').text,
1323+ "Input should be split around the 'space' character.");
1324+ Assert.isNull(
1325+ this.autocomplete.parseQuery(' '),
1326+ "Space for input and delimiter should not parse.");
1327+ },
1328+
1329+ test_parsed_query_is_stripped_of_leading_whitespace: function() {
1330+ this.autocomplete.set('delimiter', ',');
1331+
1332+ Assert.areEqual(
1333+ 'a',
1334+ this.autocomplete.parseQuery(' a').text,
1335+ "Leading whitespace at the start of the input string should " +
1336+ "be stripped.");
1337+
1338+ Assert.areEqual(
1339+ 'b',
1340+ this.autocomplete.parseQuery('a, b').text,
1341+ "Leading whitespace between the last separator and the " +
1342+ "current query should be stripped.");
1343+ },
1344+
1345+ test_query_is_taken_from_middle_of_input: function() {
1346+ // Pick a caret position that is in the middle of the second result.
1347+ var input = "aaa bbb ccc";
1348+ var caret = 6;
1349+
1350+ Assert.areEqual(
1351 'bbb',
1352- 'aaa'
1353- ]
1354- });
1355-
1356- var matches = autocomplete.findMatches('aa');
1357-
1358- Y.ArrayAssert.itemsAreEquivalent(
1359- [{text: 'aaa', offset: 0}],
1360- matches,
1361- this.matches_are_equal,
1362- "One row should have matched the query 'aa'.");
1363- },
1364-
1365- test_match_ordering: function() {
1366- // Matches, in reverse order.
1367- var autocomplete = new Y.lazr.AutoComplete({
1368- data: [
1369- 'bbaa',
1370- 'baab',
1371- 'aabb'
1372- ]
1373- });
1374-
1375- var matches = autocomplete.findMatches('aa');
1376-
1377- Y.ArrayAssert.itemsAreEquivalent(
1378- [{text: 'aabb', offset: 0},
1379- {text: 'baab', offset: 1},
1380- {text: 'bbaa', offset: 2}],
1381- matches,
1382- this.matches_are_equal,
1383- "The match array should have all of it's keys in order.");
1384- },
1385-
1386- test_mixed_case_text_matches: function() {
1387- var autocomplete = new Y.lazr.AutoComplete({
1388- data: ['aBc']
1389- });
1390-
1391- var matches = autocomplete.findMatches('b');
1392-
1393- Y.ArrayAssert.itemsAreEquivalent(
1394- [{text:'aBc', offset: 1}],
1395- matches,
1396- this.matches_are_equal,
1397- "The match algorithm should be case insensitive.");
1398- },
1399-
1400- test_mixed_case_matches_come_in_stable_order: function() {
1401- // Data with the mixed-case coming first in order.
1402- var autocomplete = new Y.lazr.AutoComplete({
1403- data: ['aBc', 'aaa', 'abc']
1404- });
1405-
1406- var matches = autocomplete.findMatches('b');
1407-
1408- Y.ArrayAssert.itemsAreEquivalent(
1409- [{text: 'aBc', offset: 1},
1410- {text: 'abc', offset: 1}],
1411- matches,
1412- this.matches_are_equal,
1413- "Mixed-case matches should arrive in stable order.");
1414- }
1415-}));
1416-
1417-
1418-suite.add(new Y.Test.Case({
1419-
1420- name:'test selecting results',
1421-
1422- setUp: function() {
1423- this.input = make_input();
1424- this.autocomp = new Y.lazr.AutoComplete({
1425- input: this.input
1426- });
1427- this.autocomp.render();
1428- },
1429-
1430- tearDown: function() {
1431- cleanup_widget(this.autocomp);
1432- kill_input(this.input);
1433- },
1434-
1435- /* A helper to option the completions list for a given input string. */
1436- complete_input: function(value) {
1437- this.input.value = value;
1438- var last_charcode = value.charCodeAt(value.length - 1);
1439- Y.Event.simulate(this.input, 'keyup', { keyCode: last_charcode });
1440- },
1441-
1442- /* A helper to select the selected completion result with the Tab key. */
1443- press_selection_key: function() {
1444- Y.Event.simulate(this.input, "keydown", { keyCode: 9 });
1445- },
1446-
1447- test_pressing_enter_completes_current_input: function() {
1448- this.autocomp.set('data', ['aaaa', 'aabb']);
1449-
1450- // Open the completion options
1451- this.complete_input('aa');
1452-
1453- // Press 'Enter'
1454- Y.Event.simulate(this.input, "keydown", { keyCode: 13 });
1455-
1456- Assert.areEqual(
1457- 'aaaa ',
1458- this.input.value,
1459- "The first completion should have been appended to the input's " +
1460- "value after pressing the 'Enter' key.");
1461- },
1462-
1463- test_pressing_tab_completes_current_input: function() {
1464- this.autocomp.set('data', ['aaaa', 'aabb']);
1465-
1466- // Open the completion options
1467- this.complete_input('aa');
1468-
1469- // Press 'Tab'
1470- Y.Event.simulate(this.input, "keydown", { keyCode: 9 });
1471-
1472- Assert.areEqual(
1473- 'aaaa ',
1474- this.input.value,
1475- "The first completion should have been appended to the input's " +
1476- "value after pressing the 'Enter' key.");
1477- },
1478-
1479- test_clicking_on_first_result_completes_input: function() {
1480- this.autocomp.set('data', ['aaaa', 'aabb']);
1481- this.complete_input('aa');
1482-
1483- // Click on the first displayed result
1484- var options = this.autocomp.get('contentBox').all('.item');
1485- var first_item = Y.Node.getDOMNode(options.item(0));
1486- Y.Event.simulate(first_item, 'click');
1487-
1488- Assert.areEqual(
1489- 'aaaa ',
1490- this.input.value,
1491- "The first completion should have been appended to the input's " +
1492- "value after clicking it's list node.");
1493- },
1494-
1495- test_selecting_results_hides_completion_list: function() {
1496- this.autocomp.set('data', 'aaa');
1497- this.complete_input('a');
1498- this.press_selection_key();
1499-
1500- Assert.isFalse(
1501- this.autocomp.get('visible'),
1502- "The completion list should be hidden after a result is " +
1503- "selected.");
1504- },
1505-
1506- test_completed_input_replaces_current_input: function() {
1507- this.autocomp.set('data', ['abba']);
1508-
1509- // Match the one and only result, but match the second character in
1510- // it. Throw in some pre-existing user input just to be sure things
1511- // work.
1512- this.complete_input('xxx b');
1513- this.press_selection_key();
1514-
1515- Assert.areEqual(
1516- 'xxx abba ',
1517- this.input.value,
1518- "The user's current query should have been replaced with the " +
1519- "selected value.");
1520- },
1521-
1522- test_completed_input_has_delimiter_appended_to_it: function() {
1523- var delimiter = ' ';
1524- this.autocomp.set('data', ['aaaa']);
1525- this.autocomp.set('delimiter', delimiter);
1526-
1527- this.complete_input('a');
1528- this.press_selection_key();
1529-
1530- Assert.areEqual(
1531- delimiter,
1532- this.input.value.charAt(this.input.value.length - 1),
1533- "The last character of the input should be the current " +
1534- "query delimiter.");
1535- },
1536-
1537- test_down_arrow_selects_second_result_in_list: function() {
1538- this.autocomp.set('data', ['first_item', 'second_item']);
1539-
1540- // Match the first result. It should be selected by default.
1541- this.complete_input('item');
1542-
1543- // Simulate pressing the down arrow key.
1544- Y.Event.simulate(this.input, 'keydown', { keyCode: 40 });
1545-
1546- // Now, select the second result.
1547- this.press_selection_key();
1548-
1549- Assert.areEqual(
1550- 'second_item ',
1551- this.input.value,
1552- "Pressing the down-arrow key should select the second option " +
1553- "in the completions list.");
1554- }
1555-}));
1556-
1557-Y.lp.testing.Runner.run(suite);
1558-
1559+ this.autocomplete.parseQuery(input, caret).text,
1560+ "The current query should be picked out of the middle of the " +
1561+ "text input if the caret has been positioned there.");
1562+ },
1563+
1564+ test_query_is_taken_from_beginning_of_input: function() {
1565+ // Pick a caret position that is in the first input's query
1566+ var input = "aaa bbb";
1567+ var caret = 2;
1568+
1569+ Assert.areEqual(
1570+ 'aaa',
1571+ this.autocomplete.parseQuery(input, caret).text,
1572+ "The first block of text should become the current query if " +
1573+ "the caret is positioned within it.");
1574+ }
1575+ }));
1576+
1577+ tests.suite.add(new Y.Test.Case({
1578+
1579+ name:'test results matching algorithm',
1580+
1581+ /* A helper function to determine if two match result items are equal */
1582+ matches_are_equal: function(a, b) {
1583+ if (typeof a == 'undefined') {
1584+ Assert.fail("Match set 'a' is of type 'undefined'!");
1585+ }
1586+ if (typeof b == 'undefined') {
1587+ Assert.fail("Match set 'b' is of type 'undefined'!");
1588+ }
1589+ return (a.text == b.text) && (a.offset == b.offset);
1590+ },
1591+
1592+ test_no_matches_returns_an_empty_array: function() {
1593+ var autocomplete = new Y.lazr.AutoComplete({
1594+ data: ['ccc']
1595+ });
1596+
1597+ var matches = autocomplete.findMatches('aa');
1598+ Y.ArrayAssert.isEmpty(matches,
1599+ "No data should have matched the query 'aa'");
1600+ },
1601+
1602+ test_match_last_item: function() {
1603+ var autocomplete = new Y.lazr.AutoComplete({
1604+ data: [
1605+ 'ccc',
1606+ 'bbb',
1607+ 'aaa'
1608+ ]
1609+ });
1610+
1611+ var matches = autocomplete.findMatches('aa');
1612+
1613+ Y.ArrayAssert.itemsAreEquivalent(
1614+ [{text: 'aaa', offset: 0}],
1615+ matches,
1616+ this.matches_are_equal,
1617+ "One row should have matched the query 'aa'.");
1618+ },
1619+
1620+ test_match_ordering: function() {
1621+ // Matches, in reverse order.
1622+ var autocomplete = new Y.lazr.AutoComplete({
1623+ data: [
1624+ 'bbaa',
1625+ 'baab',
1626+ 'aabb'
1627+ ]
1628+ });
1629+
1630+ var matches = autocomplete.findMatches('aa');
1631+
1632+ Y.ArrayAssert.itemsAreEquivalent(
1633+ [{text: 'aabb', offset: 0},
1634+ {text: 'baab', offset: 1},
1635+ {text: 'bbaa', offset: 2}],
1636+ matches,
1637+ this.matches_are_equal,
1638+ "The match array should have all of it's keys in order.");
1639+ },
1640+
1641+ test_mixed_case_text_matches: function() {
1642+ var autocomplete = new Y.lazr.AutoComplete({
1643+ data: ['aBc']
1644+ });
1645+
1646+ var matches = autocomplete.findMatches('b');
1647+
1648+ Y.ArrayAssert.itemsAreEquivalent(
1649+ [{text:'aBc', offset: 1}],
1650+ matches,
1651+ this.matches_are_equal,
1652+ "The match algorithm should be case insensitive.");
1653+ },
1654+
1655+ test_mixed_case_matches_come_in_stable_order: function() {
1656+ // Data with the mixed-case coming first in order.
1657+ var autocomplete = new Y.lazr.AutoComplete({
1658+ data: ['aBc', 'aaa', 'abc']
1659+ });
1660+
1661+ var matches = autocomplete.findMatches('b');
1662+
1663+ Y.ArrayAssert.itemsAreEquivalent(
1664+ [{text: 'aBc', offset: 1},
1665+ {text: 'abc', offset: 1}],
1666+ matches,
1667+ this.matches_are_equal,
1668+ "Mixed-case matches should arrive in stable order.");
1669+ }
1670+ }));
1671+
1672+
1673+ tests.suite.add(new Y.Test.Case({
1674+
1675+ name:'test selecting results',
1676+
1677+ setUp: function() {
1678+ this.input = make_input();
1679+ this.autocomp = new Y.lazr.AutoComplete({
1680+ input: this.input
1681+ });
1682+ this.autocomp.render();
1683+ },
1684+
1685+ tearDown: function() {
1686+ cleanup_widget(this.autocomp);
1687+ kill_input(this.input);
1688+ },
1689+
1690+ // A helper to option the completions list for a given input string.
1691+ complete_input: function(value) {
1692+ this.input.value = value;
1693+ var last_charcode = value.charCodeAt(value.length - 1);
1694+ Y.Event.simulate(this.input, 'keyup', { keyCode: last_charcode });
1695+ },
1696+
1697+ // A helper to select the selected completion result with the Tab key.
1698+ press_selection_key: function() {
1699+ Y.Event.simulate(this.input, "keydown", { keyCode: 9 });
1700+ },
1701+
1702+ test_pressing_enter_completes_current_input: function() {
1703+ this.autocomp.set('data', ['aaaa', 'aabb']);
1704+
1705+ // Open the completion options
1706+ this.complete_input('aa');
1707+
1708+ // Press 'Enter'
1709+ Y.Event.simulate(this.input, "keydown", { keyCode: 13 });
1710+
1711+ Assert.areEqual(
1712+ 'aaaa ',
1713+ this.input.value,
1714+ "The first completion should have been appended to the " +
1715+ "input's value after pressing the 'Enter' key.");
1716+ },
1717+
1718+ test_pressing_tab_completes_current_input: function() {
1719+ this.autocomp.set('data', ['aaaa', 'aabb']);
1720+
1721+ // Open the completion options
1722+ this.complete_input('aa');
1723+
1724+ // Press 'Tab'
1725+ Y.Event.simulate(this.input, "keydown", { keyCode: 9 });
1726+
1727+ Assert.areEqual(
1728+ 'aaaa ',
1729+ this.input.value,
1730+ "The first completion should have been appended to the " +
1731+ "input's value after pressing the 'Enter' key.");
1732+ },
1733+
1734+ test_clicking_on_first_result_completes_input: function() {
1735+ this.autocomp.set('data', ['aaaa', 'aabb']);
1736+ this.complete_input('aa');
1737+
1738+ // Click on the first displayed result
1739+ var options = this.autocomp.get('contentBox').all('.item');
1740+ var first_item = Y.Node.getDOMNode(options.item(0));
1741+ Y.Event.simulate(first_item, 'click');
1742+
1743+ Assert.areEqual(
1744+ 'aaaa ',
1745+ this.input.value,
1746+ "The first completion should have been appended to the " +
1747+ "input's value after clicking it's list node.");
1748+ },
1749+
1750+ test_selecting_results_hides_completion_list: function() {
1751+ this.autocomp.set('data', 'aaa');
1752+ this.complete_input('a');
1753+ this.press_selection_key();
1754+
1755+ Assert.isFalse(
1756+ this.autocomp.get('visible'),
1757+ "The completion list should be hidden after a result is " +
1758+ "selected.");
1759+ },
1760+
1761+ test_completed_input_replaces_current_input: function() {
1762+ this.autocomp.set('data', ['abba']);
1763+
1764+ // Match the one and only result, but match the second character in
1765+ // it. Throw in some pre-existing user input just to be sure things
1766+ // work.
1767+ this.complete_input('xxx b');
1768+ this.press_selection_key();
1769+
1770+ Assert.areEqual(
1771+ 'xxx abba ',
1772+ this.input.value,
1773+ "The user's current query should have been replaced with the " +
1774+ "selected value.");
1775+ },
1776+
1777+ test_completed_input_has_delimiter_appended_to_it: function() {
1778+ var delimiter = ' ';
1779+ this.autocomp.set('data', ['aaaa']);
1780+ this.autocomp.set('delimiter', delimiter);
1781+
1782+ this.complete_input('a');
1783+ this.press_selection_key();
1784+
1785+ Assert.areEqual(
1786+ delimiter,
1787+ this.input.value.charAt(this.input.value.length - 1),
1788+ "The last character of the input should be the current " +
1789+ "query delimiter.");
1790+ },
1791+
1792+ test_down_arrow_selects_second_result_in_list: function() {
1793+ this.autocomp.set('data', ['first_item', 'second_item']);
1794+
1795+ // Match the first result. It should be selected by default.
1796+ this.complete_input('item');
1797+
1798+ // Simulate pressing the down arrow key.
1799+ Y.Event.simulate(this.input, 'keydown', { keyCode: 40 });
1800+
1801+ // Now, select the second result.
1802+ this.press_selection_key();
1803+
1804+ Assert.areEqual(
1805+ 'second_item ',
1806+ this.input.value,
1807+ "Pressing the down-arrow key should select the second option " +
1808+ "in the completions list.");
1809+ }
1810+ }));
1811+
1812+}, '0.1', {
1813+ 'requires': ['test', 'console', 'lp.autocomplete', 'node', 'event',
1814+ 'event-simulate', 'lazr.autocomplete']
1815 });
1816
1817=== modified file 'lib/lp/app/javascript/indicator/tests/test_indicator.html'
1818--- lib/lp/app/javascript/indicator/tests/test_indicator.html 2011-12-01 20:55:49 +0000
1819+++ lib/lp/app/javascript/indicator/tests/test_indicator.html 2012-02-15 12:51:42 +0000
1820@@ -1,28 +1,47 @@
1821 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
1822 "http://www.w3.org/TR/html4/strict.dtd">
1823+<!--
1824+Copyright 2012 Canonical Ltd. This software is licensed under the
1825+GNU Affero General Public License version 3 (see the file LICENSE).
1826+-->
1827+
1828 <html>
1829 <head>
1830- <title>Indicator Tests</title>
1831-
1832- <!-- YUI and test setup -->
1833- <script type="text/javascript"
1834- src="../../../../../canonical/launchpad/icing/yui/yui/yui.js">
1835- </script>
1836- <link rel="stylesheet" href="../../../../app/javascript/testing/test.css" />
1837- <link rel="stylesheet" href="../assets/indicator-core.css" />
1838- <script type="text/javascript"
1839- src="../../../../app/javascript/testing/testrunner.js"></script>
1840-
1841- <!-- The module under test -->
1842- <script type="text/javascript" src="../indicator.js"></script>
1843-
1844- <!-- The test suite -->
1845- <script type="text/javascript" src="test_indicator.js"></script>
1846-
1847-</head>
1848-<body class="yui3-skin-sam">
1849- <ul id="suites">
1850- <li>lp.large_indicator.test</li>
1851- </ul>
1852-</body>
1853+ <title>Indicator Tests</title>
1854+
1855+ <!-- YUI and test setup -->
1856+ <script type="text/javascript"
1857+ src="../../../../../../build/js/yui/yui/yui.js">
1858+ </script>
1859+ <link rel="stylesheet"
1860+ href="../../../../../../build/js/yui/console/assets/console-core.css" />
1861+ <link rel="stylesheet"
1862+ href="../../../../../../build/js/yui/console/assets/skins/sam/console.css" />
1863+ <link rel="stylesheet"
1864+ href="../../../../../../build/js/yui/test/assets/skins/sam/test.css" />
1865+
1866+ <script type="text/javascript"
1867+ src="../../../../../../build/js/lp/app/testing/testrunner.js"></script>
1868+
1869+ <link rel="stylesheet" href="../../../../app/javascript/testing/test.css" />
1870+
1871+ <!-- Dependencies -->
1872+ <!-- <script type="text/javascript" src="../../../../../../build/js/lp/..."></script> -->
1873+
1874+ <!-- The module under test. -->
1875+ <script type="text/javascript" src="../indicator.js"></script>
1876+
1877+ <!-- Placeholder for any css asset for this module. -->
1878+ <link rel="stylesheet" href="../assets/indicator-core.css" />
1879+
1880+ <!-- The test suite. -->
1881+ <script type="text/javascript" src="test_indicator.js"></script>
1882+
1883+ </head>
1884+ <body class="yui3-skin-sam">
1885+ <ul id="suites">
1886+ <!-- <li>lp.large_indicator.test</li> -->
1887+ <li>lp.indicator.test</li>
1888+ </ul>
1889+ </body>
1890 </html>
1891
1892=== modified file 'lib/lp/app/javascript/indicator/tests/test_indicator.js'
1893--- lib/lp/app/javascript/indicator/tests/test_indicator.js 2012-01-10 21:25:39 +0000
1894+++ lib/lp/app/javascript/indicator/tests/test_indicator.js 2012-02-15 12:51:42 +0000
1895@@ -1,12 +1,13 @@
1896 /* Copyright (c) 2011, Canonical Ltd. All rights reserved. */
1897
1898-YUI.add('lp.large_indicator.test', function (Y) {
1899+YUI.add('lp.indicator.test', function (Y) {
1900
1901-var large_indicator = Y.namespace('lp.large_indicator.test');
1902-var suite = new Y.Test.Suite('Indicator Tests');
1903+var tests = Y.namespace('lp.indicator.test');
1904+tests.suite = new Y.Test.Suite('Indicator Tests');
1905 var Assert = Y.Assert;
1906
1907-suite.add(new Y.Test.Case({
1908+// add the suite to the NS for the testrunner.js to find
1909+tests.suite.add(new Y.Test.Case({
1910
1911 name: 'indicator_tests',
1912
1913@@ -215,6 +216,5 @@
1914 }
1915 }));
1916
1917-large_indicator.suite = suite;
1918
1919 }, '0.1', {'requires': ['test', 'lp.app.indicator']});
1920
1921=== modified file 'lib/lp/app/javascript/inlineedit/tests/test_inline_edit.html'
1922--- lib/lp/app/javascript/inlineedit/tests/test_inline_edit.html 2011-11-30 14:05:07 +0000
1923+++ lib/lp/app/javascript/inlineedit/tests/test_inline_edit.html 2012-02-15 12:51:42 +0000
1924@@ -1,28 +1,55 @@
1925-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
1926- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
1927+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
1928+ "http://www.w3.org/TR/html4/strict.dtd">
1929+<!--
1930+Copyright 2012 Canonical Ltd. This software is licensed under the
1931+GNU Affero General Public License version 3 (see the file LICENSE).
1932+-->
1933+
1934 <html>
1935 <head>
1936- <title>Inline Edit</title>
1937-
1938- <!-- YUI and test setup -->
1939- <script type="text/javascript"
1940- src="../../../../../canonical/launchpad/icing/yui/yui/yui.js">
1941- </script>
1942- <link rel="stylesheet" href="../../../../app/javascript/testing/test.css" />
1943- <script type="text/javascript"
1944- src="../../../../app/javascript/testing/testrunner.js"></script>
1945-
1946- <!-- The module under test -->
1947- <script type="text/javascript" src="../editor.js"></script>
1948- <script type="text/javascript" src="../../anim/anim.js"></script>
1949- <script type="text/javascript" src="../../lazr/lazr.js"></script>
1950- <script type="text/javascript" src="../../extras/extras.js"></script>
1951- <script type="text/javascript" src="../../formwidgets/resizing_textarea.js"></script>
1952-
1953- <!-- The test suite -->
1954- <script type="text/javascript" src="test_inline_edit.js"></script>
1955-
1956-</head>
1957-<body class="yui3-skin-sam">
1958-</body>
1959+ <title>inline_edit tests</title>
1960+
1961+ <!-- YUI and test setup -->
1962+ <script type="text/javascript"
1963+ src="../../../../../../build/js/yui/yui/yui.js">
1964+ </script>
1965+ <link rel="stylesheet"
1966+ href="../../../../../../build/js/yui/console/assets/console-core.css" />
1967+ <link rel="stylesheet"
1968+ href="../../../../../../build/js/yui/console/assets/skins/sam/console.css" />
1969+ <link rel="stylesheet"
1970+ href="../../../../../../build/js/yui/test/assets/skins/sam/test.css" />
1971+
1972+ <script type="text/javascript"
1973+ src="../../../../../../build/js/lp/app/testing/testrunner.js"></script>
1974+
1975+ <link rel="stylesheet" href="../../../../app/javascript/testing/test.css" />
1976+
1977+ <!-- Dependencies -->
1978+ <script type="text/javascript"
1979+ src="../../../../../../build/js/lp/app/anim/anim.js"></script>
1980+ <script type="text/javascript"
1981+ src="../../../../../../build/js/lp/app/lazr/lazr.js"></script>
1982+ <script type="text/javascript"
1983+ src="../../../../../../build/js/lp/app/extras/extras.js"></script>
1984+ <script type="text/javascript"
1985+ src="../../../../../../build/js/lp/app/formwidgets/resizing_textarea.js"></script>
1986+
1987+
1988+ <!-- The module under test. -->
1989+ <script type="text/javascript" src="../editor.js"></script>
1990+
1991+ <!-- Placeholder for any css asset for this module. -->
1992+ <!-- <link rel="stylesheet" href="../assets/inline_edit-core.css" /> -->
1993+
1994+ <!-- The test suite. -->
1995+ <script type="text/javascript" src="test_inline_edit.js"></script>
1996+
1997+ </head>
1998+ <body class="yui3-skin-sam">
1999+ <ul id="suites">
2000+ <!-- <li>lp.large_indicator.test</li> -->
2001+ <li>lp.inline_edit.test</li>
2002+ </ul>
2003+ </body>
2004 </html>
2005
2006=== modified file 'lib/lp/app/javascript/inlineedit/tests/test_inline_edit.js'
2007--- lib/lp/app/javascript/inlineedit/tests/test_inline_edit.js 2011-12-21 07:54:40 +0000
2008+++ lib/lp/app/javascript/inlineedit/tests/test_inline_edit.js 2012-02-15 12:51:42 +0000
2009@@ -1,993 +1,1006 @@
2010-/* Copyright (c) 2009, Canonical Ltd. All rights reserved. */
2011-
2012-YUI().use('lp.testing.runner', 'test', 'console', 'node', 'lazr.editor',
2013- 'lp.app.formwidgets.resizing_textarea', 'event', 'event-simulate',
2014- 'plugin', function(Y) {
2015-
2016-var SAMPLE_HTML = [
2017- '<h1>Single-line editing</h1>',
2018- ' <div id="editable_single_text">',
2019- ' <span id="single_text"',
2020- ' class="yui3-editable_text-text">Some editable inline text.</span>',
2021- ' <button id="single_edit" class="yui3-editable_text-trigger">',
2022- ' Edit this</button>',
2023- ' </div>',
2024- ' <hr />',
2025- ' <h1>Multi-line editing</h1>',
2026- ' <div id="editable_multi_text">',
2027- ' <button id="multi_edit" class="yui3-editable_text-trigger">',
2028- ' Edit this</button>',
2029- ' <span id="multi_text" class="yui3-editable_text-text">',
2030- ' <p>Some editable multi-line text.</p></span>',
2031- ' </div>'
2032-].join('');
2033-
2034-var Assert = Y.Assert; // For easy access to isTrue(), etc.
2035-
2036-/* Helper to stamp a Node with an ID attribute. Needed for YUI 2.X
2037- * testing, which is heavily ID-based.
2038- *
2039- * Returns the node's 'id' attribute.
2040- */
2041-function id_for(node) {
2042- if (!node.getAttribute('id')) {
2043- var id = Y.stamp(node);
2044- node.setAttribute('id', id);
2045- }
2046- return node.getAttribute('id');
2047-}
2048-
2049-/*
2050- * A wrapper for the Y.Event.simulate() function. The wrapper accepts
2051- * CSS selectors and Node instances instead of raw nodes.
2052- */
2053-function simulate(selector, evtype) {
2054- var rawnode = Y.Node.getDOMNode(Y.one(selector));
2055- Y.Event.simulate(rawnode, evtype);
2056-}
2057-
2058-/* Helper function that creates a new editor instance. */
2059-function make_editor(cfg) {
2060- return new Y.InlineEditor(cfg);
2061-}
2062-
2063-/* Helper function to clean up a dynamically added widget instance. */
2064-function cleanup_widget(widget) {
2065- // Nuke the boundingBox, but only if we've touched the DOM.
2066- if (widget.get('rendered')) {
2067- var bb = widget.get('boundingBox');
2068- if (bb && Y.Node.getDOMNode(bb)) {
2069- var parentNode = bb.get('parentNode');
2070- if (parentNode && Y.Node.getDOMNode(parentNode)) {
2071- parentNode.removeChild(bb);
2072- }
2073- }
2074- }
2075- // Kill the widget itself.
2076- widget.destroy();
2077-}
2078-
2079-function setup_sample_html() {
2080- if (! Y.one("#scaffolding")) {
2081- Y.one(document.body).appendChild(
2082- Y.Node.create("<div id='scaffolding'></div>"));
2083- }
2084-
2085- Y.one("#scaffolding").set("innerHTML", SAMPLE_HTML);
2086-}
2087-
2088-function make_editable_text(cfg) {
2089- // For the editor
2090- // TODO: fix this ugly hack
2091- var defaults = {
2092- contentBox: '#editable_single_text',
2093- boundingBox: '#inline-edit-container'
2094- };
2095- return new Y.EditableText(Y.merge(defaults, cfg));
2096-}
2097-
2098-// Helper: convert size specification like "120px" to a number (in casu, 120).
2099-var strip_px = /px$/;
2100-function parse_size(size) {
2101- return parseInt(size.replace(strip_px, ''), 10);
2102-}
2103-
2104-var suite = new Y.Test.Suite("Inline Editor Tests");
2105-
2106-suite.add(new Y.Test.Case({
2107-
2108- name: 'inline_editor_basics',
2109-
2110- setUp: function() {
2111- this.editor = make_editor();
2112- },
2113-
2114- tearDown: function() {
2115- cleanup_widget(this.editor);
2116- },
2117-
2118- test_input_value_set_during_sync: function() {
2119- /* The input element's value should be set during the syncUI()
2120- * call.
2121- */
2122- var ed = this.editor,
2123- desired_value = 'x';
2124-
2125- Assert.areNotEqual(
2126- desired_value,
2127- ed.get('value'),
2128- "Sanity check: the editor's value shouldn't equal our " +
2129- "desired value.");
2130- Assert.isFalse(
2131- ed.get('rendered'),
2132- "Sanity check: the widget shouldn't be rendered yet.");
2133-
2134- ed.set('value', desired_value);
2135- ed.render();
2136- Assert.areEqual(
2137- desired_value,
2138- ed.get('input_field').get('value'),
2139- "The editor's input field's value should have been set.");
2140- },
2141-
2142- test_getInput_method: function() {
2143- this.editor.render();
2144- Assert.areEqual(
2145- this.editor.get('input_field').get('value'),
2146- this.editor.getInput(),
2147- "The getInput() method should return the same value as " +
2148- "the editor's input field's current value.");
2149- },
2150-
2151- test_validate_values: function() {
2152- Assert.isFalse(this.editor.get('accept_empty'),
2153- "The editor shouldn't accept empty values by default.");
2154-
2155- var prev = this.editor.get('value');
2156- this.editor.set('value', null);
2157- Assert.areEqual(
2158- prev,
2159- this.editor.get('value'),
2160- "The editor's value should not have changed.");
2161-
2162- this.editor.set('value', '');
2163- Assert.areEqual(
2164- prev,
2165- this.editor.get('value'),
2166- "The editor should not accept the empty string as a " +
2167- "value if 'accept_empty' is false.");
2168-
2169- /* The control can be asked to accept empty values. */
2170- this.editor.set('accept_empty', true);
2171- this.editor.set('value', '');
2172- Assert.areEqual(
2173- '',
2174- this.editor.get('value'),
2175- "The editor should have accepted the empty string as a " +
2176- "valid value if 'accept_empty' is true.");
2177- },
2178-
2179- test_validate_empty_editor_input: function() {
2180- var ed = this.editor;
2181-
2182- // A helper to catch the 'save' event.
2183- var got_save = false;
2184- var after_save = function(ev) { got_save = true; };
2185- ed.after('ieditor:save', after_save);
2186-
2187- ed.render();
2188-
2189- Assert.isFalse(ed.hasErrors(),
2190- "Sanity check: the editor shouldn't be displaying any " +
2191- "errors.");
2192- Assert.isFalse(ed.get('accept_empty'),
2193- "Sanity check: the editor shouldn't accept empty inputs.");
2194-
2195- ed.get('input_field').set('value', '');
2196- ed.save();
2197-
2198- Assert.isTrue(ed.hasErrors(),
2199- "The editor should be displaying an error after the " +
2200- "trying to save an empty input.");
2201- Assert.isFalse(got_save,
2202- "The editor should not have fired a 'save' event.");
2203- },
2204-
2205- test_set_and_clear_error_message: function() {
2206- this.editor.render();
2207-
2208- var ed = this.editor,
2209- edisplay = ed.get('error_message'),
2210- c_hidden = 'yui3-ieditor-errors-hidden';
2211-
2212- Assert.isNotNull(
2213- edisplay,
2214- "The editor should have a valid error display node.");
2215-
2216- Assert.isTrue(
2217- edisplay.hasClass(c_hidden),
2218- "The error display should start out hidden.");
2219- Assert.isFalse(
2220- ed.get("in_error"),
2221- "The editor's 'in_error' attribute should not be set.");
2222-
2223- var msg = "An error has occured.";
2224- ed.showError(msg);
2225-
2226- Assert.areEqual(
2227- msg,
2228- edisplay.get('text'),
2229- "The error display's text should be set.");
2230- Assert.isFalse(
2231- edisplay.hasClass(c_hidden),
2232- "The error display should be visible when an error is set.");
2233- Assert.isTrue(
2234- ed.hasErrors(),
2235- "The editor .hasErrors() method should return true if " +
2236- "there are errors being displayed.");
2237- Assert.isTrue(
2238- ed.get("in_error"),
2239- "The editor's 'in_error' attribute should be set.");
2240-
2241- ed.clearErrors();
2242- Assert.isTrue(
2243- edisplay.hasClass(c_hidden),
2244- "The error display should be hidden when the error " +
2245- "is cleared.");
2246- Assert.isFalse(
2247- ed.hasErrors(),
2248- "The editor .hasErrors() method should return false " +
2249- "if there are no errors being displayed.");
2250- },
2251-
2252- test_save_input_to_editor: function() {
2253- var expected_value = 'abc',
2254- ed = this.editor;
2255-
2256- Assert.areNotEqual(
2257- expected_value,
2258- ed.get('value'),
2259- "Sanity check");
2260-
2261- ed.render();
2262- ed.get('input_field').set('value', expected_value);
2263- ed.save();
2264-
2265- Assert.areEqual(
2266- expected_value,
2267- ed.get('value'),
2268- "The value of the editor's input field should have been " +
2269- "saved to the editor's 'value' attribute.");
2270- },
2271-
2272- test_focus_method_focuses_editor_input: function() {
2273- this.editor.render();
2274-
2275- var input = this.editor.get('input_field'),
2276- test = this,
2277- focused = false;
2278-
2279- Y.on('focus', function() {
2280- focused = true;
2281- }, input);
2282-
2283- this.editor.focus();
2284-
2285- Assert.isTrue(focused,
2286- "The editor's input field should have received focus " +
2287- "after calling the editor's focus method.");
2288- },
2289-
2290- test_input_receives_focus_after_editor_errors: function() {
2291- this.editor.render();
2292-
2293- var ed = this.editor,
2294- input = this.editor.get('input_field'),
2295- got_focus = false;
2296-
2297- Assert.isFalse(
2298- ed.get('in_error'),
2299- "Sanity check: the editor should be clear of errors.");
2300- Assert.isFalse(
2301- ed.get('accept_empty'),
2302- "Sanity check: the editor should not accept empty " +
2303- "values.");
2304-
2305- // Force an error by setting the editor's input to the
2306- // empty string.
2307- input.set('value', '');
2308-
2309- var test = this;
2310- // Add our focus event listener.
2311- Y.on('focus', function() {
2312- got_focus = true;
2313- }, input);
2314-
2315- ed.save();
2316- Assert.isTrue(
2317- ed.get('in_error'),
2318- "Sanity check: the editor should be in an error state " +
2319- "after saving an empty value.");
2320-
2321- Assert.isTrue(
2322- got_focus,
2323- "The editor's input field should have the current " +
2324- "focus.");
2325- },
2326-
2327- test_widget_has_a_disabled_tabindex_when_focused: function() {
2328- // The tabindex attribute appears when the widget is focused.
2329- this.editor.render();
2330- this.editor.focus();
2331-
2332- // Be aware that in IE, get('tabIndex') and getAttribute('tabIndex')
2333- // return different values when set to -1. This is due to YUI's
2334- // getAttribute() calling dom_node.getAttribute('tabIndex', 2), which
2335- // is an IE extension.
2336- // http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx
2337- Assert.areEqual(
2338- -1,
2339- this.editor.get('boundingBox').get('tabIndex'),
2340- "The widget should have a tabindex of -1 (disabled).");
2341- },
2342-
2343- test_enter_key_saves_input: function() {
2344- this.editor.render();
2345-
2346- var ed = this.editor,
2347- input_element = Y.Node.getDOMNode(
2348+/* Copyright (c) 2012, Canonical Ltd. All rights reserved. */
2349+
2350+YUI.add('lp.inline_edit.test', function (Y) {
2351+
2352+ var tests = Y.namespace('lp.inline_edit.test');
2353+ tests.suite = new Y.Test.Suite('inline_edit Tests');
2354+ var Assert = Y.Assert; // For easy access to isTrue(), etc.
2355+
2356+ var SAMPLE_HTML = [
2357+ '<h1>Single-line editing</h1>',
2358+ ' <div id="editable_single_text">',
2359+ ' <span id="single_text"',
2360+ ' class="yui3-editable_text-text">Some editable inline text.</span>',
2361+ ' <button id="single_edit" class="yui3-editable_text-trigger">',
2362+ ' Edit this</button>',
2363+ ' </div>',
2364+ ' <hr />',
2365+ ' <h1>Multi-line editing</h1>',
2366+ ' <div id="editable_multi_text">',
2367+ ' <button id="multi_edit" class="yui3-editable_text-trigger">',
2368+ ' Edit this</button>',
2369+ ' <span id="multi_text" class="yui3-editable_text-text">',
2370+ ' <p>Some editable multi-line text.</p></span>',
2371+ ' </div>'
2372+ ].join('');
2373+
2374+
2375+ /* Helper to stamp a Node with an ID attribute. Needed for YUI 2.X
2376+ * testing, which is heavily ID-based.
2377+ *
2378+ * Returns the node's 'id' attribute.
2379+ */
2380+ function id_for(node) {
2381+ if (!node.getAttribute('id')) {
2382+ var id = Y.stamp(node);
2383+ node.setAttribute('id', id);
2384+ }
2385+ return node.getAttribute('id');
2386+ }
2387+
2388+ /*
2389+ * A wrapper for the Y.Event.simulate() function. The wrapper accepts
2390+ * CSS selectors and Node instances instead of raw nodes.
2391+ */
2392+ function simulate(selector, evtype) {
2393+ var rawnode = Y.Node.getDOMNode(Y.one(selector));
2394+ Y.Event.simulate(rawnode, evtype);
2395+ }
2396+
2397+ /* Helper function that creates a new editor instance. */
2398+ function make_editor(cfg) {
2399+ return new Y.InlineEditor(cfg);
2400+ }
2401+
2402+ /* Helper function to clean up a dynamically added widget instance. */
2403+ function cleanup_widget(widget) {
2404+ // Nuke the boundingBox, but only if we've touched the DOM.
2405+ if (widget.get('rendered')) {
2406+ var bb = widget.get('boundingBox');
2407+ if (bb && Y.Node.getDOMNode(bb)) {
2408+ var parentNode = bb.get('parentNode');
2409+ if (parentNode && Y.Node.getDOMNode(parentNode)) {
2410+ parentNode.removeChild(bb);
2411+ }
2412+ }
2413+ }
2414+ // Kill the widget itself.
2415+ widget.destroy();
2416+ }
2417+
2418+ function setup_sample_html() {
2419+ if (! Y.one("#scaffolding")) {
2420+ Y.one(document.body).appendChild(
2421+ Y.Node.create("<div id='scaffolding'></div>"));
2422+ }
2423+
2424+ Y.one("#scaffolding").set("innerHTML", SAMPLE_HTML);
2425+ }
2426+
2427+ function make_editable_text(cfg) {
2428+ // For the editor
2429+ // TODO: fix this ugly hack
2430+ var defaults = {
2431+ contentBox: '#editable_single_text',
2432+ boundingBox: '#inline-edit-container'
2433+ };
2434+ return new Y.EditableText(Y.merge(defaults, cfg));
2435+ }
2436+
2437+ // Helper: convert size specification like "120px" to a number (in casu,
2438+ // 120).
2439+ var strip_px = /px$/;
2440+ function parse_size(size) {
2441+ return parseInt(size.replace(strip_px, ''), 10);
2442+ }
2443+
2444+ tests.suite.add(new Y.Test.Case({
2445+ name: 'inline_editor_basics',
2446+
2447+ setUp: function() {
2448+ this.editor = make_editor();
2449+ },
2450+
2451+ tearDown: function() {
2452+ cleanup_widget(this.editor);
2453+ },
2454+ test_library_exists: function () {
2455+ Y.Assert.isObject(Y.InlineEditor,
2456+ "We should be able to locate the lp.${LIBRARY} module");
2457+ },
2458+
2459+ test_input_value_set_during_sync: function() {
2460+ /* The input element's value should be set during the syncUI()
2461+ * call.
2462+ */
2463+ var ed = this.editor,
2464+ desired_value = 'x';
2465+
2466+ Assert.areNotEqual(
2467+ desired_value,
2468+ ed.get('value'),
2469+ "Sanity check: the editor's value shouldn't equal our " +
2470+ "desired value.");
2471+ Assert.isFalse(
2472+ ed.get('rendered'),
2473+ "Sanity check: the widget shouldn't be rendered yet.");
2474+
2475+ ed.set('value', desired_value);
2476+ ed.render();
2477+ Assert.areEqual(
2478+ desired_value,
2479+ ed.get('input_field').get('value'),
2480+ "The editor's input field's value should have been set.");
2481+ },
2482+
2483+ test_getInput_method: function() {
2484+ this.editor.render();
2485+ Assert.areEqual(
2486+ this.editor.get('input_field').get('value'),
2487+ this.editor.getInput(),
2488+ "The getInput() method should return the same value as " +
2489+ "the editor's input field's current value.");
2490+ },
2491+
2492+ test_validate_values: function() {
2493+ Assert.isFalse(this.editor.get('accept_empty'),
2494+ "The editor shouldn't accept empty values by default.");
2495+
2496+ var prev = this.editor.get('value');
2497+ this.editor.set('value', null);
2498+ Assert.areEqual(
2499+ prev,
2500+ this.editor.get('value'),
2501+ "The editor's value should not have changed.");
2502+
2503+ this.editor.set('value', '');
2504+ Assert.areEqual(
2505+ prev,
2506+ this.editor.get('value'),
2507+ "The editor should not accept the empty string as a " +
2508+ "value if 'accept_empty' is false.");
2509+
2510+ /* The control can be asked to accept empty values. */
2511+ this.editor.set('accept_empty', true);
2512+ this.editor.set('value', '');
2513+ Assert.areEqual(
2514+ '',
2515+ this.editor.get('value'),
2516+ "The editor should have accepted the empty string as a " +
2517+ "valid value if 'accept_empty' is true.");
2518+ },
2519+
2520+ test_validate_empty_editor_input: function() {
2521+ var ed = this.editor;
2522+
2523+ // A helper to catch the 'save' event.
2524+ var got_save = false;
2525+ var after_save = function(ev) { got_save = true; };
2526+ ed.after('ieditor:save', after_save);
2527+
2528+ ed.render();
2529+
2530+ Assert.isFalse(ed.hasErrors(),
2531+ "Sanity check: the editor shouldn't be displaying any " +
2532+ "errors.");
2533+ Assert.isFalse(ed.get('accept_empty'),
2534+ "Sanity check: the editor shouldn't accept empty inputs.");
2535+
2536+ ed.get('input_field').set('value', '');
2537+ ed.save();
2538+
2539+ Assert.isTrue(ed.hasErrors(),
2540+ "The editor should be displaying an error after the " +
2541+ "trying to save an empty input.");
2542+ Assert.isFalse(got_save,
2543+ "The editor should not have fired a 'save' event.");
2544+ },
2545+
2546+ test_set_and_clear_error_message: function() {
2547+ this.editor.render();
2548+
2549+ var ed = this.editor,
2550+ edisplay = ed.get('error_message'),
2551+ c_hidden = 'yui3-ieditor-errors-hidden';
2552+
2553+ Assert.isNotNull(
2554+ edisplay,
2555+ "The editor should have a valid error display node.");
2556+
2557+ Assert.isTrue(
2558+ edisplay.hasClass(c_hidden),
2559+ "The error display should start out hidden.");
2560+ Assert.isFalse(
2561+ ed.get("in_error"),
2562+ "The editor's 'in_error' attribute should not be set.");
2563+
2564+ var msg = "An error has occured.";
2565+ ed.showError(msg);
2566+
2567+ Assert.areEqual(
2568+ msg,
2569+ edisplay.get('text'),
2570+ "The error display's text should be set.");
2571+ Assert.isFalse(
2572+ edisplay.hasClass(c_hidden),
2573+ "The error display should be visible when an error is set.");
2574+ Assert.isTrue(
2575+ ed.hasErrors(),
2576+ "The editor .hasErrors() method should return true if " +
2577+ "there are errors being displayed.");
2578+ Assert.isTrue(
2579+ ed.get("in_error"),
2580+ "The editor's 'in_error' attribute should be set.");
2581+
2582+ ed.clearErrors();
2583+ Assert.isTrue(
2584+ edisplay.hasClass(c_hidden),
2585+ "The error display should be hidden when the error " +
2586+ "is cleared.");
2587+ Assert.isFalse(
2588+ ed.hasErrors(),
2589+ "The editor .hasErrors() method should return false " +
2590+ "if there are no errors being displayed.");
2591+ },
2592+
2593+ test_save_input_to_editor: function() {
2594+ var expected_value = 'abc',
2595+ ed = this.editor;
2596+
2597+ Assert.areNotEqual(
2598+ expected_value,
2599+ ed.get('value'),
2600+ "Sanity check");
2601+
2602+ ed.render();
2603+ ed.get('input_field').set('value', expected_value);
2604+ ed.save();
2605+
2606+ Assert.areEqual(
2607+ expected_value,
2608+ ed.get('value'),
2609+ "The value of the editor's input field should have been " +
2610+ "saved to the editor's 'value' attribute.");
2611+ },
2612+
2613+ test_focus_method_focuses_editor_input: function() {
2614+ this.editor.render();
2615+
2616+ var input = this.editor.get('input_field'),
2617+ test = this,
2618+ focused = false;
2619+
2620+ Y.on('focus', function() {
2621+ focused = true;
2622+ }, input);
2623+
2624+ this.editor.focus();
2625+
2626+ Assert.isTrue(focused,
2627+ "The editor's input field should have received focus " +
2628+ "after calling the editor's focus method.");
2629+ },
2630+
2631+ test_input_receives_focus_after_editor_errors: function() {
2632+ this.editor.render();
2633+
2634+ var ed = this.editor,
2635+ input = this.editor.get('input_field'),
2636+ got_focus = false;
2637+
2638+ Assert.isFalse(
2639+ ed.get('in_error'),
2640+ "Sanity check: the editor should be clear of errors.");
2641+ Assert.isFalse(
2642+ ed.get('accept_empty'),
2643+ "Sanity check: the editor should not accept empty " +
2644+ "values.");
2645+
2646+ // Force an error by setting the editor's input to the
2647+ // empty string.
2648+ input.set('value', '');
2649+
2650+ var test = this;
2651+ // Add our focus event listener.
2652+ Y.on('focus', function() {
2653+ got_focus = true;
2654+ }, input);
2655+
2656+ ed.save();
2657+ Assert.isTrue(
2658+ ed.get('in_error'),
2659+ "Sanity check: the editor should be in an error state " +
2660+ "after saving an empty value.");
2661+
2662+ Assert.isTrue(
2663+ got_focus,
2664+ "The editor's input field should have the current " +
2665+ "focus.");
2666+ },
2667+
2668+ test_widget_has_a_disabled_tabindex_when_focused: function() {
2669+ // The tabindex attribute appears when the widget is focused.
2670+ this.editor.render();
2671+ this.editor.focus();
2672+
2673+ // Be aware that in IE, get('tabIndex') and
2674+ // getAttribute('tabIndex') return different values when set to
2675+ // -1. This is due to YUI's getAttribute() calling
2676+ // dom_node.getAttribute('tabIndex', 2), which is an IE extension.
2677+ // http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx
2678+ Assert.areEqual(
2679+ -1,
2680+ this.editor.get('boundingBox').get('tabIndex'),
2681+ "The widget should have a tabindex of -1 (disabled).");
2682+ },
2683+
2684+ test_enter_key_saves_input: function() {
2685+ this.editor.render();
2686+
2687+ var ed = this.editor,
2688+ input_element = Y.Node.getDOMNode(
2689+ this.editor.get('input_field'));
2690+
2691+ input_element.value = 'abc';
2692+
2693+ // A helper to flag the 'save' event.
2694+ var saved = false;
2695+ function saveCheck(e) {
2696+ saved = true;
2697+ }
2698+
2699+ ed.after('ieditor:save', saveCheck, this);
2700+
2701+ // Simulate an 'Enter' key event in the editor's input field.
2702+ Y.Event.simulate(input_element, "keydown", { keyCode: 13 });
2703+
2704+ Assert.isFalse(ed.hasErrors());
2705+ Assert.isTrue(saved,
2706+ "Pressing the 'Enter' key inside the editor's input field " +
2707+ "should save the input.");
2708+ },
2709+
2710+ test_enter_key_ignored_in_multiline: function() {
2711+ this.editor.set('multiline', true);
2712+ this.editor.render();
2713+
2714+ var ed = this.editor;
2715+ var input_element = Y.Node.getDOMNode(
2716 this.editor.get('input_field'));
2717
2718- input_element.value = 'abc';
2719-
2720- // A helper to flag the 'save' event.
2721- var saved = false;
2722- function saveCheck(e) {
2723- saved = true;
2724- }
2725-
2726- ed.after('ieditor:save', saveCheck, this);
2727-
2728- // Simulate an 'Enter' key event in the editor's input field.
2729- Y.Event.simulate(input_element, "keydown", { keyCode: 13 });
2730-
2731- Assert.isFalse(ed.hasErrors());
2732- Assert.isTrue(saved,
2733- "Pressing the 'Enter' key inside the editor's input field " +
2734- "should save the input.");
2735- },
2736-
2737- test_enter_key_ignored_in_multiline: function() {
2738- this.editor.set('multiline', true);
2739- this.editor.render();
2740-
2741- var ed = this.editor;
2742- var input_element = Y.Node.getDOMNode(this.editor.get('input_field'));
2743-
2744- input_element.value = 'abc';
2745-
2746- // A helper to flag the 'save' event.
2747- var saved = false;
2748- function saveCheck(e) {
2749- saved = true;
2750- }
2751-
2752- ed.after('ieditor:save', saveCheck, this);
2753-
2754- // Simulate an 'Enter' key event in the editor's input field.
2755- Y.Event.simulate(input_element, "keydown", { keyCode: 13 });
2756-
2757- // Restore to previous state.
2758- this.editor.set('multiline', false);
2759-
2760- Assert.isFalse(ed.hasErrors());
2761- Assert.isFalse(saved,
2762- "Pressing the 'Enter' key in multiline mode " +
2763- "should not trigger a save.");
2764- },
2765-
2766- test_input_should_be_trimmed_of_whitespace: function() {
2767- this.editor.render();
2768-
2769- var input = this.editor.get('input_field');
2770-
2771- // Set a whitespace value as the input.
2772- input.set('value', ' ');
2773-
2774- this.editor.save();
2775-
2776- Assert.isTrue(
2777- this.editor.hasErrors(),
2778- "The editor should be displaying an error after trying to " +
2779- "save a whitespace value.");
2780- }
2781-}));
2782-
2783-suite.add(new Y.Test.Case({
2784- name: 'Initial value',
2785-
2786- setUp: function() {
2787- this.editor = make_editor({initial_value_override: 'Initial value'});
2788- },
2789-
2790- tearDown: function() {
2791- cleanup_widget(this.editor);
2792- },
2793-
2794- test_initial_value_override: function() {
2795- this.editor.render();
2796- Assert.areEqual(
2797- 'Initial value',
2798- this.editor.get('input_field').get('value'),
2799- "The editor's input field should have the initial value.");
2800- }
2801-}));
2802-
2803-suite.add(new Y.Test.Case({
2804- name: 'Editable text initial values',
2805-
2806- setUp: function() {
2807- setup_sample_html();
2808- this.etext = make_editable_text(
2809- {initial_value_override: 'Initial value'});
2810- },
2811-
2812- tearDown: function() {
2813- // Reset the <span>.
2814- cleanup_widget(this.etext);
2815- },
2816-
2817- test_save_initial_value_override: function() {
2818- this.etext.render();
2819-
2820- Assert.areEqual(
2821- 'Initial value',
2822- this.etext.editor.get('input_field').get('value'),
2823- "The input_field should have been set to the initial value.");
2824-
2825- this.etext.editor.save();
2826- Assert.areEqual(
2827- 'Initial value',
2828- this.etext.editor.get('value'),
2829- "The editor's initial value did not get saved.");
2830- Assert.areEqual(
2831- null,
2832- this.etext.editor.get('initial_value_override'),
2833- "The editor's initial_value_override should be null.");
2834- },
2835-
2836- test_cancel_does_not_modify_value: function() {
2837- this.etext.render();
2838-
2839- Assert.areEqual(
2840- 'Some editable inline text.',
2841- this.etext.editor.get('value'),
2842- "The editor's value is not what it should be.");
2843- Assert.areEqual(
2844- 'Initial value',
2845- this.etext.editor.get('initial_value_override'),
2846- "The editor's initial_value_override is not what it should be.");
2847-
2848- this.etext.editor.cancel();
2849- Assert.areEqual(
2850- 'Some editable inline text.',
2851- this.etext.editor.get('value'),
2852- "The editor's value did not get reset.");
2853- Assert.areEqual(
2854- 'Initial value',
2855- this.etext.editor.get('initial_value_override'),
2856- "The editor's initial_value_override did not get preserved.");
2857- }
2858-}));
2859-
2860-suite.add(new Y.Test.Case({
2861-
2862- name: "Inline editor input sizing for a positive size value",
2863-
2864- setUp: function() {
2865- this.expected_size = 32;
2866- this.editor = make_editor({size: this.expected_size});
2867- },
2868-
2869- tearDown: function() {
2870- cleanup_widget(this.editor);
2871- },
2872-
2873- test_editor_size_attribute_matches_user_value: function() {
2874- Assert.areEqual(
2875- this.editor.get('size'),
2876- this.expected_size,
2877- "The editor's 'size' attribute should match the user's " +
2878- "specified size.");
2879- },
2880-
2881- test_input_field_size_matches_the_editor_size: function() {
2882- this.editor.render();
2883- var input = this.editor.get('input_field');
2884- Assert.areEqual(
2885- this.expected_size + 'px',
2886- input.getStyle('width'),
2887- "The editor's input field size should have been set from the " +
2888- "'size' attribute.");
2889- }
2890-
2891-}));
2892-
2893-suite.add(new Y.Test.Case({
2894-
2895- name: "Inline editor input sizing for a null size value",
2896-
2897- setUp: function() {
2898- this.editor = make_editor();
2899- this.editor.render();
2900- },
2901-
2902- tearDown: function() {
2903- cleanup_widget(this.editor);
2904- },
2905-
2906- test_editor_size_attribute_is_null: function() {
2907- Assert.areEqual(
2908- null,
2909- this.editor.get('size'),
2910- "The editor's 'size' attribute should default to 'null'.");
2911- },
2912-
2913- test_editor_input_has_browser_default_size: function() {
2914- var input = this.editor.get('input_field');
2915- Assert.isFalse(
2916- input.hasAttribute('size'),
2917- "The editor's input field should have the browser default " +
2918- "size if the editor's size is 'null'.");
2919- }
2920-}));
2921-
2922-/*
2923- * XXX mars 20090206
2924- *
2925- * The following test is just for the attribute validators. Most of this is
2926- * made necessary because YUI doesn't publish attribute validation errors.
2927- *
2928- * See ticket http://yuilibrary.com/projects/yui3/ticket/2525946
2929- */
2930-suite.add(new Y.Test.Case({
2931-
2932- name: "Inline editor size attribute validation",
2933-
2934- setUp: function() {
2935- this.initial_size = null;
2936- this.editor = make_editor({size: this.initial_size});
2937- },
2938-
2939- test_editor_accepts_null_as_size: function() {
2940- this.editor.set('size', null);
2941- Assert.areEqual(
2942- null,
2943- this.editor.get('size'),
2944- "The editor should accept a null value for the size attribute.");
2945- },
2946-
2947- test_editor_accepts_positive_numbers_as_size: function() {
2948- this.editor.set('size', 123);
2949- Assert.areEqual(
2950- 123,
2951- this.editor.get('size'),
2952- "The editor should accept a positive number as a valid size.");
2953- },
2954-
2955- test_editor_rejects_negative_numbers_for_size: function() {
2956- this.editor.set('size', -2);
2957- Assert.areEqual(
2958- this.initial_size,
2959- this.editor.get('size'),
2960- "The editor should not accept negative numbers for its size.");
2961- },
2962-
2963- test_editor_rejects_characters_for_size: function() {
2964- this.editor.set('size', 'a');
2965- Assert.areEqual(
2966- this.initial_size,
2967- this.editor.get('size'),
2968- "The editor should not accept strings for its size.");
2969- }
2970-}));
2971-
2972-
2973-suite.add(new Y.Test.Case({
2974-
2975- name: 'editor_save_state_change',
2976-
2977- setUp: function() {
2978- this.editor = make_editor();
2979- },
2980-
2981- tearDown: function() {
2982- cleanup_widget(this.editor);
2983- },
2984-
2985- test_ui_initial_state_is_not_waiting: function() {
2986- this.editor.render();
2987- Assert.isFalse(
2988- this.editor.get('boundingBox').hasClass('yui3-ieditor-waiting'),
2989- "The editor UI should not start out in the 'waiting' state.");
2990- },
2991-
2992- test_set_ui_waiting_state: function() {
2993- var ed = this.editor;
2994- ed.render();
2995-
2996- ed._uiSetWaiting();
2997-
2998- Assert.isTrue(
2999- ed.get('input_field').get('disabled'),
3000- "The editor's input should be disabled while in the " +
3001- "'waiting' state.");
3002- Assert.isTrue(
3003- ed.get('boundingBox').hasClass('yui3-ieditor-waiting'),
3004- "The editor's UI should reflect the 'waiting' state " +
3005- "with an appropriate class.");
3006- },
3007-
3008- test_clear_ui_waiting_state: function() {
3009- var ed = this.editor;
3010- ed.render();
3011-
3012- ed._uiSetWaiting();
3013- ed._uiClearWaiting();
3014-
3015- Assert.isFalse(
3016- ed.get('input_field').get('disabled'),
3017- "The editor's input should be re-enabled when clearing " +
3018- "the 'waiting' state.");
3019- Assert.isFalse(
3020- ed.get('boundingBox').hasClass('yui3-ieditor-waiting'),
3021- "The editor's UI should have the 'waiting' state " +
3022- "class removed.");
3023- }
3024-}));
3025-
3026-
3027-suite.add(new Y.Test.Case({
3028-
3029- name: 'editable_text',
3030-
3031- setUp: function() {
3032- setup_sample_html();
3033- this.etext = make_editable_text();
3034- },
3035-
3036- tearDown: function() {
3037- cleanup_widget(this.etext);
3038- },
3039-
3040- test_initial_values_from_DOM: function() {
3041- Assert.areEqual(
3042- Y.one("#single_text"),
3043- this.etext.get('text'),
3044- "The editor's text node should have been set from the " +
3045- "DOM.");
3046-
3047- Assert.areEqual(
3048- Y.one('#single_edit'),
3049- this.etext.get('trigger'),
3050- "The editor's trigger node should have been set from " +
3051- "the DOM.");
3052-
3053- Assert.areEqual(
3054- 'Some editable inline text.',
3055- this.etext.editor.get('value'),
3056- "The editor's initial value should be set from it's " +
3057- "text node.");
3058-
3059- Assert.areEqual(
3060- this.etext.editor.get('value'),
3061- this.etext.get('value'),
3062- "The editable text's value should be the same as the " +
3063- "editor's.");
3064- },
3065-
3066- test_show: function() {
3067- /* The show() method should display the editor, and hide the
3068- * existing contents.
3069- */
3070- this.etext.render();
3071- this.etext.show_editor();
3072- Assert.isTrue(this.etext.editor.get('visible'),
3073- "The editor's 'visible' attribute should be true.");
3074- },
3075-
3076- test_hide: function() {
3077- /* The hide() method should hide the editor, and display the
3078- * original contents.
3079- */
3080- this.etext.render();
3081- this.etext.show_editor();
3082- this.etext.hide_editor();
3083- Assert.isFalse(this.etext.editor.get('visible'),
3084- "The editor's 'visible' attribute should be False.");
3085- },
3086-
3087- test_trigger_edit: function() {
3088- /* Clicking on the editable text's "Edit" button should
3089- * make the editor visible.
3090- */
3091- Assert.isFalse(this.etext.editor.get('visible'),
3092- "Sanity check, the editor should be hidden.");
3093-
3094- this.etext.render();
3095- simulate('#single_edit', 'click');
3096-
3097- Assert.isTrue(this.etext.editor.get('visible'),
3098- "The editor should be visible.");
3099- },
3100-
3101- test_text_is_updated_to_saved_value: function() {
3102- this.etext.render();
3103-
3104- // Grab the normalized text.
3105- var expected_value = 'abc';
3106-
3107- Assert.areNotEqual(
3108- expected_value,
3109- this.etext.get('value'),
3110- "Sanity check");
3111-
3112- simulate('#single_edit', 'click');
3113- this.etext.editor
3114- .get('input_field')
3115- .set('value', expected_value);
3116-
3117- this.etext.editor.save();
3118-
3119- Assert.areEqual(
3120- expected_value,
3121- this.etext.editor.get('value'),
3122- "Sanity check: the editor's value should have been " +
3123- "saved.");
3124-
3125- Assert.areEqual(
3126- expected_value,
3127- this.etext.get('value'),
3128- "The editable text's current value should be updated " +
3129- "after saving some new text in the editor.");
3130- },
3131-
3132- test_text_is_escaped: function() {
3133- this.etext.render();
3134-
3135- var input_value = '<i>l33t inject0r d00d</i> 0wnz y00';
3136- var shown_value = '&lt;i&gt;l33t inject0r d00d&lt;/i&gt; 0wnz y00';
3137-
3138- simulate('#single_edit', 'click');
3139- this.etext.editor.setInput(input_value);
3140- this.etext.editor.save();
3141-
3142- Assert.areEqual(
3143- shown_value,
3144- this.etext.get('text').get('innerHTML'),
3145- "Input text should be escaped before being inserted in HTML.");
3146- Assert.areEqual(
3147- input_value,
3148- this.etext.editor.getInput(),
3149- "Input text should be retained verbatim.");
3150- },
3151-
3152- test_accept_empty_attribute_passthrough: function() {
3153- var et = this.etext;
3154-
3155- Assert.areEqual(
3156- et.get('accept_empty'),
3157- et.editor.get('accept_empty'),
3158- "The editor and inline editor's 'accept_empty " +
3159- "should start out the same.");
3160-
3161- et.set('accept_empty', true);
3162- Assert.isTrue(
3163- et.editor.get('accept_empty'),
3164- "The inline editor's 'accept_empty' attribute should " +
3165- "also be set to 'true'.");
3166- Assert.isTrue(
3167- et.get('accept_empty'),
3168- "The editor's 'accept_empty' attribute should be true.");
3169-
3170- et.set('accept_empty', false);
3171- Assert.isFalse(
3172- et.get('accept_empty'),
3173- "The editor's 'accept_empty' attribute should be false.");
3174- Assert.isFalse(
3175- et.editor.get('accept_empty'),
3176- "The inline editor's 'accept_empty' attribute should " +
3177- "also be set to 'false'.");
3178- },
3179-
3180- test_widget_has_a_disabled_tabindex_when_focused: function() {
3181- // The tabindex attribute appears when the widget is focused.
3182- this.etext.render();
3183- this.etext.focus();
3184-
3185- // Be aware that in IE, get('tabIndex') and getAttribute('tabIndex')
3186- // return different values when set to -1. This is due to YUI's
3187- // getAttribute() calling dom_node.getAttribute('tabIndex', 2), which
3188- // is an IE extension.
3189- // http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx
3190-
3191- // On IE and KHTML, EditableText._onRender() will prevent the
3192- // default widget rendering that would set the tabIndex on the
3193- // boundingBox, so this test will fail for those browsers.
3194- Assert.areEqual(
3195- -1,
3196- this.etext.get('boundingBox').get('tabIndex'),
3197- "The widget should have a tabindex of -1 (disabled).");
3198- },
3199-
3200- test_trigger_is_disabled_if_the_widget_is_not_rendered: function() {
3201- var trigger = this.etext.get('trigger');
3202- Assert.isInstanceOf(
3203- Y.Node, trigger,
3204- "Sanity check: the editor's trigger should be a valid node.");
3205- Assert.isFalse(
3206- this.etext.get('rendered'),
3207- "Sanity check: the editor should not be rendered.");
3208-
3209- simulate(trigger, 'click');
3210- // Peek inside the box a bit, and check that the nested editor
3211- // instance is still invisible. Assume that if it is, then
3212- // the show_editor() method was never called.
3213- Assert.isFalse(
3214- this.etext.editor.get('visible'),
3215- "Triggering an unrendered editor should not display the widget.");
3216- }
3217-}));
3218-
3219-suite.add(new Y.Test.Case({
3220-
3221- name: "EditableText single-line/multi-line modes",
3222-
3223- setUp: function() {
3224- setup_sample_html();
3225- this.single = make_editable_text({
3226- contentBox: '#editable_single_text',
3227- multiline: false
3228- });
3229- this.single.render();
3230- this.single.show_editor();
3231- this.multi = make_editable_text({
3232- contentBox: '#editable_multi_text',
3233- multiline: true
3234- });
3235- this.multi.render();
3236- this.multi.show_editor();
3237- },
3238-
3239- tearDown: function() {
3240- cleanup_widget(this.single);
3241- cleanup_widget(this.multi);
3242- },
3243-
3244- test_multi_line_has_larger_minimum: function() {
3245- var single = this.single.editor;
3246- var multi = this.multi.editor;
3247-
3248- single.setInput('');
3249- multi.setInput('');
3250-
3251- var single_height = single.get('input_field').getStyle('height');
3252- var multi_height = multi.get('input_field').getStyle('height');
3253-
3254- single_height = parse_size(single_height);
3255- multi_height = parse_size(multi_height);
3256-
3257- Assert.areNotEqual(
3258- multi_height,
3259- single_height,
3260- "Multi-line and single-line should have different sizes.");
3261- Assert.isTrue(
3262- multi_height > single_height,
3263- "Multi-line editor should start out larger.");
3264- },
3265-
3266- test_single_line_top_button_box: function() {
3267- var box = this.single.editor.get("top_buttons");
3268- Assert.areEqual(
3269- null,
3270- box,
3271- "Single-line editor should not have a top button box.");
3272- },
3273-
3274- test_multi_line_top_button_box: function() {
3275- var box = this.multi.editor.get("top_buttons");
3276- Assert.areNotEqual(
3277- null,
3278- box,
3279- "Multi-line editor should have a top button box.");
3280- }
3281-}));
3282-
3283-suite.add(new Y.Test.Case({
3284- name: "EditableText text value",
3285-
3286- setUp: function() {
3287- setup_sample_html();
3288- this.multi = make_editable_text({
3289-
3290- contentBox: '#editable_multi_text',
3291- multiline: true
3292- });
3293- this.multi.render();
3294- },
3295-
3296- tearDown: function() {
3297- cleanup_widget(this.multi);
3298- },
3299-
3300- test_text_value_no_trailing_newlines: function() {
3301- var text = this.multi.get('value');
3302- Assert.areEqual(
3303- "Some editable multi-line text.",
3304- text,
3305- "The editor kills trailing whitespace.");
3306- }
3307-}));
3308-
3309-function FailedSavePlugin() {
3310- FailedSavePlugin.superclass.constructor.apply(this, arguments);
3311-}
3312-
3313-FailedSavePlugin.NAME = 'failedsave';
3314-FailedSavePlugin.NS = 'test';
3315-
3316-Y.extend(FailedSavePlugin, Y.Plugin.Base, {
3317- initializer: function(config) {
3318- this.doBefore("_saveData", this._altSave);
3319- },
3320-
3321- _altSave: function() {
3322- var host = this.get('host');
3323- // Set the UI 'waiting' status.
3324- host._uiSetWaiting();
3325- host.showError("Some error occurred.");
3326- // Make sure we clear the 'waiting' status.
3327- host._uiClearWaiting();
3328- return new Y.Do.Halt();
3329- }
3330- });
3331-
3332-suite.add(new Y.Test.Case({
3333- name: "Edit buttons enabled on error",
3334-
3335- setUp: function() {
3336- setup_sample_html();
3337- this.multi = make_editable_text({
3338-
3339- contentBox: '#editable_multi_text',
3340- multiline: true
3341- });
3342- this.multi.render();
3343- this.multi.show_editor();
3344- },
3345-
3346- tearDown: function() {
3347- cleanup_widget(this.multi);
3348- },
3349-
3350- test_error_on_save_enabled_buttons: function() {
3351- var editor = this.multi.editor;
3352- editor.plug({fn:FailedSavePlugin});
3353- // Now saving should invoke an error.
3354- editor.save();
3355- Assert.isTrue(editor.get('in_error'), "Editor should be in error");
3356- // Both the submit and cancel buttons should be visible.
3357- Assert.areEqual(
3358- 'inline-block',
3359- editor.get('submit_button').getStyle('display'),
3360- "Submit should be set to display:inline");
3361- Assert.areEqual(
3362- 'inline-block',
3363- editor.get('cancel_button').getStyle('display'),
3364- "Cancel should be set to display:inline");
3365- }
3366-}));
3367-
3368-Y.lp.testing.Runner.run(suite);
3369-
3370+ input_element.value = 'abc';
3371+
3372+ // A helper to flag the 'save' event.
3373+ var saved = false;
3374+ function saveCheck(e) {
3375+ saved = true;
3376+ }
3377+
3378+ ed.after('ieditor:save', saveCheck, this);
3379+
3380+ // Simulate an 'Enter' key event in the editor's input field.
3381+ Y.Event.simulate(input_element, "keydown", { keyCode: 13 });
3382+
3383+ // Restore to previous state.
3384+ this.editor.set('multiline', false);
3385+
3386+ Assert.isFalse(ed.hasErrors());
3387+ Assert.isFalse(saved,
3388+ "Pressing the 'Enter' key in multiline mode " +
3389+ "should not trigger a save.");
3390+ },
3391+
3392+ test_input_should_be_trimmed_of_whitespace: function() {
3393+ this.editor.render();
3394+
3395+ var input = this.editor.get('input_field');
3396+
3397+ // Set a whitespace value as the input.
3398+ input.set('value', ' ');
3399+
3400+ this.editor.save();
3401+
3402+ Assert.isTrue(
3403+ this.editor.hasErrors(),
3404+ "The editor should be displaying an error after trying to " +
3405+ "save a whitespace value.");
3406+ }
3407+
3408+
3409+ }));
3410+
3411+ tests.suite.add(new Y.Test.Case({
3412+ name: 'Initial value',
3413+
3414+ setUp: function() {
3415+ this.editor = make_editor({
3416+ initial_value_override: 'Initial value'
3417+ });
3418+ },
3419+
3420+ tearDown: function() {
3421+ cleanup_widget(this.editor);
3422+ },
3423+
3424+ test_initial_value_override: function() {
3425+ this.editor.render();
3426+ Assert.areEqual(
3427+ 'Initial value',
3428+ this.editor.get('input_field').get('value'),
3429+ "The editor's input field should have the initial value.");
3430+ }
3431+ }));
3432+
3433+ tests.suite.add(new Y.Test.Case({
3434+ name: 'Editable text initial values',
3435+
3436+ setUp: function() {
3437+ setup_sample_html();
3438+ this.etext = make_editable_text(
3439+ {initial_value_override: 'Initial value'});
3440+ },
3441+
3442+ tearDown: function() {
3443+ // Reset the <span>.
3444+ cleanup_widget(this.etext);
3445+ },
3446+
3447+ test_save_initial_value_override: function() {
3448+ this.etext.render();
3449+
3450+ Assert.areEqual(
3451+ 'Initial value',
3452+ this.etext.editor.get('input_field').get('value'),
3453+ "The input_field should have been set to the initial value.");
3454+
3455+ this.etext.editor.save();
3456+ Assert.areEqual(
3457+ 'Initial value',
3458+ this.etext.editor.get('value'),
3459+ "The editor's initial value did not get saved.");
3460+ Assert.areEqual(
3461+ null,
3462+ this.etext.editor.get('initial_value_override'),
3463+ "The editor's initial_value_override should be null.");
3464+ },
3465+
3466+ test_cancel_does_not_modify_value: function() {
3467+ this.etext.render();
3468+
3469+ Assert.areEqual(
3470+ 'Some editable inline text.',
3471+ this.etext.editor.get('value'),
3472+ "The editor's value is not what it should be.");
3473+ Assert.areEqual(
3474+ 'Initial value',
3475+ this.etext.editor.get('initial_value_override'),
3476+ "The editor's initial_value_override is not correct.");
3477+
3478+ this.etext.editor.cancel();
3479+ Assert.areEqual(
3480+ 'Some editable inline text.',
3481+ this.etext.editor.get('value'),
3482+ "The editor's value did not get reset.");
3483+ Assert.areEqual(
3484+ 'Initial value',
3485+ this.etext.editor.get('initial_value_override'),
3486+ "The editor's initial_value_override did not get preserved.");
3487+ }
3488+ }));
3489+
3490+ tests.suite.add(new Y.Test.Case({
3491+
3492+ name: "Inline editor input sizing for a positive size value",
3493+
3494+ setUp: function() {
3495+ this.expected_size = 32;
3496+ this.editor = make_editor({size: this.expected_size});
3497+ },
3498+
3499+ tearDown: function() {
3500+ cleanup_widget(this.editor);
3501+ },
3502+
3503+ test_editor_size_attribute_matches_user_value: function() {
3504+ Assert.areEqual(
3505+ this.editor.get('size'),
3506+ this.expected_size,
3507+ "The editor's 'size' attribute should match the user's " +
3508+ "specified size.");
3509+ },
3510+
3511+ test_input_field_size_matches_the_editor_size: function() {
3512+ this.editor.render();
3513+ var input = this.editor.get('input_field');
3514+ Assert.areEqual(
3515+ this.expected_size + 'px',
3516+ input.getStyle('width'),
3517+ "The editor's input field size should have been set from the " +
3518+ "'size' attribute.");
3519+ }
3520+
3521+ }));
3522+
3523+ tests.suite.add(new Y.Test.Case({
3524+
3525+ name: "Inline editor input sizing for a null size value",
3526+
3527+ setUp: function() {
3528+ this.editor = make_editor();
3529+ this.editor.render();
3530+ },
3531+
3532+ tearDown: function() {
3533+ cleanup_widget(this.editor);
3534+ },
3535+
3536+ test_editor_size_attribute_is_null: function() {
3537+ Assert.areEqual(
3538+ null,
3539+ this.editor.get('size'),
3540+ "The editor's 'size' attribute should default to 'null'.");
3541+ },
3542+
3543+ test_editor_input_has_browser_default_size: function() {
3544+ var input = this.editor.get('input_field');
3545+ Assert.isFalse(
3546+ input.hasAttribute('size'),
3547+ "The editor's input field should have the browser default " +
3548+ "size if the editor's size is 'null'.");
3549+ }
3550+ }));
3551+
3552+ /*
3553+ * XXX mars 20090206
3554+ *
3555+ * The following test is just for the attribute validators. Most of this is
3556+ * made necessary because YUI doesn't publish attribute validation errors.
3557+ *
3558+ * See ticket http://yuilibrary.com/projects/yui3/ticket/2525946
3559+ */
3560+ tests.suite.add(new Y.Test.Case({
3561+
3562+ name: "Inline editor size attribute validation",
3563+
3564+ setUp: function() {
3565+ this.initial_size = null;
3566+ this.editor = make_editor({size: this.initial_size});
3567+ },
3568+
3569+ test_editor_accepts_null_as_size: function() {
3570+ this.editor.set('size', null);
3571+ Assert.areEqual(
3572+ null,
3573+ this.editor.get('size'),
3574+ "The editor should accept a null value for the size attr.");
3575+ },
3576+
3577+ test_editor_accepts_positive_numbers_as_size: function() {
3578+ this.editor.set('size', 123);
3579+ Assert.areEqual(
3580+ 123,
3581+ this.editor.get('size'),
3582+ "The editor should accept a positive number as a valid size.");
3583+ },
3584+
3585+ test_editor_rejects_negative_numbers_for_size: function() {
3586+ this.editor.set('size', -2);
3587+ Assert.areEqual(
3588+ this.initial_size,
3589+ this.editor.get('size'),
3590+ "The editor should not accept negative numbers for its size.");
3591+ },
3592+
3593+ test_editor_rejects_characters_for_size: function() {
3594+ this.editor.set('size', 'a');
3595+ Assert.areEqual(
3596+ this.initial_size,
3597+ this.editor.get('size'),
3598+ "The editor should not accept strings for its size.");
3599+ }
3600+ }));
3601+
3602+
3603+ tests.suite.add(new Y.Test.Case({
3604+
3605+ name: 'editor_save_state_change',
3606+
3607+ setUp: function() {
3608+ this.editor = make_editor();
3609+ },
3610+
3611+ tearDown: function() {
3612+ cleanup_widget(this.editor);
3613+ },
3614+
3615+ test_ui_initial_state_is_not_waiting: function() {
3616+ this.editor.render();
3617+ Assert.isFalse(
3618+ this.editor.get('boundingBox').hasClass('yui3-ieditor-waiting'),
3619+ "The editor UI should not start out in the 'waiting' state.");
3620+ },
3621+
3622+ test_set_ui_waiting_state: function() {
3623+ var ed = this.editor;
3624+ ed.render();
3625+
3626+ ed._uiSetWaiting();
3627+
3628+ Assert.isTrue(
3629+ ed.get('input_field').get('disabled'),
3630+ "The editor's input should be disabled while in the " +
3631+ "'waiting' state.");
3632+ Assert.isTrue(
3633+ ed.get('boundingBox').hasClass('yui3-ieditor-waiting'),
3634+ "The editor's UI should reflect the 'waiting' state " +
3635+ "with an appropriate class.");
3636+ },
3637+
3638+ test_clear_ui_waiting_state: function() {
3639+ var ed = this.editor;
3640+ ed.render();
3641+
3642+ ed._uiSetWaiting();
3643+ ed._uiClearWaiting();
3644+
3645+ Assert.isFalse(
3646+ ed.get('input_field').get('disabled'),
3647+ "The editor's input should be re-enabled when clearing " +
3648+ "the 'waiting' state.");
3649+ Assert.isFalse(
3650+ ed.get('boundingBox').hasClass('yui3-ieditor-waiting'),
3651+ "The editor's UI should have the 'waiting' state " +
3652+ "class removed.");
3653+ }
3654+ }));
3655+
3656+
3657+ tests.suite.add(new Y.Test.Case({
3658+
3659+ name: 'editable_text',
3660+
3661+ setUp: function() {
3662+ setup_sample_html();
3663+ this.etext = make_editable_text();
3664+ },
3665+
3666+ tearDown: function() {
3667+ cleanup_widget(this.etext);
3668+ },
3669+
3670+ test_initial_values_from_DOM: function() {
3671+ Assert.areEqual(
3672+ Y.one("#single_text"),
3673+ this.etext.get('text'),
3674+ "The editor's text node should have been set from the " +
3675+ "DOM.");
3676+
3677+ Assert.areEqual(
3678+ Y.one('#single_edit'),
3679+ this.etext.get('trigger'),
3680+ "The editor's trigger node should have been set from " +
3681+ "the DOM.");
3682+
3683+ Assert.areEqual(
3684+ 'Some editable inline text.',
3685+ this.etext.editor.get('value'),
3686+ "The editor's initial value should be set from it's " +
3687+ "text node.");
3688+
3689+ Assert.areEqual(
3690+ this.etext.editor.get('value'),
3691+ this.etext.get('value'),
3692+ "The editable text's value should be the same as the " +
3693+ "editor's.");
3694+ },
3695+
3696+ test_show: function() {
3697+ /* The show() method should display the editor, and hide the
3698+ * existing contents.
3699+ */
3700+ this.etext.render();
3701+ this.etext.show_editor();
3702+ Assert.isTrue(this.etext.editor.get('visible'),
3703+ "The editor's 'visible' attribute should be true.");
3704+ },
3705+
3706+ test_hide: function() {
3707+ /* The hide() method should hide the editor, and display the
3708+ * original contents.
3709+ */
3710+ this.etext.render();
3711+ this.etext.show_editor();
3712+ this.etext.hide_editor();
3713+ Assert.isFalse(this.etext.editor.get('visible'),
3714+ "The editor's 'visible' attribute should be False.");
3715+ },
3716+
3717+ test_trigger_edit: function() {
3718+ /* Clicking on the editable text's "Edit" button should
3719+ * make the editor visible.
3720+ */
3721+ Assert.isFalse(this.etext.editor.get('visible'),
3722+ "Sanity check, the editor should be hidden.");
3723+
3724+ this.etext.render();
3725+ simulate('#single_edit', 'click');
3726+
3727+ Assert.isTrue(this.etext.editor.get('visible'),
3728+ "The editor should be visible.");
3729+ },
3730+
3731+ test_text_is_updated_to_saved_value: function() {
3732+ this.etext.render();
3733+
3734+ // Grab the normalized text.
3735+ var expected_value = 'abc';
3736+
3737+ Assert.areNotEqual(
3738+ expected_value,
3739+ this.etext.get('value'),
3740+ "Sanity check");
3741+
3742+ simulate('#single_edit', 'click');
3743+ this.etext.editor
3744+ .get('input_field')
3745+ .set('value', expected_value);
3746+
3747+ this.etext.editor.save();
3748+
3749+ Assert.areEqual(
3750+ expected_value,
3751+ this.etext.editor.get('value'),
3752+ "Sanity check: the editor's value should have been " +
3753+ "saved.");
3754+
3755+ Assert.areEqual(
3756+ expected_value,
3757+ this.etext.get('value'),
3758+ "The editable text's current value should be updated " +
3759+ "after saving some new text in the editor.");
3760+ },
3761+
3762+ test_text_is_escaped: function() {
3763+ this.etext.render();
3764+
3765+ var input_value = '<i>l33t inject0r d00d</i> 0wnz y00';
3766+ var shown_value = '&lt;i&gt;l33t inject0r d00d&lt;/i&gt; 0wnz y00';
3767+
3768+ simulate('#single_edit', 'click');
3769+ this.etext.editor.setInput(input_value);
3770+ this.etext.editor.save();
3771+
3772+ Assert.areEqual(
3773+ shown_value,
3774+ this.etext.get('text').get('innerHTML'),
3775+ "Input text should be escaped before being inserted in HTML.");
3776+ Assert.areEqual(
3777+ input_value,
3778+ this.etext.editor.getInput(),
3779+ "Input text should be retained verbatim.");
3780+ },
3781+
3782+ test_accept_empty_attribute_passthrough: function() {
3783+ var et = this.etext;
3784+
3785+ Assert.areEqual(
3786+ et.get('accept_empty'),
3787+ et.editor.get('accept_empty'),
3788+ "The editor and inline editor's 'accept_empty " +
3789+ "should start out the same.");
3790+
3791+ et.set('accept_empty', true);
3792+ Assert.isTrue(
3793+ et.editor.get('accept_empty'),
3794+ "The inline editor's 'accept_empty' attribute should " +
3795+ "also be set to 'true'.");
3796+ Assert.isTrue(
3797+ et.get('accept_empty'),
3798+ "The editor's 'accept_empty' attribute should be true.");
3799+
3800+ et.set('accept_empty', false);
3801+ Assert.isFalse(
3802+ et.get('accept_empty'),
3803+ "The editor's 'accept_empty' attribute should be false.");
3804+ Assert.isFalse(
3805+ et.editor.get('accept_empty'),
3806+ "The inline editor's 'accept_empty' attribute should " +
3807+ "also be set to 'false'.");
3808+ },
3809+
3810+ test_widget_has_a_disabled_tabindex_when_focused: function() {
3811+ // The tabindex attribute appears when the widget is focused.
3812+ this.etext.render();
3813+ this.etext.focus();
3814+
3815+ // Be aware that in IE, get('tabIndex') and
3816+ // getAttribute('tabIndex') return different values when set to
3817+ // -1. This is due to YUI's getAttribute() calling
3818+ // dom_node.getAttribute('tabIndex', 2), which is an IE extension.
3819+ // http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx
3820+
3821+ // On IE and KHTML, EditableText._onRender() will prevent the
3822+ // default widget rendering that would set the tabIndex on the
3823+ // boundingBox, so this test will fail for those browsers.
3824+ Assert.areEqual(
3825+ -1,
3826+ this.etext.get('boundingBox').get('tabIndex'),
3827+ "The widget should have a tabindex of -1 (disabled).");
3828+ },
3829+
3830+ test_trigger_is_disabled_if_the_widget_is_not_rendered: function() {
3831+ var trigger = this.etext.get('trigger');
3832+ Assert.isInstanceOf(
3833+ Y.Node, trigger,
3834+ "Sanity check: the editor's trigger should be a valid node.");
3835+ Assert.isFalse(
3836+ this.etext.get('rendered'),
3837+ "Sanity check: the editor should not be rendered.");
3838+
3839+ simulate(trigger, 'click');
3840+ // Peek inside the box a bit, and check that the nested editor
3841+ // instance is still invisible. Assume that if it is, then
3842+ // the show_editor() method was never called.
3843+ Assert.isFalse(
3844+ this.etext.editor.get('visible'),
3845+ "Triggering an unrendered editor should not display widget.");
3846+ }
3847+ }));
3848+
3849+ tests.suite.add(new Y.Test.Case({
3850+
3851+ name: "EditableText single-line/multi-line modes",
3852+
3853+ setUp: function() {
3854+ setup_sample_html();
3855+ this.single = make_editable_text({
3856+ contentBox: '#editable_single_text',
3857+ multiline: false
3858+ });
3859+ this.single.render();
3860+ this.single.show_editor();
3861+ this.multi = make_editable_text({
3862+ contentBox: '#editable_multi_text',
3863+ multiline: true
3864+ });
3865+ this.multi.render();
3866+ this.multi.show_editor();
3867+ },
3868+
3869+ tearDown: function() {
3870+ cleanup_widget(this.single);
3871+ cleanup_widget(this.multi);
3872+ },
3873+
3874+ test_multi_line_has_larger_minimum: function() {
3875+ var single = this.single.editor;
3876+ var multi = this.multi.editor;
3877+
3878+ single.setInput('');
3879+ multi.setInput('');
3880+
3881+ var single_height = single.get('input_field').getStyle('height');
3882+ var multi_height = multi.get('input_field').getStyle('height');
3883+
3884+ single_height = parse_size(single_height);
3885+ multi_height = parse_size(multi_height);
3886+
3887+ Assert.areNotEqual(
3888+ multi_height,
3889+ single_height,
3890+ "Multi-line and single-line should have different sizes.");
3891+ Assert.isTrue(
3892+ multi_height > single_height,
3893+ "Multi-line editor should start out larger.");
3894+ },
3895+
3896+ test_single_line_top_button_box: function() {
3897+ var box = this.single.editor.get("top_buttons");
3898+ Assert.areEqual(
3899+ null,
3900+ box,
3901+ "Single-line editor should not have a top button box.");
3902+ },
3903+
3904+ test_multi_line_top_button_box: function() {
3905+ var box = this.multi.editor.get("top_buttons");
3906+ Assert.areNotEqual(
3907+ null,
3908+ box,
3909+ "Multi-line editor should have a top button box.");
3910+ }
3911+ }));
3912+
3913+ tests.suite.add(new Y.Test.Case({
3914+ name: "EditableText text value",
3915+
3916+ setUp: function() {
3917+ setup_sample_html();
3918+ this.multi = make_editable_text({
3919+
3920+ contentBox: '#editable_multi_text',
3921+ multiline: true
3922+ });
3923+ this.multi.render();
3924+ },
3925+
3926+ tearDown: function() {
3927+ cleanup_widget(this.multi);
3928+ },
3929+
3930+ test_text_value_no_trailing_newlines: function() {
3931+ var text = this.multi.get('value');
3932+ Assert.areEqual(
3933+ "Some editable multi-line text.",
3934+ text,
3935+ "The editor kills trailing whitespace.");
3936+ }
3937+ }));
3938+
3939+ function FailedSavePlugin() {
3940+ FailedSavePlugin.superclass.constructor.apply(this, arguments);
3941+ }
3942+
3943+ FailedSavePlugin.NAME = 'failedsave';
3944+ FailedSavePlugin.NS = 'test';
3945+
3946+ Y.extend(FailedSavePlugin, Y.Plugin.Base, {
3947+ initializer: function(config) {
3948+ this.doBefore("_saveData", this._altSave);
3949+ },
3950+
3951+ _altSave: function() {
3952+ var host = this.get('host');
3953+ // Set the UI 'waiting' status.
3954+ host._uiSetWaiting();
3955+ host.showError("Some error occurred.");
3956+ // Make sure we clear the 'waiting' status.
3957+ host._uiClearWaiting();
3958+ return new Y.Do.Halt();
3959+ }
3960+ });
3961+
3962+ tests.suite.add(new Y.Test.Case({
3963+ name: "Edit buttons enabled on error",
3964+
3965+ setUp: function() {
3966+ setup_sample_html();
3967+ this.multi = make_editable_text({
3968+
3969+ contentBox: '#editable_multi_text',
3970+ multiline: true
3971+ });
3972+ this.multi.render();
3973+ this.multi.show_editor();
3974+ },
3975+
3976+ tearDown: function() {
3977+ cleanup_widget(this.multi);
3978+ },
3979+
3980+ test_error_on_save_enabled_buttons: function() {
3981+ var editor = this.multi.editor;
3982+ editor.plug({fn:FailedSavePlugin});
3983+ // Now saving should invoke an error.
3984+ editor.save();
3985+ Assert.isTrue(editor.get('in_error'), "Editor should be in error");
3986+ // Both the submit and cancel buttons should be visible.
3987+ Assert.areEqual(
3988+ 'inline-block',
3989+ editor.get('submit_button').getStyle('display'),
3990+ "Submit should be set to display:inline");
3991+ Assert.areEqual(
3992+ 'inline-block',
3993+ editor.get('cancel_button').getStyle('display'),
3994+ "Cancel should be set to display:inline");
3995+ }
3996+ }));
3997+
3998+}, '0.1', {'requires': ['test', 'console', 'lazr.editor', 'node',
3999+ 'lp.app.formwidgets.resizing_textarea', 'event',
4000+ 'event-simulate', 'plugin']
4001 });
4002+
4003+
4004+
4005+
4006
4007=== modified file 'lib/lp/app/javascript/inlinehelp/tests/test_inlinehelp.html'
4008--- lib/lp/app/javascript/inlinehelp/tests/test_inlinehelp.html 2011-12-22 19:13:18 +0000
4009+++ lib/lp/app/javascript/inlinehelp/tests/test_inlinehelp.html 2012-02-15 12:51:42 +0000
4010@@ -1,31 +1,47 @@
4011+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
4012+ "http://www.w3.org/TR/html4/strict.dtd">
4013 <!--
4014-Copyright 2011 Canonical Ltd. This software is licensed under the
4015+Copyright 2012 Canonical Ltd. This software is licensed under the
4016 GNU Affero General Public License version 3 (see the file LICENSE).
4017 -->
4018-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
4019 <html>
4020 <head>
4021- <title>Test InlineHelp </title>
4022- <!-- YUI and test setup -->
4023- <script type="text/javascript"
4024- src="../../../../../canonical/launchpad/icing/yui/yui/yui.js">
4025- </script>
4026- <link rel="stylesheet" href="../../../../app/javascript/testing/test.css" />
4027- <script type="text/javascript"
4028- src="../../../../app/javascript/testing/testrunner.js"></script>
4029-
4030- <script type="text/javascript" src="../../overlay/overlay.js"></script>
4031-
4032- <!-- The module under test -->
4033- <script type="text/javascript" src="../inlinehelp.js"></script>
4034-
4035- <!-- The test suite -->
4036- <script type="text/javascript" src="test_inlinehelp.js"></script>
4037- </head>
4038- <body class="yui3-skin-sam">
4039- <ul id="suites">
4040- <li>lp.app.inlinehelp.test</li>
4041- </ul>
4042- </body>
4043+ <title>Test inlinehelp</title>
4044+
4045+ <!-- YUI and test setup -->
4046+ <script type="text/javascript"
4047+ src="../../../../../../build/js/yui/yui/yui.js">
4048+ </script>
4049+ <link rel="stylesheet"
4050+ href="../../../../../../build/js/yui/console/assets/console-core.css" />
4051+ <link rel="stylesheet"
4052+ href="../../../../../../build/js/yui/console/assets/skins/sam/console.css" />
4053+ <link rel="stylesheet"
4054+ href="../../../../../../build/js/yui/test/assets/skins/sam/test.css" />
4055+
4056+ <script type="text/javascript"
4057+ src="../../../../../../build/js/lp/app/testing/testrunner.js"></script>
4058+
4059+ <link rel="stylesheet" href="../../../../app/javascript/testing/test.css" />
4060+
4061+ <!-- Dependencies -->
4062+ <script type="text/javascript"
4063+ src="../../../../../../build/js/lp/app/overlay/overlay.js"></script>
4064+
4065+ <!-- The module under test. -->
4066+ <script type="text/javascript" src="../inlinehelp.js"></script>
4067+
4068+ <!-- Placeholder for any css asset for this module. -->
4069+ <!-- <link rel="stylesheet" href="../assets/inlinehelp-core.css" /> -->
4070+
4071+ <!-- The test suite. -->
4072+ <script type="text/javascript" src="test_inlinehelp.js"></script>
4073+
4074+ </head>
4075+ <body class="yui3-skin-sam">
4076+ <ul id="suites">
4077+ <!-- <li>lp.large_indicator.test</li> -->
4078+ <li>lp.app.inlinehelp.test</li>
4079+ </ul>
4080+ </body>
4081 </html>
4082-
4083
4084=== modified file 'lib/lp/app/javascript/inlinehelp/tests/test_inlinehelp.js'
4085--- lib/lp/app/javascript/inlinehelp/tests/test_inlinehelp.js 2012-01-03 16:23:11 +0000
4086+++ lib/lp/app/javascript/inlinehelp/tests/test_inlinehelp.js 2012-02-15 12:51:42 +0000
4087@@ -2,11 +2,10 @@
4088
4089 YUI.add('lp.app.inlinehelp.test', function (Y) {
4090
4091- var suite = new Y.Test.Suite('InlineHelp Tests');
4092+ var tests = Y.namespace('lp.app.inlinehelp.test');
4093+ tests.suite = new Y.Test.Suite('InlineHelp Tests');
4094 var Assert = Y.Assert;
4095- var test_module = Y.namespace('lp.app.inlinehelp.test');
4096-
4097- suite.add(new Y.Test.Case({
4098+ tests.suite.add(new Y.Test.Case({
4099 name: 'inlinehelp.init_help',
4100
4101 setUp: function () {
4102@@ -129,7 +128,6 @@
4103 }
4104 }));
4105
4106-test_module.suite = suite;
4107
4108 }, '0.1', {
4109 'requires': ['node', 'console', 'test', 'lp.app.inlinehelp',
4110
4111=== modified file 'lib/lp/app/javascript/ordering/tests/test_orderby_widget.html'
4112--- lib/lp/app/javascript/ordering/tests/test_orderby_widget.html 2011-11-02 19:06:42 +0000
4113+++ lib/lp/app/javascript/ordering/tests/test_orderby_widget.html 2012-02-15 12:51:42 +0000
4114@@ -1,28 +1,47 @@
4115 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
4116 "http://www.w3.org/TR/html4/strict.dtd">
4117+<!--
4118+Copyright 2012 Canonical Ltd. This software is licensed under the
4119+GNU Affero General Public License version 3 (see the file LICENSE).
4120+-->
4121+
4122 <html>
4123 <head>
4124- <title>Order By Widget Tests</title>
4125-
4126- <!-- YUI and test setup -->
4127- <script type="text/javascript"
4128- src="../../../../../canonical/launchpad/icing/yui/yui/yui.js">
4129- </script>
4130- <link rel="stylesheet" href="../../../../app/javascript/testing/test.css" />
4131- <script type="text/javascript"
4132- src="../../../../app/javascript/testing/testrunner.js"></script>
4133-
4134- <!-- The module under test -->
4135- <script type="text/javascript" src="../ordering.js"></script>
4136- <link rel="stylesheet" href="../assets/ordering-core.css" />
4137-
4138- <!-- The test suite -->
4139- <script type="text/javascript" src="test_orderby_widget.js"></script>
4140-
4141-</head>
4142-<body class="yui3-skin-sam">
4143- <ul id="suites">
4144- <li>lp.orderbybar.test</li>
4145- </ul>
4146-</body>
4147+ <title>Test orderbybar</title>
4148+
4149+ <!-- YUI and test setup -->
4150+ <script type="text/javascript"
4151+ src="../../../../../../build/js/yui/yui/yui.js">
4152+ </script>
4153+ <link rel="stylesheet"
4154+ href="../../../../../../build/js/yui/console/assets/console-core.css" />
4155+ <link rel="stylesheet"
4156+ href="../../../../../../build/js/yui/console/assets/skins/sam/console.css" />
4157+ <link rel="stylesheet"
4158+ href="../../../../../../build/js/yui/test/assets/skins/sam/test.css" />
4159+
4160+ <script type="text/javascript"
4161+ src="../../../../../../build/js/lp/app/testing/testrunner.js"></script>
4162+
4163+ <link rel="stylesheet" href="../../../../app/javascript/testing/test.css" />
4164+
4165+ <!-- Dependencies -->
4166+ <!-- <script type="text/javascript" src="../../../../../../build/js/lp/..."></script> -->
4167+
4168+ <!-- The module under test. -->
4169+ <script type="text/javascript" src="../ordering.js"></script>
4170+
4171+ <!-- Placeholder for any css asset for this module. -->
4172+ <link rel="stylesheet" href="../assets/ordering-core.css" />
4173+
4174+ <!-- The test suite. -->
4175+ <script type="text/javascript" src="test_orderby_widget.js"></script>
4176+
4177+ </head>
4178+ <body class="yui3-skin-sam">
4179+ <ul id="suites">
4180+ <!-- <li>lp.large_indicator.test</li> -->
4181+ <li>lp.orderbybar.test</li>
4182+ </ul>
4183+ </body>
4184 </html>
4185
4186=== modified file 'lib/lp/app/javascript/ordering/tests/test_orderby_widget.js'
4187--- lib/lp/app/javascript/ordering/tests/test_orderby_widget.js 2011-12-21 13:23:46 +0000
4188+++ lib/lp/app/javascript/ordering/tests/test_orderby_widget.js 2012-02-15 12:51:42 +0000
4189@@ -1,386 +1,380 @@
4190-/* Copyright (c) 2011, Canonical Ltd. All rights reserved. */
4191-
4192+/* Copyright (c) 2012, Canonical Ltd. All rights reserved. */
4193 YUI.add('lp.orderbybar.test', function(Y) {
4194
4195-var basic_test = Y.namespace('lp.orderbybar.test');
4196-
4197-var suite = new Y.Test.Suite('OrderByBar Tests');
4198-
4199-var Assert = Y.Assert;
4200-var ArrayAssert = Y.ArrayAssert;
4201-
4202-suite.add(new Y.Test.Case({
4203-
4204- name: 'orderbybar_widget_tests',
4205-
4206- orderby: null,
4207-
4208- _should: {
4209- error: {
4210- test_sort_order_validator:
4211- new Error('sort_order must be either "asc" or "desc"'),
4212- test_active_sort_validator:
4213- new Error('active attribute was not found in sort_keys')
4214- }
4215- },
4216-
4217- tearDown: function() {
4218- if (Y.Lang.isValue(this.orderby)) {
4219- this.orderby.destroy();
4220- }
4221- },
4222-
4223- /**
4224- * Unpack a list of key, name pairs into individual lists.
4225- *
4226- * [[Foo, 'Foo Item'], ['Bar', 'Bar item']] becomes
4227- * ['Foo', 'Bar'] and ['Foo Item', 'Bar Item'].
4228- */
4229- getIdsAndNames: function(keys) {
4230- var ids = [];
4231- var names = [];
4232- var len = keys.length;
4233- var i;
4234- for (i=0; i<len; i++) {
4235- ids.push(keys[i][0]);
4236- names.push(keys[i][1]);
4237- }
4238- return [ids, names];
4239- },
4240-
4241- /*
4242- * Helper function to create the srcNode on the page. Widgets
4243- * will append to the body tag if srcNode is not supplied.
4244- */
4245- makeSrcNode: function(id) {
4246- // Calling the widget's destroy method, which teardown does,
4247- // will clean this up.
4248- var parent_node = Y.Node.create('<div></div>');
4249- parent_node.set('id', id);
4250- Y.one('body').appendChild(parent_node);
4251- },
4252-
4253- test_default_sort_keys: function() {
4254- // The default sort keys should exist in a newly created widget.
4255- this.orderby = new Y.lp.ordering.OrderByBar();
4256- var expected_sort_keys = [
4257- ['bugnumber', 'Number'],
4258- ['bugtitle', 'Title'],
4259- ['status', 'Status'],
4260- ['importance', 'Importance'],
4261- ['bug-heat-icons', 'Heat'],
4262- ['package', 'Package name'],
4263- ['milestone', 'Milestone'],
4264- ['assignee', 'Assignee'],
4265- ['bug-age', 'Age']
4266- ];
4267- var expected = this.getIdsAndNames(expected_sort_keys);
4268- var actual = this.getIdsAndNames(this.orderby.get('sort_keys'));
4269- ArrayAssert.itemsAreSame(expected[0], actual[0]);
4270- ArrayAssert.itemsAreSame(expected[1], actual[1]);
4271- },
4272-
4273- test_user_supplied_sort_keys: function() {
4274- // Call sites can supply their own sort keys to a widget.
4275- var user_supplied_sort_keys = [
4276- ['foo', 'Foo item', 'asc'],
4277- ['bar', 'Bar item', 'asc'],
4278- ['baz', 'Baz item', 'asc']
4279- ];
4280- this.orderby = new Y.lp.ordering.OrderByBar({
4281- sort_keys: user_supplied_sort_keys});
4282- var expected = this.getIdsAndNames(user_supplied_sort_keys);
4283- var actual = this.getIdsAndNames(this.orderby.get('sort_keys'));
4284- ArrayAssert.itemsAreSame(expected[0], actual[0]);
4285- ArrayAssert.itemsAreSame(expected[1], actual[1]);
4286- },
4287-
4288- test_rendered_items_html: function() {
4289- // We should be able to get a node from the DOM via an ID
4290- // created from sort keys, and the name should be used as
4291- // a button display name in HTML.
4292- var test_sort_keys = [
4293- ['foo', 'Foo item', 'asc'],
4294- ['bar', 'Bar item', 'asc']
4295- ];
4296- this.makeSrcNode('test-div');
4297- this.orderby = new Y.lp.ordering.OrderByBar({
4298- sort_keys: test_sort_keys,
4299- srcNode: Y.one('#test-div'),
4300- active: 'foo'
4301- });
4302- this.orderby.render();
4303- var foo_node = Y.one('#sort-foo');
4304- Assert.isNotNull(foo_node);
4305- Assert.areEqual(foo_node.get('firstChild').get('text'), 'Foo item');
4306- var bar_node = Y.one('#sort-bar');
4307- Assert.isNotNull(bar_node);
4308- Assert.areEqual(bar_node.get('firstChild').get('text'), 'Bar item');
4309- },
4310-
4311- test_render_active_sort_default: function() {
4312- // Confirm that there is a default active sort class applied.
4313- this.makeSrcNode('test-div');
4314- this.orderby = new Y.lp.ordering.OrderByBar({
4315- srcNode: Y.one('#test-div')
4316- });
4317- this.orderby.render();
4318- var li_node = Y.one('#sort-importance');
4319- Assert.isTrue(li_node.hasClass('active-sort'));
4320- },
4321-
4322- test_render_active_sort_user_supplied: function() {
4323- // The active sort class is also set when "active"
4324- // is supplied via config.
4325- this.makeSrcNode('test-div');
4326- this.orderby = new Y.lp.ordering.OrderByBar({
4327- srcNode: Y.one('#test-div'),
4328- active: 'status'
4329- });
4330- this.orderby.render();
4331- var li_node = Y.one('#sort-status');
4332- Assert.isTrue(li_node.hasClass('active-sort'));
4333- },
4334-
4335- test_active_sort_arrow_display_asc: function() {
4336- // Buttons using "asc" order get a down arrow added to the li.
4337- this.makeSrcNode('test-div');
4338- this.orderby = new Y.lp.ordering.OrderByBar({
4339- srcNode: Y.one('#test-div'),
4340- sort_order: 'asc'
4341- });
4342- this.orderby.render();
4343- var arrow_span = Y.one('.active-sort span');
4344- var expected_text = '<span class="sprite order-ascending"></span>';
4345- Assert.areEqual(expected_text, arrow_span.get('innerHTML'));
4346- },
4347-
4348- test_active_sort_arrow_display_desc: function() {
4349- // Buttons using "desc" order get an up arrow added to the li.
4350- this.makeSrcNode('test-div');
4351- this.orderby = new Y.lp.ordering.OrderByBar({
4352- srcNode: Y.one('#test-div'),
4353- sort_order: 'desc'
4354- });
4355- this.orderby.render();
4356- var arrow_span = Y.one('.active-sort span');
4357- var expected_text = '<span class="sprite order-descending"></span>';
4358- Assert.areEqual(expected_text, arrow_span.get('innerHTML'));
4359- },
4360-
4361- test_active_sort_click_class_change: function() {
4362- // Click a node should add the active_sort class
4363- // and remove that class from the previously active node.
4364- this.makeSrcNode('test-div');
4365- this.orderby = new Y.lp.ordering.OrderByBar({
4366- srcNode: Y.one('#test-div')
4367- });
4368- this.orderby.render();
4369- var importance_node = Y.one('#sort-importance');
4370- Assert.isTrue(importance_node.hasClass('active-sort'));
4371- var status_node = Y.one('#sort-status');
4372- status_node.simulate('click');
4373- Assert.isTrue(status_node.hasClass('active-sort'));
4374- },
4375-
4376- test_active_sort_validator: function() {
4377- // This should fail because we do not allow
4378- // a "active" value not found in sort_keys.
4379- var test_sort_keys = [
4380- ['foo', 'Foo item', 'asc'],
4381- ['bar', 'Bar item', 'asc']
4382- ];
4383- this.orderby = new Y.lp.ordering.OrderByBar({
4384- sort_keys: test_sort_keys,
4385- active: 'foobarbazdonotexists'
4386- });
4387- this.orderby.render();
4388- },
4389-
4390- test_sort_order_validator: function() {
4391- // This should fail when using a sort order
4392- // other than "asc" or "desc".
4393- this.orderby = new Y.lp.ordering.OrderByBar({
4394- sort_order: 'foobar'
4395- });
4396- this.orderby.render();
4397- },
4398-
4399- test_click_current_sort_arrow_changes: function() {
4400- // Clicking the currently sorted on button should change
4401- // the arrow and widget state to show a sort change should
4402- // happen.
4403- this.makeSrcNode('test-div');
4404- var test_sort_keys = [
4405- ['foo', 'Foo item', 'asc'],
4406- ['bar', 'Bar item', 'asc']
4407- ];
4408- this.orderby = new Y.lp.ordering.OrderByBar({
4409- srcNode: Y.one('#test-div'),
4410- sort_keys: test_sort_keys,
4411- active: 'foo',
4412- sort_order: 'asc'
4413- });
4414- this.orderby.render();
4415- var foo_node = Y.one('#sort-foo');
4416- var expected_starting_text =
4417- '<span class="sprite order-ascending"></span>';
4418- var expected_ending_text =
4419- '<span class="sprite order-descending"></span>';
4420- Assert.areEqual(
4421- expected_starting_text, foo_node.one('span').get('innerHTML'));
4422- Assert.isTrue(foo_node.one('span').hasClass('asc'));
4423- foo_node.simulate('click');
4424- Assert.areEqual(
4425- expected_ending_text, foo_node.one('span').get('innerHTML'));
4426- Assert.isTrue(foo_node.one('span').hasClass('desc'));
4427- },
4428-
4429- test_click_different_sort_arrows_change: function() {
4430- // Clicking a button other than the currently sorted on button
4431- // should change the arrow and widget state to show a sort
4432- // change should happen.
4433- this.makeSrcNode('test-div');
4434- var test_sort_keys = [
4435- ['foo', 'Foo item', 'asc'],
4436- ['bar', 'Bar item', 'asc']
4437- ];
4438- this.orderby = new Y.lp.ordering.OrderByBar({
4439- srcNode: Y.one('#test-div'),
4440- sort_keys: test_sort_keys,
4441- active: 'foo',
4442- sort_order: 'asc'
4443- });
4444- this.orderby.render();
4445- var bar_node = Y.one('#sort-bar');
4446- bar_node.simulate('click');
4447- var expected_arrow = '<span class="sprite order-ascending"></span>';
4448- Assert.areEqual(
4449- expected_arrow, bar_node.one('span').get('innerHTML'));
4450- Assert.isTrue(bar_node.one('span').hasClass('asc'));
4451- // Ensure the original button doesn't have sort classes.
4452- Assert.isFalse(Y.one('#sort-foo').one('span').hasClass('asc'));
4453- Assert.isFalse(Y.one('#sort-foo').one('span').hasClass('desc'));
4454- },
4455-
4456- test_click_different_sort_arrows_change_default_order: function() {
4457- // A newly active sort button has the order as specified by
4458- // the constructor parameter sort_keys.
4459- this.makeSrcNode('test-div');
4460- var test_sort_keys = [
4461- ['foo', 'Foo item', 'asc'],
4462- ['bar', 'Bar item', 'desc'],
4463- ['baz', 'Baz item', 'asc']
4464- ];
4465- this.orderby = new Y.lp.ordering.OrderByBar({
4466- srcNode: Y.one('#test-div'),
4467- sort_keys: test_sort_keys,
4468- active: 'foo',
4469- sort_order: 'asc'
4470- });
4471- this.orderby.render();
4472- var bar_node = Y.one('#sort-bar');
4473- bar_node.simulate('click');
4474- Assert.isTrue(bar_node.one('span').hasClass('desc'));
4475- Assert.areEqual('desc', this.orderby.get('sort_order'));
4476- var baz_node = Y.one('#sort-baz');
4477- baz_node.simulate('click');
4478- Assert.isTrue(baz_node.one('span').hasClass('asc'));
4479- Assert.areEqual('asc', this.orderby.get('sort_order'));
4480- },
4481-
4482- test_sort_clause_default: function() {
4483- // sort_clause defaults to "importance".
4484- this.orderby = new Y.lp.ordering.OrderByBar();
4485- this.orderby.render();
4486- Assert.areEqual('importance', this.orderby.get('sort_clause'));
4487- },
4488-
4489- test_sort_event_fires_with_data: function() {
4490- // A custom sort event fires from the widget to signal a
4491- // sort order change should happen in the page. The
4492- // callback receives the objects sort_clause for use in
4493- // a URL.
4494- this.makeSrcNode('test-div');
4495- var test_sort_keys = [
4496- ['foo', 'Foo item', 'asc'],
4497- ['bar', 'Bar item', 'asc']
4498- ];
4499- this.orderby = new Y.lp.ordering.OrderByBar({
4500- srcNode: Y.one('#test-div'),
4501- sort_keys: test_sort_keys,
4502- active: 'foo',
4503- sort_order: 'asc'
4504- });
4505- this.orderby.render();
4506- var foo_node = Y.one('#sort-foo');
4507- var event_fired = false;
4508- Y.on('orderbybar:sort', function(e) {
4509- event_fired = true;
4510- // Confirm that we get the sort statement we expect, too.
4511- Assert.areEqual('-foo', e);
4512- });
4513- foo_node.simulate('click');
4514- Assert.isTrue(event_fired);
4515- },
4516-
4517- test_add_settings_slot: function() {
4518- // The widget optionally can add a div for settings/config
4519- // widgets to hook onto.
4520- this.makeSrcNode('test-div');
4521- this.orderby = new Y.lp.ordering.OrderByBar({
4522- srcNode: Y.one('#test-div'),
4523- config_slot: true
4524- });
4525- this.orderby.render();
4526- var config_slot = Y.one('#test-div').one('.config-widget');
4527- Assert.isNotNull(config_slot);
4528- },
4529-
4530- test_settings_slot_node_attribute: function() {
4531- // The widget keeps a reference to the settings slot
4532- // node if config_slot is true.
4533- this.makeSrcNode('test-div');
4534- this.orderby = new Y.lp.ordering.OrderByBar({
4535- srcNode: Y.one('#test-div'),
4536- config_slot: true
4537- });
4538- this.orderby.render();
4539- var config_slot = Y.one('#test-div').one('.config-widget');
4540- Assert.areEqual(config_slot, this.orderby.get('config_node'));
4541- },
4542-
4543- test_hide_show_sort_buttons: function() {
4544- // By default, all sort buttons are shown.
4545- this.orderby = new Y.lp.ordering.OrderByBar();
4546- this.orderby.render();
4547- Y.each(this.orderby.get('li_nodes'), function(node) {
4548- Assert.isFalse(node._isHidden());
4549- });
4550-
4551- var visibility_rules = {
4552- 'bugnumber': true,
4553- 'bugtitle': true,
4554- 'status': true,
4555- 'importance': false,
4556- 'bug-heat-icons': false,
4557- 'package': false,
4558- 'milestone': false,
4559- 'assignee': false,
4560- 'bug-age': false
4561- };
4562- this.orderby.updateVisibility(visibility_rules);
4563- Y.each(this.orderby.get('li_nodes'), function(node) {
4564- sort_name = node.get('id').replace('sort-', '');
4565- if (visibility_rules[sort_name] === true) {
4566+ var tests = Y.namespace('lp.orderbybar.test');
4567+ tests.suite = new Y.Test.Suite('OrderByBar Tests');
4568+
4569+ var Assert = Y.Assert;
4570+ var ArrayAssert = Y.ArrayAssert;
4571+
4572+ tests.suite.add(new Y.Test.Case({
4573+ name: 'orderbybar_widget_tests',
4574+ orderby: null,
4575+
4576+ _should: {
4577+ error: {
4578+ test_sort_order_validator:
4579+ new Error('sort_order must be either "asc" or "desc"'),
4580+ test_active_sort_validator:
4581+ new Error('active attribute was not found in sort_keys')
4582+ }
4583+ },
4584+
4585+ tearDown: function() {
4586+ if (Y.Lang.isValue(this.orderby)) {
4587+ this.orderby.destroy();
4588+ }
4589+ },
4590+
4591+ /**
4592+ * Unpack a list of key, name pairs into individual lists.
4593+ *
4594+ * [[Foo, 'Foo Item'], ['Bar', 'Bar item']] becomes
4595+ * ['Foo', 'Bar'] and ['Foo Item', 'Bar Item'].
4596+ */
4597+ getIdsAndNames: function(keys) {
4598+ var ids = [];
4599+ var names = [];
4600+ var len = keys.length;
4601+ var i;
4602+ for (i=0; i<len; i++) {
4603+ ids.push(keys[i][0]);
4604+ names.push(keys[i][1]);
4605+ }
4606+ return [ids, names];
4607+ },
4608+
4609+ /*
4610+ * Helper function to create the srcNode on the page. Widgets
4611+ * will append to the body tag if srcNode is not supplied.
4612+ */
4613+ makeSrcNode: function(id) {
4614+ // Calling the widget's destroy method, which teardown does,
4615+ // will clean this up.
4616+ var parent_node = Y.Node.create('<div></div>');
4617+ parent_node.set('id', id);
4618+ Y.one('body').appendChild(parent_node);
4619+ },
4620+
4621+ test_default_sort_keys: function() {
4622+ // The default sort keys should exist in a newly created widget.
4623+ this.orderby = new Y.lp.ordering.OrderByBar();
4624+ var expected_sort_keys = [
4625+ ['bugnumber', 'Number'],
4626+ ['bugtitle', 'Title'],
4627+ ['status', 'Status'],
4628+ ['importance', 'Importance'],
4629+ ['bug-heat-icons', 'Heat'],
4630+ ['package', 'Package name'],
4631+ ['milestone', 'Milestone'],
4632+ ['assignee', 'Assignee'],
4633+ ['bug-age', 'Age']
4634+ ];
4635+ var expected = this.getIdsAndNames(expected_sort_keys);
4636+ var actual = this.getIdsAndNames(this.orderby.get('sort_keys'));
4637+ ArrayAssert.itemsAreSame(expected[0], actual[0]);
4638+ ArrayAssert.itemsAreSame(expected[1], actual[1]);
4639+ },
4640+
4641+ test_user_supplied_sort_keys: function() {
4642+ // Call sites can supply their own sort keys to a widget.
4643+ var user_supplied_sort_keys = [
4644+ ['foo', 'Foo item', 'asc'],
4645+ ['bar', 'Bar item', 'asc'],
4646+ ['baz', 'Baz item', 'asc']
4647+ ];
4648+ this.orderby = new Y.lp.ordering.OrderByBar({
4649+ sort_keys: user_supplied_sort_keys});
4650+ var expected = this.getIdsAndNames(user_supplied_sort_keys);
4651+ var actual = this.getIdsAndNames(this.orderby.get('sort_keys'));
4652+ ArrayAssert.itemsAreSame(expected[0], actual[0]);
4653+ ArrayAssert.itemsAreSame(expected[1], actual[1]);
4654+ },
4655+
4656+ test_rendered_items_html: function() {
4657+ // We should be able to get a node from the DOM via an ID
4658+ // created from sort keys, and the name should be used as
4659+ // a button display name in HTML.
4660+ var test_sort_keys = [
4661+ ['foo', 'Foo item', 'asc'],
4662+ ['bar', 'Bar item', 'asc']
4663+ ];
4664+ this.makeSrcNode('test-div');
4665+ this.orderby = new Y.lp.ordering.OrderByBar({
4666+ sort_keys: test_sort_keys,
4667+ srcNode: Y.one('#test-div'),
4668+ active: 'foo'
4669+ });
4670+ this.orderby.render();
4671+ var foo_node = Y.one('#sort-foo');
4672+ Assert.isNotNull(foo_node);
4673+ Assert.areEqual(foo_node.get('firstChild').get('text'), 'Foo item');
4674+ var bar_node = Y.one('#sort-bar');
4675+ Assert.isNotNull(bar_node);
4676+ Assert.areEqual(bar_node.get('firstChild').get('text'), 'Bar item');
4677+ },
4678+
4679+ test_render_active_sort_default: function() {
4680+ // Confirm that there is a default active sort class applied.
4681+ this.makeSrcNode('test-div');
4682+ this.orderby = new Y.lp.ordering.OrderByBar({
4683+ srcNode: Y.one('#test-div')
4684+ });
4685+ this.orderby.render();
4686+ var li_node = Y.one('#sort-importance');
4687+ Assert.isTrue(li_node.hasClass('active-sort'));
4688+ },
4689+
4690+ test_render_active_sort_user_supplied: function() {
4691+ // The active sort class is also set when "active"
4692+ // is supplied via config.
4693+ this.makeSrcNode('test-div');
4694+ this.orderby = new Y.lp.ordering.OrderByBar({
4695+ srcNode: Y.one('#test-div'),
4696+ active: 'status'
4697+ });
4698+ this.orderby.render();
4699+ var li_node = Y.one('#sort-status');
4700+ Assert.isTrue(li_node.hasClass('active-sort'));
4701+ },
4702+
4703+ test_active_sort_arrow_display_asc: function() {
4704+ // Buttons using "asc" order get a down arrow added to the li.
4705+ this.makeSrcNode('test-div');
4706+ this.orderby = new Y.lp.ordering.OrderByBar({
4707+ srcNode: Y.one('#test-div'),
4708+ sort_order: 'asc'
4709+ });
4710+ this.orderby.render();
4711+ var arrow_span = Y.one('.active-sort span');
4712+ var expected_text = '<span class="sprite order-ascending"></span>';
4713+ Assert.areEqual(expected_text, arrow_span.get('innerHTML'));
4714+ },
4715+
4716+ test_active_sort_arrow_display_desc: function() {
4717+ // Buttons using "desc" order get an up arrow added to the li.
4718+ this.makeSrcNode('test-div');
4719+ this.orderby = new Y.lp.ordering.OrderByBar({
4720+ srcNode: Y.one('#test-div'),
4721+ sort_order: 'desc'
4722+ });
4723+ this.orderby.render();
4724+ var arrow_span = Y.one('.active-sort span');
4725+ var expected_text = '<span class="sprite order-descending"></span>';
4726+ Assert.areEqual(expected_text, arrow_span.get('innerHTML'));
4727+ },
4728+
4729+ test_active_sort_click_class_change: function() {
4730+ // Click a node should add the active_sort class
4731+ // and remove that class from the previously active node.
4732+ this.makeSrcNode('test-div');
4733+ this.orderby = new Y.lp.ordering.OrderByBar({
4734+ srcNode: Y.one('#test-div')
4735+ });
4736+ this.orderby.render();
4737+ var importance_node = Y.one('#sort-importance');
4738+ Assert.isTrue(importance_node.hasClass('active-sort'));
4739+ var status_node = Y.one('#sort-status');
4740+ status_node.simulate('click');
4741+ Assert.isTrue(status_node.hasClass('active-sort'));
4742+ },
4743+
4744+ test_active_sort_validator: function() {
4745+ // This should fail because we do not allow
4746+ // a "active" value not found in sort_keys.
4747+ var test_sort_keys = [
4748+ ['foo', 'Foo item', 'asc'],
4749+ ['bar', 'Bar item', 'asc']
4750+ ];
4751+ this.orderby = new Y.lp.ordering.OrderByBar({
4752+ sort_keys: test_sort_keys,
4753+ active: 'foobarbazdonotexists'
4754+ });
4755+ this.orderby.render();
4756+ },
4757+
4758+ test_sort_order_validator: function() {
4759+ // This should fail when using a sort order
4760+ // other than "asc" or "desc".
4761+ this.orderby = new Y.lp.ordering.OrderByBar({
4762+ sort_order: 'foobar'
4763+ });
4764+ this.orderby.render();
4765+ },
4766+
4767+ test_click_current_sort_arrow_changes: function() {
4768+ // Clicking the currently sorted on button should change
4769+ // the arrow and widget state to show a sort change should
4770+ // happen.
4771+ this.makeSrcNode('test-div');
4772+ var test_sort_keys = [
4773+ ['foo', 'Foo item', 'asc'],
4774+ ['bar', 'Bar item', 'asc']
4775+ ];
4776+ this.orderby = new Y.lp.ordering.OrderByBar({
4777+ srcNode: Y.one('#test-div'),
4778+ sort_keys: test_sort_keys,
4779+ active: 'foo',
4780+ sort_order: 'asc'
4781+ });
4782+ this.orderby.render();
4783+ var foo_node = Y.one('#sort-foo');
4784+ var expected_starting_text =
4785+ '<span class="sprite order-ascending"></span>';
4786+ var expected_ending_text =
4787+ '<span class="sprite order-descending"></span>';
4788+ Assert.areEqual(
4789+ expected_starting_text, foo_node.one('span').get('innerHTML'));
4790+ Assert.isTrue(foo_node.one('span').hasClass('asc'));
4791+ foo_node.simulate('click');
4792+ Assert.areEqual(
4793+ expected_ending_text, foo_node.one('span').get('innerHTML'));
4794+ Assert.isTrue(foo_node.one('span').hasClass('desc'));
4795+ },
4796+
4797+ test_click_different_sort_arrows_change: function() {
4798+ // Clicking a button other than the currently sorted on button
4799+ // should change the arrow and widget state to show a sort
4800+ // change should happen.
4801+ this.makeSrcNode('test-div');
4802+ var test_sort_keys = [
4803+ ['foo', 'Foo item', 'asc'],
4804+ ['bar', 'Bar item', 'asc']
4805+ ];
4806+ this.orderby = new Y.lp.ordering.OrderByBar({
4807+ srcNode: Y.one('#test-div'),
4808+ sort_keys: test_sort_keys,
4809+ active: 'foo',
4810+ sort_order: 'asc'
4811+ });
4812+ this.orderby.render();
4813+ var bar_node = Y.one('#sort-bar');
4814+ bar_node.simulate('click');
4815+ var expected_arrow = '<span class="sprite order-ascending"></span>';
4816+ Assert.areEqual(
4817+ expected_arrow, bar_node.one('span').get('innerHTML'));
4818+ Assert.isTrue(bar_node.one('span').hasClass('asc'));
4819+ // Ensure the original button doesn't have sort classes.
4820+ Assert.isFalse(Y.one('#sort-foo').one('span').hasClass('asc'));
4821+ Assert.isFalse(Y.one('#sort-foo').one('span').hasClass('desc'));
4822+ },
4823+
4824+ test_click_different_sort_arrows_change_default_order: function() {
4825+ // A newly active sort button has the order as specified by
4826+ // the constructor parameter sort_keys.
4827+ this.makeSrcNode('test-div');
4828+ var test_sort_keys = [
4829+ ['foo', 'Foo item', 'asc'],
4830+ ['bar', 'Bar item', 'desc'],
4831+ ['baz', 'Baz item', 'asc']
4832+ ];
4833+ this.orderby = new Y.lp.ordering.OrderByBar({
4834+ srcNode: Y.one('#test-div'),
4835+ sort_keys: test_sort_keys,
4836+ active: 'foo',
4837+ sort_order: 'asc'
4838+ });
4839+ this.orderby.render();
4840+ var bar_node = Y.one('#sort-bar');
4841+ bar_node.simulate('click');
4842+ Assert.isTrue(bar_node.one('span').hasClass('desc'));
4843+ Assert.areEqual('desc', this.orderby.get('sort_order'));
4844+ var baz_node = Y.one('#sort-baz');
4845+ baz_node.simulate('click');
4846+ Assert.isTrue(baz_node.one('span').hasClass('asc'));
4847+ Assert.areEqual('asc', this.orderby.get('sort_order'));
4848+ },
4849+
4850+ test_sort_clause_default: function() {
4851+ // sort_clause defaults to "importance".
4852+ this.orderby = new Y.lp.ordering.OrderByBar();
4853+ this.orderby.render();
4854+ Assert.areEqual('importance', this.orderby.get('sort_clause'));
4855+ },
4856+
4857+ test_sort_event_fires_with_data: function() {
4858+ // A custom sort event fires from the widget to signal a
4859+ // sort order change should happen in the page. The
4860+ // callback receives the objects sort_clause for use in
4861+ // a URL.
4862+ this.makeSrcNode('test-div');
4863+ var test_sort_keys = [
4864+ ['foo', 'Foo item', 'asc'],
4865+ ['bar', 'Bar item', 'asc']
4866+ ];
4867+ this.orderby = new Y.lp.ordering.OrderByBar({
4868+ srcNode: Y.one('#test-div'),
4869+ sort_keys: test_sort_keys,
4870+ active: 'foo',
4871+ sort_order: 'asc'
4872+ });
4873+ this.orderby.render();
4874+ var foo_node = Y.one('#sort-foo');
4875+ var event_fired = false;
4876+ Y.on('orderbybar:sort', function(e) {
4877+ event_fired = true;
4878+ // Confirm that we get the sort statement we expect, too.
4879+ Assert.areEqual('-foo', e);
4880+ });
4881+ foo_node.simulate('click');
4882+ Assert.isTrue(event_fired);
4883+ },
4884+
4885+ test_add_settings_slot: function() {
4886+ // The widget optionally can add a div for settings/config
4887+ // widgets to hook onto.
4888+ this.makeSrcNode('test-div');
4889+ this.orderby = new Y.lp.ordering.OrderByBar({
4890+ srcNode: Y.one('#test-div'),
4891+ config_slot: true
4892+ });
4893+ this.orderby.render();
4894+ var config_slot = Y.one('#test-div').one('.config-widget');
4895+ Assert.isNotNull(config_slot);
4896+ },
4897+
4898+ test_settings_slot_node_attribute: function() {
4899+ // The widget keeps a reference to the settings slot
4900+ // node if config_slot is true.
4901+ this.makeSrcNode('test-div');
4902+ this.orderby = new Y.lp.ordering.OrderByBar({
4903+ srcNode: Y.one('#test-div'),
4904+ config_slot: true
4905+ });
4906+ this.orderby.render();
4907+ var config_slot = Y.one('#test-div').one('.config-widget');
4908+ Assert.areEqual(config_slot, this.orderby.get('config_node'));
4909+ },
4910+
4911+ test_hide_show_sort_buttons: function() {
4912+ // By default, all sort buttons are shown.
4913+ this.orderby = new Y.lp.ordering.OrderByBar();
4914+ this.orderby.render();
4915+ Y.each(this.orderby.get('li_nodes'), function(node) {
4916 Assert.isFalse(node._isHidden());
4917- } else {
4918- Assert.isTrue(node._isHidden());
4919- }
4920- });
4921- }
4922-}));
4923+ });
4924
4925-basic_test.suite = suite;
4926+ var visibility_rules = {
4927+ 'bugnumber': true,
4928+ 'bugtitle': true,
4929+ 'status': true,
4930+ 'importance': false,
4931+ 'bug-heat-icons': false,
4932+ 'package': false,
4933+ 'milestone': false,
4934+ 'assignee': false,
4935+ 'bug-age': false
4936+ };
4937+ this.orderby.updateVisibility(visibility_rules);
4938+ Y.each(this.orderby.get('li_nodes'), function(node) {
4939+ sort_name = node.get('id').replace('sort-', '');
4940+ if (visibility_rules[sort_name] === true) {
4941+ Assert.isFalse(node._isHidden());
4942+ } else {
4943+ Assert.isTrue(node._isHidden());
4944+ }
4945+ });
4946+ }
4947+ }));
4948
4949 }, '0.1', {'requires': ['test', 'node-event-simulate', 'lp.ordering']});
4950
4951=== modified file 'lib/lp/app/javascript/overlay/tests/test_overlay.html'
4952--- lib/lp/app/javascript/overlay/tests/test_overlay.html 2011-08-19 14:59:06 +0000
4953+++ lib/lp/app/javascript/overlay/tests/test_overlay.html 2012-02-15 12:51:42 +0000
4954@@ -1,24 +1,47 @@
4955-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
4956- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
4957+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
4958+ "http://www.w3.org/TR/html4/strict.dtd">
4959+<!--
4960+Copyright 2012 Canonical Ltd. This software is licensed under the
4961+GNU Affero General Public License version 3 (see the file LICENSE).
4962+-->
4963+
4964 <html>
4965 <head>
4966- <title>Pretty Overlay</title>
4967-
4968- <!-- YUI and test setup -->
4969- <script type="text/javascript"
4970- src="../../../../../canonical/launchpad/icing/yui/yui/yui.js">
4971- </script>
4972- <link rel="stylesheet" href="../../../../app/javascript/testing/test.css" />
4973- <script type="text/javascript"
4974- src="../../../../app/javascript/testing/testrunner.js"></script>
4975-
4976- <!-- The module under test -->
4977- <script type="text/javascript" src="../overlay.js"></script>
4978-
4979- <!-- The test suite -->
4980- <script type="text/javascript" src="test_overlay.js"></script>
4981-
4982-</head>
4983-<body class="yui3-skin-sam">
4984-</body>
4985+ <title>Test overlay</title>
4986+
4987+ <!-- YUI and test setup -->
4988+ <script type="text/javascript"
4989+ src="../../../../../../build/js/yui/yui/yui.js">
4990+ </script>
4991+ <link rel="stylesheet"
4992+ href="../../../../../../build/js/yui/console/assets/console-core.css" />
4993+ <link rel="stylesheet"
4994+ href="../../../../../../build/js/yui/console/assets/skins/sam/console.css" />
4995+ <link rel="stylesheet"
4996+ href="../../../../../../build/js/yui/test/assets/skins/sam/test.css" />
4997+
4998+ <script type="text/javascript"
4999+ src="../../../../../../build/js/lp/app/testing/testrunner.js"></script>
5000+
The diff has been truncated for viewing.