7d61bcff0f0cd6e7c3548922c10c6ab5658a5bd3
[platform/framework/web/web-ui-fw.git] / libs / js / jquery-mobile-1.0.1pre / tests / unit / navigation / navigation_core.js
1 /*
2  * mobile navigation unit tests
3  */
4 (function($){
5         // TODO move siteDirectory over to the nav path helper
6         var changePageFn = $.mobile.changePage,
7                 originalTitle = document.title,
8                 originalLinkBinding = $.mobile.linkBindingEnabled,
9                 siteDirectory = location.pathname.replace( /[^/]+$/, "" ),
10                 home = $.mobile.path.parseUrl(location.pathname).directory,
11                 navigateTestRoot = function(){
12                         $.testHelper.openPage( "#" + location.pathname + location.search );
13                 };
14
15         module('jquery.mobile.navigation.js', {
16                 setup: function(){
17                         $.mobile.changePage = changePageFn;
18                         document.title = originalTitle;
19
20                         var pageReset = function( hash ) {
21                                 hash = hash || "";
22
23                                 stop();
24
25                                 $(document).one( "pagechange", function() {
26                                         start();
27                                 });
28
29                                 location.hash = "#" + hash;
30                         };
31
32                         // force the page reset for hash based tests
33                         if ( location.hash && !$.support.pushState ) {
34                                 pageReset();
35                         }
36
37                         // force the page reset for all pushstate tests
38                         if ( $.support.pushState ) {
39                                 pageReset( home );
40                         }
41
42                         $.mobile.urlHistory.stack = [];
43                         $.mobile.urlHistory.activeIndex = 0;
44                         $.Event.prototype.which = undefined;
45                         $.mobile.linkBindingEnabled = originalLinkBinding;
46                 }
47         });
48
49         asyncTest( "window.history.back() from external to internal page", function(){
50
51                 $.testHelper.pageSequence([
52
53                         // open our test page
54                         function(){
55                                 $.testHelper.openPage("#active-state-page1");
56                         },
57
58                         function(){
59                                 ok( $.mobile.activePage[0] === $( "#active-state-page1" )[ 0 ], "successful navigation to internal page." );
60
61                                 //location.hash = siteDirectory + "external.html";
62                                 $.mobile.changePage("external.html");
63                         },
64
65                         function(){
66                                 ok( $.mobile.activePage[0] !== $( "#active-state-page1" )[ 0 ], "successful navigation to external page." );
67
68                                 window.history.back();
69                         },
70
71                         function(){
72                                 ok( $.mobile.activePage[0] === $( "#active-state-page1" )[ 0 ], "successful navigation back to internal page." );
73
74                                 start();
75                         }
76                 ]);
77         });
78
79         asyncTest( "external page is removed from the DOM after pagehide", function(){
80                 $.testHelper.pageSequence([
81                         navigateTestRoot,
82
83                         function(){
84                                 $.mobile.changePage( "external.html" );
85                         },
86
87                         // page is pulled and displayed in the dom
88                         function(){
89                                 same( $( "#external-test" ).length, 1 );
90                                 window.history.back();
91                         },
92
93                         // external-test is *NOT* cached in the dom after transitioning away
94                         function(){
95                                 same( $( "#external-test" ).length, 0 );
96                                 start();
97                         }
98                 ]);
99         });
100
101         asyncTest( "preventDefault on pageremove event can prevent external page from being removed from the DOM", function(){
102                 var preventRemoval = true,
103                         removeCallback = function( e ) {
104                                 if ( preventRemoval ) {
105                                         e.preventDefault();
106                                 }
107                         };
108
109                 $( document ).bind( "pageremove", removeCallback );
110
111                 $.testHelper.pageSequence([
112                         navigateTestRoot,
113
114                         function(){
115                                 $.mobile.changePage( "external.html" );
116                         },
117
118                         // page is pulled and displayed in the dom
119                         function(){
120                                 same( $( "#external-test" ).length, 1 );
121                                 window.history.back();
122                         },
123
124                         // external-test *IS* cached in the dom after transitioning away
125                         function(){
126                                 same( $( "#external-test" ).length, 1 );
127
128                                 // Switch back to the page again!
129                                 $.mobile.changePage( "external.html" );
130                         },
131
132                         // page is still present and displayed in the dom
133                         function(){
134                                 same( $( "#external-test" ).length, 1 );
135
136                                 // Now turn off our removal prevention.
137                                 preventRemoval = false;
138
139                                 window.history.back();
140                         },
141
142                         // external-test is *NOT* cached in the dom after transitioning away
143                         function(){
144                                 same( $( "#external-test" ).length, 0 );
145                                 $( document ).unbind( "pageremove", removeCallback );
146                                 start();
147                         }
148                 ]);
149         });
150
151         asyncTest( "external page is cached in the DOM after pagehide", function(){
152                 $.testHelper.pageSequence([
153                         navigateTestRoot,
154
155                         function(){
156                                 $.mobile.changePage( "cached-external.html" );
157                         },
158
159                         // page is pulled and displayed in the dom
160                         function(){
161                                 same( $( "#external-test-cached" ).length, 1 );
162                                 window.history.back();
163                         },
164
165                         // external test page is cached in the dom after transitioning away
166                         function(){
167                                 same( $( "#external-test-cached" ).length, 1 );
168                                 start();
169                         }
170                 ]);
171         });
172
173         asyncTest( "external page is cached in the DOM after pagehide when option is set globally", function(){
174                 $.testHelper.pageSequence([
175                         navigateTestRoot,
176
177                         function(){
178                                 $.mobile.page.prototype.options.domCache = true;
179                                 $.mobile.changePage( "external.html" );
180                         },
181
182                         // page is pulled and displayed in the dom
183                         function(){
184                                 same( $( "#external-test" ).length, 1 );
185                                 window.history.back();
186                         },
187
188                         // external test page is cached in the dom after transitioning away
189                         function(){
190                                 same( $( "#external-test" ).length, 1 );
191                                 $.mobile.page.prototype.options.domCache = false;
192                                 $( "#external-test" ).remove();
193                                 start();
194                         }]);
195         });
196
197         asyncTest( "page last scroll distance is remembered while navigating to and from pages", function(){
198                 $.testHelper.pageSequence([
199                         function(){
200                                 $( "body" ).height( $( window ).height() + 500 );
201                                 $.mobile.changePage( "external.html" );
202                         },
203
204                         function(){
205                                 // wait for the initial scroll to 0
206                                 setTimeout( function() {
207                                         window.scrollTo( 0, 300 );
208                                         same( $(window).scrollTop(), 300, "scrollTop is 300 after setting it" );
209                                 }, 300);
210
211                                 // wait for the scrollstop to fire and for the scroll to be
212                                 // recorded 100 ms afterward (see changes made to handle hash
213                                 // scrolling in some browsers)
214                                 setTimeout( navigateTestRoot, 500 );
215                         },
216
217                         function(){
218                                 history.back();
219                         },
220
221                         function(){
222                                 // Give the silentScroll function some time to kick in.
223                                 setTimeout(function() {
224                                         same( $(window).scrollTop(), 300, "scrollTop is 300 after returning to the page" );
225                                         $( "body" ).height( "" );
226                                         start();
227                                 }, 300 );
228                         }
229                 ]);
230         });
231
232         asyncTest( "forms with data attribute ajax set to false will not call changePage", function(){
233                 var called = false;
234                 var newChangePage = function(){
235                         called = true;
236                 };
237
238                 $.testHelper.sequence([
239                         // avoid initial page load triggering changePage early
240                         function(){
241                                 $.mobile.changePage = newChangePage;
242
243                                 $('#non-ajax-form').one('submit', function(event){
244                                         ok(true, 'submit callbacks are fired');
245                                         event.preventDefault();
246                                 }).submit();
247                         },
248
249                         function(){
250                                 ok(!called, "change page should not be called");
251                                 start();
252                         }], 1000);
253         });
254
255         asyncTest( "forms with data attribute ajax not set or set to anything but false will call changePage", function(){
256                 var called = 0,
257                                 newChangePage = function(){
258                                         called++;
259                                 };
260
261                 $.testHelper.sequence([
262                         // avoid initial page load triggering changePage early
263                         function(){
264                                 $.mobile.changePage = newChangePage;
265                                 $('#ajax-form, #rand-ajax-form').submit();
266                         },
267
268                         function(){
269                                 ok(called >= 2, "change page should be called at least twice");
270                                 start();
271                         }], 300);
272         });
273
274
275         asyncTest( "anchors with no href attribute will do nothing when clicked", function(){
276                 var fired = false;
277
278                 $(window).bind("hashchange.temp", function(){
279                         fired = true;
280                 });
281
282                 $( "<a>test</a>" ).appendTo( $.mobile.firstPage ).click();
283
284                 setTimeout(function(){
285                         same(fired, false, "hash shouldn't change after click");
286                         $(window).unbind("hashchange.temp");
287                         start();
288                 }, 500);
289         });
290
291         test( "urlHistory is working properly", function(){
292
293                 //urlHistory
294                 same( $.type( $.mobile.urlHistory.stack ), "array", "urlHistory.stack is an array" );
295
296                 //preload the stack
297                 $.mobile.urlHistory.stack[0] = { url: "foo", transition: "bar" };
298                 $.mobile.urlHistory.stack[1] = { url: "baz", transition: "shizam" };
299                 $.mobile.urlHistory.stack[2] = { url: "shizoo", transition: "shizaah" };
300
301                 //active index
302                 same( $.mobile.urlHistory.activeIndex , 0, "urlHistory.activeIndex is 0" );
303
304                 //getActive
305                 same( $.type( $.mobile.urlHistory.getActive() ) , "object", "active item is an object" );
306                 same( $.mobile.urlHistory.getActive().url , "foo", "active item has url foo" );
307                 same( $.mobile.urlHistory.getActive().transition , "bar", "active item has transition bar" );
308
309                 //get prev / next
310                 same( $.mobile.urlHistory.getPrev(), undefined, "urlHistory.getPrev() is undefined when active index is 0" );
311                 $.mobile.urlHistory.activeIndex = 1;
312                 same( $.mobile.urlHistory.getPrev().url, "foo", "urlHistory.getPrev() has url foo when active index is 1" );
313                 $.mobile.urlHistory.activeIndex = 0;
314                 same( $.mobile.urlHistory.getNext().url, "baz", "urlHistory.getNext() has url baz when active index is 0" );
315
316                 //add new
317                 $.mobile.urlHistory.activeIndex = 2;
318                 $.mobile.urlHistory.addNew("test");
319                 same( $.mobile.urlHistory.stack.length, 4, "urlHistory.addNew() adds an item after the active index" );
320                 same( $.mobile.urlHistory.activeIndex, 3, "urlHistory.addNew() moves the activeIndex to the newly added item" );
321
322                 //clearForward
323                 $.mobile.urlHistory.activeIndex = 0;
324                 $.mobile.urlHistory.clearForward();
325                 same( $.mobile.urlHistory.stack.length, 1, "urlHistory.clearForward() clears the url stack after the active index" );
326         });
327
328         //url listening
329         function testListening( prop ){
330                 var stillListening = false;
331                 $(document).bind("pagebeforehide", function(){
332                         stillListening = true;
333                 });
334                 location.hash = "foozball";
335                 setTimeout(function(){
336                         ok( prop == stillListening, prop + " = false disables default hashchange event handler");
337                         location.hash = "";
338                         prop = true;
339                         start();
340                 }, 1000);
341         }
342
343         asyncTest( "ability to disable our hash change event listening internally", function(){
344                 testListening( ! $.mobile.urlHistory.ignoreNextHashChange );
345         });
346
347         asyncTest( "ability to disable our hash change event listening globally", function(){
348                 testListening( $.mobile.hashListeningEnabled );
349         });
350
351         var testDataUrlHash = function( linkSelector, matches ) {
352                 $.testHelper.pageSequence([
353                         function(){ window.location.hash = ""; },
354                         function(){ $(linkSelector).click(); },
355                         function(){
356                                 $.testHelper.assertUrlLocation(
357                                         $.extend(matches, {
358                                                 report: "url or hash should match"
359                                         })
360                                 );
361
362                                 start();
363                         }
364                 ]);
365
366                 stop();
367         };
368
369         test( "when loading a page where data-url is not defined on a sub element hash defaults to the url", function(){
370                 testDataUrlHash( "#non-data-url a", {hashOrPush: siteDirectory + "data-url-tests/non-data-url.html"} );
371         });
372
373         test( "data url works for nested paths", function(){
374                 var url = "foo/bar.html";
375                 testDataUrlHash( "#nested-data-url a", {hash: url, push: home + url} );
376         });
377
378         test( "data url works for single quoted paths and roles", function(){
379                 var url = "foo/bar/single.html";
380                 testDataUrlHash( "#single-quotes-data-url a", {hash: url, push: home + url} );
381         });
382
383         test( "data url works when role and url are reversed on the page element", function(){
384                 var url = "foo/bar/reverse.html";
385                 testDataUrlHash( "#reverse-attr-data-url a", {hash: url, push: home + url} );
386         });
387
388         asyncTest( "last entry choosen amongst multiple identical url history stack entries on hash change", function(){
389                 // make sure the stack is clear after initial page load an any other delayed page loads
390                 // TODO better browser state management
391                 $.mobile.urlHistory.stack = [];
392                 $.mobile.urlHistory.activeIndex = 0;
393
394                 $.testHelper.pageSequence([
395                         function(){ $.testHelper.openPage("#dup-history-first"); },
396                         function(){ $("#dup-history-first a").click(); },
397                         function(){ $("#dup-history-second a:first").click(); },
398                         function(){ $("#dup-history-first a").click(); },
399                         function(){ $("#dup-history-second a:last").click(); },
400                         function(){ $("#dup-history-dialog a:contains('Close')").click(); },
401                         function(){
402
403                                 // fourth page (third index) in the stack to account for first page being hash manipulation,
404                                 // the third page is dup-history-second which has two entries in history
405                                 // the test is to make sure the index isn't 1 in this case, or the first entry for dup-history-second
406                                 same($.mobile.urlHistory.activeIndex, 3, "should be the fourth page in the stack");
407                                 start();
408                         }]);
409         });
410
411         asyncTest( "going back from a page entered from a dialog skips the dialog and goes to the previous page", function(){
412                 $.testHelper.pageSequence([
413                         // setup
414                         function(){ $.testHelper.openPage("#skip-dialog-first"); },
415
416                         // transition to the dialog
417                         function(){ $("#skip-dialog-first a").click(); },
418
419                         // transition to the second page
420                         function(){ $("#skip-dialog a").click(); },
421
422                         // transition past the dialog via data-rel=back link on the second page
423                         function(){ $("#skip-dialog-second a").click(); },
424
425                         // make sure we're at the first page and not the dialog
426                         function(){
427                                 $.testHelper.assertUrlLocation({
428                                         hash: "skip-dialog-first",
429                                         push: home + "#skip-dialog-first",
430                                         report: "should be the first page in the sequence"
431                                 });
432
433                                 start();
434                         }]);
435         });
436
437         asyncTest( "going forward from a page entered from a dialog skips the dialog and goes to the next page", function(){
438                 $.testHelper.pageSequence([
439                         // setup
440                         function(){ $.testHelper.openPage("#skip-dialog-first"); },
441
442                         // transition to the dialog
443                         function(){ $("#skip-dialog-first a").click(); },
444
445                         // transition to the second page
446                         function(){ $("#skip-dialog a").click(); },
447
448                         // transition to back past the dialog
449                         function(){ window.history.back(); },
450
451                         // transition to the second page past the dialog through history
452                         function(){ window.history.forward(); },
453
454                         // make sure we're on the second page and not the dialog
455                         function(){
456                                 $.testHelper.assertUrlLocation({
457                                         hash: "skip-dialog-second",
458                                         push: home + "#skip-dialog-second",
459                                         report: "should be the second page after the dialog"
460                                 });
461
462                                 start();
463                         }]);
464         });
465
466         asyncTest( "going back from a dialog triggered from a dialog should result in the first dialog ", function(){
467                 $.testHelper.pageSequence([
468                         // setup
469                         function(){ $.testHelper.openPage("#nested-dialog-page"); },
470
471                         // transition to the dialog
472                         function(){ $("#nested-dialog-page a").click(); },
473
474                         // transition to the second dialog
475                         function(){ $("#nested-dialog-first a").click(); },
476
477                         // transition to back to the first dialog
478                         function(){ window.history.back(); },
479
480                         // make sure we're on first dialog
481                         function(){
482                                 same($(".ui-page-active")[0], $("#nested-dialog-first")[0], "should be the first dialog");
483                                 start();
484                         }]);
485         });
486
487         asyncTest( "loading a relative file path after an embeded page works", function(){
488                 $.testHelper.pageSequence([
489                         // transition second page
490                         function(){ $.testHelper.openPage("#relative-after-embeded-page-first"); },
491
492                         // transition second page
493                         function(){ $("#relative-after-embeded-page-first a").click(); },
494
495                         // transition to the relative ajax loaded page
496                         function(){ $("#relative-after-embeded-page-second a").click(); },
497
498                         // make sure the page was loaded properly via ajax
499                         function(){
500                                 // data attribute intentionally left without namespace
501                                 same($(".ui-page-active").data("other"), "for testing", "should be relative ajax loaded page");
502                                 start();
503                         }]);
504         });
505
506         asyncTest( "Page title updates properly when clicking back to previous page", function(){
507                 $.testHelper.pageSequence([
508                         function(){
509                                 $.testHelper.openPage("#relative-after-embeded-page-first");
510                         },
511
512                         function(){
513                                 window.history.back();
514                         },
515
516                         function(){
517                                 same(document.title, "jQuery Mobile Navigation Test Suite");
518                                 start();
519                         }
520                 ]);
521         });
522
523         asyncTest( "Page title updates properly when clicking a link back to first page", function(){
524                 var title = document.title;
525
526                 $.testHelper.pageSequence([
527                         function(){
528                                 $.testHelper.openPage("#ajax-title-page");
529                         },
530
531                         function(){
532                                 $("#titletest1").click();
533                         },
534
535                         function(){
536                                 same(document.title, "Title Tag");
537                                 $.mobile.activePage.find("#title-check-link").click();
538                         },
539
540                         function(){
541                                 same(document.title, title);
542                                 start();
543                         }
544                 ]);
545         });
546
547         asyncTest( "Page title updates properly from title tag when loading an external page", function(){
548                 $.testHelper.pageSequence([
549                         function(){
550                                 $.testHelper.openPage("#ajax-title-page");
551                         },
552
553                         function(){
554                                 $("#titletest1").click();
555                         },
556
557                         function(){
558                                 same(document.title, "Title Tag");
559                                 start();
560                         }
561                 ]);
562         });
563
564         asyncTest( "Page title updates properly from data-title attr  when loading an external page", function(){
565                 $.testHelper.pageSequence([
566                         function(){
567                                 $.testHelper.openPage("#ajax-title-page");
568                         },
569
570                         function(){
571                                 $("#titletest2").click();
572                         },
573
574                         function(){
575                                 same(document.title, "Title Attr");
576                                 start();
577                         }
578                 ]);
579         });
580
581         asyncTest( "Page title updates properly from heading text in header when loading an external page", function(){
582                 $.testHelper.pageSequence([
583                         function(){
584                                 $.testHelper.openPage("#ajax-title-page");
585                         },
586
587                         function(){
588                                 $("#titletest3").click();
589                         },
590
591                         function(){
592                                 same(document.title, "Title Heading");
593                                 start();
594                         }
595                 ]);
596         });
597
598         asyncTest( "Page links to the current active page result in the same active page", function(){
599                 $.testHelper.pageSequence([
600                         function(){
601                                 $.testHelper.openPage("#self-link");
602                         },
603
604                         function(){
605                                 $("a[href='#self-link']").click();
606                         },
607
608                         function(){
609                                 same($.mobile.activePage[0], $("#self-link")[0], "self-link page is still the active page" );
610                                 start();
611                         }
612                 ]);
613         });
614
615         asyncTest( "links on subdirectory pages with query params append the params and load the page", function(){
616                 $.testHelper.pageSequence([
617                         function(){
618                                 $.testHelper.openPage("#data-url-tests/non-data-url.html");
619                         },
620
621                         function(){
622                                 $("#query-param-anchor").click();
623                         },
624
625                         function(){
626                                 $.testHelper.assertUrlLocation({
627                                         hashOrPush: home + "data-url-tests/non-data-url.html?foo=bar",
628                                         report: "the hash or url has query params"
629                                 });
630
631                                 ok($(".ui-page-active").jqmData("url").indexOf("?foo=bar") > -1, "the query params are in the data url");
632                                 start();
633                         }
634                 ]);
635         });
636
637         asyncTest( "identical query param link doesn't add additional set of query params", function(){
638                 $.testHelper.pageSequence([
639                         function(){
640                                 $.testHelper.openPage("#data-url-tests/non-data-url.html");
641                         },
642
643                         function(){
644                                 $("#query-param-anchor").click();
645                         },
646
647                         function(){
648                                 $.testHelper.assertUrlLocation({
649                                         hashOrPush: home + "data-url-tests/non-data-url.html?foo=bar",
650                                         report: "the hash or url has query params"
651                                 });
652
653                                 $("#query-param-anchor").click();
654                         },
655
656                         function(){
657                                 $.testHelper.assertUrlLocation({
658                                         hashOrPush: home + "data-url-tests/non-data-url.html?foo=bar",
659                                         report: "the hash or url still has query params"
660                                 });
661
662                                 start();
663                         }
664                 ]);
665         });
666
667         // Special handling inside navigation because query params must be applied to the hash
668         // or absolute reference and dialogs apply extra information int the hash that must be removed
669         asyncTest( "query param link from a dialog to itself should be a not add another dialog", function(){
670                 var firstDialogLoc;
671
672                 $.testHelper.pageSequence([
673                         // open our test page
674                         function(){
675                                 $.testHelper.openPage("#dialog-param-link");
676                         },
677
678                         // navigate to the subdirectory page with the query link
679                         function(){
680                                 $("#dialog-param-link a").click();
681                         },
682
683                         // navigate to the query param self reference link
684                         function(){
685                                 $("#dialog-param-link-page a").click();
686                         },
687
688                         // attempt to navigate to the same link
689                         function(){
690                                 // store the current hash for comparison (with one dialog hash key)
691                                 firstDialogLoc = location.hash || location.href;
692                                 $("#dialog-param-link-page a").click();
693                         },
694
695                         function(){
696                                 same(location.hash || location.href, firstDialogLoc, "additional dialog hash key not added");
697                                 start();
698                         }
699                 ]);
700         });
701
702         asyncTest( "query data passed as string to changePage is appended to URL", function(){
703                 $.testHelper.pageSequence([
704                         // open our test page
705                         function(){
706                                 $.mobile.changePage( "form-tests/changepage-data.html", {
707                                         data: "foo=1&bar=2"
708                                 } );
709                         },
710
711                         function(){
712                                 $.testHelper.assertUrlLocation({
713                                         hashOrPush: home + "form-tests/changepage-data.html?foo=1&bar=2",
714                                         report: "the hash or url still has query params"
715                                 });
716
717                                 start();
718                         }
719                 ]);
720         });
721
722         asyncTest( "query data passed as object to changePage is appended to URL", function(){
723                 $.testHelper.pageSequence([
724                         // open our test page
725                         function(){
726                                 $.mobile.changePage( "form-tests/changepage-data.html", {
727                                         data: {
728                                                 foo: 3,
729                                                 bar: 4
730                                         }
731                                 } );
732                         },
733
734                         function(){
735                                 $.testHelper.assertUrlLocation({
736                                         hashOrPush: home + "form-tests/changepage-data.html?foo=3&bar=4",
737                                         report: "the hash or url still has query params"
738                                 });
739
740                                 start();
741                         }
742                 ]);
743         });
744
745         asyncTest( "refresh of a dialog url should not duplicate page", function(){
746                 $.testHelper.pageSequence([
747                         // open our test page
748                         function(){
749                                 same($(".foo-class").length, 1, "should only have one instance of foo-class in the document");
750                                 location.hash = "#foo&ui-state=dialog";
751                         },
752
753                         function(){
754                                 $.testHelper.assertUrlLocation({
755                                         hash: "foo&ui-state=dialog",
756                                         push: home + "#foo&ui-state=dialog",
757                                         report: "hash should match what was loaded"
758                                 });
759
760                                 same( $(".foo-class").length, 1, "should only have one instance of foo-class in the document" );
761                                 start();
762                         }
763                 ]);
764         });
765
766         asyncTest( "internal form with no action submits to document URL", function(){
767                 $.testHelper.pageSequence([
768                         // open our test page
769                         function(){
770                                 $.testHelper.openPage("#internal-no-action-form-page");
771                         },
772
773                         function(){
774                                 $("#internal-no-action-form-page form").eq(0).submit();
775                         },
776
777                         function(){
778                                 $.testHelper.assertUrlLocation({
779                                         hashOrPush: home + "?foo=1&bar=2",
780                                         report: "hash should match what was loaded"
781                                 });
782
783                                 start();
784                         }
785                 ]);
786         });
787
788         asyncTest( "external page containing form with no action submits to page URL", function(){
789                 $.testHelper.pageSequence([
790                         // open our test page
791                         function(){
792                                 $.testHelper.openPage("#internal-no-action-form-page");
793                         },
794
795                         function(){
796                                 $("#internal-no-action-form-page a").eq(0).click();
797                         },
798
799                         function(){
800                                 $("#external-form-no-action-page form").eq(0).submit();
801                         },
802
803                         function(){
804                                 $.testHelper.assertUrlLocation({
805                                         hashOrPush: home + "form-tests/form-no-action.html?foo=1&bar=2",
806                                         report: "hash should match page url and not document url"
807                                 });
808
809                                 start();
810                         }
811                 ]);
812         });
813
814         asyncTest( "handling of active button state when navigating", 1, function(){
815
816                 $.testHelper.pageSequence([
817                         // open our test page
818                         function(){
819                                 $.testHelper.openPage("#active-state-page1");
820                         },
821
822                         function(){
823                                 $("#active-state-page1 a").eq(0).click();
824                         },
825
826                         function(){
827                                 $("#active-state-page2 a").eq(0).click();
828                         },
829
830                         function(){
831                                 ok(!$("#active-state-page1 a").hasClass( $.mobile.activeBtnClass ), "No button should not have class " + $.mobile.activeBtnClass );
832                                 start();
833                         }
834                 ]);
835         });
836
837         // issue 2444 https://github.com/jquery/jquery-mobile/issues/2444
838         // results from preventing spurious hash changes
839         asyncTest( "dialog should return to its parent page when open and closed multiple times", function() {
840                 $.testHelper.pageSequence([
841                         // open our test page
842                         function(){
843                                 $.testHelper.openPage("#default-trans-dialog");
844                         },
845
846                         function(){
847                                 $.mobile.activePage.find( "a" ).click();
848                         },
849
850                         function(){
851                                 window.history.back();
852                         },
853
854                         function(){
855                                 same( $.mobile.activePage[0], $( "#default-trans-dialog" )[0] );
856                                 $.mobile.activePage.find( "a" ).click();
857                         },
858
859                         function(){
860                                 window.history.back();
861                         },
862
863                         function(){
864                                 same( $.mobile.activePage[0], $( "#default-trans-dialog" )[0] );
865                                 start();
866                         }
867                 ]);
868         });
869
870         asyncTest( "clicks with middle mouse button are ignored", function() {
871                 $.testHelper.pageSequence([
872                         function() {
873                                 $.testHelper.openPage( "#odd-clicks-page" );
874                         },
875
876                         function() {
877                                 $( "#right-or-middle-click" ).click();
878                         },
879
880                         // make sure the page is opening first without the mocked button click value
881                         // only necessary to prevent issues with test specific fixtures
882                         function() {
883                                 same($.mobile.activePage[0], $("#odd-clicks-page-dest")[0]);
884                                 $.testHelper.openPage( "#odd-clicks-page" );
885
886                                 // mock the which value to simulate a middle click
887                                 $.Event.prototype.which = 2;
888                         },
889
890                         function() {
891                                 $( "#right-or-middle-click" ).click();
892                         },
893
894                         function( timeout ) {
895                                 ok( timeout, "page event handler timed out due to ignored click" );
896                                 ok($.mobile.activePage[0] !== $("#odd-clicks-page-dest")[0], "pages are not the same");
897                                 start();
898                         }
899                 ]);
900         });
901
902         asyncTest( "disabling link binding disables navigation via links and highlighting", function() {
903                 $.mobile.linkBindingEnabled = false;
904
905                 $.testHelper.pageSequence([
906                         function() {
907                                 $.testHelper.openPage("#bar");
908                         },
909
910                         function() {
911                                 $.mobile.activePage.find( "a" ).click();
912                         },
913
914                         function( timeout ) {
915                                 ok( !$.mobile.activePage.find( "a" ).hasClass( $.mobile.activeBtnClass ), "vlick handler doesn't add the activebtn class" );
916                                 ok( timeout, "no page change was fired" );
917                                 start();
918                         }
919                 ]);
920         });
921
922         asyncTest( "handling of button active state when navigating by clicking back button", 1, function(){
923                 $.testHelper.pageSequence([
924                         // open our test page
925                         function(){
926                                 $.testHelper.openPage("#active-state-page1");
927                         },
928
929                         function(){
930                                 $("#active-state-page1 a").eq(0).click();
931                         },
932
933                         function(){
934                                 $("#active-state-page2 a").eq(1).click();
935                         },
936
937                         function(){
938                                 $("#active-state-page1 a").eq(0).click();
939                         },
940
941                         function(){
942                                 ok(!$("#active-state-page2 a").hasClass( $.mobile.activeBtnClass ), "No button should not have class " + $.mobile.activeBtnClass );
943                                 start();
944                         }
945                 ]);
946         });
947
948         asyncTest( "can navigate to dynamically injected page with dynamically injected link", function(){
949                 $.testHelper.pageSequence([
950                         // open our test page
951                         function(){
952                                 $.testHelper.openPage("#inject-links-page");
953                         },
954
955                         function(){
956                                 var $ilpage = $( "#inject-links-page" ),
957                                         $link = $( "<a href='#injected-test-page'>injected-test-page link</a>" );
958
959                                 // Make sure we actually navigated to the expected page.
960                                 ok( $.mobile.activePage[ 0 ] == $ilpage[ 0 ], "navigated successfully to #inject-links-page" );
961
962                                 // Now dynamically insert a page.
963                                 $ilpage.parent().append( "<div data-role='page' id='injected-test-page'>testing...</div>" );
964
965                                 // Now inject a link to this page dynamically and attempt to navigate
966                                 // to the page we just inserted.
967                                 $link.appendTo( $ilpage ).click();
968                         },
969
970                         function(){
971                                 // Make sure we actually navigated to the expected page.
972                                 ok( $.mobile.activePage[ 0 ] == $( "#injected-test-page" )[ 0 ], "navigated successfully to #injected-test-page" );
973
974                                 start();
975                         }
976                 ]);
977         });
978
979         asyncTest( "application url with dialogHashKey loads application's first page", function(){
980                 $.testHelper.pageSequence([
981                         // open our test page
982                         function(){
983                                 // Navigate to any page except the first page of the application.
984                                 $.testHelper.openPage("#foo");
985                         },
986
987                         function(){
988                                 ok( $.mobile.activePage[ 0 ] === $( "#foo" )[ 0 ], "navigated successfully to #foo" );
989
990                                 // Now navigate to an hash that contains just a dialogHashKey.
991                                 $.mobile.changePage("#" + $.mobile.dialogHashKey);
992                         },
993
994                         function(){
995                                 // Make sure we actually navigated to the first page.
996                                 ok( $.mobile.activePage[ 0 ] === $.mobile.firstPage[ 0 ], "navigated successfully to first-page" );
997
998                                 // Now make sure opening the page didn't result in page duplication.
999                                 ok( $.mobile.firstPage.hasClass( "first-page" ), "first page has expected class" );
1000                                 same( $( ".first-page" ).length, 1, "first page was not duplicated" );
1001
1002                                 start();
1003                         }
1004                 ]);
1005         });
1006
1007         asyncTest( "navigate to non-existent internal page throws pagechangefailed", function(){
1008                 var pagechangefailed = false,
1009                         pageChangeFailedCB = function( e ) {
1010                         pagechangefailed = true;
1011                 }
1012
1013                 $( document ).bind( "pagechangefailed", pageChangeFailedCB );
1014
1015                 $.testHelper.pageSequence([
1016                         // open our test page
1017                         function(){
1018                                 // Make sure there's only one copy of the first-page in the DOM to begin with.
1019                                 ok( $.mobile.firstPage.hasClass( "first-page" ), "first page has expected class" );
1020                                 same( $( ".first-page" ).length, 1, "first page was not duplicated" );
1021
1022                                 // Navigate to any page except the first page of the application.
1023                                 $.testHelper.openPage("#foo");
1024                         },
1025
1026                         function(){
1027                                 var $foo = $( "#foo" );
1028                                 ok( $.mobile.activePage[ 0 ] === $foo[ 0 ], "navigated successfully to #foo" );
1029                                 same( pagechangefailed, false, "no page change failures" );
1030
1031                                 // Now navigate to a non-existent page.
1032                                 $foo.find( "#bad-internal-page-link" ).click();
1033                         },
1034
1035                         function(){
1036                                 // Make sure a pagechangefailed event was triggered.
1037                                 same( pagechangefailed, true, "pagechangefailed dispatched" );
1038
1039                                 // Make sure we didn't navigate away from #foo.
1040                                 ok( $.mobile.activePage[ 0 ] === $( "#foo" )[ 0 ], "did not navigate away from #foo" );
1041
1042                                 // Now make sure opening the page didn't result in page duplication.
1043                                 same( $( ".first-page" ).length, 1, "first page was not duplicated" );
1044
1045                                 $( document ).unbind( "pagechangefailed", pageChangeFailedCB );
1046
1047                                 start();
1048                         }
1049                 ]);
1050         });
1051
1052         asyncTest( "prefetched links with data rel dialog result in a dialog", function() {
1053                 $.testHelper.pageSequence([
1054                         // open our test page
1055                         function(){
1056                                 // Navigate to any page except the first page of the application.
1057                                 $.testHelper.openPage("#prefetched-dialog-page");
1058                         },
1059
1060                         function() {
1061                                 $("#prefetched-dialog-link").click();
1062                         },
1063
1064                         function() {
1065                                 ok( $.mobile.activePage.is(".ui-dialog"), "prefetched page is rendered as a dialog" );
1066         start();
1067                         }
1068                 ]);
1069         });
1070
1071         asyncTest( "first page gets reloaded if pruned from the DOM", function(){
1072                 var hideCallbackTriggered = false;
1073
1074                 function hideCallback( e, data )
1075                 {
1076                         var page = e.target;
1077                         ok( ( page === $.mobile.firstPage[ 0 ] ), "hide called with prevPage set to firstPage");
1078                         if ( page === $.mobile.firstPage[ 0 ] ) {
1079                                  $( page ).remove();
1080                         }
1081                         hideCallbackTriggered = true;
1082                 }
1083
1084                 $(document).bind('pagehide', hideCallback);
1085
1086                 $.testHelper.pageSequence([
1087                         function(){
1088                                 // Make sure the first page is actually in the DOM.
1089                                 ok( $.mobile.firstPage.parent().length !== 0, "first page is currently in the DOM" );
1090
1091                                 // Make sure the first page is the active page.
1092                                 ok( $.mobile.activePage[ 0 ] === $.mobile.firstPage[ 0 ], "first page is the active page" );
1093
1094                                 // Now make sure the first page has an id that we can use to reload it.
1095                                 ok( $.mobile.firstPage[ 0 ].id, "first page has an id" );
1096
1097                                 // Make sure there is only one first page in the DOM.
1098                                 same( $( ".first-page" ).length, 1, "only one instance of the first page in the DOM" );
1099
1100                                 // Navigate to any page except the first page of the application.
1101                                 $.testHelper.openPage("#foo");
1102                         },
1103
1104                         function(){
1105                                 // Make sure the active page is #foo.
1106                                 ok( $.mobile.activePage[ 0 ] === $( "#foo" )[ 0 ], "navigated successfully to #foo" );
1107
1108                                 // Make sure our hide callback was triggered.
1109                                 ok( hideCallbackTriggered, "hide callback was triggered" );
1110
1111                                 // Make sure the first page was actually pruned from the document.
1112                                 ok( $.mobile.firstPage.parent().length === 0, "first page was pruned from the DOM" );
1113                                 same( $( ".first-page" ).length, 0, "no instance of the first page in the DOM" );
1114
1115                                 // Remove our hideCallback.
1116                                 $(document).unbind('pagehide', hideCallback);
1117
1118                                 // Navigate back to the first page!
1119                                 $.testHelper.openPage( "#" + $.mobile.firstPage[0].id );
1120                         },
1121
1122                         function(){
1123                                 var firstPage = $( ".first-page" );
1124
1125                                 // We should only have one first page in the document at any time!
1126                                 same( firstPage.length, 1, "single instance of first page recreated in the DOM" );
1127
1128                                 // Make sure the first page in the DOM is actually a different DOM element than the original
1129                                 // one we started with.
1130                                 ok( $.mobile.firstPage[ 0 ] !== firstPage[ 0 ], "first page is a new DOM element");
1131
1132                                 // Make sure we actually navigated to the new first page.
1133                                 ok( $.mobile.activePage[ 0 ] === firstPage[ 0 ], "navigated successfully to new first-page");
1134
1135                                 // Reset the $.mobile.firstPage for subsequent tests.
1136                                 // XXX: Should we just get rid of the new one and restore the old?
1137                                 $.mobile.firstPage = $.mobile.activePage;
1138
1139                                 start();
1140                         }
1141                 ]);
1142         });
1143 })(jQuery);