PageRenderTime 47ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 1ms

/tests/integration/navigation/navigation_core.js

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