1
/* Copyright (c) 2010, Canonical Ltd. All rights reserved. */
3
YUI().use('lazr.wizard', 'lazr.testing.runner',
4
'lazr.testing.mockio', 'node', 'event', 'event-simulate',
5
'dump', 'console', function(Y) {
7
var Assert = Y.Assert; // For easy access to isTrue(), etc.
11
* A wrapper for the Y.Event.simulate() function. The wrapper accepts
12
* CSS selectors and Node instances instead of raw nodes.
14
function simulate(widget, selector, evtype, options) {
15
var rawnode = Y.Node.getDOMNode(widget.one(selector));
16
Y.Event.simulate(rawnode, evtype, options);
19
/* Helper function to cleanup and destroy a form wizard instance */
20
function cleanup_wizard(wizard) {
21
if (wizard.get('rendered')) {
22
var bb = wizard.get('boundingBox');
23
if (Y.Node.getDOMNode(bb)){
24
bb.get('parentNode').removeChild(bb);
28
// Kill the widget itself.
32
/* Helper function that creates a new form wizard instance. */
33
function make_wizard(cfg) {
34
var wizard = new Y.lazr.wizard.Wizard(cfg);
39
/* Helper array of steps for creating a wizard */
41
new Y.lazr.wizard.Step({
42
form_content: "Hello."
46
var suite = new Y.Test.Suite("Wizard Tests");
48
suite.add(new Y.Test.Case({
50
name: 'wizard_basics',
54
new Y.lazr.wizard.Step({
58
'<input type="text" name="field1" id="field1" />',
59
'Here is another input: ',
60
'<input type="text" name="field2" id="field2" />'
62
funcLoad: function() {},
63
funcCleanUp: function() {}
66
this.wizard = make_wizard({
67
headerContent: 'Form for testing',
72
// Ensure window size is constant for tests
73
this.width = window.top.outerWidth;
74
this.height = window.top.outerHeight;
75
window.top.resizeTo(800, 600);
78
tearDown: function() {
79
window.top.resizeTo(this.width, this.height);
80
cleanup_wizard(this.wizard);
84
// The following tests will only be deemed to have passed if
85
// they raise an error.
87
test_wizard_needs_steps: true,
88
test_cant_go_back_when_at_first_step: true,
89
test_cant_go_forward_when_at_last_step: true
93
test_wizard_can_be_instantiated: function() {
94
var wizard = new Y.lazr.wizard.Wizard({
100
"Wizard could not be instantiated.");
101
cleanup_wizard(wizard);
104
test_wizard_needs_steps: function() {
105
// The wizard will raise an error if no steps are provided. Note
106
// that this test will raise the error; this is expected
107
// behaviour and is declared in the _should object, above.
108
var wizard = new Y.lazr.wizard.Wizard();
110
Y.lazr.wizard.Wizard,
112
"Wizard could not be instantiated.");
113
cleanup_wizard(wizard);
116
test_body_content_is_single_node: function() {
119
new Y.NodeList(this.wizard.getStdModNode("body")).size(),
120
"The body content should be a single node, not a node list.");
123
test_form_content_in_body_content: function() {
124
// The form_content should be included in the body of the
125
// wizard during initialization.
126
var body_content = this.wizard.getStdModNode("body");
128
// Ensure the body_content contains our form node.
130
body_content.contains(this.wizard.form_node),
131
"The form node is part of the body content.");
133
// And then make sure that the user-supplied form_content is
134
// included in the form node:
136
body_content.get("innerHTML").search(
137
this.wizard.get("form_content")));
140
test_first_input_has_focus: function() {
141
// The first input element in the form content should have
143
var first_input = this.wizard.form_node.one('#field1');
145
// Hide the wizard and ensure that the first input does not
153
var onFocus = function(e) {
157
first_input.on('focus', onFocus);
160
Assert.isTrue(focused,
161
"The form wizard's first input field receives focus " +
162
"when the wizard is shown.");
165
test_form_submit_in_body_content: function() {
166
// The body content should include the submit button.
167
var body_content = this.wizard.getStdModNode("body");
169
body_content.contains(
170
this.wizard.get("form_submit_button")),
171
"The body content includes the form_submit_button.");
174
test_users_submit_button_in_body_content: function() {
175
// If a user supplies a custom submit button, it should be included
176
// in the form instead of the default one.
177
var submit_button = Y.Node.create(
178
'<input type="submit" value="Hit me!" />');
179
var wizard = new Y.lazr.wizard.Wizard({
181
new Y.lazr.wizard.Step({
182
form_content: 'Here is an input: ' +
183
'<input type="text" name="field1" id="field1" />'
186
form_submit_button: submit_button
190
// Ensure the button has been used in the form:
192
wizard.form_node.contains(submit_button),
193
"The form should include the users submit button.");
195
cleanup_wizard(wizard);
198
test_form_cancel_in_body_content: function() {
199
// The body content should include the cancel button.
200
var body_content = this.wizard.getStdModNode("body");
202
body_content.contains(
203
this.wizard.get("form_cancel_button")),
204
"The body content includes the form_cancel_button.");
207
test_users_cancel_button_in_body_content: function() {
208
// If a user supplies a custom cancel button, it should be included
209
// in the form instead of the default one.
210
var cancel_button = Y.Node.create(
211
'<button type="" value="cancel" />');
212
var wizard = new Y.lazr.wizard.Wizard({
214
new Y.lazr.wizard.Step({
215
form_content: 'Here is an input: ' +
216
'<input type="text" name="field1" id="field1" />'
219
form_cancel_button: cancel_button
223
// Ensure the button has been used in the form:
225
wizard.form_node.contains(cancel_button),
226
"The form should include the users cancel button.");
228
cleanup_wizard(wizard);
231
test_hide_when_cancel_clicked: function() {
232
// The form wizard should hide when the cancel button is clicked.
234
var bounding_box = this.wizard.get('boundingBox');
236
bounding_box.hasClass('yui3-lazr-wizard-hidden'),
237
"The form is not hidden initially.");
240
this.wizard.form_node,
241
"button[type=button]",
245
bounding_box.hasClass('yui3-lazr-wizard-hidden'),
246
"The form is hidden after cancel is clicked.");
249
test_error_displayed_on_showError: function() {
250
// The error message should be in the body content.
252
this.wizard.showError("My special error");
254
var body_content = this.wizard.getStdModNode("body");
256
body_content.get("innerHTML").search("My special error"),
258
"The error text was included in the body content.");
261
test_tags_stripped_from_errors: function() {
262
// Any tags in error messages will be stripped out.
263
// That is, as long as they begin and end with ASCII '<' and '>'
264
// chars. Not sure what to do about unicode, for example.
265
this.wizard.showError("<h2>My special error</h2>");
267
var body_content = this.wizard.getStdModNode("body");
270
body_content.get("innerHTML").search("<h2>"),
271
"The tags were stripped from the error message.");
274
test_error_cleared_on_clearError: function() {
275
// The error message should be cleared from the body content.
276
this.wizard.showError("My special error");
277
this.wizard.clearError();
278
var body_content = this.wizard.getStdModNode("body");
280
body_content.get("innerHTML").search("My special error"),
282
"The error text is cleared from the body content.");
285
test_wizard_centered_when_shown: function() {
286
// If the 'centered' attribute is set, the wizard should be
287
// centered in the viewport when shown.
288
Assert.areEqual('[0, 0]', Y.dump(this.wizard.get('xy')),
289
"Position is initially 0,0.");
291
Assert.areEqual('[0, 0]', Y.dump(this.wizard.get('xy')),
292
"Position is not updated if widget not centered.");
295
this.wizard.set('centered', true);
297
var centered_pos_before_resize = this.wizard.get('xy');
298
Assert.areNotEqual('[0, 0]', Y.dump(centered_pos_before_resize),
299
"Position is updated when centered attr set.");
302
var centered = false;
303
function watch_centering() {
306
Y.Do.after(watch_centering, this.wizard, 'centered');
308
// The position is updated after resizing the window and re-showing:
309
window.top.resizeTo(850, 550);
312
Assert.isTrue(centered,
313
"The wizard centers itself when it is shown with the centered " +
317
test_form_content_as_node: function() {
318
// The form content can also be passed as a node, rather than
320
var form_content_div = Y.Node.create("<div />");
321
var input_node = Y.Node.create(
322
'<input type="text" name="field1" value="val1" />');
323
form_content_div.appendChild(input_node);
325
var wizard = make_wizard({
326
headerContent: 'Form for testing',
328
new Y.lazr.wizard.Step({
329
form_content: form_content_div
335
wizard.form_node.contains(input_node),
336
"Failed to pass the form content as a Y.Node instance.");
337
cleanup_wizard(wizard);
340
test_first_step_load_called_on_wizard_load: function() {
341
// The load() function of the first step will be called when the
342
// wizard is instantiated.
343
var load_called = false;
345
new Y.lazr.wizard.Step({
346
form_content: "Nothing to see here.",
347
funcLoad: function() {
352
var wizard = make_wizard({
359
"The funcLoad callback of the first step was not called.");
362
test_step_load_called_on_wizard_next: function() {
363
// When wizard.next() is called, the next Step's funcLoad
364
// callback will be called.
365
var load_called = false;
367
new Y.lazr.wizard.Step({
368
form_content: "Nothing to see here."
370
new Y.lazr.wizard.Step({
371
form_content: "Still nothing to see here.",
372
funcLoad: function() {
377
var wizard = make_wizard({
385
"The funcLoad callback of the next step was not called.");
388
test_step_load_called_on_wizard_prev: function() {
389
// When wizard.previous() is called, the previous Step's funcLoad
390
// callback will be called.
391
var load_called = false;
393
new Y.lazr.wizard.Step({
394
form_content: "Nothing to see here.",
395
funcLoad: function() {
399
new Y.lazr.wizard.Step({
400
form_content: "Still nothing to see here."
403
var wizard = make_wizard({
413
"The funcLoad callback of the first step was not called.");
416
test_cant_go_back_when_at_first_step: function() {
417
// When a wizard is on its first step, calling wizard.previous()
418
// will raise an error.
420
new Y.lazr.wizard.Step({
421
form_content: "Nothing to see here."
424
var wizard = make_wizard({
431
test_cant_go_forward_when_at_last_step: function() {
432
// When a wizard is on its last step, calling wizard.next()
433
// will raise an error.
435
new Y.lazr.wizard.Step({
436
form_content: "Nothing to see here."
439
var wizard = make_wizard({
446
test_hasNextStep_returns_true_with_next_step: function() {
447
// Wizard.hasNextStep() will return True if there are more
448
// steps after the current one.
450
new Y.lazr.wizard.Step({
451
form_content: "Nothing to see here."
453
new Y.lazr.wizard.Step({
454
form_content: "Still nothing to see here."
457
var wizard = make_wizard({
463
wizard.hasNextStep(),
464
"Wizard.hasNextStep() should return true.");
467
test_hasNextStep_returns_false_with_no_next_step: function() {
468
// Wizard.hasNextStep() will return false if there are no more
469
// steps after the current one.
471
new Y.lazr.wizard.Step({
472
form_content: "Nothing to see here."
475
var wizard = make_wizard({
481
wizard.hasNextStep(),
482
"Wizard.hasNextStep() should return false.");
485
test_hasPreviousStep_returns_true_with_prev_step: function() {
486
// Wizard.hasPreviousStep() will return True if there are steps
487
// before the current one.
489
new Y.lazr.wizard.Step({
490
form_content: "Nothing to see here."
492
new Y.lazr.wizard.Step({
493
form_content: "Still nothing to see here."
496
var wizard = make_wizard({
503
wizard.hasPreviousStep(),
504
"Wizard.hasPreviousStep() should return true.");
507
test_hasPreviousStep_returns_false_with_no_prev_step: function() {
508
// Wizard.hasPreviousStep() will return false if there are no
509
// steps before the current one.
511
new Y.lazr.wizard.Step({
512
form_content: "Nothing to see here."
515
var wizard = make_wizard({
521
wizard.hasPreviousStep(),
522
"Wizard.hasPreviousStep() should return false.");
525
test_stepChange_fired_on_step_change: function() {
526
// Changing the step will fire a wizard:stepChange event.
527
var stepChange_fired = false;
529
new Y.lazr.wizard.Step({
530
form_content: "Nothing to see here."
532
new Y.lazr.wizard.Step({
533
form_content: "Still nothing to see here."
536
var wizard = make_wizard({
539
wizard.on("wizard:stepChange", function() {
540
stepChange_fired = true;
546
stepChange_fired, "wizard:stepChange did not fire.");
549
test_addStep_adds_step: function() {
550
// Wizard.addStep() adds a step to the Wizard.
552
new Y.lazr.wizard.Step({
553
form_content: "Nothing to see here."
556
var wizard = make_wizard({
560
// There's only one step at this point, so hasNextStep() returns
563
wizard.hasNextStep(),
564
"Wizard.hasNextStep() should return false.");
565
wizard.addStep(new Y.lazr.wizard.Step({
566
form_content: "Still nothing. Move along."
569
wizard.hasNextStep(),
570
"Wizard.hasNextStep() should return true.");
573
test_destructor_deletes_steps: function() {
574
// Wizard.destructor() deletes all the of the Wizard's Steps.
576
new Y.lazr.wizard.Step({
577
form_content: "Nothing to see here."
580
var wizard = make_wizard({
586
var steps = wizard.get("steps");
588
0, steps.length, "There should be no steps left.");
594
suite.add(new Y.Test.Case({
598
test_submit_callback_called_on_submit: function() {
599
// Set an expectation that the form_submit_callback will be
600
// called with the correct data:
601
var callback_called = false;
602
var submit_callback = function(ignore){
603
callback_called = true;
605
var wizard = make_wizard({
607
new Y.lazr.wizard.Step({
609
'<input type="text" name="field1" value="val1" />',
612
form_submit_callback: submit_callback
616
"input[type=submit]",
621
"The form_submit_callback should be called.");
622
cleanup_wizard(wizard);
625
test_submit_with_callback_prevents_propagation: function() {
626
// The onsubmit event is not propagated when user provides
629
var wizard = make_wizard({
631
new Y.lazr.wizard.Step({
633
'<input type="text" name="field1" value="val1" />',
636
form_submit_callback: function() {}
639
var event_was_propagated = false;
641
var onSubmit = function(e) {
642
event_was_propagated = true;
645
Y.on('submit', onSubmit, wizard.form_node);
647
simulate(wizard.form_node, "input[type=submit]", 'click');
650
event_was_propagated,
651
"The onsubmit event should not be propagated.");
652
cleanup_wizard(wizard);
655
test_submit_without_callback: function() {
656
// The form should submit as a normal form if no callback
658
var wizard = make_wizard({
660
new Y.lazr.wizard.Step({
662
'<input type="text" name="field1" value="val1" />',
667
var event_was_propagated = false;
669
var onSubmit = function(e) {
670
event_was_propagated = true;
674
Y.on('submit', onSubmit, wizard.form_node);
678
"input[type=submit]",
680
Assert.isTrue(event_was_propagated,
681
"The normal form submission event is propagated as " +
682
"normal when no callback is provided.");
683
cleanup_wizard(wizard);
686
test_getFormData_returns_correct_data_for_simple_inputs: function() {
687
// The getFormData method should return the values of simple
690
var wizard = make_wizard({
691
headerContent: 'Form for testing',
693
new Y.lazr.wizard.Step({
695
'Here is an input: ',
696
'<input type="text" name="field1" value="val1" />',
697
'<input type="text" name="field2" value="val2" />',
698
'<input type="text" name="field3" value="val3" />'
704
'{field1 => [val1], field2 => [val2], field3 => [val3]}',
705
Y.dump(wizard.getFormData()),
706
"The getFormData method returns simple input data correctly.");
707
cleanup_wizard(wizard);
710
test_getFormData_returns_inputs_nested_several_levels: function() {
711
// The getFormData method should return the values of inputs
712
// even when they are several levels deep in the form node
713
var wizard = make_wizard({
714
headerContent: 'Form for testing',
716
new Y.lazr.wizard.Step({
718
'Here is an input: ',
720
' <input type="text" name="field1" value="val1" />',
722
' <input type="text" name="field2" value="val2" ',
725
' <input type="text" name="field3" ',
736
'{field1 => [val1], field2 => [val2], field3 => [val3]}',
737
Y.dump(wizard.getFormData()),
738
"The getFormData method returns simple input data correctly.");
739
cleanup_wizard(wizard);
745
Y.lazr.testing.Runner.add(suite);
746
Y.lazr.testing.Runner.run();