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